From 6364b14762047e38076e2d179ddc34b2b2067d14 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 28 Oct 2018 00:15:27 +0800 Subject: [PATCH] =?UTF-8?q?MP4=E3=80=81hls=E9=80=82=E9=85=8D=E6=96=B0?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 10 +++ src/Common/MediaSink.h | 12 ++- src/MediaFile/HLSMaker.cpp | 28 ++++++- src/MediaFile/HLSMaker.h | 36 ++++++--- src/MediaFile/MediaRecorder.cpp | 11 ++- src/MediaFile/MediaRecorder.h | 1 - src/MediaFile/Mp4Maker.cpp | 126 ++++++++++++++++++------------- src/MediaFile/Mp4Maker.h | 43 +++++++---- src/Player/Track.h | 34 ++++++--- src/Rtmp/RtmpToRtspMediaSource.h | 2 +- src/Rtsp/RtspToRtmpMediaSource.h | 2 +- 11 files changed, 200 insertions(+), 105 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 44aed5ad..95aa4681 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -83,5 +83,15 @@ bool MediaSink::isAllTrackReady() const { return _allTrackReady; } +Track::Ptr MediaSink::getTrack(TrackType type) const { + lock_guard lck(_mtx); + for (auto &pr : _track_map){ + if(pr.second->getTrackType() == type){ + return pr.second; + } + } + return nullptr; +} + }//namespace mediakit diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index a2c043a7..5cc0d056 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -58,7 +58,7 @@ public: * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 * @param track */ - void addTrack(const Track::Ptr & track); + virtual void addTrack(const Track::Ptr & track); /** @@ -66,6 +66,14 @@ public: * @return */ bool isAllTrackReady() const ; + + + /** + * 获取特定类型的Track + * @param type + * @return + */ + Track::Ptr getTrack(TrackType type) const ; protected: /** * 某track已经准备好,其ready()状态返回true, @@ -85,7 +93,7 @@ protected: */ virtual void onTrackFrame(const Frame::Ptr &frame) {}; private: - mutex _mtx; + mutable mutex _mtx; map _track_map; map > _trackReadyCallback; bool _allTrackReady = false; diff --git a/src/MediaFile/HLSMaker.cpp b/src/MediaFile/HLSMaker.cpp index 4fc5136b..35e88d77 100644 --- a/src/MediaFile/HLSMaker.cpp +++ b/src/MediaFile/HLSMaker.cpp @@ -131,12 +131,12 @@ bool HLSMaker::write_index_file(int iFirstSegment, unsigned int uiLastSegment, i return true; } -void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp, int type) { +void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp) { if(_ui32LastStamp == 0){ _ui32LastStamp = timeStamp; } int stampInc = timeStamp - _ui32LastStamp; - + auto type = ((uint8_t*)data)[4] & 0x1F; switch (type) { case 7: //SPS if (stampInc >= _ui32SegmentDuration * 1000) { @@ -182,5 +182,29 @@ bool HLSMaker::removets() { return true; } +void HLSMaker::onTrackFrame(const Frame::Ptr &frame) { + switch (frame->getCodecId()){ + case CodecH264:{ + if( frame->prefixSize() == 4){ + inputH264(frame->data(), frame->size(),frame->stamp()); + }else{ + WarnL << "h264必须要有头4个字节的前缀"; + } + } + break; + case CodecAAC:{ + if( frame->prefixSize() == 7) { + inputAAC(frame->data(), frame->size(), frame->stamp()); + }else{ + WarnL << "adts必须要有头7个字节的adts头"; + } + } + break; + + default: + break; + } +} + } /* namespace mediakit */ diff --git a/src/MediaFile/HLSMaker.h b/src/MediaFile/HLSMaker.h index 6a464eb7..80ba6ccb 100644 --- a/src/MediaFile/HLSMaker.h +++ b/src/MediaFile/HLSMaker.h @@ -27,18 +27,19 @@ #ifndef HLSMAKER_H_ #define HLSMAKER_H_ +#include #include "TSMaker.h" -#include "Common/config.h" #include "Util/TimeTicker.h" #include "Util/File.h" #include "Util/util.h" #include "Util/logger.h" -#include +#include "Common/config.h" +#include "Common/MediaSink.h" using namespace toolkit; namespace mediakit { -class HLSMaker { +class HLSMaker : public MediaSink{ public: HLSMaker(const string &strM3u8File, uint32_t ui32BufSize = 64 * 1024, @@ -47,16 +48,28 @@ public: virtual ~HLSMaker(); +protected: + /** + * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 + * @param frame + */ + void onTrackFrame(const Frame::Ptr &frame) override ; +private: //时间戳:参考频率1000 - void inputH264( void *pData, - uint32_t ui32Length, - uint32_t ui32TimeStamp, - int iType); + void inputH264(void *pData, + uint32_t ui32Length, + uint32_t ui32TimeStamp); //时间戳:参考频率1000 - void inputAAC( void *pData, - uint32_t ui32Length, - uint32_t ui32TimeStamp); + void inputAAC(void *pData, + uint32_t ui32Length, + uint32_t ui32TimeStamp); + + bool write_index_file(int iFirstSegment, + unsigned int uiLastSegment, + int iEnd); + + bool removets(); private: TSMaker _ts; string _strM3u8File; @@ -69,8 +82,7 @@ private: uint32_t _ui32LastStamp; std::deque _iDurations; - bool write_index_file(int iFirstSegment, unsigned int uiLastSegment, int iEnd); - bool removets(); + }; } /* namespace mediakit */ diff --git a/src/MediaFile/MediaRecorder.cpp b/src/MediaFile/MediaRecorder.cpp index bcb20d8c..79a48147 100644 --- a/src/MediaFile/MediaRecorder.cpp +++ b/src/MediaFile/MediaRecorder.cpp @@ -37,7 +37,6 @@ namespace mediakit { MediaRecorder::MediaRecorder(const string &strVhost_tmp, const string &strApp, const string &strId, - const std::shared_ptr &pPlayer, bool enableHls, bool enableMp4) { @@ -63,7 +62,7 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp, if(enableMp4){ auto mp4FilePath = recordPath + "/" + strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/"; - _mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId,pPlayer)); + _mp4Maker.reset(new Mp4Maker(mp4FilePath,strVhost,strApp,strId)); } #endif //ENABLE_MP4V2 } @@ -73,22 +72,22 @@ MediaRecorder::~MediaRecorder() { void MediaRecorder::inputH264(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType) { if(_hlsMaker){ - _hlsMaker->inputH264(pData, ui32Length, ui32TimeStamp, iType); +// _hlsMaker->inputH264(pData, ui32Length, ui32TimeStamp, iType); } #ifdef ENABLE_MP4V2 if(_mp4Maker){ - _mp4Maker->inputH264(pData, ui32Length, ui32TimeStamp, iType); +// _mp4Maker->inputH264(pData, ui32Length, ui32TimeStamp, iType); } #endif //ENABLE_MP4V2 } void MediaRecorder::inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp) { if(_hlsMaker){ - _hlsMaker->inputAAC(pData, ui32Length, ui32TimeStamp); +// _hlsMaker->inputAAC(pData, ui32Length, ui32TimeStamp); } #ifdef ENABLE_MP4V2 if(_mp4Maker){ - _mp4Maker->inputAAC(pData, ui32Length, ui32TimeStamp); + // _mp4Maker->inputAAC(pData, ui32Length, ui32TimeStamp); } #endif //ENABLE_MP4V2 } diff --git a/src/MediaFile/MediaRecorder.h b/src/MediaFile/MediaRecorder.h index 205ae698..2c34cde7 100644 --- a/src/MediaFile/MediaRecorder.h +++ b/src/MediaFile/MediaRecorder.h @@ -45,7 +45,6 @@ public: MediaRecorder(const string &strVhost, const string &strApp, const string &strId, - const std::shared_ptr &pPlayer, bool enableHls = true, bool enableMp4 = false); virtual ~MediaRecorder(); diff --git a/src/MediaFile/Mp4Maker.cpp b/src/MediaFile/Mp4Maker.cpp index 0356ac84..34396be2 100644 --- a/src/MediaFile/Mp4Maker.cpp +++ b/src/MediaFile/Mp4Maker.cpp @@ -57,10 +57,8 @@ string timeStr(const char *fmt) { Mp4Maker::Mp4Maker(const string& strPath, const string &strVhost, const string &strApp, - const string &strStreamId, - const PlayerBase::Ptr &pPlayer) { + const string &strStreamId) { DebugL << strPath; - _pPlayer = pPlayer; _strPath = strPath; /////record 业务逻辑////// @@ -74,7 +72,8 @@ Mp4Maker::~Mp4Maker() { closeFile(); } -void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType){ +void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp){ + auto iType = ((uint8_t*)pData)[0] & 0x1F; switch (iType) { case 1: //P case 5: { //IDR @@ -84,16 +83,14 @@ void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStam if(iTimeInc == 0 || iTimeInc == 500){ WarnL << "abnormal time stamp increment:" << ui32TimeStamp << " " << _ui32LastVideoTime; } - _inputH264((char *) _strLastVideo.data(), _strLastVideo.size(), iTimeInc, _iLastVideoType); + inputH264_l((char *) _strLastVideo.data(), _strLastVideo.size(), iTimeInc); } - //_strLastVideo.assign(("\x0\x0\x0\x2\x9\xf0"), 6); - uint32_t *p = (uint32_t *) pData; - *p = htonl(ui32Length - 4); - _strLastVideo.assign((char *) pData, ui32Length); - memcpy(pData, "\x00\x00\x00\x01", 4); + + uint32_t prefixe = htonl(ui32Length); + _strLastVideo.assign((char *) &prefixe, 4); + _strLastVideo.append((char *)pData,ui32Length); _ui32LastVideoTime = ui32TimeStamp; - _iLastVideoType = iType; } break; default: @@ -107,16 +104,17 @@ void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp if(iTimeInc == 0 || iTimeInc == 500){ WarnL << "abnormal time stamp increment:" << ui32TimeStamp << " " << _ui32LastAudioTime; } - _inputAAC((char *)_strLastAudio.data(), _strLastAudio.size(), iTimeInc); + inputAAC_l((char *) _strLastAudio.data(), _strLastAudio.size(), iTimeInc); } _strLastAudio.assign((char *)pData, ui32Length); _ui32LastAudioTime = ui32TimeStamp; } -void Mp4Maker::_inputH264(void* pData, uint32_t ui32Length, uint32_t ui32Duration, int iType) { +void Mp4Maker::inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui32Duration) { GET_CONFIG_AND_REGISTER(uint32_t,recordSec,Record::kFileSecond); - if(iType == 5 && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){ + auto iType = ((uint8_t*)pData)[0] & 0x1F; + if(iType == 5 && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){ //在I帧率处新建MP4文件 //如果文件未创建或者文件超过10分钟则创建新文件 createFile(); @@ -126,27 +124,21 @@ void Mp4Maker::_inputH264(void* pData, uint32_t ui32Length, uint32_t ui32Duratio } } -void Mp4Maker::_inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32Duration) { +void Mp4Maker::inputAAC_l(void *pData, uint32_t ui32Length, uint32_t ui32Duration) { GET_CONFIG_AND_REGISTER(uint32_t,recordSec,Record::kFileSecond); - //todo(xzl) 修复此处 - -// -// if (!_pPlayer->containVideo() && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)) { -// //在I帧率处新建MP4文件 -// //如果文件未创建或者文件超过10分钟则创建新文件 -// createFile(); -// } -// if (_hAudio != MP4_INVALID_TRACK_ID) { -// auto duration = ui32Duration * _pPlayer->getAudioSampleRate() /1000.0; -// MP4WriteSample(_hMp4, _hAudio, (uint8_t*)pData + 7, ui32Length - 7,duration,0,false); -// } + if (!_haveVideo && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)) { + //在I帧率处新建MP4文件 + //如果文件未创建或者文件超过10分钟则创建新文件 + createFile(); + } + if (_hAudio != MP4_INVALID_TRACK_ID) { + auto duration = ui32Duration * _audioSampleRate /1000.0; + MP4WriteSample(_hMp4, _hAudio, (uint8_t*)pData, ui32Length,duration,0,false); + } } void Mp4Maker::createFile() { - if(!_pPlayer->isInited()){ - return; - } closeFile(); auto strDate = timeStr("%Y-%m-%d"); @@ -184,30 +176,38 @@ void Mp4Maker::createFile() { _strFile = strFile; _ticker.resetTime(); - //todo(xzl) 修复此处 + auto videoTrack = dynamic_pointer_cast(getTrack(TrackVideo)); + if(videoTrack){ + auto &sps = videoTrack->getSps(); + auto &pps = videoTrack->getPps(); + _hVideo = MP4AddH264VideoTrack(_hMp4, + 90000, + MP4_INVALID_DURATION, + videoTrack->getVideoWidth(), + videoTrack->getVideoHeight(), + sps[1], + sps[2], + sps[3], + 3); + if(_hVideo != MP4_INVALID_TRACK_ID){ + MP4AddH264SequenceParameterSet(_hMp4, _hVideo, (uint8_t *)sps.data(), sps.size()); + MP4AddH264PictureParameterSet(_hMp4, _hVideo, (uint8_t *)pps.data(), pps.size()); + }else{ + WarnL << "添加视频通道失败:" << strFileTmp; + } + } -// if(_pPlayer->containVideo()){ -// auto &sps = _pPlayer->getSps(); -// auto &pps = _pPlayer->getPps(); -// _hVideo = MP4AddH264VideoTrack(_hMp4, 90000, MP4_INVALID_DURATION, -// _pPlayer->getVideoWidth(), _pPlayer->getVideoHeight(), -// sps[5], sps[6], sps[7], 3); -// if(_hVideo !=MP4_INVALID_TRACK_ID){ -// MP4AddH264SequenceParameterSet(_hMp4, _hVideo, (uint8_t *)sps.data() + 4, sps.size() - 4); -// MP4AddH264PictureParameterSet(_hMp4, _hVideo, (uint8_t *)pps.data() + 4, pps.size() - 4); -// }else{ -// WarnL << "添加视频通道失败:" << strFileTmp; -// } -// } -// if(_pPlayer->containAudio()){ -// _hAudio = MP4AddAudioTrack(_hMp4, _pPlayer->getAudioSampleRate(), MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE); -// if (_hAudio != MP4_INVALID_TRACK_ID) { -// auto &cfg = _pPlayer->getAudioCfg(); -// MP4SetTrackESConfiguration(_hMp4, _hAudio,(uint8_t *)cfg.data(), cfg.size()); -// }else{ -// WarnL << "添加音频通道失败:" << strFileTmp; -// } -// } + auto audioTrack = dynamic_pointer_cast(getTrack(TrackAudio)); + if(audioTrack){ + _audioSampleRate = audioTrack->getAudioSampleRate(); + _hAudio = MP4AddAudioTrack(_hMp4, _audioSampleRate, MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE); + if (_hAudio != MP4_INVALID_TRACK_ID) { + auto &cfg = audioTrack->getAacCfg(); + MP4SetTrackESConfiguration(_hMp4, _hAudio,(uint8_t *)cfg.data(), cfg.size()); + }else{ + WarnL << "添加音频通道失败:" << strFileTmp; + } + } } void Mp4Maker::closeFile() { @@ -232,6 +232,26 @@ void Mp4Maker::closeFile() { } } +void Mp4Maker::onTrackFrame(const Frame::Ptr &frame) { + switch (frame->getCodecId()){ + case CodecH264:{ + inputH264(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(),frame->stamp()); + } + break; + case CodecAAC:{ + inputAAC(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(),frame->stamp()); + } + break; + + default: + break; + } +} + +void Mp4Maker::onAllTrackReady() { + _haveVideo = getTrack(TrackVideo).operator bool(); +} + } /* namespace mediakit */ diff --git a/src/MediaFile/Mp4Maker.h b/src/MediaFile/Mp4Maker.h index 8cfe3dff..3ff1e699 100644 --- a/src/MediaFile/Mp4Maker.h +++ b/src/MediaFile/Mp4Maker.h @@ -37,12 +37,14 @@ #include "Util/logger.h" #include "Util/TimeTicker.h" #include "Util/TimeTicker.h" +#include "Common/MediaSink.h" +#include "Player/Track.h" + using namespace toolkit; namespace mediakit { -class Mp4Info -{ +class Mp4Info { public: time_t ui64StartedTime; //GMT标准时间,单位秒 time_t ui64TimeLen;//录像长度,单位秒 @@ -55,43 +57,54 @@ public: string strStreamId;//流ID string strVhost;//vhost }; -class Mp4Maker { +class Mp4Maker : public MediaSink{ public: typedef std::shared_ptr Ptr; Mp4Maker(const string &strPath, const string &strVhost , const string &strApp, - const string &strStreamId, - const PlayerBase::Ptr &pPlayer); + const string &strStreamId); virtual ~Mp4Maker(); +private: + /** + * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 + * @param frame + */ + void onTrackFrame(const Frame::Ptr &frame) override ; + + /** + * 所有Track准备好了 + */ + void onAllTrackReady() override; +private: + void createFile(); + void closeFile(); + //时间戳:参考频率1000 - void inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType); + void inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp); //时间戳:参考频率1000 void inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp); + + void inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui64Duration); + void inputAAC_l(void *pData, uint32_t ui32Length, uint32_t ui64Duration); private: MP4FileHandle _hMp4 = MP4_INVALID_FILE_HANDLE; MP4TrackId _hVideo = MP4_INVALID_TRACK_ID; MP4TrackId _hAudio = MP4_INVALID_TRACK_ID; - PlayerBase::Ptr _pPlayer; string _strPath; string _strFile; string _strFileTmp; Ticker _ticker; - SmoothTicker _mediaTicker[2]; - - void createFile(); - void closeFile(); - void _inputH264(void *pData, uint32_t ui32Length, uint32_t ui64Duration, int iType); - void _inputAAC(void *pData, uint32_t ui32Length, uint32_t ui64Duration); string _strLastVideo; string _strLastAudio; uint32_t _ui32LastVideoTime = 0; uint32_t _ui32LastAudioTime = 0; - int _iLastVideoType = 0; - Mp4Info _info; + + bool _haveVideo = false; + int _audioSampleRate; }; } /* namespace mediakit */ diff --git a/src/Player/Track.h b/src/Player/Track.h index c9a34326..81fea5c9 100644 --- a/src/Player/Track.h +++ b/src/Player/Track.h @@ -238,21 +238,29 @@ public: case 5:{ //I if(!_sps.empty()){ - H264Frame::Ptr insertFrame = std::make_shared(); - insertFrame->timeStamp = frame->stamp(); - insertFrame->type = 7; - insertFrame->buffer = _sps; - insertFrame->iPrefixSize = 0; - VideoTrack::inputFrame(insertFrame); + if(!_spsFrame){ + H264Frame::Ptr insertFrame = std::make_shared(); + insertFrame->timeStamp = frame->stamp(); + insertFrame->type = 7; + insertFrame->buffer.assign("\x0\x0\x0\x1"); + insertFrame->buffer.append(_sps); + insertFrame->iPrefixSize = 4; + _spsFrame = insertFrame; + } + VideoTrack::inputFrame(_spsFrame); } if(!_pps.empty()){ - H264Frame::Ptr insertFrame = std::make_shared(); - insertFrame->timeStamp = frame->stamp(); - insertFrame->type = 8; - insertFrame->buffer = _pps; - insertFrame->iPrefixSize = 0; - VideoTrack::inputFrame(insertFrame); + if(!_ppsFrame){ + H264Frame::Ptr insertFrame = std::make_shared(); + insertFrame->timeStamp = frame->stamp(); + insertFrame->type = 8; + insertFrame->buffer.assign("\x0\x0\x0\x1"); + insertFrame->buffer.append(_pps); + insertFrame->iPrefixSize = 4; + _ppsFrame = insertFrame; + } + VideoTrack::inputFrame(_ppsFrame); } VideoTrack::inputFrame(frame); } @@ -282,6 +290,8 @@ private: int _width = 0; int _height = 0; float _fps = 0; + H264Frame::Ptr _spsFrame; + H264Frame::Ptr _ppsFrame; }; /** diff --git a/src/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpToRtspMediaSource.h index 41a8dc6f..381b90c0 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpToRtspMediaSource.h @@ -61,7 +61,7 @@ public: void onGetMetaData(const AMFValue &_metadata) override { try { _pParser.reset(new RtmpDemuxer(_metadata)); - _pRecorder.reset(new MediaRecorder(getVhost(),getApp(),getId(),_pParser,_bEnableHls,_bEnableMp4)); + _pRecorder.reset(new MediaRecorder(getVhost(),getApp(),getId(),_bEnableHls,_bEnableMp4)); //todo(xzl) 修复此处 // _pParser->setOnAudioCB(std::bind(&RtmpToRtspMediaSource::onGetAAC, this, placeholders::_1)); diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspToRtmpMediaSource.h index 76187bd0..b6ed65c6 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspToRtmpMediaSource.h @@ -52,7 +52,7 @@ public: RtspMediaSource::onGetSDP(strSdp); try { _pParser.reset(new RtspDemuxer(_sdpAttr)); - _pRecorder.reset(new MediaRecorder(getVhost(),getApp(),getId(),_pParser,_bEnableHls,_bEnableMp4)); + _pRecorder.reset(new MediaRecorder(getVhost(),getApp(),getId(),_bEnableHls,_bEnableMp4)); //todo(xzl) 修复此处 // _pParser->setOnAudioCB( std::bind(&RtspToRtmpMediaSource::onGetAAC, this, placeholders::_1)); // _pParser->setOnVideoCB( std::bind(&RtspToRtmpMediaSource::onGetH264, this, placeholders::_1));