hls/mp4录制不再能随时开启或关闭

This commit is contained in:
xiongziliang
2020-04-04 14:33:12 +08:00
parent a581f78d12
commit cefb2a7cc4
11 changed files with 35 additions and 488 deletions

View File

@@ -78,7 +78,6 @@ vector<Track::Ptr> MediaSource::getTracks(bool trackReady) const {
void MediaSource::setTrackSource(const std::weak_ptr<TrackSource> &track_src) {
_track_source = track_src;
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, *this);
}
void MediaSource::setListener(const std::weak_ptr<MediaSourceEvent> &listener){

View File

@@ -57,20 +57,12 @@ public:
}
if(enable_hls){
Recorder::startRecord(Recorder::type_hls,vhost, app, stream, "", true, false);
_hls = Recorder::createRecorder(Recorder::type_hls,vhost, app, stream);
}
if(enable_mp4){
Recorder::startRecord(Recorder::type_mp4,vhost, app, stream, "", true, false);
_mp4 = Recorder::createRecorder(Recorder::type_mp4,vhost, app, stream);
}
_get_hls_media_source = [vhost,app,stream](){
auto recorder = dynamic_pointer_cast<HlsRecorder>(Recorder::getRecorder(Recorder::type_hls,vhost,app,stream));
if(recorder){
return recorder->getMediaSource();
}
return MediaSource::Ptr();
};
}
virtual ~MultiMediaSourceMuxer(){}
@@ -84,6 +76,12 @@ public:
if(_rtsp){
_rtsp->resetTracks();
}
if(_hls){
_hls->resetTracks();
}
if(_mp4){
_mp4->resetTracks();
}
}
/**
@@ -99,7 +97,7 @@ public:
_rtsp->setListener(listener);
}
auto hls_src = _get_hls_media_source();
auto hls_src = getHlsMediaSource();
if(hls_src){
hls_src->setListener(listener);
}
@@ -110,7 +108,7 @@ public:
* @return
*/
int totalReaderCount() const{
auto hls_src = _get_hls_media_source();
auto hls_src = getHlsMediaSource();
return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0);
}
@@ -118,7 +116,6 @@ public:
if(_rtmp){
_rtmp->setTimeStamp(stamp);
}
if(_rtsp){
_rtsp->setTimeStamp(stamp);
}
@@ -139,6 +136,12 @@ protected:
if(_rtsp){
_rtsp->addTrack(track);
}
if(_hls){
_hls->addTrack(track);
}
if(_mp4){
_mp4->addTrack(track);
}
}
/**
@@ -152,6 +155,12 @@ protected:
if(_rtsp) {
_rtsp->inputFrame(frame);
}
if(_hls){
_hls->inputFrame(frame);
}
if(_mp4){
_mp4->inputFrame(frame);
}
}
/**
@@ -167,7 +176,7 @@ protected:
_rtsp->onAllTrackReady();
}
auto hls_src = _get_hls_media_source();
auto hls_src = getHlsMediaSource();
if(hls_src){
hls_src->setTrackSource(shared_from_this());
}
@@ -176,11 +185,20 @@ protected:
_listener->onAllTrackReady();
}
}
MediaSource::Ptr getHlsMediaSource() const{
auto recorder = dynamic_pointer_cast<HlsRecorder>(_hls);
if(recorder){
return recorder->getMediaSource();
}
return nullptr;
}
private:
RtmpMediaSourceMuxer::Ptr _rtmp;
RtspMediaSourceMuxer::Ptr _rtsp;
MediaSinkInterface::Ptr _hls;
MediaSinkInterface::Ptr _mp4;
Listener *_listener = nullptr;
function<MediaSource::Ptr ()> _get_hls_media_source;
};

View File

@@ -55,7 +55,6 @@ bool loadIniConfig(const char *ini_path){
////////////广播名称///////////
namespace Broadcast {
const string kBroadcastMediaChanged = "kBroadcastMediaChanged";
const string kBroadcastMediaResetTracks = "kBroadcastMediaResetTracks";
const string kBroadcastRecordMP4 = "kBroadcastRecordMP4";
const string kBroadcastHttpRequest = "kBroadcastHttpRequest";
const string kBroadcastHttpAccess = "kBroadcastHttpAccess";

View File

@@ -72,10 +72,6 @@ namespace Broadcast {
extern const string kBroadcastMediaChanged;
#define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender
//MediaSource重置Track事件
extern const string kBroadcastMediaResetTracks;
#define BroadcastMediaResetTracksArgs MediaSource &sender
//录制mp4文件成功后广播
extern const string kBroadcastRecordMP4;
#define BroadcastRecordMP4Args const MP4Info &info

View File

@@ -70,286 +70,6 @@ string Recorder::getRecordPath(Recorder::type type, const string &vhost, const s
return "";
}
}
////////////////////////////////////////////////////////////////////////////////////////
class RecorderHelper {
public:
typedef std::shared_ptr<RecorderHelper> Ptr;
/**
* 构建函数
* @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
*/
RecorderHelper(const MediaSinkInterface::Ptr &recorder, bool bContinueRecord) {
_recorder = recorder;
_continueRecord = bContinueRecord;
}
~RecorderHelper() {
resetTracks();
}
// 附则于track上
void attachTracks(vector<Track::Ptr> &&tracks, const string &schema){
if(isTracksSame(tracks)){
return;
}
resetTracks();
_tracks = std::move(tracks);
_schema = schema;
for (auto &track : _tracks) {
_recorder->addTrack(track);
track->addDelegate(_recorder);
}
}
// 判断新的tracks是否与之前的一致
bool isTracksSame(const vector<Track::Ptr> &tracks){
if(tracks.size() != _tracks.size()) {
return false;
}
int i = 0;
for(auto &track : tracks){
if(track != _tracks[i++]){
return false;
}
}
return true;
}
// 重置所有track
void resetTracks(){
if(_tracks.empty()){
return;
}
for (auto &track : _tracks) {
track->delDelegate(_recorder.get());
}
_tracks.clear();
_recorder->resetTracks();
}
// 返回false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除)
bool continueRecord(){
return _continueRecord;
}
bool isRecording() {
return !_tracks.empty();
}
const string &getSchema() const{
return _schema;
}
const MediaSinkInterface::Ptr& getRecorder() const{
return _recorder;
}
private:
MediaSinkInterface::Ptr _recorder;
vector<Track::Ptr> _tracks;
bool _continueRecord;
string _schema;
};
template<Recorder::type type>
class MediaSourceWatcher {
public:
static MediaSourceWatcher& Instance(){
static MediaSourceWatcher instance;
return instance;
}
Recorder::status getRecordStatus(const string &vhost, const string &app, const string &stream_id) {
return getRecordStatus_l(getRecorderKey(vhost, app, stream_id));
}
MediaSinkInterface::Ptr getRecorder(const string &vhost, const string &app, const string &stream_id) const{
auto key = getRecorderKey(vhost, app, stream_id);
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
auto it = _recorder_map.find(key);
if (it == _recorder_map.end()) {
return nullptr;
}
return it->second->getRecorder();
}
int startRecord(const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) {
auto key = getRecorderKey(vhost, app, stream_id);
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
if (getRecordStatus_l(key) != Recorder::status_not_record) {
// 已经在录制了
return 0;
}
auto src = findMediaSource(vhost, app, stream_id);
if (!waitForRecord && !src) {
// 暂时无法开启录制
return -1;
}
auto recorder = Recorder::createRecorder(type, vhost, app, stream_id, customized_path);
if (!recorder) {
// 创建录制器失败
WarnL << "不支持该录制类型:" << type;
return -2;
}
auto helper = std::make_shared<RecorderHelper>(recorder, continueRecord);
if(src){
auto tracks = src->getTracks(needTrackReady());
if(tracks.size()){
helper->attachTracks(std::move(tracks),src->getSchema());
}
auto hls_recorder = dynamic_pointer_cast<HlsRecorder>(recorder);
if(hls_recorder){
hls_recorder->getMediaSource()->setListener(src->getListener());
}
}
_recorder_map[key] = std::move(helper);
return 0;
}
bool stopRecord(const string &vhost, const string &app, const string &stream_id) {
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
return _recorder_map.erase(getRecorderKey(vhost, app, stream_id));
}
void stopAll(){
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
_recorder_map.clear();
}
private:
MediaSourceWatcher(){
//保存NoticeCenter的强引用防止在MediaSourceWatcher单例释放前释放NoticeCenter单例
_notice_center = NoticeCenter::Instance().shared_from_this();
_notice_center->addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){
if(!bRegist){
removeRecorder(sender);
}
});
_notice_center->addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){
addRecorder(sender);
});
}
~MediaSourceWatcher(){
_notice_center->delListener(this,Broadcast::kBroadcastMediaChanged);
_notice_center->delListener(this,Broadcast::kBroadcastMediaResetTracks);
}
void addRecorder(MediaSource &sender){
auto tracks = sender.getTracks(needTrackReady());
auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId());
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
auto it = _recorder_map.find(key);
if(it == _recorder_map.end()){
// 录像记录不存在
return;
}
if(!it->second->isRecording() || it->second->getSchema() == sender.getSchema()){
// 绑定的协议一致或者并未正在录制则替换tracks
if (!tracks.empty()) {
it->second->attachTracks(std::move(tracks),sender.getSchema());
}
}
}
void removeRecorder(MediaSource &sender){
auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId());
lock_guard<decltype(_recorder_mtx)> lck(_recorder_mtx);
auto it = _recorder_map.find(key);
if(it == _recorder_map.end() || it->second->getSchema() != sender.getSchema()){
// 录像记录不存在或绑定的协议不一致
return;
}
if(it->second->continueRecord()){
// 如果可以继续录制那么只重置tracks,不删除对象
it->second->resetTracks();
}else{
// 删除对象(意味着可能删除hls临时文件)
_recorder_map.erase(it);
}
}
Recorder::status getRecordStatus_l(const string &key) {
auto it = _recorder_map.find(key);
if (it == _recorder_map.end()) {
return Recorder::status_not_record;
}
return it->second->isRecording() ? Recorder::status_recording : Recorder::status_wait_record;
}
// 查找MediaSource以便录制
MediaSource::Ptr findMediaSource(const string &vhost, const string &app, const string &stream_id) {
bool need_ready = needTrackReady();
auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id);
if (src) {
auto ret = src->getTracks(need_ready);
if (!ret.empty()) {
return std::move(src);
}
}
src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id);
if (src) {
auto ret = src->getTracks(need_ready);
if (!ret.empty()) {
return std::move(src);
}
}
return nullptr;
}
string getRecorderKey(const string &vhost, const string &app, const string &stream_id) const{
return vhost + "/" + app + "/" + stream_id;
}
/**
* 有些录制类型不需要track就绪即可录制
*/
bool needTrackReady(){
switch (type){
case Recorder::type_hls:
return false;
case Recorder::type_mp4:
return true;
default:
return true;
}
}
private:
mutable recursive_mutex _recorder_mtx;
NoticeCenter::Ptr _notice_center;
unordered_map<string, RecorderHelper::Ptr> _recorder_map;
};
Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
switch (type){
case type_mp4:
return MediaSourceWatcher<type_mp4>::Instance().getRecordStatus(vhost,app,stream_id);
case type_hls:
return MediaSourceWatcher<type_hls>::Instance().getRecordStatus(vhost,app,stream_id);
}
return status_not_record;
}
std::shared_ptr<MediaSinkInterface> Recorder::getRecorder(type type, const string &vhost, const string &app, const string &stream_id){
switch (type){
case type_mp4:
return MediaSourceWatcher<type_mp4>::Instance().getRecorder(vhost,app,stream_id);
case type_hls:
return MediaSourceWatcher<type_hls>::Instance().getRecorder(vhost,app,stream_id);
}
return nullptr;
}
std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path){
auto path = Recorder::getRecordPath(type, vhost, app, stream_id);
@@ -375,30 +95,4 @@ std::shared_ptr<MediaSinkInterface> Recorder::createRecorder(type type, const st
}
}
int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) {
switch (type){
case type_mp4:
return MediaSourceWatcher<type_mp4>::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord);
case type_hls:
return MediaSourceWatcher<type_hls>::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord);
}
WarnL << "unknown record type: " << type;
return -3;
}
bool Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) {
switch (type){
case type_mp4:
return MediaSourceWatcher<type_mp4>::Instance().stopRecord(vhost,app,stream_id);
case type_hls:
return MediaSourceWatcher<type_hls>::Instance().stopRecord(vhost,app,stream_id);
}
return false;
}
void Recorder::stopAll() {
MediaSourceWatcher<type_hls>::Instance().stopAll();
MediaSourceWatcher<type_mp4>::Instance().stopAll();
}
} /* namespace mediakit */

View File

@@ -26,26 +26,14 @@
#ifndef SRC_MEDIAFILE_RECORDER_H_
#define SRC_MEDIAFILE_RECORDER_H_
#include <memory>
#include <string>
using namespace std;
namespace mediakit {
class MediaSinkInterface;
class Recorder{
public:
typedef enum {
// 未录制
status_not_record = 0,
// 等待MediaSource注册注册成功后立即开始录制
status_wait_record = 1,
// MediaSource已注册并且正在录制
status_recording = 2,
} status;
typedef enum {
// 录制hls
type_hls = 0,
@@ -64,52 +52,6 @@ public:
*/
static string getRecordPath(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path = "");
/**
* 获取录制状态
* @param type hls还是MP4录制
* @param vhost 虚拟主机
* @param app 应用名
* @param stream_id 流id
* @return 录制状态
*/
static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id);
/**
* 开始录制
* @param type hls还是MP4录制
* @param vhost 虚拟主机
* @param app 应用名
* @param stream_id 流id
* @param customized_path 录像文件保存自定义目录,默认为空则自动生成
* @param waitForRecord 是否等待流注册后再录制未注册时置false将返回失败
* @param continueRecord 流注销时是否继续等待录制还是立即停止录制
* @return 0代表成功负数代表失败
*/
static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path,bool waitForRecord, bool continueRecord);
/**
* 停止录制
* @param type hls还是MP4录制
* @param vhost 虚拟主机
* @param app 应用名
* @param stream_id 流id
*/
static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id);
/**
* 停止所有录制,一般程序退出时调用
*/
static void stopAll();
/**
* 获取录制对象
* @param type hls还是MP4录制
* @param vhost 虚拟主机
* @param app 应用名
* @param stream_id 流id
*/
static std::shared_ptr<MediaSinkInterface> getRecorder(type type, const string &vhost, const string &app, const string &stream_id);
/**
* 创建录制器对象
* @param type hls还是MP4录制
@@ -119,12 +61,11 @@ public:
* @param customized_path 录像文件保存自定义目录,默认为空则自动生成
* @return 对象指针可能为nullptr
*/
static std::shared_ptr<MediaSinkInterface> createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path);
static std::shared_ptr<MediaSinkInterface> createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path = "");
private:
Recorder() = delete;
~Recorder() = delete;
};
} /* namespace mediakit */
#endif /* SRC_MEDIAFILE_RECORDER_H_ */