From 46be8a38e74a94012e991bcfe15db3a9c059c716 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 3 Apr 2020 20:45:58 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99MP4=E7=82=B9=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- Android/app/src/main/cpp/CMakeLists.txt | 10 +- CMakeLists.txt | 18 +- server/WebHook.cpp | 4 +- src/Common/Device.cpp | 4 +- src/Common/MediaSource.cpp | 2 +- src/Extension/AAC.h | 2 +- src/Extension/H265.h | 2 +- src/Player/PlayerProxy.cpp | 2 +- src/Record/MP4Muxer.cpp | 123 ++------ src/Record/MP4Muxer.h | 56 +--- src/Record/MP4Reader.cpp | 389 +++++++----------------- src/Record/MP4Reader.h | 103 +++---- src/Record/MP4Recorder.cpp | 8 +- src/Record/MP4Recorder.h | 6 +- src/Record/Recorder.cpp | 2 +- src/Rtp/RtpProcess.cpp | 2 +- 17 files changed, 205 insertions(+), 530 deletions(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 737b8d85..9f12aee3 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 737b8d852eeb1a36bc77854f327fbbef7cfb81be +Subproject commit 9f12aee385993d14b18a1d773be5eedbbe4fa2c2 diff --git a/Android/app/src/main/cpp/CMakeLists.txt b/Android/app/src/main/cpp/CMakeLists.txt index 2f1fbb0c..42f69762 100644 --- a/Android/app/src/main/cpp/CMakeLists.txt +++ b/Android/app/src/main/cpp/CMakeLists.txt @@ -40,7 +40,7 @@ set(ENABLE_MYSQL false) set(ENABLE_MP4V2 true) set(ENABLE_FAAC false) set(ENABLE_X264 false) -set(ENABLE_MP4RECORD true) +set(ENABLE_MP4 true) #添加两个静态库 if(ENABLE_HLS) @@ -52,9 +52,9 @@ else() endif() -if(ENABLE_MP4RECORD) - message(STATUS "ENABLE_MP4RECORD defined") - add_definitions(-DENABLE_MP4RECORD) +if(ENABLE_MP4) + message(STATUS "ENABLE_MP4 defined") + add_definitions(-DENABLE_MP4) list(APPEND LINK_LIB_LIST mov flv) endif() @@ -87,7 +87,7 @@ if(ENABLE_HLS) endif(WIN32) endif() -if(ENABLE_MP4RECORD) +if(ENABLE_MP4) aux_source_directory(${MediaServer_Root}/libmov/include src_mov) aux_source_directory(${MediaServer_Root}/libmov/source src_mov) include_directories(${MediaServer_Root}/libmov/include) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee853b01..bf4568b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,10 +43,9 @@ INCLUDE_DIRECTORIES(${MediaKit_Root}) set(ENABLE_HLS true) set(ENABLE_OPENSSL true) set(ENABLE_MYSQL false) -set(ENABLE_MP4V2 true) set(ENABLE_FAAC false) set(ENABLE_X264 false) -set(ENABLE_MP4RECORD true) +set(ENABLE_MP4 true) set(ENABLE_RTPPROXY true) set(LINK_LIB_LIST zlmediakit zltoolkit) @@ -70,15 +69,6 @@ if (MYSQL_FOUND AND ENABLE_MYSQL) list(APPEND LINK_LIB_LIST ${MYSQL_LIBRARIES}) endif () -#查找MP4V2是否安装 -find_package(MP4V2 QUIET) -if (MP4V2_FOUND AND ENABLE_MP4V2) - include_directories(${MP4V2_INCLUDE_DIR}) - list(APPEND LINK_LIB_LIST ${MP4V2_LIBRARY}) - add_definitions(-DENABLE_MP4V2) - message(STATUS "found library:${MP4V2_LIBRARY},ENABLE_MP4V2 defined") -endif () - #查找x264是否安装 find_package(X264 QUIET) if (X264_FOUND AND ENABLE_X264) @@ -127,9 +117,9 @@ if(ENABLE_HLS) endif() #添加mov、flv库用于MP4录制 -if(ENABLE_MP4RECORD) - message(STATUS "ENABLE_MP4RECORD defined") - add_definitions(-DENABLE_MP4RECORD) +if(ENABLE_MP4) + message(STATUS "ENABLE_MP4 defined") + add_definitions(-DENABLE_MP4) aux_source_directory(${MediaServer_Root}/libmov/include src_mov) aux_source_directory(${MediaServer_Root}/libmov/source src_mov) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 65933573..025f7cb0 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -362,7 +362,7 @@ void installWebHook(){ do_http_hook(hook_stream_not_found,body, nullptr); }); -#ifdef ENABLE_MP4RECORD +#ifdef ENABLE_MP4 //录制mp4文件成功后广播 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ if(!hook_enable || hook_record_mp4.empty()){ @@ -382,7 +382,7 @@ void installWebHook(){ //执行hook do_http_hook(hook_record_mp4,body, nullptr); }); -#endif //ENABLE_MP4RECORD +#endif //ENABLE_MP4 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){ diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 2c8653c3..2599d010 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -152,12 +152,12 @@ void DevChannel::inputAAC(const char *pcDataWithoutAdts,int iDataLen, uint32_t u uiStamp = (uint32_t)_aTicker[1].elapsedTime(); } if(pcAdtsHeader + 7 == pcDataWithoutAdts){ - inputFrame(std::make_shared((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,7)); + inputFrame(std::make_shared((char *)pcDataWithoutAdts - 7,iDataLen + 7,uiStamp,0,7)); } else { char *dataWithAdts = new char[iDataLen + 7]; memcpy(dataWithAdts,pcAdtsHeader,7); memcpy(dataWithAdts + 7 , pcDataWithoutAdts , iDataLen); - inputFrame(std::make_shared(dataWithAdts,iDataLen + 7,uiStamp,7)); + inputFrame(std::make_shared(dataWithAdts,iDataLen + 7,uiStamp,0,7)); delete [] dataWithAdts; } } diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index e06fc7fe..3d6e901b 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -276,7 +276,7 @@ MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost_tmp if(!ret && bMake){ //未查找媒体源,则创建一个 - ret = MP4Reader::onMakeMediaSource(schema, vhost,app,id); + ret = onMakeMediaSource(schema, vhost,app,id); } return ret; } diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 063756f6..a2cd5fe7 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -112,7 +112,7 @@ class AACFrameNoCacheAble : public FrameNoCacheAble { public: typedef std::shared_ptr Ptr; - AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,int prefixeSize = 7){ + AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,uint32_t pts = 0,int prefixeSize = 7){ _ptr = ptr; _size = size; _dts = dts; diff --git a/src/Extension/H265.h b/src/Extension/H265.h index cfed9412..c3339604 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -268,7 +268,7 @@ public: */ void inputFrame(const Frame::Ptr &frame) override{ int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); - if(type == H265Frame::NAL_VPS){ + if(frame->configFrame()){ bool first_frame = true; splitH264(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 6b0b8be3..fc0e0f0f 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -215,7 +215,7 @@ public: auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS; if(_iAudioIndex != iAudioIndex){ _iAudioIndex = iAudioIndex; - auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS); + auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS, 0); FrameDispatcher::inputFrame(aacFrame); } } diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 69ceb463..f8c5afdc 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -24,53 +24,35 @@ * SOFTWARE. */ -#ifdef ENABLE_MP4RECORD - +#ifdef ENABLE_MP4 #include "MP4Muxer.h" #include "Util/File.h" -#include "Common/config.h" - namespace mediakit{ -#if defined(_WIN32) || defined(_WIN64) - #define fseek64 _fseeki64 - #define ftell64 _ftelli64 -#else - #define fseek64 fseek - #define ftell64 ftell -#endif - -void MP4MuxerBase::init(int flags) { - static struct mov_buffer_t s_io = { - [](void* ctx, void* data, uint64_t bytes) { - MP4MuxerBase *thiz = (MP4MuxerBase *)ctx; - return thiz->onRead(data,bytes); - }, - [](void* ctx, const void* data, uint64_t bytes){ - MP4MuxerBase *thiz = (MP4MuxerBase *)ctx; - return thiz->onWrite(data,bytes); - }, - [](void* ctx, uint64_t offset) { - MP4MuxerBase *thiz = (MP4MuxerBase *)ctx; - return thiz->onSeek(offset); - }, - [](void* ctx){ - MP4MuxerBase *thiz = (MP4MuxerBase *)ctx; - return thiz->onTell(); - } - }; - _mov_writter.reset(mov_writer_create(&s_io,this,flags),[](mov_writer_t *ptr){ - if(ptr){ - mov_writer_destroy(ptr); - } - }); +MP4Muxer::MP4Muxer(const char *file) { + _file_name = file; + openMP4(); +} + +MP4Muxer::~MP4Muxer() { + closeMP4(); +} + +void MP4Muxer::openMP4(){ + closeMP4(); + openFile(_file_name.data(), "wb+"); + _mov_writter = createWriter(); +} +void MP4Muxer::closeMP4(){ + _mov_writter = nullptr; + closeFile(); } -/////////////////////////////////// void MP4Muxer::resetTracks() { _codec_to_trackid.clear(); _started = false; _have_video = false; + openMP4(); } void MP4Muxer::inputFrame(const Frame::Ptr &frame) { @@ -263,70 +245,5 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { } } -MP4MuxerFile::MP4MuxerFile(const char *file){ - _file_name = file; - openFile(file); -} - -void MP4MuxerFile::openFile(const char *file) { - //创建文件 - auto fp = File::createfile_file(file,"wb+"); - if(!fp){ - throw std::runtime_error(string("打开文件失败:") + file); - } - - GET_CONFIG(uint32_t,mp4BufSize,Record::kFileBufSize); - - //新建文件io缓存 - std::shared_ptr file_buf(new char[mp4BufSize],[](char *ptr){ - if(ptr){ - delete [] ptr; - } - }); - - if(file_buf){ - //设置文件io缓存 - setvbuf(fp, file_buf.get(), _IOFBF, mp4BufSize); - } - - //创建智能指针 - _file.reset(fp,[file_buf](FILE *fp) { - fclose(fp); - }); - - GET_CONFIG(bool, mp4FastStart, Record::kFastStart); - init(mp4FastStart ? MOV_FLAG_FASTSTART : 0); -} - -MP4MuxerFile::~MP4MuxerFile() { - _mov_writter = nullptr; -} - -int MP4MuxerFile::onRead(void *data, uint64_t bytes) { - if (bytes == fread(data, 1, bytes, _file.get())){ - return 0; - } - return 0 != ferror(_file.get()) ? ferror(_file.get()) : -1 /*EOF*/; -} - -int MP4MuxerFile::onWrite(const void *data, uint64_t bytes) { - return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get()); -} - -int MP4MuxerFile::onSeek(uint64_t offset) { - return fseek64(_file.get(), offset, SEEK_SET); -} - -uint64_t MP4MuxerFile::onTell() { - return ftell64(_file.get()); -} - - -void MP4MuxerFile::resetTracks(){ - MP4Muxer::resetTracks(); - openFile(_file_name.data()); -} - }//namespace mediakit - -#endif//#ifdef ENABLE_MP4RECORD +#endif//#ifdef ENABLE_MP4 diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index 3285107e..f86d4b6a 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -27,40 +27,21 @@ #ifndef ZLMEDIAKIT_MP4MUXER_H #define ZLMEDIAKIT_MP4MUXER_H -#ifdef ENABLE_MP4RECORD +#ifdef ENABLE_MP4 #include "Common/MediaSink.h" -#include "mov-writer.h" -#include "mpeg4-hevc.h" -#include "mpeg4-avc.h" -#include "mpeg4-aac.h" -#include "mov-buffer.h" -#include "mov-format.h" #include "Extension/AAC.h" #include "Extension/H264.h" #include "Extension/H265.h" #include "Common/Stamp.h" +#include "MP4.h" namespace mediakit{ -class MP4MuxerBase{ +class MP4Muxer : public MediaSinkInterface, public MP4File{ public: - MP4MuxerBase() = default; - virtual ~MP4MuxerBase() = default; -protected: - virtual int onRead(void* data, uint64_t bytes) = 0; - virtual int onWrite(const void* data, uint64_t bytes) = 0; - virtual int onSeek( uint64_t offset) = 0; - virtual uint64_t onTell() = 0; - void init(int flags); -protected: - std::shared_ptr _mov_writter; -}; - -class MP4Muxer : public MediaSinkInterface , public MP4MuxerBase{ -public: - MP4Muxer() = default; - ~MP4Muxer() override = default; + MP4Muxer(const char *file); + ~MP4Muxer() override; /** * 添加已经ready状态的track @@ -75,6 +56,11 @@ public: * 重置所有track */ void resetTracks() override ; + +private: + void openMP4(); + void closeMP4(); + private: struct track_info{ int track_id = -1; @@ -84,28 +70,10 @@ private: List _frameCached; bool _started = false; bool _have_video = false; -}; - - -class MP4MuxerFile : public MP4Muxer { -public: - typedef std::shared_ptr Ptr; - MP4MuxerFile(const char *file); - ~MP4MuxerFile(); - void resetTracks() override ; -protected: - int onRead(void* data, uint64_t bytes) override; - int onWrite(const void* data, uint64_t bytes) override; - int onSeek( uint64_t offset) override; - uint64_t onTell() override ; - void openFile(const char *file); -private: - std::shared_ptr _file; + MP4File::Writer _mov_writter; string _file_name; }; }//namespace mediakit - -#endif//#ifdef ENABLE_MP4RECORD - +#endif//#ifdef ENABLE_MP4 #endif //ZLMEDIAKIT_MP4MUXER_H diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 0011d91b..9111a7e0 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -24,20 +24,13 @@ * SOFTWARE. */ +#ifdef ENABLE_MP4 #include "MP4Reader.h" #include "Common/config.h" -#include "Util/mini.h" -#include "Util/File.h" -#include "Http/HttpSession.h" -#include "Extension/AAC.h" -#include "Extension/H264.h" #include "Thread/WorkThreadPool.h" - using namespace toolkit; - namespace mediakit { -#ifdef ENABLE_MP4V2 MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) { _poller = WorkThreadPool::Instance().getPoller(); auto strFileName = filePath; @@ -52,135 +45,115 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string & strFileName = File::absolutePath(strFileName,recordPath); } - _hMP4File = MP4Read(strFileName.data()); - if(_hMP4File == MP4_INVALID_FILE_HANDLE){ - throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl); - } - _video_trId = MP4FindTrackId(_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0); - if(_video_trId != MP4_INVALID_TRACK_ID){ - if(strcmp(MP4GetTrackMediaDataName(_hMP4File, _video_trId),"avc1") ==0){ - auto _video_timescale = MP4GetTrackTimeScale(_hMP4File, _video_trId); - auto _video_duration = MP4GetTrackDuration(_hMP4File, _video_trId); - _video_num_samples = MP4GetTrackNumberOfSamples(_hMP4File, _video_trId); - _video_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File, _video_trId); - _video_width = MP4GetTrackVideoWidth(_hMP4File, _video_trId); - _video_height = MP4GetTrackVideoHeight(_hMP4File, _video_trId); - _video_framerate = MP4GetTrackVideoFrameRate(_hMP4File, _video_trId); - _pcVideoSample = std::shared_ptr (new uint8_t[_video_sample_max_size],[](uint8_t *ptr){ - delete [] ptr; - }); - uint8_t **seqheader; - uint8_t **pictheader; - uint32_t *pictheadersize; - uint32_t *seqheadersize; - uint32_t ix; - if(MP4GetTrackH264SeqPictHeaders(_hMP4File, _video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){ - for (ix = 0; seqheadersize[ix] != 0; ix++) { - _strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]); - float framerate; - getAVCInfo(_strSps, (int &)_video_width, (int &)_video_height, framerate); - _video_framerate = framerate; - _strSps = string("\x0\x0\x0\x1",4) + _strSps; - MP4Free(seqheader[ix]); - } - MP4Free(seqheader); - MP4Free(seqheadersize); - for (ix = 0; pictheadersize[ix] != 0; ix++) { - _strPps.assign("\x0\x0\x0\x1",4); - _strPps.append((char *)(pictheader[ix]), pictheadersize[ix]); - MP4Free(pictheader[ix]); - } - MP4Free(pictheader); - MP4Free(pictheadersize); - } - _video_ms = 1000.0 * _video_duration / _video_timescale; - /*InfoL << "\r\n" - << _video_ms << "\r\n" - << _video_num_samples << "\r\n" - << _video_framerate << "\r\n" - << _video_width << "\r\n" - << _video_height << "\r\n";*/ - } else { - //如果不是h264,则忽略 - _video_trId = MP4_INVALID_TRACK_ID; + _demuxer = std::make_shared(strFileName.data()); + _mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _demuxer->getDurationMS() / 1000.0, true, true, false, false)); + for(auto &track : _demuxer->getTracks(false)){ + _mediaMuxer->addTrack(track); + if(track->getTrackType() == TrackVideo){ + _have_video = true; } } - - - _audio_trId = MP4FindTrackId(_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0); - if (_audio_trId != MP4_INVALID_TRACK_ID) { - if (strcmp(MP4GetTrackMediaDataName(_hMP4File, _audio_trId), "mp4a") == 0) { - _audio_sample_rate = MP4GetTrackTimeScale(_hMP4File, _audio_trId); - auto _audio_duration = MP4GetTrackDuration(_hMP4File, _audio_trId); - _audio_num_samples = MP4GetTrackNumberOfSamples(_hMP4File,_audio_trId); - _audio_num_channels = MP4GetTrackAudioChannels(_hMP4File, _audio_trId); - _audio_sample_max_size = MP4GetTrackMaxSampleSize(_hMP4File,_audio_trId); - uint8_t *ppConfig; - uint32_t pConfigSize; - if(MP4GetTrackESConfiguration(_hMP4File,_audio_trId,&ppConfig,&pConfigSize)){ - _strAacCfg.assign((char *)ppConfig, pConfigSize); - makeAdtsHeader(_strAacCfg, _adts); - writeAdtsHeader(_adts,_adts.buffer); - getAACInfo(_adts, (int &)_audio_sample_rate, (int &)_audio_num_channels); - MP4Free(ppConfig); - } - _audio_ms = 1000.0 * _audio_duration / _audio_sample_rate; - /*InfoL << "\r\n" - << _audio_ms << "\r\n" - << _audio_num_samples << "\r\n" - << _audio_num_channels << "\r\n" - << _audio_sample_rate << "\r\n";*/ - }else{ - _audio_trId = MP4_INVALID_TRACK_ID; - } - } - if(_audio_trId == MP4_INVALID_TRACK_ID && _video_trId == MP4_INVALID_TRACK_ID){ - MP4Close(_hMP4File); - _hMP4File = MP4_INVALID_FILE_HANDLE; - throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl); - } - - _iDuration = MAX(_video_ms,_audio_ms); - _mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _iDuration / 1000.0, true, true, false, false)); - if (_audio_trId != MP4_INVALID_TRACK_ID) { - AACTrack::Ptr track = std::make_shared(_strAacCfg); - _mediaMuxer->addTrack(track); - } - - if (_video_trId != MP4_INVALID_TRACK_ID) { - H264Track::Ptr track = std::make_shared(_strSps,_strPps); - _mediaMuxer->addTrack(track); - } - //添加完毕所有track,防止单track情况下最大等待3秒 _mediaMuxer->addTrackCompleted(); } - -MP4Reader::~MP4Reader() { - if (_hMP4File != MP4_INVALID_FILE_HANDLE) { - MP4Close(_hMP4File); - _hMP4File = MP4_INVALID_FILE_HANDLE; +bool MP4Reader::readSample() { + bool eof = false; + while (!eof) { + auto frame = _demuxer->readFrame(false, &eof); + if (!frame) { + break; + } + _mediaMuxer->inputFrame(frame); + if (frame->dts() > nextStampForStop()) { + break; + } } -} + if(!eof && _mediaMuxer->totalReaderCount() > 0){ + //文件未看完且观看者大于0个 + _alive.resetTime(); + } + + //重头开始循环读取 + GET_CONFIG(bool,fileRepeat,Record::kFileRepeat); + if(eof && fileRepeat){ + //文件看完了,且需要从头开始看 + seekTo(0); + } + //读取mp4完毕后10秒才销毁对象 + return _alive.elapsedTime() < 10 * 1000; +} void MP4Reader::startReadMP4() { + GET_CONFIG(uint32_t, sampleMS, Record::kSampleMS); auto strongSelf = shared_from_this(); - GET_CONFIG(uint32_t,sampleMS,Record::kSampleMS); - - _timer = std::make_shared(sampleMS / 1000.0f,[strongSelf](){ - return strongSelf->readSample(0,false); - }, _poller); - - //先读sampleMS毫秒的数据用于产生MediaSouce - readSample(sampleMS, false); _mediaMuxer->setListener(strongSelf); + + //先获取关键帧 + seekTo(0); + //设置下次读取停止事件 + setNextStampForStop(_seek_to + sampleMS); + //读sampleMS毫秒的数据用于产生MediaSource + readSample(); + + //启动定时器 + _timer = std::make_shared(sampleMS / 1000.0f, [strongSelf]() { + lock_guard lck(strongSelf->_mtx); + return strongSelf->readSample(); + }, _poller); } - bool MP4Reader::seekTo(MediaSource &sender,uint32_t ui32Stamp){ - seek(ui32Stamp); - return true; + +uint32_t MP4Reader::nextStampForStop() { + return _seek_to + _seek_ticker.elapsedTime(); } + +void MP4Reader::setNextStampForStop(uint32_t ui32Stamp){ + _seek_to = ui32Stamp; + _seek_ticker.resetTime(); + _alive.resetTime(); +} + +bool MP4Reader::seekTo(MediaSource &sender,uint32_t ui32Stamp){ + return seekTo(ui32Stamp); +} + +bool MP4Reader::seekTo(uint32_t ui32Stamp){ + lock_guard lck(_mtx); + if (ui32Stamp > _demuxer->getDurationMS()) { + //超过文件长度 + return false; + } + auto stamp = _demuxer->seekTo(ui32Stamp); + if(stamp == -1){ + //seek失败 + return false; + } + InfoL << stamp; + //设置当前时间戳 + setNextStampForStop(stamp); + + if(!_have_video){ + //没有视频,不需要搜索关键帧 + return true; + } + //搜索到下一帧关键帧 + bool eof = false; + while (!eof) { + auto frame = _demuxer->readFrame(false, &eof); + if(!frame){ + break; + } + if(frame->keyFrame() || frame->configFrame()){ + //定位到key帧 + _mediaMuxer->inputFrame(frame); + setNextStampForStop(frame->dts()); + return true; + } + } + return false; +} + bool MP4Reader::close(MediaSource &sender,bool force){ if(!_mediaMuxer || (!force && _mediaMuxer->totalReaderCount())){ return false; @@ -194,172 +167,32 @@ int MP4Reader::totalReaderCount(MediaSource &sender) { return _mediaMuxer ? _mediaMuxer->totalReaderCount() : sender.readerCount(); } -bool MP4Reader::readSample(int iTimeInc,bool justSeekSyncFrame) { - TimeTicker(); - lock_guard lck(_mtx); - auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完 - auto bFlag1 = readAudioSample(iTimeInc,justSeekSyncFrame);//数据没读完 - auto bFlag2 = _mediaMuxer->totalReaderCount() > 0;//读取者大于0 - if((bFlag0 || bFlag1) && bFlag2){ - _alive.resetTime(); - } - //重头开始循环读取 - GET_CONFIG(bool,fileRepeat,Record::kFileRepeat); - if (fileRepeat && !bFlag0 && !bFlag1) { - seek(0); - } - //DebugL << "alive ..."; - //3秒延时关闭 - return _alive.elapsedTime() < 3 * 1000; -} -inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { - if (_video_trId != MP4_INVALID_TRACK_ID) { - auto iNextSample = getVideoSampleId(iTimeInc); - MP4SampleId iIdx = _video_current; - for (; iIdx < iNextSample; iIdx++) { - uint8_t *pBytes = _pcVideoSample.get(); - uint32_t numBytes = _video_sample_max_size; - MP4Duration pRenderingOffset; - if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){ - if (!justSeekSyncFrame) { - uint32_t dts = (double) _video_ms * iIdx / _video_num_samples; - uint32_t pts = dts + pRenderingOffset / 90; - uint32_t iOffset = 0; - while (iOffset < numBytes) { - uint32_t iFrameLen; - memcpy(&iFrameLen,pBytes + iOffset,4); - iFrameLen = ntohl(iFrameLen); - if(iFrameLen + iOffset + 4> numBytes){ - break; - } - memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4); - writeH264(pBytes + iOffset, iFrameLen + 4, dts, pts); - iOffset += (iFrameLen + 4); - } - }else if(_bSyncSample){ - break; - } - }else{ - ErrorL << "读取视频失败:" << iIdx + 1; - } - } - _video_current = iIdx; - return _video_current < _video_num_samples; - } - return false; -} - -inline bool MP4Reader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) { - if (_audio_trId != MP4_INVALID_TRACK_ID) { - auto iNextSample = getAudioSampleId(iTimeInc); - for (auto i = _audio_current; i < iNextSample; i++) { - uint32_t numBytes = _audio_sample_max_size; - uint8_t *pBytes = _adts.buffer + 7; - if(MP4ReadSample(_hMP4File, _audio_trId, i + 1, &pBytes, &numBytes)){ - if (!justSeekSyncFrame) { - uint32_t dts = (double) _audio_ms * i / _audio_num_samples; - _adts.aac_frame_length = 7 + numBytes; - writeAdtsHeader(_adts, _adts.buffer); - writeAAC(_adts.buffer, _adts.aac_frame_length, dts); - } - }else{ - ErrorL << "读取音频失败:" << i+ 1; - } - } - _audio_current = iNextSample; - return _audio_current < _audio_num_samples; - } - return false; -} - -inline void MP4Reader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) { - _mediaMuxer->inputFrame(std::make_shared((char*)pucData,iLen,dts,pts)); -} - -inline void MP4Reader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) { - _mediaMuxer->inputFrame(std::make_shared((char*)pucData,iLen,uiStamp)); -} - -inline MP4SampleId MP4Reader::getVideoSampleId(int iTimeInc ) { - MP4SampleId video_current = (double)_video_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _video_ms; - video_current = MAX(0,MIN(_video_num_samples, video_current)); - return video_current; - -} - -inline MP4SampleId MP4Reader::getAudioSampleId(int iTimeInc) { - MP4SampleId audio_current = (double)_audio_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _audio_ms ; - audio_current = MAX(0,MIN(_audio_num_samples,audio_current)); - return audio_current; -} -inline void MP4Reader::setSeekTime(uint32_t iSeekTime){ - _iSeekTime = MAX(0, MIN(iSeekTime,_iDuration)); - _ticker.resetTime(); - if (_audio_trId != MP4_INVALID_TRACK_ID) { - _audio_current = getAudioSampleId(); - } - if (_video_trId != MP4_INVALID_TRACK_ID) { - _video_current = getVideoSampleId(); - } -} - -inline uint32_t MP4Reader::getVideoCurrentTime(){ - return (double)_video_current * _video_ms /_video_num_samples; -} -void MP4Reader::seek(uint32_t iSeekTime,bool bReStart){ - lock_guard lck(_mtx); - if(iSeekTime == 0 || _video_trId == MP4_INVALID_TRACK_ID){ - setSeekTime(iSeekTime); - }else{ - setSeekTime(iSeekTime - 5000); - //在之后的10秒查找关键帧 - readVideoSample(10000, true); - if (_bSyncSample) { - //找到关键帧 - auto iIdr = _video_current; - setSeekTime(getVideoCurrentTime()); - _video_current = iIdr; - }else{ - //未找到关键帧 - setSeekTime(iSeekTime); - } - } - _mediaMuxer->setTimeStamp(_iSeekTime); - - if(bReStart){ - _timer.reset(); - startReadMP4(); - } -} - -#endif //ENABLE_MP4V2 +} /* namespace mediakit */ +#endif //ENABLE_MP4 - -MediaSource::Ptr MP4Reader::onMakeMediaSource(const string &strSchema, - const string &strVhost, - const string &strApp, - const string &strId, - const string &filePath, - bool checkApp ){ -#ifdef ENABLE_MP4V2 - GET_CONFIG(string,appName,Record::kAppName); +namespace mediakit { +MediaSource::Ptr onMakeMediaSource(const string &strSchema, + const string &strVhost, + const string &strApp, + const string &strId, + const string &filePath, + bool checkApp) { +#ifdef ENABLE_MP4 + GET_CONFIG(string, appName, Record::kAppName); if (checkApp && strApp != appName) { return nullptr; } try { - MP4Reader::Ptr pReader(new MP4Reader(strVhost,strApp, strId,filePath)); + MP4Reader::Ptr pReader(new MP4Reader(strVhost, strApp, strId, filePath)); pReader->startReadMP4(); - return MediaSource::find(strSchema,strVhost,strApp, strId, false); + return MediaSource::find(strSchema, strVhost, strApp, strId, false); } catch (std::exception &ex) { WarnL << ex.what(); return nullptr; } #else return nullptr; -#endif //ENABLE_MP4V2 +#endif //ENABLE_MP4 } - - - -} /* namespace mediakit */ +}//namespace mediakit diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index 02ded721..8fc3667b 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -26,21 +26,16 @@ #ifndef SRC_MEDIAFILE_MEDIAREADER_H_ #define SRC_MEDIAFILE_MEDIAREADER_H_ - +#ifdef ENABLE_MP4 +#include "MP4Demuxer.h" #include "Common/MultiMediaSourceMuxer.h" -#include "Extension/AAC.h" - -#ifdef ENABLE_MP4V2 -#include -#endif //ENABLE_MP4V2 using namespace toolkit; - namespace mediakit { class MP4Reader : public std::enable_shared_from_this ,public MediaSourceEvent{ public: typedef std::shared_ptr Ptr; - virtual ~MP4Reader(); + virtual ~MP4Reader() = default; /** * 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource @@ -50,82 +45,54 @@ public: * @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 */ MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath = ""); + /** * 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有, * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) */ void startReadMP4(); - - /** - * 自动生成MP4Reader对象然后查找相关的MediaSource对象 - * @param strSchema 协议名 - * @param strVhost 虚拟主机 - * @param strApp 应用名 - * @param strId 流id - * @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 - * @param checkApp 是否检查app,防止服务器上文件被乱访问 - * @return MediaSource - */ - static MediaSource::Ptr onMakeMediaSource(const string &strSchema, - const string &strVhost, - const string &strApp, - const string &strId, - const string &filePath = "", - bool checkApp = true); - private: //MediaSourceEvent override bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; bool close(MediaSource &sender,bool force) override; int totalReaderCount(MediaSource &sender) override; -#ifdef ENABLE_MP4V2 - void seek(uint32_t iSeekTime,bool bReStart = true); - inline void setSeekTime(uint32_t iSeekTime); - inline uint32_t getVideoCurrentTime(); - inline MP4SampleId getVideoSampleId(int iTimeInc = 0); - inline MP4SampleId getAudioSampleId(int iTimeInc = 0); - bool readSample(int iTimeInc, bool justSeekSyncFrame); - inline bool readVideoSample(int iTimeInc,bool justSeekSyncFrame); - inline bool readAudioSample(int iTimeInc,bool justSeekSyncFrame); - inline void writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts); - inline void writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp); + + bool readSample(); + uint32_t nextStampForStop(); + void setNextStampForStop(uint32_t ui32Stamp); + bool seekTo(uint32_t ui32Stamp); private: - MP4FileHandle _hMP4File = MP4_INVALID_FILE_HANDLE; - MP4TrackId _video_trId = MP4_INVALID_TRACK_ID; - uint32_t _video_ms = 0; - uint32_t _video_num_samples = 0; - uint32_t _video_sample_max_size = 0; - uint32_t _video_width = 0; - uint32_t _video_height = 0; - uint32_t _video_framerate = 0; - string _strPps; - string _strSps; - bool _bSyncSample = false; - - MP4TrackId _audio_trId = MP4_INVALID_TRACK_ID; - uint32_t _audio_ms = 0; - uint32_t _audio_num_samples = 0; - uint32_t _audio_sample_max_size = 0; - uint32_t _audio_sample_rate = 0; - uint32_t _audio_num_channels = 0; - string _strAacCfg; - AACFrame _adts; - - int _iDuration = 0; - MultiMediaSourceMuxer::Ptr _mediaMuxer; - MP4SampleId _video_current = 0; - MP4SampleId _audio_current = 0; - std::shared_ptr _pcVideoSample; - - int _iSeekTime = 0 ; - Ticker _ticker; - Ticker _alive; recursive_mutex _mtx; + MultiMediaSourceMuxer::Ptr _mediaMuxer; + uint32_t _seek_to; + Ticker _seek_ticker; + Ticker _alive; Timer::Ptr _timer; EventPoller::Ptr _poller; -#endif //ENABLE_MP4V2 + MP4Demuxer::Ptr _demuxer; + bool _have_video = false; }; } /* namespace mediakit */ +#endif //ENABLE_MP4 + +namespace mediakit { +/** + * 自动生成MP4Reader对象然后查找相关的MediaSource对象 + * @param strSchema 协议名 + * @param strVhost 虚拟主机 + * @param strApp 应用名 + * @param strId 流id + * @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 + * @param checkApp 是否检查app,防止服务器上文件被乱访问 + * @return MediaSource + */ +MediaSource::Ptr onMakeMediaSource(const string &strSchema, + const string &strVhost, + const string &strApp, + const string &strId, + const string &filePath = "", + bool checkApp = true); +} /* namespace mediakit */ #endif /* SRC_MEDIAFILE_MEDIAREADER_H_ */ diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 1247db71..e012cae1 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#ifdef ENABLE_MP4RECORD +#ifdef ENABLE_MP4 #include #include #include "Common/config.h" @@ -69,7 +69,7 @@ void MP4Recorder::createFile() { + strTime + ".mp4"; try { - _muxer = std::make_shared(strFileTmp.data()); + _muxer = std::make_shared(strFileTmp.data()); for(auto &track :_tracks){ //添加track _muxer->addTrack(track); @@ -91,7 +91,7 @@ void MP4Recorder::asyncClose() { //获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间 const_cast(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime; //关闭mp4非常耗时,所以要放在后台线程执行 - const_cast(muxer).reset(); + const_cast(muxer).reset(); //临时文件名改成正式文件名,防止mp4未完成时被访问 rename(strFileTmp.data(),strFile.data()); //获取文件大小 @@ -145,4 +145,4 @@ void MP4Recorder::resetTracks() { } /* namespace mediakit */ -#endif //ENABLE_MP4RECORD +#endif //ENABLE_MP4 diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index 110fa785..0133356b 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -54,7 +54,7 @@ public: string strVhost;//vhost }; -#ifdef ENABLE_MP4RECORD +#ifdef ENABLE_MP4 class MP4Recorder : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; @@ -90,11 +90,11 @@ private: Ticker _createFileTicker; MP4Info _info; bool _haveVideo = false; - MP4MuxerFile::Ptr _muxer; + MP4Muxer::Ptr _muxer; list _tracks; }; -#endif ///ENABLE_MP4RECORD +#endif ///ENABLE_MP4 } /* namespace mediakit */ diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index d5710b9b..c78c7322 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -364,7 +364,7 @@ std::shared_ptr Recorder::createRecorder(type type, const st } case Recorder::type_mp4: { -#if defined(ENABLE_MP4RECORD) +#if defined(ENABLE_MP4) return std::make_shared(path, vhost, app, stream_id); #endif return nullptr; diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 366b7385..5de99950 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -282,7 +282,7 @@ void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d WarnL << "audio track change to AAC from codecid:" << getCodecName(_codecid_audio); return; } - _muxer->inputFrame(std::make_shared((char *) data, bytes, dts, 7)); + _muxer->inputFrame(std::make_shared((char *) data, bytes, dts, 0, 7)); break; } default: