Pre Merge pull request !24 from yinxiang/hls_record

This commit is contained in:
yinxiang 2023-01-10 09:11:04 +00:00 committed by Gitee
commit 30f4a267c2
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
11 changed files with 784 additions and 572 deletions

View File

@ -94,6 +94,7 @@ maxStreamWaitMS=15000
#某个流无人观看时触发hook.on_stream_none_reader事件的最大等待时间单位毫秒 #某个流无人观看时触发hook.on_stream_none_reader事件的最大等待时间单位毫秒
#在配合hook.on_stream_none_reader事件时可以做到无人观看自动停止拉流或停止接收推流 #在配合hook.on_stream_none_reader事件时可以做到无人观看自动停止拉流或停止接收推流
streamNoneReaderDelayMS=20000 streamNoneReaderDelayMS=20000
noRecordStreamNoneReaderDelayMS=130000
#拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, #拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
#如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) #如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
resetWhenRePlay=1 resetWhenRePlay=1

View File

@ -662,11 +662,17 @@ void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
//没有任何人观看该视频源,表明该源可以关闭了 //没有任何人观看该视频源,表明该源可以关闭了
GET_CONFIG(string, record_app, Record::kAppName); GET_CONFIG(string, record_app, Record::kAppName);
GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS); GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS);
GET_CONFIG(int, no_record_stream_none_reader_delay, General::kNoRecordStreamNoneReaderDelayMS);
//如果mp4点播, 无人观看时我们强制关闭点播 //如果mp4点播, 无人观看时我们强制关闭点播
bool is_mp4_vod = sender.getApp() == record_app; bool is_mp4_vod = sender.getApp() == record_app;
weak_ptr<MediaSource> weak_sender = sender.shared_from_this(); weak_ptr<MediaSource> weak_sender = sender.shared_from_this();
_async_close_timer = std::make_shared<Timer>(stream_none_reader_delay / 1000.0f, [weak_sender, is_mp4_vod]() { if(sender.isRecording(Recorder::type_hls)) {//如果正在录像
WarnL << "************The stream is Recording.*************";
WarnL << sender.getUrl();
_async_close_timer = std::make_shared<Timer>(
stream_none_reader_delay / 1000.0f,
[weak_sender, is_mp4_vod]() {
auto strong_sender = weak_sender.lock(); auto strong_sender = weak_sender.lock();
if (!strong_sender) { if (!strong_sender) {
//对象已经销毁 //对象已经销毁
@ -687,7 +693,42 @@ void MediaSourceEvent::onReaderChanged(MediaSource &sender, int size){
strong_sender->close(false); strong_sender->close(false);
} }
return false; return false;
}, nullptr); },
nullptr);
} else {//没有录像的话
WarnL << "************The stream is Not Recording.*************";
WarnL << sender.getUrl();
_async_close_timer = std::make_shared<Timer>(
no_record_stream_none_reader_delay / 1000.0f,
[weak_sender, is_mp4_vod]() {
auto strong_sender = weak_sender.lock();
if (strong_sender->isRecording(Recorder::type_hls)) {
return false;
}
if (!strong_sender) {
//对象已经销毁
return false;
}
if (strong_sender->totalReaderCount()) {
//还有人观看该视频,不触发关闭事件
return false;
}
if (!is_mp4_vod) {
//直播时触发无人观看事件,让开发者自行选择是否关闭
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastStreamNoneReader, *strong_sender);
} else {
//这个是mp4点播我们自动关闭
WarnL << "MP4点播无人观看,自动关闭:" << strong_sender->getUrl();
strong_sender->close(false);
}
return false;
},
nullptr);
}
} }
string MediaSourceEvent::getOriginUrl(MediaSource &sender) const { string MediaSourceEvent::getOriginUrl(MediaSource &sender) const {

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* *
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
@ -192,8 +192,9 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
}); });
switch (type) { switch (type) {
case Recorder::type_hls : { case Recorder::type_hls : {
if (start && !_hls) { if (!_hls)
//开始录制 {
//创建hls对象
_option.hls_save_path = custom_path; _option.hls_save_path = custom_path;
auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, _option)); auto hls = dynamic_pointer_cast<HlsRecorder>(makeRecorder(sender, getTracks(), type, _option));
if (hls) { if (hls) {
@ -201,10 +202,8 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
hls->setListener(shared_from_this()); hls->setListener(shared_from_this());
} }
_hls = hls; _hls = hls;
} else if (!start && _hls) {
//停止录制
_hls = nullptr;
} }
_hls->startRecord(start);
return true; return true;
} }
case Recorder::type_mp4 : { case Recorder::type_mp4 : {
@ -227,7 +226,12 @@ bool MultiMediaSourceMuxer::setupRecord(MediaSource &sender, Recorder::type type
bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) { bool MultiMediaSourceMuxer::isRecording(MediaSource &sender, Recorder::type type) {
switch (type){ switch (type){
case Recorder::type_hls : case Recorder::type_hls :
return !!_hls; //return !!_hls;
if (_hls){
return _hls->getRecordFlag();
}else{
return false;
}
case Recorder::type_mp4 : case Recorder::type_mp4 :
return !!_mp4; return !!_mp4;
default: default:

View File

@ -66,6 +66,7 @@ namespace General {
const string kMediaServerId = GENERAL_FIELD "mediaServerId"; const string kMediaServerId = GENERAL_FIELD "mediaServerId";
const string kFlowThreshold = GENERAL_FIELD "flowThreshold"; const string kFlowThreshold = GENERAL_FIELD "flowThreshold";
const string kStreamNoneReaderDelayMS = GENERAL_FIELD "streamNoneReaderDelayMS"; const string kStreamNoneReaderDelayMS = GENERAL_FIELD "streamNoneReaderDelayMS";
const string kNoRecordStreamNoneReaderDelayMS = GENERAL_FIELD "noRecordStreamNoneReaderDelayMS";
const string kMaxStreamWaitTimeMS = GENERAL_FIELD "maxStreamWaitMS"; const string kMaxStreamWaitTimeMS = GENERAL_FIELD "maxStreamWaitMS";
const string kEnableVhost = GENERAL_FIELD "enableVhost"; const string kEnableVhost = GENERAL_FIELD "enableVhost";
const string kResetWhenRePlay = GENERAL_FIELD "resetWhenRePlay"; const string kResetWhenRePlay = GENERAL_FIELD "resetWhenRePlay";
@ -79,6 +80,7 @@ const string kUnreadyFrameCache = GENERAL_FIELD "unready_frame_cache";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kFlowThreshold] = 1024; mINI::Instance()[kFlowThreshold] = 1024;
mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000; mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000;
mINI::Instance()[kNoRecordStreamNoneReaderDelayMS] = 130 * 1000;
mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000; mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000;
mINI::Instance()[kEnableVhost] = 0; mINI::Instance()[kEnableVhost] = 0;
mINI::Instance()[kResetWhenRePlay] = 1; mINI::Instance()[kResetWhenRePlay] = 1;

View File

@ -158,6 +158,7 @@ extern const std::string kFlowThreshold;
// 流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件 // 流无人观看并且超过若干时间后才触发kBroadcastStreamNoneReader事件
// 默认连续5秒无人观看然后触发kBroadcastStreamNoneReader事件 // 默认连续5秒无人观看然后触发kBroadcastStreamNoneReader事件
extern const std::string kStreamNoneReaderDelayMS; extern const std::string kStreamNoneReaderDelayMS;
extern const std::string kNoRecordStreamNoneReaderDelayMS;
// 等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间, // 等待流注册超时时间,收到播放器后请求后,如果未找到相关流,服务器会等待一定时间,
// 如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功, // 如果在这个时间内,相关流注册上了,那么服务器会立即响应播放器播放成功,
// 否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败 // 否则会最多等待kMaxStreamWaitTimeMS毫秒然后响应播放器播放失败

View File

@ -8,10 +8,19 @@
* may be found in the AUTHORS file in the root of the source tree. * may be found in the AUTHORS file in the root of the source tree.
*/ */
#include "Util/File.h"
#include "HlsMaker.h" #include "HlsMaker.h"
#include "Common/config.h" #if defined(_WIN32)
#include <io.h>
#define _access access
#else
#include <unistd.h>
#include <dirent.h>
#endif // WIN32
using namespace std; using namespace std;
using namespace toolkit;
namespace mediakit { namespace mediakit {
@ -20,9 +29,27 @@ HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number, bool seg_keep) {
_seg_number = seg_number; _seg_number = seg_number;
_seg_duration = seg_duration; _seg_duration = seg_duration;
_seg_keep = seg_keep; _seg_keep = seg_keep;
_is_record = false;
} }
HlsMaker::~HlsMaker() { HlsMaker::~HlsMaker() {
_is_close_stream = true;
}
void HlsMaker::startRecord(bool isRecord) {
//本来已经在录像,再次点击录像,或者本来已经停止录像,再次点击停止录像,直接返回
if (isRecord == _is_record) {
return;
}
if(isRecord) {
_seg_keep = true;
}else{
_seg_keep = false;
_is_close_stream = true;
}
_is_record = isRecord;
} }
@ -126,13 +153,15 @@ void HlsMaker::addNewSegment(uint64_t stamp) {
//关闭并保存上一个切片如果_seg_number==0,那么是点播。 //关闭并保存上一个切片如果_seg_number==0,那么是点播。
flushLastSegment(false); flushLastSegment(false);
//新增切片 //新增切片
_last_file_name = onOpenSegment(_file_index++); _last_file_name = onOpenSegment(_file_index++);
//记录本次切片的起始时间戳 //记录本次切片的起始时间戳
_last_seg_timestamp = _last_timestamp ? _last_timestamp : stamp; _last_seg_timestamp = _last_timestamp ? _last_timestamp : stamp;
} }
void HlsMaker::flushLastSegment(bool eof){ void HlsMaker::flushLastSegment(bool eof) {
if (_last_file_name.empty()) { if (_last_file_name.empty()) {
//不存在上个切片 //不存在上个切片
return; return;
@ -148,6 +177,11 @@ void HlsMaker::flushLastSegment(bool eof){
onFlushLastSegment(seg_dur); onFlushLastSegment(seg_dur);
//然后写m3u8文件 //然后写m3u8文件
makeIndexFile(eof); makeIndexFile(eof);
//判断当前是否在录像正在录像的话生成录像的m3u8文件
if (_is_record) {
createM3u8FileForRecord();
}
} }
bool HlsMaker::isLive() { bool HlsMaker::isLive() {
@ -164,6 +198,111 @@ void HlsMaker::clear() {
_last_seg_timestamp = 0; _last_seg_timestamp = 0;
_seg_dur_list.clear(); _seg_dur_list.clear();
_last_file_name.clear(); _last_file_name.clear();
}
std::string HlsMaker::getM3u8TSBody(const std::string &file_content) {
string new_file = file_content;
if (file_content.find("#EXT-X-ENDLIST") != file_content.npos) {
//找到了,则去掉"#EXT-X-ENDLIST"
new_file = file_content.substr(0, file_content.length() - 15);
}
string body = new_file.substr(new_file.find_last_of("#"));
//此时的body为
//#EXTINF:4.534,
//2022-09-14/08/2022-09-14_08-35-16.ts
string extinf = body.substr(0, body.find(",")+2);
string tsFile = body.substr(body.find_last_of("/") + 1);
body.append("#EXT-X-ENDLIST\n");
return extinf + tsFile + "#EXT-X-ENDLIST\n";
}
std::string HlsMaker::getTsFile(const std::string &file_content) {
// 最后一个TS的body为
// 2022-09-13/13/58-13_43.ts
// #EXT-X-ENDLIST
string body = file_content.substr(file_content.find_last_of(",") + 2);
string ts_file_name = body.substr(body.find_last_of("/") + 1);
if (ts_file_name.find("#EXT-X-ENDLIST") == ts_file_name.npos ) {
ts_file_name = ts_file_name.substr(0, ts_file_name.length() - 4); //没找到,去掉“.ts\n”只留名字
} else {
ts_file_name = ts_file_name.substr(0, ts_file_name.length() - 19); //找到的话,去掉“.ts\n#EXT-X-ENDLIST\n”只留名字
}
return ts_file_name;
}
void HlsMaker::createM3u8FileForRecord() {
// 1.读取直播目录下的m3u8文件获取当前的ts文件以及时长并生成m3u8文件的路径
string live_file = File::loadFile((getPathPrefix() + "/hls.m3u8").data());
if (live_file.empty()) {
return;
}
string body = getM3u8TSBody(live_file);
string ts_file_name = getTsFile(live_file); // ts_file: 2022-09-14_11-06-03
string m3u8_file = getPathPrefix() + "/" + ts_file_name.substr(0, 10) + "/" + ts_file_name.substr(11, 2) + "/";
// 2.判断该目录下有没有m3u8文件没有的话生成第一个m3u8文件有的话重命名
int handle = -1;
DIR *dir_info = opendir(m3u8_file.data());
struct dirent *dir_entry;
if (dir_info) {
while ((dir_entry =readdir(dir_info)) != NULL) {
if (end_with(dir_entry->d_name, ".m3u8")) {
handle = 0;
break;
}
}
closedir(dir_info);
} else {
return;
}
if (-1 == handle) {//第一次播放流
_m3u8_file_path = m3u8_file + ts_file_name + ".m3u8";
_is_close_stream = false;
} else {//断流过,一次以上播放
if (_is_close_stream) {
_m3u8_file_path = m3u8_file + ts_file_name + ".m3u8";
_is_close_stream = false;
}
if (_m3u8_file_path.length() == 0) { //服务重启后进来_m3u8_file_path为空
_m3u8_file_path = m3u8_file + ts_file_name + ".m3u8";
}
}
if (_m3u8_file_path.empty()) {
WarnL << "create m3u8 file failed, _m3u8_file_path is empty." ;
return;
}
//3.写m3u8文件
string m3u8Header = "#EXTM3U\n"
"#EXT-X-PLAYLIST-TYPE:EVENT\n"
"#EXT-X-VERSION:4\n"
"#EXT-X-TARGETDURATION:2\n"
"#EXT-X-MEDIA-SEQUENCE:0\n";
if (access(_m3u8_file_path.data(), 0) != 0) { //文件不存在
auto file = File::create_file(_m3u8_file_path.data(), "wb");
if (file) {
fwrite(m3u8Header.data(), m3u8Header.size(), 1, file);
fwrite(body.data(), body.size(), 1, file);
fclose(file);
}
} else {
// 第二次进来,去掉 "#EXT-X-ENDLIST\n"再重新追加file_content保存文件
auto file = File::create_file(_m3u8_file_path.data(), "r+");
if (file) {
fseek(file, -15, SEEK_END);
fwrite(body.data(), body.size(), 1, file);
fclose(file);
}
}
} }
}//namespace mediakit }//namespace mediakit

View File

@ -8,12 +8,16 @@
* may be found in the AUTHORS file in the root of the source tree. * may be found in the AUTHORS file in the root of the source tree.
*/ */
#ifndef HLSMAKER_H #ifndef HLSMAKERSUB_H
#define HLSMAKER_H #define HLSMAKERSUB_H
#include <string>
#include <deque> #include <deque>
#include <tuple> #include <tuple>
#include "Common/config.h"
#include "Util/TimeTicker.h"
#include "Util/File.h"
#include "Util/util.h"
#include "Util/logger.h"
namespace mediakit { namespace mediakit {
@ -50,6 +54,8 @@ public:
* *
*/ */
void clear(); void clear();
//设置是否录像标志
void startRecord(bool isRecord);
protected: protected:
/** /**
@ -82,6 +88,7 @@ protected:
* @param duration_ms ts , * @param duration_ms ts ,
*/ */
virtual void onFlushLastSegment(uint64_t duration_ms) {}; virtual void onFlushLastSegment(uint64_t duration_ms) {};
virtual std::string getPathPrefix() = 0;
/** /**
* ts切片并且写入m3u8索引 * ts切片并且写入m3u8索引
@ -107,6 +114,11 @@ private:
*/ */
void addNewSegment(uint64_t timestamp); void addNewSegment(uint64_t timestamp);
//新增函数,实现录像功能
std::string getTsFile(const std::string &file_content);
std::string getM3u8TSBody(const std::string &file_content);
void createM3u8FileForRecord();
private: private:
float _seg_duration = 0; float _seg_duration = 0;
uint32_t _seg_number = 0; uint32_t _seg_number = 0;
@ -116,7 +128,13 @@ private:
uint64_t _file_index = 0; uint64_t _file_index = 0;
std::string _last_file_name; std::string _last_file_name;
std::deque<std::tuple<int,std::string> > _seg_dur_list; std::deque<std::tuple<int,std::string> > _seg_dur_list;
bool _is_record = false;
bool _is_close_stream = false;
std::string _m3u8_file_path;
public:
std::map<uint64_t /*index*/, std::string /*file_path*/> _segment_file_paths;
}; };
}//namespace mediakit }//namespace mediakit
#endif //HLSMAKER_H #endif //HLSMAKERSUB_H

View File

@ -13,21 +13,19 @@
#include "HlsMakerImp.h" #include "HlsMakerImp.h"
#include "Util/util.h" #include "Util/util.h"
#include "Util/uv_errno.h" #include "Util/uv_errno.h"
#include "Util/File.h"
#include "Common/config.h"
using namespace std; using namespace std;
using namespace toolkit; using namespace toolkit;
namespace mediakit { namespace mediakit {
HlsMakerImp::HlsMakerImp(const string &m3u8_file, HlsMakerImp::HlsMakerImp(
const string &m3u8_file,
const string &params, const string &params,
uint32_t bufSize, uint32_t bufSize,
float seg_duration, float seg_duration,
uint32_t seg_number, uint32_t seg_number,
bool seg_keep):HlsMaker(seg_duration, seg_number, seg_keep) { bool seg_keep):HlsMaker(seg_duration, seg_number, seg_keep) {
_poller = EventPollerPool::Instance().getPoller();
_path_prefix = m3u8_file.substr(0, m3u8_file.rfind('/')); _path_prefix = m3u8_file.substr(0, m3u8_file.rfind('/'));
_path_hls = m3u8_file; _path_hls = m3u8_file;
_params = params; _params = params;
@ -56,33 +54,29 @@ void HlsMakerImp::clearCache(bool immediately, bool eof) {
clear(); clear();
_file = nullptr; _file = nullptr;
//删除_segment_file_paths路径对应的直播文件
for (auto it : _segment_file_paths) {
auto ts_path = it.second;
File::delete_file(ts_path.data());
}
_segment_file_paths.clear(); _segment_file_paths.clear();
//hls直播才删除文件 //删除缓存的m3u8文件
GET_CONFIG(uint32_t, delay, Hls::kDeleteDelaySec); File::delete_file((_path_prefix + "/hls.m3u8").data());
if (!delay || immediately) {
File::delete_file(_path_prefix.data());
} else {
auto path_prefix = _path_prefix;
_poller->doDelayTask(delay * 1000, [path_prefix]() {
File::delete_file(path_prefix.data());
return 0;
});
}
} }
string HlsMakerImp::onOpenSegment(uint64_t index) { string HlsMakerImp::onOpenSegment(uint64_t index) {
string segment_name, segment_path; string segment_name, segment_path;
{
auto strDate = getTimeStr("%Y-%m-%d"); auto strDate = getTimeStr("%Y-%m-%d");
auto strHour = getTimeStr("%H"); auto strHour = getTimeStr("%H");
auto strTime = getTimeStr("%M-%S"); auto strTime = getTimeStr("%M-%S");
segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << ".ts"; segment_name = StrPrinter << strDate + "_" + strHour + "-" + strTime << ".ts";
segment_path = _path_prefix + "/" + segment_name; segment_path = _path_prefix + "/" + strDate + "/" + strHour + "/" + segment_name;
if (isLive()) { if ((!isKeep())) {
_segment_file_paths.emplace(index, segment_path); _segment_file_paths.emplace(index, segment_path);
} }
}
_file = makeFile(segment_path, true); _file = makeFile(segment_path, true);
//保存本切片的元数据 //保存本切片的元数据
@ -93,11 +87,12 @@ string HlsMakerImp::onOpenSegment(uint64_t index) {
if (!_file) { if (!_file) {
WarnL << "create file failed," << segment_path << " " << get_uv_errmsg(); WarnL << "create file failed," << segment_path << " " << get_uv_errmsg();
return "";
} }
if (_params.empty()) { if (_params.empty()) {
return segment_name; return strDate + "/" + strHour + "/" + segment_name;
} }
return segment_name + "?" + _params; return strDate + "/" + strHour + "/" + segment_name + "?" + _params;
} }
void HlsMakerImp::onDelSegment(uint64_t index) { void HlsMakerImp::onDelSegment(uint64_t index) {
@ -168,4 +163,8 @@ HlsMediaSource::Ptr HlsMakerImp::getMediaSource() const {
return _media_src; return _media_src;
} }
std::string HlsMakerImp::getPathPrefix() {
return _path_prefix;
}
}//namespace mediakit }//namespace mediakit

View File

@ -8,8 +8,8 @@
* may be found in the AUTHORS file in the root of the source tree. * may be found in the AUTHORS file in the root of the source tree.
*/ */
#ifndef HLSMAKERIMP_H #ifndef HLSMAKERIMPSUB_H
#define HLSMAKERIMP_H #define HLSMAKERIMPSUB_H
#include <memory> #include <memory>
#include <string> #include <string>
@ -21,7 +21,8 @@ namespace mediakit {
class HlsMakerImp : public HlsMaker{ class HlsMakerImp : public HlsMaker{
public: public:
HlsMakerImp(const std::string &m3u8_file, HlsMakerImp(
const std::string &m3u8_file,
const std::string &params, const std::string &params,
uint32_t bufSize = 64 * 1024, uint32_t bufSize = 64 * 1024,
float seg_duration = 5, float seg_duration = 5,
@ -55,6 +56,7 @@ protected:
void onWriteSegment(const char *data, size_t len) override; void onWriteSegment(const char *data, size_t len) override;
void onWriteHls(const std::string &data) override; void onWriteHls(const std::string &data) override;
void onFlushLastSegment(uint64_t duration_ms) override; void onFlushLastSegment(uint64_t duration_ms) override;
std::string getPathPrefix() override;
private: private:
std::shared_ptr<FILE> makeFile(const std::string &file,bool setbuf = false); std::shared_ptr<FILE> makeFile(const std::string &file,bool setbuf = false);
@ -69,9 +71,8 @@ private:
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;
toolkit::EventPoller::Ptr _poller;
std::map<uint64_t/*index*/,std::string/*file_path*/> _segment_file_paths;
}; };
}//namespace mediakit }//namespace mediakit
#endif //HLSMAKERIMP_H #endif //HLSMAKERIMPSUB_H

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* *
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
@ -26,7 +26,6 @@ public:
GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep); GET_CONFIG(bool, hlsKeep, Hls::kSegmentKeep);
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize); GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration); GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration);
_option = option; _option = option;
_hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep); _hls = std::make_shared<HlsMakerImp>(m3u8_file, params, hlsBufSize, hlsDuration, hlsNum, hlsKeep);
//清空上次的残余文件 //清空上次的残余文件
@ -73,6 +72,12 @@ public:
//缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存 //缓存尚未清空时还允许触发inputFrame函数以便及时清空缓存
return _option.hls_demand ? (_clear_cache ? true : _enabled) : true; return _option.hls_demand ? (_clear_cache ? true : _enabled) : true;
} }
void startRecord(bool flag) {
_hls->startRecord(flag);
_is_record = flag;
}
bool getRecordFlag() { return _is_record; }
private: private:
void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override { void onWrite(std::shared_ptr<toolkit::Buffer> buffer, uint64_t timestamp, bool key_pos) override {
@ -88,6 +93,7 @@ private:
bool _clear_cache = false; bool _clear_cache = false;
ProtocolOption _option; ProtocolOption _option;
std::shared_ptr<HlsMakerImp> _hls; std::shared_ptr<HlsMakerImp> _hls;
bool _is_record = false;
}; };
}//namespace mediakit }//namespace mediakit
#endif //HLSRECORDER_H #endif //HLSRECORDER_H

View File

@ -1,4 +1,4 @@
/* /*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
* *
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
@ -39,7 +39,7 @@ MP4Recorder::~MP4Recorder() {
} }
void MP4Recorder::createFile() { void MP4Recorder::createFile() {
closeFile(); //closeFile();
auto date = getTimeStr("%Y-%m-%d"); auto date = getTimeStr("%Y-%m-%d");
auto time = getTimeStr("%H-%M-%S"); auto time = getTimeStr("%H-%M-%S");
auto full_path_tmp = _folder_path + date + "/." + time + ".mp4"; auto full_path_tmp = _folder_path + date + "/." + time + ".mp4";
@ -93,7 +93,7 @@ void MP4Recorder::asyncClose() {
void MP4Recorder::closeFile() { void MP4Recorder::closeFile() {
if (_muxer) { if (_muxer) {
asyncClose(); //asyncClose();
_muxer = nullptr; _muxer = nullptr;
} }
} }
@ -119,7 +119,7 @@ bool MP4Recorder::inputFrame(const Frame::Ptr &frame) {
// 2、到了切片时间并且只有音频 // 2、到了切片时间并且只有音频
// 3、到了切片时间有视频并且遇到视频的关键帧 // 3、到了切片时间有视频并且遇到视频的关键帧
_last_dts = 0; _last_dts = 0;
createFile(); //createFile();
} }
} }
@ -140,7 +140,7 @@ bool MP4Recorder::addTrack(const Track::Ptr &track) {
} }
void MP4Recorder::resetTracks() { void MP4Recorder::resetTracks() {
closeFile(); //closeFile();
_tracks.clear(); _tracks.clear();
_have_video = false; _have_video = false;
} }