Merge branch 'feature/on-record-ts' of https://github.com/wasphin/ZLMediaKit into pr

This commit is contained in:
xiongziliang 2020-09-20 11:01:45 +08:00
commit 0fce108de5
12 changed files with 147 additions and 31 deletions

View File

@ -18,64 +18,64 @@
#include "Rtsp/RtspSession.h"
using namespace mediakit;
///////////////////////////////////////////MP4Info/////////////////////////////////////////////
///////////////////////////////////////////RecordInfo/////////////////////////////////////////////
API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->ui64StartedTime;
}
API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->ui64TimeLen;
}
API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->ui64FileSize;
}
API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strFilePath.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strFileName.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strFolder.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strUrl.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strVhost.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strAppName.c_str();
}
API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){
assert(ctx);
MP4Info *info = (MP4Info *)ctx;
RecordInfo *info = (RecordInfo *)ctx;
return info->strStreamId.c_str();
}

View File

@ -66,6 +66,8 @@ segDur=2
segNum=3
#HLS切片从m3u8文件中移除后继续保留在磁盘上的个数
segRetain=5
# 是否广播 ts 切片完成通知
broadcastRecordTs=0
[hook]
#在推流时如果url参数匹对admin_params那么可以不经过hook鉴权直接推流成功播放时亦然
@ -83,6 +85,8 @@ on_play=https://127.0.0.1/index/hook/on_play
on_publish=https://127.0.0.1/index/hook/on_publish
#录制mp4切片完成事件
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
# 录制 hls ts 切片完成事件
on_record_ts=https://127.0.0.1/index/hook/on_record_ts
#rtsp播放鉴权事件此事件中比对rtsp的用户名密码
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
#rtsp播放是否开启专属鉴权事件置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权

View File

@ -53,6 +53,7 @@ const string kOnRtspAuth = HOOK_FIELD"on_rtsp_auth";
const string kOnStreamChanged = HOOK_FIELD"on_stream_changed";
const string kOnStreamNotFound = HOOK_FIELD"on_stream_not_found";
const string kOnRecordMp4 = HOOK_FIELD"on_record_mp4";
const string kOnRecordTs = HOOK_FIELD"on_record_ts";
const string kOnShellLogin = HOOK_FIELD"on_shell_login";
const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader";
const string kOnHttpAccess = HOOK_FIELD"on_http_access";
@ -70,6 +71,7 @@ onceToken token([](){
mINI::Instance()[kOnStreamChanged] = "https://127.0.0.1/index/hook/on_stream_changed";
mINI::Instance()[kOnStreamNotFound] = "https://127.0.0.1/index/hook/on_stream_not_found";
mINI::Instance()[kOnRecordMp4] = "https://127.0.0.1/index/hook/on_record_mp4";
mINI::Instance()[kOnRecordTs] = "https://127.0.0.1/index/hook/on_record_ts";
mINI::Instance()[kOnShellLogin] = "https://127.0.0.1/index/hook/on_shell_login";
mINI::Instance()[kOnStreamNoneReader] = "https://127.0.0.1/index/hook/on_stream_none_reader";
mINI::Instance()[kOnHttpAccess] = "https://127.0.0.1/index/hook/on_http_access";
@ -190,6 +192,7 @@ void installWebHook(){
GET_CONFIG(string,hook_stream_chaned,Hook::kOnStreamChanged);
GET_CONFIG(string,hook_stream_not_found,Hook::kOnStreamNotFound);
GET_CONFIG(string,hook_record_mp4,Hook::kOnRecordMp4);
GET_CONFIG(string,hook_record_ts,Hook::kOnRecordTs);
GET_CONFIG(string,hook_shell_login,Hook::kOnShellLogin);
GET_CONFIG(string,hook_stream_none_reader,Hook::kOnStreamNoneReader);
GET_CONFIG(string,hook_http_access,Hook::kOnHttpAccess);
@ -361,6 +364,25 @@ void installWebHook(){
});
#endif //ENABLE_MP4
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) {
if (!hook_enable || hook_record_ts.empty()) {
return;
}
ArgsType body;
body["start_time"] = (Json::UInt64)info.ui64StartedTime;
body["time_len"] = (Json::UInt64)info.ui64TimeLen;
body["file_size"] = (Json::UInt64)info.ui64FileSize;
body["file_path"] = info.strFilePath;
body["file_name"] = info.strFileName;
body["folder"] = info.strFolder;
body["url"] = info.strUrl;
body["app"] = info.strAppName;
body["stream"] = info.strStreamId;
body["vhost"] = info.strVhost;
// 执行 hook
do_http_hook(hook_record_ts, body, nullptr);
});
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){
invoker("");

View File

@ -40,6 +40,7 @@ bool loadIniConfig(const char *ini_path){
namespace Broadcast {
const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
const string kBroadcastRecordTs = "kBroadcastRecoredTs";
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";
const string kBroadcastOnGetRtspRealm = "kBroadcastOnGetRtspRealm";
@ -251,6 +252,8 @@ const string kSegmentRetain = HLS_FIELD"segRetain";
const string kFileBufSize = HLS_FIELD"fileBufSize";
//录制文件路径
const string kFilePath = HLS_FIELD"filePath";
// 是否广播 ts 切片完成通知
const string kBroadcastRecordTs = HLS_FIELD"broadcastRecordTs";
onceToken token([](){
mINI::Instance()[kSegmentDuration] = 2;
@ -258,6 +261,7 @@ onceToken token([](){
mINI::Instance()[kSegmentRetain] = 5;
mINI::Instance()[kFileBufSize] = 64 * 1024;
mINI::Instance()[kFilePath] = "./www";
mINI::Instance()[kBroadcastRecordTs] = false;
},nullptr);
} //namespace Hls

View File

@ -58,7 +58,11 @@ extern const string kBroadcastMediaChanged;
//录制mp4文件成功后广播
extern const string kBroadcastRecordMP4;
#define BroadcastRecordMP4Args const MP4Info &info
#define BroadcastRecordMP4Args const RecordInfo &info
// 录制 ts 文件后广播
extern const string kBroadcastRecordTs;
#define BroadcastRecordTsArgs const RecordInfo &info
//收到http api请求广播
extern const string kBroadcastHttpRequest;
@ -281,6 +285,8 @@ extern const string kSegmentRetain;
extern const string kFileBufSize;
//录制文件路径
extern const string kFilePath;
// 是否广播 ts 切片完成通知
extern const string kBroadcastRecordTs;
} //namespace Hls
////////////Rtp代理相关配置///////////

View File

@ -121,6 +121,11 @@ void HlsMaker::flushLastSegment(bool eof){
delOldSegment();
makeIndexFile(eof);
_last_file_name.clear();
onFlushLastSegment(seg_dur);
}
void HlsMaker::onFlushLastSegment(uint32_t) {
}
bool HlsMaker::isLive() {

View File

@ -22,6 +22,20 @@ using namespace toolkit;
namespace mediakit {
class TsInfo {
public:
time_t ui64StartedTime; // GMT 标准时间,单位秒
time_t ui64TimeLen; // 录像长度,单位毫秒
off_t ui64FileSize; // 文件大小,单位 BYTE
string strFilePath; // 文件路径
string strFileName; // 文件名称
string strFolder; // 文件夹路径
string strUrl; // 播放路径
string strAppName; // 应用名称
string strStreamId; // 流 ID
string strVhost; // vhost
};
class HlsMaker {
public:
/**
@ -84,6 +98,12 @@ protected:
*/
void flushLastSegment(bool eof);
/**
* ts ,
* @param duration ts ,
*/
virtual void onFlushLastSegment(uint32_t duration);
private:
/**
* m3u8文件

View File

@ -8,7 +8,10 @@
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <ctime>
#include <sys/stat.h>
#include "HlsMakerImp.h"
#include "Thread/WorkThreadPool.h"
#include "Util/util.h"
#include "Util/uv_errno.h"
@ -28,6 +31,8 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
_file_buf.reset(new char[bufSize], [](char *ptr) {
delete[] ptr;
});
_info.strFolder = _path_prefix;
}
HlsMakerImp::~HlsMakerImp() {
@ -59,13 +64,19 @@ string HlsMakerImp::onOpenSegment(int index) {
}
}
_file = makeFile(segment_path, true);
_info.ui64StartedTime = ::time(NULL);
_info.strFileName = segment_name;
_info.strFilePath = segment_path;
_info.strUrl = _info.strAppName + "/" + _info.strStreamId + "/" + segment_name;
if (!_file) {
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
}
if (_params.empty()) {
return std::move(segment_name);
return segment_name;
}
return std::move(segment_name + "?" + _params);
return segment_name + "?" + _params;
}
void HlsMakerImp::onDelSegment(int index) {
@ -97,6 +108,21 @@ void HlsMakerImp::onWriteHls(const char *data, int len) {
//DebugL << "\r\n" << string(data,len);
}
void HlsMakerImp::onFlushLastSegment(uint32_t duration) {
GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs);
if (broadcastRecordTs) {
auto info = _info;
info.ui64TimeLen = duration;
WorkThreadPool::Instance().getExecutor()->async([info]() {
struct stat fileData;
stat(info.strFilePath.data(), &fileData);
const_cast<RecordInfo&>(info).ui64FileSize = fileData.st_size;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, info);
});
}
}
std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file, bool setbuf) {
auto file_buf = _file_buf;
@ -113,6 +139,9 @@ std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file, bool setbuf) {
void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_media_src = std::make_shared<HlsMediaSource>(vhost, app, stream_id);
_info.strAppName = app;
_info.strStreamId = stream_id;
_info.strVhost = vhost;
}
HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {

View File

@ -16,6 +16,8 @@
#include <stdlib.h>
#include "HlsMaker.h"
#include "HlsMediaSource.h"
#include "RecordInfo.h"
using namespace std;
namespace mediakit {
@ -54,6 +56,7 @@ protected:
void onDelSegment(int index) override;
void onWriteSegment(const char *data, int len) override;
void onWriteHls(const char *data, int len) override;
void onFlushLastSegment(uint32_t duration) override;
private:
std::shared_ptr<FILE> makeFile(const string &file,bool setbuf = false);
@ -66,6 +69,7 @@ private:
std::shared_ptr<FILE> _file;
std::shared_ptr<char> _file_buf;
HlsMediaSource::Ptr _media_src;
RecordInfo _info;
map<int /*index*/,string/*file_path*/> _segment_file_paths;
};

View File

@ -73,7 +73,7 @@ void MP4Recorder::asyncClose() {
auto info = _info;
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
//获取文件录制时间放在关闭mp4之前是为了忽略关闭mp4执行时间
const_cast<MP4Info&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
const_cast<RecordInfo&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime;
//关闭mp4非常耗时所以要放在后台线程执行
muxer->closeMP4();
//临时文件名改成正式文件名防止mp4未完成时被访问
@ -81,7 +81,7 @@ void MP4Recorder::asyncClose() {
//获取文件大小
struct stat fileData;
stat(strFile.data(), &fileData);
const_cast<MP4Info&>(info).ui64FileSize = fileData.st_size;
const_cast<RecordInfo&>(info).ui64FileSize = fileData.st_size;
/////record 业务逻辑//////
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
});

View File

@ -20,24 +20,12 @@
#include "Util/TimeTicker.h"
#include "Common/MediaSink.h"
#include "MP4Muxer.h"
#include "RecordInfo.h"
using namespace toolkit;
namespace mediakit {
class MP4Info {
public:
time_t ui64StartedTime; //GMT标准时间单位秒
time_t ui64TimeLen;//录像长度,单位秒
off_t ui64FileSize;//文件大小单位BYTE
string strFilePath;//文件路径
string strFileName;//文件名称
string strFolder;//文件夹路径
string strUrl;//播放路径
string strAppName;//应用名称
string strStreamId;//流ID
string strVhost;//vhost
};
#ifdef ENABLE_MP4
class MP4Recorder : public MediaSinkInterface{
public:
@ -72,7 +60,7 @@ private:
string _strFile;
string _strFileTmp;
Ticker _createFileTicker;
MP4Info _info;
RecordInfo _info;
bool _haveVideo = false;
MP4Muxer::Ptr _muxer;
list<Track::Ptr> _tracks;

34
src/Record/RecordInfo.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef RECORDINFO_H_
#define RECORDINFO_H_
#include "Common/config.h"
namespace mediakit {
class RecordInfo {
public:
time_t ui64StartedTime; // GMT 标准时间,单位秒
time_t ui64TimeLen; // 录像长度,需要注意 mp4 单位是秒,而 hls ts 单位是毫秒
off_t ui64FileSize; // 文件大小,单位 BYTE
string strFilePath; // 文件路径
string strFileName; // 文件名称
string strFolder; // 文件夹路径
string strUrl; // 播放路径
string strAppName; // 应用名称
string strStreamId; // 流 ID
string strVhost; // vhost
};
} // namespace mediakit
#endif // RECORDINFO_H_