合并pr(添加录制ts完成通知): #479

This commit is contained in:
xiongziliang 2020-09-20 11:40:42 +08:00
parent 0fce108de5
commit f84981dc75
11 changed files with 94 additions and 145 deletions

View File

@ -19,25 +19,25 @@ extern "C" {
///////////////////////////////////////////MP4Info///////////////////////////////////////////// ///////////////////////////////////////////MP4Info/////////////////////////////////////////////
//MP4Info对象的C映射 //MP4Info对象的C映射
typedef void* mk_mp4_info; typedef void* mk_mp4_info;
//MP4Info::ui64StartedTime // GMT 标准时间,单位秒
API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx); API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx);
//MP4Info::ui64TimeLen // 录像长度,单位秒
API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx); API_EXPORT float API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx);
//MP4Info::ui64FileSize // 文件大小,单位 BYTE
API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx); API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx);
//MP4Info::strFilePath // 文件路径
API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx);
//MP4Info::strFileName // 文件名称
API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx);
//MP4Info::strFolder // 文件夹路径
API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx);
//MP4Info::strUrl // 播放路径
API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx);
//MP4Info::strVhost // 应用名称
API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx);
//MP4Info::strAppName // 流 ID
API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx);
//MP4Info::strStreamId // 虚拟主机
API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx); API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx);
///////////////////////////////////////////Parser///////////////////////////////////////////// ///////////////////////////////////////////Parser/////////////////////////////////////////////

View File

@ -22,61 +22,61 @@ using namespace mediakit;
API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx){ API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->ui64StartedTime; return info->start_time;
} }
API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){ API_EXPORT float API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->ui64TimeLen; return info->time_len;
} }
API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx){ API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->ui64FileSize; return info->file_size;
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strFilePath.c_str(); return info->file_path.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strFileName.c_str(); return info->file_name.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strFolder.c_str(); return info->folder.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strUrl.c_str(); return info->url.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strVhost.c_str(); return info->vhost.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strAppName.c_str(); return info->app.c_str();
} }
API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){ API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){
assert(ctx); assert(ctx);
RecordInfo *info = (RecordInfo *)ctx; RecordInfo *info = (RecordInfo *)ctx;
return info->strStreamId.c_str(); return info->stream.c_str();
} }
///////////////////////////////////////////Parser///////////////////////////////////////////// ///////////////////////////////////////////Parser/////////////////////////////////////////////

View File

@ -342,25 +342,29 @@ void installWebHook(){
do_http_hook(hook_stream_not_found,body, nullptr); do_http_hook(hook_stream_not_found,body, nullptr);
}); });
static auto getRecordInfo = [](const RecordInfo &info) {
ArgsType body;
body["start_time"] = (Json::UInt64) info.start_time;
body["file_size"] = (Json::UInt64) info.file_size;
body["time_len"] = info.time_len;
body["file_path"] = info.file_path;
body["file_name"] = info.file_name;
body["folder"] = info.folder;
body["url"] = info.url;
body["app"] = info.app;
body["stream"] = info.stream;
body["vhost"] = info.vhost;
return body;
};
#ifdef ENABLE_MP4 #ifdef ENABLE_MP4
//录制mp4文件成功后广播 //录制mp4文件成功后广播
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){
if(!hook_enable || hook_record_mp4.empty()){ if (!hook_enable || hook_record_mp4.empty()) {
return; 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 //执行hook
do_http_hook(hook_record_mp4,body, nullptr); do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr);
}); });
#endif //ENABLE_MP4 #endif //ENABLE_MP4
@ -368,19 +372,8 @@ void installWebHook(){
if (!hook_enable || hook_record_ts.empty()) { if (!hook_enable || hook_record_ts.empty()) {
return; 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 // 执行 hook
do_http_hook(hook_record_ts, body, nullptr); do_http_hook(hook_record_ts, getRecordInfo(info), nullptr);
}); });
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
@ -421,7 +414,6 @@ void installWebHook(){
} }
strongSrc->close(false); strongSrc->close(false);
}); });
}); });
/** /**

View File

@ -118,16 +118,12 @@ void HlsMaker::flushLastSegment(bool eof){
seg_dur = 100; seg_dur = 100;
} }
_seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name))); _seg_dur_list.push_back(std::make_tuple(seg_dur, std::move(_last_file_name)));
_last_file_name.clear();
delOldSegment(); delOldSegment();
makeIndexFile(eof); makeIndexFile(eof);
_last_file_name.clear();
onFlushLastSegment(seg_dur); onFlushLastSegment(seg_dur);
} }
void HlsMaker::onFlushLastSegment(uint32_t) {
}
bool HlsMaker::isLive() { bool HlsMaker::isLive() {
return _seg_number != 0; return _seg_number != 0;
} }

View File

@ -22,20 +22,6 @@ using namespace toolkit;
namespace mediakit { 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 { class HlsMaker {
public: public:
/** /**
@ -92,18 +78,18 @@ protected:
*/ */
virtual void onWriteHls(const char *data, int len) = 0; virtual void onWriteHls(const char *data, int len) = 0;
/**
* ts ,
* @param duration_ms ts ,
*/
virtual void onFlushLastSegment(uint32_t duration_ms) {};
/** /**
* ts切片并且写入m3u8索引 * ts切片并且写入m3u8索引
* @param eof HLS直播是否已结束 * @param eof HLS直播是否已结束
*/ */
void flushLastSegment(bool eof); void flushLastSegment(bool eof);
/**
* ts ,
* @param duration ts ,
*/
virtual void onFlushLastSegment(uint32_t duration);
private: private:
/** /**
* m3u8文件 * m3u8文件

View File

@ -11,7 +11,6 @@
#include <ctime> #include <ctime>
#include <sys/stat.h> #include <sys/stat.h>
#include "HlsMakerImp.h" #include "HlsMakerImp.h"
#include "Thread/WorkThreadPool.h"
#include "Util/util.h" #include "Util/util.h"
#include "Util/uv_errno.h" #include "Util/uv_errno.h"
@ -32,7 +31,7 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file,
delete[] ptr; delete[] ptr;
}); });
_info.strFolder = _path_prefix; _info.folder = _path_prefix;
} }
HlsMakerImp::~HlsMakerImp() { HlsMakerImp::~HlsMakerImp() {
@ -65,10 +64,11 @@ string HlsMakerImp::onOpenSegment(int index) {
} }
_file = makeFile(segment_path, true); _file = makeFile(segment_path, true);
_info.ui64StartedTime = ::time(NULL); //保存本切片的元数据
_info.strFileName = segment_name; _info.start_time = ::time(NULL);
_info.strFilePath = segment_path; _info.file_name = segment_name;
_info.strUrl = _info.strAppName + "/" + _info.strStreamId + "/" + segment_name; _info.file_path = segment_path;
_info.url = _info.app + "/" + _info.stream + "/" + segment_name;
if (!_file) { if (!_file) {
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg(); WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
@ -108,22 +108,19 @@ void HlsMakerImp::onWriteHls(const char *data, int len) {
//DebugL << "\r\n" << string(data,len); //DebugL << "\r\n" << string(data,len);
} }
void HlsMakerImp::onFlushLastSegment(uint32_t duration) { void HlsMakerImp::onFlushLastSegment(uint32_t duration_ms) {
GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs); GET_CONFIG(bool, broadcastRecordTs, Hls::kBroadcastRecordTs);
if (broadcastRecordTs) { if (broadcastRecordTs) {
auto info = _info; //关闭ts文件以便获取正确的文件大小
info.ui64TimeLen = duration; _file = nullptr;
WorkThreadPool::Instance().getExecutor()->async([info]() { _info.time_len = duration_ms / 1000.0;
struct stat fileData; struct stat fileData;
stat(info.strFilePath.data(), &fileData); stat(_info.file_path.data(), &fileData);
const_cast<RecordInfo&>(info).ui64FileSize = fileData.st_size; _info.file_size = fileData.st_size;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, info); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordTs, _info);
});
} }
} }
std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file, bool setbuf) { std::shared_ptr<FILE> HlsMakerImp::makeFile(const string &file, bool setbuf) {
auto file_buf = _file_buf; auto file_buf = _file_buf;
auto ret = shared_ptr<FILE>(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) { auto ret = shared_ptr<FILE>(File::create_file(file.data(), "wb"), [file_buf](FILE *fp) {
@ -139,9 +136,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) { void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_media_src = std::make_shared<HlsMediaSource>(vhost, app, stream_id); _media_src = std::make_shared<HlsMediaSource>(vhost, app, stream_id);
_info.strAppName = app; _info.app = app;
_info.strStreamId = stream_id; _info.stream = stream_id;
_info.strVhost = vhost; _info.vhost = vhost;
} }
HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const { HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {

View File

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

View File

@ -25,10 +25,10 @@ MP4Recorder::MP4Recorder(const string& strPath,
const string &strStreamId) { const string &strStreamId) {
_strPath = strPath; _strPath = strPath;
/////record 业务逻辑////// /////record 业务逻辑//////
_info.strAppName = strApp; _info.app = strApp;
_info.strStreamId = strStreamId; _info.stream = strStreamId;
_info.strVhost = strVhost; _info.vhost = strVhost;
_info.strFolder = strPath; _info.folder = strPath;
} }
MP4Recorder::~MP4Recorder() { MP4Recorder::~MP4Recorder() {
closeFile(); closeFile();
@ -42,15 +42,15 @@ void MP4Recorder::createFile() {
auto strFile = _strPath + strDate + "/" + strTime + ".mp4"; auto strFile = _strPath + strDate + "/" + strTime + ".mp4";
/////record 业务逻辑////// /////record 业务逻辑//////
_info.ui64StartedTime = ::time(NULL); _info.start_time = ::time(NULL);
_info.strFileName = strTime + ".mp4"; _info.file_name = strTime + ".mp4";
_info.strFilePath = strFile; _info.file_path = strFile;
GET_CONFIG(string,appName,Record::kAppName); GET_CONFIG(string,appName,Record::kAppName);
_info.strUrl = appName + "/" _info.url = appName + "/"
+ _info.strAppName + "/" + _info.app + "/"
+ _info.strStreamId + "/" + _info.stream + "/"
+ strDate + "/" + strDate + "/"
+ strTime + ".mp4"; + strTime + ".mp4";
try { try {
_muxer = std::make_shared<MP4Muxer>(strFileTmp.data()); _muxer = std::make_shared<MP4Muxer>(strFileTmp.data());
@ -73,7 +73,7 @@ void MP4Recorder::asyncClose() {
auto info = _info; auto info = _info;
WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() { WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
//获取文件录制时间放在关闭mp4之前是为了忽略关闭mp4执行时间 //获取文件录制时间放在关闭mp4之前是为了忽略关闭mp4执行时间
const_cast<RecordInfo&>(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime; const_cast<RecordInfo&>(info).time_len = ::time(NULL) - info.start_time;
//关闭mp4非常耗时所以要放在后台线程执行 //关闭mp4非常耗时所以要放在后台线程执行
muxer->closeMP4(); muxer->closeMP4();
//临时文件名改成正式文件名防止mp4未完成时被访问 //临时文件名改成正式文件名防止mp4未完成时被访问
@ -81,7 +81,7 @@ void MP4Recorder::asyncClose() {
//获取文件大小 //获取文件大小
struct stat fileData; struct stat fileData;
stat(strFile.data(), &fileData); stat(strFile.data(), &fileData);
const_cast<RecordInfo&>(info).ui64FileSize = fileData.st_size; const_cast<RecordInfo&>(info).file_size = fileData.st_size;
/////record 业务逻辑////// /////record 业务逻辑//////
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
}); });

View File

@ -20,7 +20,6 @@
#include "Util/TimeTicker.h" #include "Util/TimeTicker.h"
#include "Common/MediaSink.h" #include "Common/MediaSink.h"
#include "MP4Muxer.h" #include "MP4Muxer.h"
#include "RecordInfo.h"
using namespace toolkit; using namespace toolkit;

View File

@ -1,34 +0,0 @@
/*
* 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_

View File

@ -16,6 +16,20 @@ using namespace std;
namespace mediakit { namespace mediakit {
class MediaSinkInterface; class MediaSinkInterface;
class RecordInfo {
public:
time_t start_time; // GMT 标准时间,单位秒
float time_len; // 录像长度,单位秒
off_t file_size; // 文件大小,单位 BYTE
string file_path; // 文件路径
string file_name; // 文件名称
string folder; // 文件夹路径
string url; // 播放路径
string app; // 应用名称
string stream; // 流 ID
string vhost; // 虚拟主机
};
class Recorder{ class Recorder{
public: public:
typedef enum { typedef enum {