From 071d0a9fd147959f3c16993e665865c6dfb40817 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 20 Sep 2020 19:45:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E7=82=BCMP4=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=92=8C=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4.cpp | 44 +++++++++++-------- src/Record/MP4.h | 88 ++++++++++++++++++++++++++++++++------ src/Record/MP4Demuxer.cpp | 14 +++--- src/Record/MP4Demuxer.h | 52 ++++++++++++++++++---- src/Record/MP4Muxer.cpp | 11 +++-- src/Record/MP4Muxer.h | 13 ++++-- src/Record/MP4Reader.cpp | 3 +- src/Record/MP4Reader.h | 8 ++-- src/Record/MP4Recorder.cpp | 7 +-- 9 files changed, 179 insertions(+), 61 deletions(-) diff --git a/src/Record/MP4.cpp b/src/Record/MP4.cpp index a6a141ed..750a257d 100644 --- a/src/Record/MP4.cpp +++ b/src/Record/MP4.cpp @@ -18,6 +18,8 @@ using namespace toolkit; namespace mediakit { +/////////////////////////////////////////////////mp4_writer_t///////////////////////////////////////////////// + struct mp4_writer_t { int is_fmp4; union { @@ -102,28 +104,32 @@ int mp4_writer_init_segment(mp4_writer_t* mp4){ } } +/////////////////////////////////////////////////MP4FileIO///////////////////////////////////////////////// + static struct mov_buffer_t s_io = { [](void *ctx, void *data, uint64_t bytes) { - MP4File *thiz = (MP4File *) ctx; + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onRead(data, bytes); }, [](void *ctx, const void *data, uint64_t bytes) { - MP4File *thiz = (MP4File *) ctx; + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onWrite(data, bytes); }, [](void *ctx, uint64_t offset) { - MP4File *thiz = (MP4File *) ctx; + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onSeek(offset); }, [](void *ctx) { - MP4File *thiz = (MP4File *) ctx; + MP4FileIO *thiz = (MP4FileIO *) ctx; return thiz->onTell(); } }; -MP4File::Writer MP4File::createWriter(int flags, bool is_fmp4){ +MP4FileIO::Writer MP4FileIO::createWriter(int flags, bool is_fmp4){ Writer writer; - writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[](mp4_writer_t *ptr){ + Ptr self = shared_from_this(); + //保存自己的强引用,防止提前释放 + writer.reset(mp4_writer_create(is_fmp4, &s_io,this, flags),[self](mp4_writer_t *ptr){ if(ptr){ mp4_writer_destroy(ptr); } @@ -134,9 +140,11 @@ MP4File::Writer MP4File::createWriter(int flags, bool is_fmp4){ return writer; } -MP4File::Reader MP4File::createReader(){ +MP4FileIO::Reader MP4FileIO::createReader(){ Reader reader; - reader.reset(mov_reader_create(&s_io,this),[](mov_reader_t *ptr){ + Ptr self = shared_from_this(); + //保存自己的强引用,防止提前释放 + reader.reset(mov_reader_create(&s_io,this),[self](mov_reader_t *ptr){ if(ptr){ mov_reader_destroy(ptr); } @@ -147,15 +155,17 @@ MP4File::Reader MP4File::createReader(){ return reader; } +/////////////////////////////////////////////////////MP4FileDisk///////////////////////////////////////////////////////// + #if defined(_WIN32) || defined(_WIN64) #define fseek64 _fseeki64 -#define ftell64 _ftelli64 + #define ftell64 _ftelli64 #else -#define fseek64 fseek -#define ftell64 ftell + #define fseek64 fseek + #define ftell64 ftell #endif -void MP4File::openFile(const char *file,const char *mode) { +void MP4FileDisk::openFile(const char *file, const char *mode) { //创建文件 auto fp = File::create_file(file, mode); if(!fp){ @@ -183,26 +193,26 @@ void MP4File::openFile(const char *file,const char *mode) { }); } -void MP4File::closeFile() { +void MP4FileDisk::closeFile() { _file = nullptr; } -int MP4File::onRead(void *data, uint64_t bytes) { +int MP4FileDisk::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 MP4File::onWrite(const void *data, uint64_t bytes) { +int MP4FileDisk::onWrite(const void *data, uint64_t bytes) { return bytes == fwrite(data, 1, bytes, _file.get()) ? 0 : ferror(_file.get()); } -int MP4File::onSeek(uint64_t offset) { +int MP4FileDisk::onSeek(uint64_t offset) { return fseek64(_file.get(), offset, SEEK_SET); } -uint64_t MP4File::onTell() { +uint64_t MP4FileDisk::onTell() { return ftell64(_file.get()); } diff --git a/src/Record/MP4.h b/src/Record/MP4.h index e9d49b83..244a83f0 100644 --- a/src/Record/MP4.h +++ b/src/Record/MP4.h @@ -23,6 +23,7 @@ using namespace std; namespace mediakit { +//以下是fmp4/mov的通用接口,简单包装了ireader/media-server的接口 typedef struct mp4_writer_t mp4_writer_t; mp4_writer_t* mp4_writer_create(int is_fmp4, const struct mov_buffer_t *buffer, void* param, int flags); void mp4_writer_destroy(mp4_writer_t* mp4); @@ -34,23 +35,84 @@ int mp4_writer_write_l(mp4_writer_t* mp4, int track, const void* data, size_t by int mp4_writer_save_segment(mp4_writer_t* mp4); int mp4_writer_init_segment(mp4_writer_t* mp4); -class MP4File { +//mp4文件IO的抽象接口类 +class MP4FileIO : public std::enable_shared_from_this { public: - friend struct mov_buffer_t; - typedef std::shared_ptr Writer; - typedef std::shared_ptr Reader; - MP4File() = default; - virtual ~MP4File() = default; + using Ptr = std::shared_ptr; + using Writer = std::shared_ptr; + using Reader = std::shared_ptr; - Writer createWriter(int flags, bool is_fmp4 = false); - Reader createReader(); - void openFile(const char *file,const char *mode); + MP4FileIO() = default; + virtual ~MP4FileIO() = default; + + /** + * 创建mp4复用器 + * @param flags 支持0、MOV_FLAG_FASTSTART、MOV_FLAG_SEGMENT + * @param is_fmp4 是否为fmp4还是普通mp4 + * @return mp4复用器 + */ + virtual Writer createWriter(int flags, bool is_fmp4 = false); + + /** + * 创建mp4解复用器 + * @return mp4解复用器 + */ + virtual Reader createReader(); + + /** + * 获取文件读写位置 + */ + virtual uint64_t onTell() = 0; + + /** + * seek至文件某处 + * @param offset 文件偏移量 + * @return 是否成功(0成功) + */ + virtual int onSeek(uint64_t offset) = 0; + + /** + * 从文件读取一定数据 + * @param data 数据存放指针 + * @param bytes 指针长度 + * @return 是否成功(0成功) + */ + virtual int onRead(void *data, uint64_t bytes) = 0; + + /** + * 写入文件一定数据 + * @param data 数据指针 + * @param bytes 数据长度 + * @return 是否成功(0成功) + */ + virtual int onWrite(const void *data, uint64_t bytes) = 0; +}; + +//磁盘MP4文件类 +class MP4FileDisk : public MP4FileIO { +public: + using Ptr = std::shared_ptr; + MP4FileDisk() = default; + ~MP4FileDisk() override = default; + + /** + * 打开磁盘文件 + * @param file 文件路径 + * @param mode fopen的方式 + */ + void openFile(const char *file, const char *mode); + + /** + * 关闭磁盘文件 + */ void closeFile(); - int onRead(void* data, uint64_t bytes); - int onWrite(const void* data, uint64_t bytes); - int onSeek( uint64_t offset); - uint64_t onTell(); +protected: + uint64_t onTell() override; + int onSeek(uint64_t offset) override; + int onRead(void *data, uint64_t bytes) override; + int onWrite(const void *data, uint64_t bytes) override; + private: std::shared_ptr _file; }; diff --git a/src/Record/MP4Demuxer.cpp b/src/Record/MP4Demuxer.cpp index d77e1734..7854effd 100644 --- a/src/Record/MP4Demuxer.cpp +++ b/src/Record/MP4Demuxer.cpp @@ -19,18 +19,20 @@ using namespace toolkit; namespace mediakit { -MP4Demuxer::MP4Demuxer(const char *file) { - openFile(file,"rb+"); - _mov_reader = createReader(); - getAllTracks(); - _duration_ms = mov_reader_getduration(_mov_reader.get()); -} +MP4Demuxer::MP4Demuxer() {} MP4Demuxer::~MP4Demuxer() { _mov_reader = nullptr; closeFile(); } +void MP4Demuxer::openMP4(const string &file){ + openFile(file.data(),"rb+"); + _mov_reader = createReader(); + getAllTracks(); + _duration_ms = mov_reader_getduration(_mov_reader.get()); +} + int MP4Demuxer::getAllTracks() { static mov_reader_trackinfo_t s_on_track = { [](void *param, uint32_t track, uint8_t object, int width, int height, const void *extra, size_t bytes) { diff --git a/src/Record/MP4Demuxer.h b/src/Record/MP4Demuxer.h index 5565cc22..2512602c 100644 --- a/src/Record/MP4Demuxer.h +++ b/src/Record/MP4Demuxer.h @@ -16,24 +16,60 @@ #include "Util/ResourcePool.h" namespace mediakit { -class MP4Demuxer : public MP4File, public TrackSource{ +class MP4Demuxer : public MP4FileDisk, public TrackSource{ public: typedef std::shared_ptr Ptr; - MP4Demuxer(const char *file); + + /** + * 创建mp4解复用器 + */ + MP4Demuxer(); ~MP4Demuxer() override; + + /** + * 打开文件 + * @param file mp4文件路径 + */ + void openMP4(const string &file); + + /** + * 移动时间轴至某处 + * @param stamp_ms 预期的时间轴位置,单位毫秒 + * @return 时间轴位置 + */ int64_t seekTo(int64_t stamp_ms); + + /** + * 读取一帧数据 + * @param keyFrame 是否为关键帧 + * @param eof 是否文件读取完毕 + * @return 帧数据,可能为空 + */ Frame::Ptr readFrame(bool &keyFrame, bool &eof); - vector getTracks(bool trackReady) const override ; + + /** + * 获取所有Track信息 + * @param trackReady 是否要求track为就绪状态 + * @return 所有Track + */ + vector getTracks(bool trackReady) const override; + + /** + * 获取文件长度 + * @return 文件长度,单位毫秒 + */ uint64_t getDurationMS() const; + private: int getAllTracks(); - void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void* extra, size_t bytes); - void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void* extra, size_t bytes); - Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf,int64_t pts, int64_t dts); + void onVideoTrack(uint32_t track_id, uint8_t object, int width, int height, const void *extra, size_t bytes); + void onAudioTrack(uint32_t track_id, uint8_t object, int channel_count, int bit_per_sample, int sample_rate, const void *extra, size_t bytes); + Frame::Ptr makeFrame(uint32_t track_id, const Buffer::Ptr &buf, int64_t pts, int64_t dts); + private: - MP4File::Reader _mov_reader; + Reader _mov_reader; uint64_t _duration_ms = 0; - map _track_to_codec; + map _track_to_codec; ResourcePool _buffer_pool; }; diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index e8f5f153..1fe8c355 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -14,21 +14,20 @@ #include "Extension/H264.h" namespace mediakit{ -MP4Muxer::MP4Muxer(const char *file) { - _file_name = file; - openMP4(); -} +MP4Muxer::MP4Muxer() {} MP4Muxer::~MP4Muxer() { closeMP4(); } -void MP4Muxer::openMP4(){ +void MP4Muxer::openMP4(const string &file){ + _file_name = file; closeMP4(); openFile(_file_name.data(), "wb+"); GET_CONFIG(bool, mp4FastStart, Record::kFastStart); _mov_writter = createWriter(mp4FastStart ? MOV_FLAG_FASTSTART : 0, false); } + void MP4Muxer::closeMP4(){ _mov_writter = nullptr; closeFile(); @@ -38,7 +37,7 @@ void MP4Muxer::resetTracks() { _codec_to_trackid.clear(); _started = false; _have_video = false; - openMP4(); + openMP4(_file_name); } void MP4Muxer::inputFrame(const Frame::Ptr &frame) { diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index f2e3fbd2..26153fea 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -23,11 +23,11 @@ namespace mediakit{ -class MP4Muxer : public MediaSinkInterface, public MP4File{ +class MP4Muxer : public MediaSinkInterface, public MP4FileDisk{ public: typedef std::shared_ptr Ptr; - MP4Muxer(const char *file); + MP4Muxer(); ~MP4Muxer() override; /** @@ -44,13 +44,18 @@ public: */ void resetTracks() override ; + /** + * 打开mp4 + * @param file 文件完整路径 + */ + void openMP4(const string &file); + /** * 手动关闭文件(对象析构时会自动关闭) */ void closeMP4(); private: - void openMP4(); void stampSync(); private: @@ -62,8 +67,8 @@ private: List _frameCached; bool _started = false; bool _have_video = false; - MP4File::Writer _mov_writter; string _file_name; + Writer _mov_writter; }; }//namespace mediakit diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 1e78c617..ea58e0a3 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -29,7 +29,8 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string & strFileName = File::absolutePath(strFileName,recordPath); } - _demuxer = std::make_shared(strFileName.data()); + _demuxer = std::make_shared(); + _demuxer->openMP4(strFileName); _mediaMuxer.reset(new MultiMediaSourceMuxer(strVhost, strApp, strId, _demuxer->getDurationMS() / 1000.0, true, true, false, false)); auto tracks = _demuxer->getTracks(false); if(tracks.empty()){ diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index bb7e365e..b8348752 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -35,6 +35,7 @@ public: * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) */ void startReadMP4(); + private: //MediaSourceEvent override bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; @@ -45,15 +46,16 @@ private: uint32_t getCurrentStamp(); void setCurrentStamp(uint32_t ui32Stamp); bool seekTo(uint32_t ui32Stamp); + private: - recursive_mutex _mtx; - MultiMediaSourceMuxer::Ptr _mediaMuxer; + bool _have_video = false; uint32_t _seek_to; + recursive_mutex _mtx; Ticker _seek_ticker; Timer::Ptr _timer; EventPoller::Ptr _poller; MP4Demuxer::Ptr _demuxer; - bool _have_video = false; + MultiMediaSourceMuxer::Ptr _mediaMuxer; }; } /* namespace mediakit */ diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 00268052..b9922b17 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -53,15 +53,16 @@ void MP4Recorder::createFile() { + strTime + ".mp4"; try { - _muxer = std::make_shared(strFileTmp.data()); - for(auto &track :_tracks){ + _muxer = std::make_shared(); + _muxer->openMP4(strFileTmp); + for (auto &track :_tracks) { //添加track _muxer->addTrack(track); } _strFileTmp = strFileTmp; _strFile = strFile; _createFileTicker.resetTime(); - }catch(std::exception &ex) { + } catch (std::exception &ex) { WarnL << ex.what(); } }