From 61a93fab6ad2d7b7671350298103bb451c2b90d8 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 13 Oct 2024 00:25:40 +0800 Subject: [PATCH] MP4 reader supports loading multi files --- src/Extension/Frame.cpp | 13 +++++- src/Extension/Frame.h | 2 + src/Record/MP4Demuxer.cpp | 88 +++++++++++++++++++++++++++++++++++++++ src/Record/MP4Demuxer.h | 53 +++++++++++++++++++++++ src/Record/MP4Reader.cpp | 4 +- src/Record/MP4Reader.h | 4 +- 6 files changed, 158 insertions(+), 6 deletions(-) diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index ceccced9..080d10ac 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -38,15 +38,24 @@ Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){ return std::make_shared(frame); } -FrameStamp::FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp) -{ +FrameStamp::FrameStamp(Frame::Ptr frame) { setIndex(frame->getIndex()); _frame = std::move(frame); +} + +FrameStamp::FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp) + : FrameStamp(std::move(frame)) { // kModifyStampSystem时采用系统时间戳,kModifyStampRelative采用相对时间戳 [AUTO-TRANSLATED:54dd5685] // When using kModifyStampSystem, the system timestamp is used, and when using kModifyStampRelative, the relative timestamp is used. stamp.revise(_frame->dts(), _frame->pts(), _dts, _pts, modify_stamp == ProtocolOption::kModifyStampSystem); } +void FrameStamp::setStamp(int64_t dts, int64_t pts) { + _dts = dts; + _pts = pts; +} + + TrackType getTrackType(CodecId codecId) { switch (codecId) { #define XX(name, type, value, str, mpeg_id, mp4_id) case name : return type; diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 49ef4766..5bf66038 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -524,6 +524,7 @@ private: class FrameStamp : public Frame { public: using Ptr = std::shared_ptr; + FrameStamp(Frame::Ptr frame); FrameStamp(Frame::Ptr frame, Stamp &stamp, int modify_stamp); ~FrameStamp() override {} @@ -538,6 +539,7 @@ public: char *data() const override { return _frame->data(); } size_t size() const override { return _frame->size(); } CodecId getCodecId() const override { return _frame->getCodecId(); } + void setStamp(int64_t dts, int64_t pts); private: int64_t _dts; diff --git a/src/Record/MP4Demuxer.cpp b/src/Record/MP4Demuxer.cpp index 4d6800d2..187224fc 100644 --- a/src/Record/MP4Demuxer.cpp +++ b/src/Record/MP4Demuxer.cpp @@ -9,7 +9,10 @@ */ #ifdef ENABLE_MP4 + +#include #include "MP4Demuxer.h" +#include "Util/File.h" #include "Util/logger.h" #include "Extension/Factory.h" @@ -189,5 +192,90 @@ uint64_t MP4Demuxer::getDurationMS() const { return _duration_ms; } +///////////////////////////////////////////////////////////////////////////////// + +void MultiMP4Demuxer::openMP4(const string &files_string) { + std::vector files; + if (File::is_dir(files_string)) { + File::scanDir(files_string, [&](const string &path, bool is_dir) { + if (!is_dir) { + files.emplace_back(path); + } + return true; + }); + std::sort(files.begin(), files.end()); + } else { + files = split(files_string, ";"); + } + + uint64_t duration_ms = 0; + for (auto &file : files) { + auto demuxer = std::make_shared(); + demuxer->openMP4(file); + _demuxers.emplace(duration_ms, demuxer); + duration_ms += demuxer->getDurationMS(); + } + CHECK(!_demuxers.empty()); + _it = _demuxers.begin(); + for (auto &track : _it->second->getTracks(false)) { + _tracks.emplace(track->getIndex(), track->clone()); + } +} + +uint64_t MultiMP4Demuxer::getDurationMS() const { + return _demuxers.empty() ? 0 : _demuxers.rbegin()->first + _demuxers.rbegin()->second->getDurationMS(); +} + +void MultiMP4Demuxer::closeMP4() { + _demuxers.clear(); + _it = _demuxers.end(); + _tracks.clear(); +} + +int64_t MultiMP4Demuxer::seekTo(int64_t stamp_ms) { + if (stamp_ms >= (int64_t)getDurationMS()) { + return -1; + } + _it = std::prev(_demuxers.upper_bound(stamp_ms)); + return _it->first + _it->second->seekTo(stamp_ms - _it->first); +} + +Frame::Ptr MultiMP4Demuxer::readFrame(bool &keyFrame, bool &eof) { + for (;;) { + auto ret = _it->second->readFrame(keyFrame, eof); + if (ret) { + auto it = _tracks.find(ret->getIndex()); + if (it != _tracks.end()) { + auto ret2 = std::make_shared(ret); + ret2->setStamp(_it->first + ret->dts(), _it->first + ret->pts()); + ret = std::move(ret2); + it->second->inputFrame(ret); + } + } + if (eof && _it != _demuxers.end()) { + // 切换到下一个文件 + if (++_it == _demuxers.end()) { + // 已经是最后一个文件了 + eof = true; + return nullptr; + } + // 下一个文件从头开始播放 + _it->second->seekTo(0); + continue; + } + return ret; + } +} + +std::vector MultiMP4Demuxer::getTracks(bool trackReady) const { + std::vector ret; + for (auto &pr : _tracks) { + if (!trackReady || pr.second->ready()) { + ret.emplace_back(pr.second); + } + } + return ret; +} + }//namespace mediakit #endif// ENABLE_MP4 diff --git a/src/Record/MP4Demuxer.h b/src/Record/MP4Demuxer.h index 87cce6a2..4d5d168e 100644 --- a/src/Record/MP4Demuxer.h +++ b/src/Record/MP4Demuxer.h @@ -11,9 +11,12 @@ #ifndef ZLMEDIAKIT_MP4DEMUXER_H #define ZLMEDIAKIT_MP4DEMUXER_H #ifdef ENABLE_MP4 + +#include #include "MP4.h" #include "Extension/Track.h" #include "Util/ResourcePool.h" + namespace mediakit { class MP4Demuxer : public TrackSource { @@ -103,6 +106,56 @@ private: toolkit::ResourcePool _buffer_pool; }; +class MultiMP4Demuxer : public TrackSource { +public: + using Ptr = std::shared_ptr; + + ~MultiMP4Demuxer() override = default; + + /** + * 批量打开mp4文件,把多个文件当做一个mp4看待 + * @param file 多个mp4文件路径,以分号分隔; 或者包含多个mp4文件的文件夹 + */ + void openMP4(const std::string &file); + + /** + * @brief 批量关闭 mp4 文件 + */ + void closeMP4(); + + /** + * 移动总体时间轴至某处 + * @param stamp_ms 预期的时间轴总体长度位置,单位毫秒 + * @return 时间轴总体长度位置 + */ + int64_t seekTo(int64_t stamp_ms); + + /** + * 读取一帧数据 + * @param keyFrame 是否为关键帧 + * @param eof 是否所有文件读取完毕 + * @return 帧数据,可能为空 + */ + Frame::Ptr readFrame(bool &keyFrame, bool &eof); + + /** + * 获取第一个文件所有Track信息 + * @param trackReady 是否要求track为就绪状态 + * @return 第一个文件所有Track信息 + */ + std::vector getTracks(bool trackReady) const override; + + /** + * 获取文件总长度 + * @return 文件总长度,单位毫秒 + */ + uint64_t getDurationMS() const; + +private: + std::map _tracks; + std::map::iterator _it; + std::map _demuxers; +}; }//namespace mediakit #endif//ENABLE_MP4 diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 55941be4..1fd41e7a 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -54,7 +54,7 @@ void MP4Reader::setup(const MediaTuple &tuple, const std::string &file_path, con _file_path = File::absolutePath(_file_path, recordPath); } - _demuxer = std::make_shared(); + _demuxer = std::make_shared(); _demuxer->openMP4(_file_path); if (tuple.stream.empty()) { @@ -164,7 +164,7 @@ void MP4Reader::startReadMP4(uint64_t sample_ms, bool ref_self, bool file_repeat _file_repeat = file_repeat; } -const MP4Demuxer::Ptr &MP4Reader::getDemuxer() const { +const MultiMP4Demuxer::Ptr &MP4Reader::getDemuxer() const { return _demuxer; } diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index 2bd95975..b5794fb4 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -68,7 +68,7 @@ public: * [AUTO-TRANSLATED:4f0dfc29] */ - const MP4Demuxer::Ptr& getDemuxer() const; + const MultiMP4Demuxer::Ptr& getDemuxer() const; private: //MediaSourceEvent override @@ -100,7 +100,7 @@ private: std::recursive_mutex _mtx; toolkit::Ticker _seek_ticker; toolkit::Timer::Ptr _timer; - MP4Demuxer::Ptr _demuxer; + MultiMP4Demuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; toolkit::EventPoller::Ptr _poller; };