From 0feac7c9242a55a853a302785d041316ca4d52db Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 17 Dec 2021 22:33:08 +0800 Subject: [PATCH] =?UTF-8?q?MP4:=20mp4=E5=BC=80=E5=A7=8B=E7=82=B9=E6=92=AD?= =?UTF-8?q?=E7=A1=AE=E4=BF=9DMediaSource=E6=B3=A8=E5=86=8C=E5=90=8E?= =?UTF-8?q?=E5=86=8D=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Reader.cpp | 95 +++++++++++++++++++++++----------------- src/Record/MP4Reader.h | 32 +++++++++----- 2 files changed, 77 insertions(+), 50 deletions(-) diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index a69ab28f..38d33a43 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -9,14 +9,16 @@ */ #ifdef ENABLE_MP4 + #include "MP4Reader.h" #include "Common/config.h" #include "Thread/WorkThreadPool.h" + using namespace toolkit; + namespace mediakit { MP4Reader::MP4Reader(const string &vhost, const string &app, const string &stream_id, const string &file_path) { - _poller = WorkThreadPool::Instance().getPoller(); _file_path = file_path; if (_file_path.empty()) { GET_CONFIG(string, recordPath, Record::kFilePath); @@ -37,12 +39,12 @@ MP4Reader::MP4Reader(const string &vhost, const string &app, const string &strea } _muxer = std::make_shared(vhost, app, stream_id, _demuxer->getDurationMS() / 1000.0f, true, true, false, false); auto tracks = _demuxer->getTracks(false); - if(tracks.empty()){ + if (tracks.empty()) { throw std::runtime_error(StrPrinter << "该mp4文件没有有效的track:" << _file_path); } - for(auto &track : tracks){ + for (auto &track : tracks) { _muxer->addTrack(track); - if(track->getTrackType() == TrackVideo){ + if (track->getTrackType() == TrackVideo) { _have_video = true; } } @@ -70,8 +72,8 @@ bool MP4Reader::readSample() { } } - GET_CONFIG(bool, fileRepeat, Record::kFileRepeat); - if (eof && (fileRepeat || _file_repeat)) { + GET_CONFIG(bool, file_repeat, Record::kFileRepeat); + if (eof && (file_repeat || _file_repeat)) { //需要从头开始看 seekTo(0); return true; @@ -80,37 +82,53 @@ bool MP4Reader::readSample() { return !eof; } +bool MP4Reader::readNextSample() { + bool keyFrame = false; + bool eof = false; + auto frame = _demuxer->readFrame(keyFrame, eof); + if (!frame) { + return false; + } + if (_muxer) { + _muxer->inputFrame(frame); + } + setCurrentStamp(frame->dts()); + return true; +} + void MP4Reader::stopReadMP4() { _timer = nullptr; } -void MP4Reader::startReadMP4(const EventPoller::Ptr &poller, uint64_t sample_ms, bool ref_self, bool file_repeat) { +void MP4Reader::startReadMP4(const EventPoller::Ptr &poller_in, uint64_t sample_ms, bool ref_self, bool file_repeat) { GET_CONFIG(uint32_t, sampleMS, Record::kSampleMS); - auto strongSelf = shared_from_this(); - if (_muxer) { _muxer->setMediaListener(strongSelf); } + auto strong_self = shared_from_this(); + if (_muxer) { + _muxer->setMediaListener(strong_self); + //一直读到所有track就绪为止 + while (!_muxer->isAllTrackReady() && readNextSample()) {} + } - //先获取关键帧 - seekTo(0); - //读sampleMS毫秒的数据用于产生MediaSource - setCurrentStamp(getCurrentStamp() + sampleMS); - readSample(); + //未指定线程,那么使用后台线程(读写文件采用后台线程) + auto poller = poller_in ? poller_in : WorkThreadPool::Instance().getPoller(); + auto timer_sec = (sample_ms ? sample_ms : sampleMS) / 1000.0f; //启动定时器 if (ref_self) { - _timer = std::make_shared((sample_ms ? sample_ms : sampleMS) / 1000.0f, [strongSelf]() { - lock_guard lck(strongSelf->_mtx); - return strongSelf->readSample(); - }, poller ? poller : _poller); + _timer = std::make_shared(timer_sec, [strong_self]() { + lock_guard lck(strong_self->_mtx); + return strong_self->readSample(); + }, poller); } else { - weak_ptr weak_self = strongSelf; - _timer = std::make_shared((sample_ms ? sample_ms : sampleMS) / 1000.0f, [weak_self]() { - auto strongSelf = weak_self.lock(); - if (!strongSelf) { + weak_ptr weak_self = strong_self; + _timer = std::make_shared(timer_sec, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { return false; } - lock_guard lck(strongSelf->_mtx); - return strongSelf->readSample(); - }, poller ? poller : _poller); + lock_guard lck(strong_self->_mtx); + return strong_self->readSample(); + }, poller); } _file_repeat = file_repeat; @@ -121,10 +139,10 @@ const MP4Demuxer::Ptr &MP4Reader::getDemuxer() const { } uint32_t MP4Reader::getCurrentStamp() { - return (uint32_t)(_seek_to + !_paused * _speed * _seek_ticker.elapsedTime()); + return (uint32_t) (_seek_to + !_paused * _speed * _seek_ticker.elapsedTime()); } -void MP4Reader::setCurrentStamp(uint32_t new_stamp){ +void MP4Reader::setCurrentStamp(uint32_t new_stamp) { auto old_stamp = getCurrentStamp(); _seek_to = new_stamp; _last_dts = new_stamp; @@ -168,22 +186,21 @@ bool MP4Reader::speed(MediaSource &sender, float speed) { return true; } -bool MP4Reader::seekTo(uint32_t ui32Stamp) { +bool MP4Reader::seekTo(uint32_t stamp_seek) { lock_guard lck(_mtx); - if (ui32Stamp > _demuxer->getDurationMS()) { + if (stamp_seek > _demuxer->getDurationMS()) { //超过文件长度 return false; } - auto stamp = _demuxer->seekTo(ui32Stamp); - if(stamp == -1){ + auto stamp = _demuxer->seekTo(stamp_seek); + if (stamp == -1) { //seek失败 return false; } - if(!_have_video){ - //没有视频,不需要搜索关键帧 - //设置当前时间戳 - setCurrentStamp((uint32_t)stamp); + if (!_have_video) { + //没有视频,不需要搜索关键帧;设置当前时间戳 + setCurrentStamp((uint32_t) stamp); return true; } //搜索到下一帧关键帧 @@ -191,11 +208,11 @@ bool MP4Reader::seekTo(uint32_t ui32Stamp) { bool eof = false; while (!eof) { auto frame = _demuxer->readFrame(keyFrame, eof); - if(!frame){ + if (!frame) { //文件读完了都未找到下一帧关键帧 continue; } - if(keyFrame || frame->keyFrame() || frame->configFrame()){ + if (keyFrame || frame->keyFrame() || frame->configFrame()) { //定位到key帧 if (_muxer) { _muxer->inputFrame(frame); @@ -208,8 +225,8 @@ bool MP4Reader::seekTo(uint32_t ui32Stamp) { return false; } -bool MP4Reader::close(MediaSource &sender,bool force){ - if(!_muxer || (!force && _muxer->totalReaderCount())){ +bool MP4Reader::close(MediaSource &sender, bool force) { + if (!_muxer || (!force && _muxer->totalReaderCount())) { return false; } _timer.reset(); diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index 29337235..fa055387 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -16,27 +16,37 @@ using namespace toolkit; namespace mediakit { -class MP4Reader : public std::enable_shared_from_this ,public MediaSourceEvent{ +class MP4Reader : public std::enable_shared_from_this, public MediaSourceEvent { public: - typedef std::shared_ptr Ptr; - virtual ~MP4Reader() = default; + using Ptr = std::shared_ptr; /** - * 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource + * 点播一个mp4文件,使之转换成MediaSource流媒体 * @param vhost 虚拟主机 * @param app 应用名 - * @param stream_id 流id + * @param stream_id 流id,置空时,只解复用mp4,但是不生成MediaSource * @param file_path 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 */ MP4Reader(const string &vhost, const string &app, const string &stream_id, const string &file_path = ""); + ~MP4Reader() override = default; /** - * 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有, - * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) + * 开始解复用MP4文件 + * @param poller 解复用mp4定时器所绑定线程,置空则随机采用一条后台线程 + * @param sample_ms 每次读取文件数据量,单位毫秒,置0时采用配置文件配置 + * @param ref_self 是否让定时器引用此对象本身,如果无其他对象引用本身,在不循环读文件时,读取文件结束后本对象将自动销毁 + * @param file_repeat 是否循环读取文件,如果配置文件设置为循环读文件,此参数无效 */ void startReadMP4(const EventPoller::Ptr &poller = nullptr, uint64_t sample_ms = 0, bool ref_self = true, bool file_repeat = false); + + /** + * 停止解复用MP4定时器 + */ void stopReadMP4(); + /** + * 获取mp4解复用器 + */ const MP4Demuxer::Ptr& getDemuxer() const; private: @@ -51,9 +61,10 @@ private: string getOriginUrl(MediaSource &sender) const override; bool readSample(); + bool readNextSample(); uint32_t getCurrentStamp(); - void setCurrentStamp(uint32_t ui32Stamp); - bool seekTo(uint32_t ui32Stamp); + void setCurrentStamp(uint32_t stamp); + bool seekTo(uint32_t stamp_seek); private: bool _file_repeat = false; @@ -61,12 +72,11 @@ private: bool _paused = false; float _speed = 1.0; uint32_t _last_dts = 0; - uint32_t _seek_to; + uint32_t _seek_to = 0; string _file_path; recursive_mutex _mtx; Ticker _seek_ticker; Timer::Ptr _timer; - EventPoller::Ptr _poller; MP4Demuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; };