diff --git a/.gitmodules b/.gitmodules index 7b1ecf89..e9de36ce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ZLToolKit"] - path = ZLToolKit + path = 3rdpart/ZLToolKit url = https://github.com/xiongziliang/ZLToolKit.git +[submodule "3rdpart/media-server"] + path = 3rdpart/media-server + url = https://github.com/xiongziliang/media-server diff --git a/ZLToolKit b/3rdpart/ZLToolKit similarity index 100% rename from ZLToolKit rename to 3rdpart/ZLToolKit diff --git a/3rdpart/media-server b/3rdpart/media-server new file mode 160000 index 00000000..6ae6ece4 --- /dev/null +++ b/3rdpart/media-server @@ -0,0 +1 @@ +Subproject commit 6ae6ece439e54252116e418179bc84e6a0189f67 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d50a6cd..2208221a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,8 @@ set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) #设置工程源码根目录 -set(ToolKit_Root ${CMAKE_SOURCE_DIR}/ZLToolKit/src) +set(ToolKit_Root ${CMAKE_SOURCE_DIR}/3rdpart/ZLToolKit/src) +set(MediaServer_Root ${CMAKE_SOURCE_DIR}/3rdpart/media-server) set(MediaKit_Root ${CMAKE_SOURCE_DIR}/src) #设置头文件目录 @@ -84,6 +85,13 @@ add_definitions(-DENABLE_RING_USEBUF) add_library(zltoolkit STATIC ${ToolKit_src_list}) add_library(zlmediakit STATIC ${MediaKit_src_list}) +#libmpeg +aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg) +aux_source_directory(${MediaServer_Root}/libmpeg/source src_mpeg) +include_directories(${MediaServer_Root}/libmpeg/include) +add_library(mpeg STATIC ${src_mpeg}) + + if (WIN32) list(APPEND LINK_LIB_LIST WS2_32 Iphlpapi shlwapi) elseif(NOT ANDROID OR IOS) diff --git a/src/MediaFile/HLSMaker.cpp b/src/MediaFile/HLSMaker.cpp index 00a5ed44..883252ca 100644 --- a/src/MediaFile/HLSMaker.cpp +++ b/src/MediaFile/HLSMaker.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016 xiongziliang <771730766@qq.com> @@ -24,194 +24,80 @@ * SOFTWARE. */ -#include "HLSMaker.h" -#include "Util/File.h" -#include "Util/uv_errno.h" -#include "Extension/H264.h" -using namespace toolkit; - +#include "HlsMaker.h" namespace mediakit { -HLSMaker::HLSMaker(const string& strM3u8File, - uint32_t ui32BufSize, - uint32_t ui32Duration, - uint32_t ui32Num) { - if (ui32BufSize < 16 * 1024) { - ui32BufSize = 16 * 1024; - } - if(ui32Num < 1){ - ui32Num = 1; - } - _ui32BufSize = ui32BufSize; - _ui64TsCnt = 0; - _strM3u8File = strM3u8File; - _ui32NumSegments = ui32Num; - _ui32SegmentDuration = ui32Duration; - _ui32LastStamp = 0; - - _strOutputPrefix = strM3u8File.substr(0, strM3u8File.rfind('.')); - _strFileName = _strOutputPrefix.substr(_strOutputPrefix.rfind('/') + 1); - _ts.init(_strOutputPrefix + "-0.ts", _ui32BufSize); +HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number) { + seg_number = MAX(1,seg_number); + seg_duration = MAX(1,seg_duration); + _seg_number = seg_number; + _seg_duration = seg_duration; } - -HLSMaker::~HLSMaker() { - _ts.clear(); - string strDir = _strOutputPrefix.substr(0,_strOutputPrefix.rfind('/')); - File::delete_file(strDir.data()); +HlsMaker::~HlsMaker() { } -bool HLSMaker::write_index_file(int iFirstSegment, unsigned int uiLastSegment, int iEnd) { - char acWriteBuf[1024]; - std::shared_ptr pM3u8File(File::createfile_file(_strM3u8File.data(), "w"),[](FILE *fp){ - if(fp){ - fflush(fp); - fclose(fp); - } - }); - if (!pM3u8File) { - WarnL << "Could not open temporary m3u8 index file (" << _strM3u8File << "), no index file will be created"; - return false; - } - if (iFirstSegment < 0) { - iFirstSegment = 0; - } +#define PRINT(...) file_size += snprintf(file_content + file_size,sizeof(file_content) - file_size, ##__VA_ARGS__) + +void HlsMaker::makeIndexFile(bool eof) { + char file_content[4 * 1024]; + int file_size = 0; - //最少1秒 int maxSegmentDuration = 0; - for (auto dur : _iDurations) { - dur /=1000; - if(dur > maxSegmentDuration){ + for (auto &tp : _seg_dur_list) { + int dur = std::get<0>(tp); + if (dur > maxSegmentDuration) { maxSegmentDuration = dur; } } - if (_ui32NumSegments) { - snprintf(acWriteBuf, - sizeof(acWriteBuf), - "#EXTM3U\r\n" - "#EXT-X-VERSION:3\r\n" - "#EXT-X-ALLOW-CACHE:NO\r\n" - "#EXT-X-TARGETDURATION:%u\r\n" - "#EXT-X-MEDIA-SEQUENCE:%u\r\n", - maxSegmentDuration + 1, - iFirstSegment); - } else { - snprintf(acWriteBuf, - sizeof(acWriteBuf), - "#EXTM3U\r\n" - "#EXT-X-VERSION:3\r\n" - "#EXT-X-ALLOW-CACHE:NO\r\n" - "#EXT-X-TARGETDURATION:%u\r\n", - maxSegmentDuration); - } - if (fwrite(acWriteBuf, strlen(acWriteBuf), 1, pM3u8File.get()) != 1) { - WarnL << "Could not write to m3u8 index file, will not continue writing to index file"; - return false; - } + PRINT("#EXTM3U\n" + "#EXT-X-VERSION:3\n" + "#EXT-X-ALLOW-CACHE:NO\n" + "#EXT-X-TARGETDURATION:%u\n" + "#EXT-X-MEDIA-SEQUENCE:%llu\n", + (maxSegmentDuration + 999) / 1000, + _file_index); - for (unsigned int i = iFirstSegment; i < uiLastSegment; i++) { - snprintf(acWriteBuf, - sizeof(acWriteBuf), - "#EXTINF:%.3f,\r\n%s-%u.ts\r\n", - _iDurations[i-iFirstSegment]/1000.0, - _strFileName.data(), - i); - if (fwrite(acWriteBuf, strlen(acWriteBuf), 1, pM3u8File.get()) != 1) { - WarnL << "Could not write to m3u8 index file, will not continue writing to index file"; - return false; - } - } - - if (iEnd) { - snprintf(acWriteBuf, sizeof(acWriteBuf), "#EXT-X-ENDLIST\r\n"); - if (fwrite(acWriteBuf, strlen(acWriteBuf), 1, pM3u8File.get()) != 1) { - WarnL << "Could not write last file and endlist tag to m3u8 index file"; - return false; - } - } - return true; -} - -void HLSMaker::inputH264(const Frame::Ptr &frame) { - auto dts = frame->dts(); - if(_ui32LastStamp == 0){ - _ui32LastStamp = dts; + for (auto &tp : _seg_dur_list) { + PRINT("#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data()); } - int stampInc = dts - _ui32LastStamp; - auto type = H264_TYPE(((uint8_t*)(frame->data() + frame->prefixSize()))[0]); - - switch (type) { - case H264Frame::NAL_SPS: //SPS - if (stampInc >= _ui32SegmentDuration * 1000) { - _ui32LastStamp = dts; - //关闭文件 - _ts.clear(); - auto strTmpFileName = StrPrinter << _strOutputPrefix << '-' << (++_ui64TsCnt) << ".ts" << endl; - if (!_ts.init(strTmpFileName, _ui32BufSize)) { - //创建文件失败 - return; - } - //记录切片时间 - _iDurations.push_back(stampInc); - if(removets()){ - //删除老的时间戳 - _iDurations.pop_front(); - } - write_index_file(_ui64TsCnt - _ui32NumSegments, _ui64TsCnt, 0); - } - case H264Frame::NAL_B_P: //P - //insert aud frame before p and SPS frame - if(dts != _ui32LastFrameStamp){ - _ts.inputH264("\x0\x0\x0\x1\x9\xf0", 6, dts * 90LL , frame->pts() * 90LL); - } - case H264Frame::NAL_IDR: //IDR - case H264Frame::NAL_PPS: //PPS - _ts.inputH264(frame->data(), frame->size(), dts * 90LL , frame->pts() * 90LL); - break; - default: - break; - } - _ui32LastFrameStamp = dts; + if (eof) { + PRINT("#EXT-X-ENDLIST\n"); + } + onWriteHls(file_content, file_size); } -void HLSMaker::inputAAC(const Frame::Ptr &frame) { - _ts.inputAAC(frame->data(), frame->size(), frame->dts() * 90LL , frame->pts() * 90LL); + +void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp) { + addNewFile(timestamp); + onWriteFile((char *) data, len); } -bool HLSMaker::removets() { - if (_ui64TsCnt < _ui32NumSegments + 2) { - return false; - } - File::delete_file((StrPrinter << _strOutputPrefix << "-" - << _ui64TsCnt - _ui32NumSegments - 2 - << ".ts" << endl).data()); - return true; +void HlsMaker::delOldFile() { + //在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致 + if (_file_index >= _seg_number + 2) { + _seg_dur_list.pop_front(); + } + + //但是实际保存的切片个数比m3u8所述多两个,这样做的目的是防止播放器在切片删除前能下载完毕 + if (_file_index >= _seg_number + 4) { + onDelFile(_file_index - _seg_number - 4); + } } -void HLSMaker::onTrackFrame(const Frame::Ptr &frame) { - switch (frame->getCodecId()){ - case CodecH264:{ - if( frame->prefixSize() != 0){ - inputH264(frame); - }else{ - WarnL << "h264必须要有头4个或3个字节的前缀"; - } - } - break; - case CodecAAC:{ - if( frame->prefixSize() == 7) { - inputAAC(frame); - }else{ - WarnL << "adts必须要有头7个字节的adts头"; - } - } - break; - - default: - break; - } +void HlsMaker::addNewFile(uint32_t timestamp) { + int stampInc = timestamp - _stamp_last; + if (stampInc >= _seg_duration * 1000) { + _stamp_last = timestamp; + auto file_name = onOpenFile(_file_index); + if (_file_index++ > 0) { + _seg_dur_list.push_back(std::make_tuple(stampInc, _last_file_name)); + delOldFile(); + makeIndexFile(); + } + _last_file_name = file_name; + } } -} /* namespace mediakit */ - +}//namespace mediakit \ No newline at end of file diff --git a/src/MediaFile/HLSMaker.h b/src/MediaFile/HLSMaker.h index c07d70d9..a82d9eae 100644 --- a/src/MediaFile/HLSMaker.h +++ b/src/MediaFile/HLSMaker.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016 xiongziliang <771730766@qq.com> @@ -24,63 +24,76 @@ * SOFTWARE. */ -#ifndef HLSMAKER_H_ -#define HLSMAKER_H_ +#ifndef HLSMAKER_H +#define HLSMAKER_H #include -#include "TSMaker.h" +#include +#include "Common/config.h" #include "Util/TimeTicker.h" #include "Util/File.h" #include "Util/util.h" #include "Util/logger.h" -#include "Common/config.h" -#include "Common/MediaSink.h" -#include "Extension/Frame.h" - using namespace toolkit; namespace mediakit { -class HLSMaker : public MediaSink{ +class HlsMaker { public: - HLSMaker(const string &strM3u8File, - uint32_t ui32BufSize = 64 * 1024, - uint32_t ui32Duration = 5, - uint32_t ui32Num = 3); - - virtual ~HLSMaker(); - -protected: - /** - * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 - * @param frame + /** + * @param seg_duration 切片文件长度 + * @param seg_number 切片个数 */ - void onTrackFrame(const Frame::Ptr &frame) override ; + HlsMaker(float seg_duration = 5, uint32_t seg_number = 3); + virtual ~HlsMaker(); + + /** + * 写入ts数据 + * @param data 数据 + * @param len 数据长度 + * @param timestamp 毫秒时间戳 + */ + void inputData(void *data, uint32_t len, uint32_t timestamp); +protected: + /** + * 创建ts切片文件回调 + * @param index + * @return + */ + virtual string onOpenFile(int index) = 0; + + /** + * 删除ts切片文件回调 + * @param index + */ + virtual void onDelFile(int index) = 0; + + /** + * 写ts切片文件回调 + * @param data + * @param len + */ + virtual void onWriteFile(const char *data, int len) = 0; + + /** + * 写m3u8文件回调 + * @param data + * @param len + */ + virtual void onWriteHls(const char *data, int len) = 0; private: - void inputH264(const Frame::Ptr &frame); - void inputAAC(const Frame::Ptr &frame); - - bool write_index_file(int iFirstSegment, - unsigned int uiLastSegment, - int iEnd); - - bool removets(); + void delOldFile(); + void addNewFile(uint32_t timestamp); + void makeIndexFile(bool eof = false); private: - TSMaker _ts; - string _strM3u8File; - string _strFileName; - string _strOutputPrefix; - uint32_t _ui32SegmentDuration; - uint32_t _ui32NumSegments; - uint64_t _ui64TsCnt; - uint32_t _ui32BufSize; - uint32_t _ui32LastStamp; - uint32_t _ui32LastFrameStamp = 0; - std::deque _iDurations; - - + string _file_prefix; + float _seg_duration = 0; + uint32_t _seg_number = 0; + uint64_t _file_index = 0; + uint32_t _stamp_last = 0; + string _last_file_name; + std::deque > _seg_dur_list; }; -} /* namespace mediakit */ - -#endif /* HLSMAKER_H_ */ +}//namespace mediakit +#endif //HLSMAKER_H diff --git a/src/MediaFile/HlsMakerImp.cpp b/src/MediaFile/HlsMakerImp.cpp new file mode 100644 index 00000000..6bd0134f --- /dev/null +++ b/src/MediaFile/HlsMakerImp.cpp @@ -0,0 +1,104 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "HlsMakerImp.h" +#include "Util/util.h" +#include "Util/uv_errno.h" +using namespace toolkit; + +namespace mediakit { + +HlsMakerImp::HlsMakerImp(const string &m3u8_file, + const string ¶ms, + uint32_t bufSize, + float seg_duration, + uint32_t seg_number) : HlsMaker(seg_duration, seg_number) { + _path_prefix = m3u8_file.substr(0, m3u8_file.rfind('/')); + _path_hls = m3u8_file; + _params = params; + _buf_size = bufSize; + _file_buf.reset(new char[bufSize],[](char *ptr){ + delete[] ptr; + }); +} + +HlsMakerImp::~HlsMakerImp() { + _file.reset(); + File::delete_file(_path_prefix.data()); +} + +string HlsMakerImp::onOpenFile(int index) { + auto full_path = fullPath(index); + _file = makeFile(full_path, true); + if(!_file){ + WarnL << "create file falied," << full_path << " " << get_uv_errmsg(); + } + //DebugL << index << " " << full_path; + if(_params.empty()){ + return StrPrinter << index << ".ts"; + } + return StrPrinter << index << ".ts" << "?" << _params; +} + +void HlsMakerImp::onDelFile(int index) { + //WarnL << index; + File::delete_file(fullPath(index).data()); +} + +void HlsMakerImp::onWriteFile(const char *data, int len) { + if (_file) { + fwrite(data, len, 1, _file.get()); + } +} + +void HlsMakerImp::onWriteHls(const char *data, int len) { + auto hls = makeFile(_path_hls); + if(hls){ + fwrite(data,len,1,hls.get()); + hls.reset(); + } else{ + WarnL << "create hls file falied," << _path_hls << " " << get_uv_errmsg(); + } + //DebugL << "\r\n" << string(data,len); +} + +string HlsMakerImp::fullPath(int index) { + return StrPrinter << _path_prefix << "/" << index << ".ts"; +} + +std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { + auto ret= shared_ptr(File::createfile_file(file.data(), "wb"), [](FILE *fp) { + if (fp) { + fclose(fp); + } + }); + if(ret && setbuf){ + setvbuf(ret.get(), _file_buf.get(), _IOFBF, _buf_size); + } + return ret; +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/MediaFile/HlsMakerImp.h b/src/MediaFile/HlsMakerImp.h new file mode 100644 index 00000000..4f354748 --- /dev/null +++ b/src/MediaFile/HlsMakerImp.h @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef HLSMAKERIMP_H +#define HLSMAKERIMP_H + +#include +#include +#include +#include "HlsMaker.h" +using namespace std; + +namespace mediakit { + +class HlsMakerImp : public HlsMaker{ +public: + HlsMakerImp(const string &m3u8_file, + const string ¶ms, + uint32_t bufSize = 64 * 1024, + float seg_duration = 5, + uint32_t seg_number = 3); + virtual ~HlsMakerImp(); +protected: + string onOpenFile(int index) override ; + void onDelFile(int index) override; + void onWriteFile(const char *data, int len) override; + void onWriteHls(const char *data, int len) override; +private: + string fullPath(int index); + std::shared_ptr makeFile(const string &file,bool setbuf = false); +private: + std::shared_ptr _file; + std::shared_ptr _file_buf; + string _path_prefix; + string _path_hls; + string _params; + int _buf_size; +}; + +}//namespace mediakit +#endif //HLSMAKERIMP_H diff --git a/src/MediaFile/HlsRecorder.h b/src/MediaFile/HlsRecorder.h new file mode 100644 index 00000000..b4d10ffe --- /dev/null +++ b/src/MediaFile/HlsRecorder.h @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef HLSRECORDER_H +#define HLSRECORDER_H + +#include "HlsMakerImp.h" +#include "TsMuxer.h" + +namespace mediakit { + +class HlsRecorder : public HlsMakerImp , public TsMuxer { +public: + template + HlsRecorder(ArgsType &&...args):HlsMakerImp(std::forward(args)...){} + ~HlsRecorder(){}; +protected: + void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { + inputData((char *)packet,bytes,timestamp); + }; +}; + +}//namespace mediakit + +#endif //HLSRECORDER_H diff --git a/src/MediaFile/MediaRecorder.cpp b/src/MediaFile/MediaRecorder.cpp index e68f6574..a5d977e7 100644 --- a/src/MediaFile/MediaRecorder.cpp +++ b/src/MediaFile/MediaRecorder.cpp @@ -30,6 +30,7 @@ #include "Util/util.h" #include "Util/mini.h" #include "Network/sockutil.h" +#include "HlsMakerImp.h" using namespace toolkit; namespace mediakit { @@ -53,7 +54,7 @@ MediaRecorder::MediaRecorder(const string &strVhost_tmp, if(enableHls) { auto m3u8FilePath = hlsPath + "/" + strVhost + "/" + strApp + "/" + strId + "/hls.m3u8"; - _hlsMaker.reset(new HLSMaker(m3u8FilePath,hlsBufSize, hlsDuration, hlsNum)); + _hlsMaker.reset(new HlsRecorder(m3u8FilePath,string(VHOST_KEY) + "=" + strVhost ,hlsBufSize, hlsDuration, hlsNum)); } #ifdef ENABLE_MP4V2 diff --git a/src/MediaFile/MediaRecorder.h b/src/MediaFile/MediaRecorder.h index 33b7c363..40b70ab8 100644 --- a/src/MediaFile/MediaRecorder.h +++ b/src/MediaFile/MediaRecorder.h @@ -34,7 +34,7 @@ #include "Mp4Maker.h" #endif //ENABLE_MP4V2 -#include "HLSMaker.h" +#include "HlsRecorder.h" using namespace toolkit; namespace mediakit { @@ -62,7 +62,7 @@ public: */ void addTrack(const Track::Ptr & track) override; private: - std::shared_ptr _hlsMaker; + std::shared_ptr _hlsMaker; #ifdef ENABLE_MP4V2 std::shared_ptr _mp4Maker; #endif //ENABLE_MP4V2 diff --git a/src/MediaFile/TSMaker.cpp b/src/MediaFile/TSMaker.cpp deleted file mode 100644 index cfc18cd4..00000000 --- a/src/MediaFile/TSMaker.cpp +++ /dev/null @@ -1,602 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2016 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "TSMaker.h" -#include "Util/logger.h" -using namespace toolkit; - -TSMaker::TSMaker() { - m_pOutVideoTs = NULL; - m_pcFileBuf = NULL; - m_uiWritePacketNum = 0; - m_pVideo_pes = new TsPes(); - m_pAudio_pes = new TsPes(); - m_pVideo_pes->ESlen = 0; - m_pAudio_pes->ESlen = 0; - memset(&m_continuityCounter, 0, sizeof m_continuityCounter); -} - -TSMaker::~TSMaker() { - flush(); - if (m_pOutVideoTs != NULL) { - fflush(m_pOutVideoTs); - fclose(m_pOutVideoTs); - } - if (m_pcFileBuf != NULL) { - delete[] m_pcFileBuf; - } - delete m_pVideo_pes; - delete m_pAudio_pes; -} -void TSMaker::clear() { - flush(); - if (m_pOutVideoTs != NULL) { - fflush(m_pOutVideoTs); - fclose(m_pOutVideoTs); - m_pOutVideoTs = NULL; - } - m_uiWritePacketNum = 0; - m_pVideo_pes->ESlen = 0; - memset(&m_continuityCounter, 0, sizeof m_continuityCounter); - -} -void TSMaker::flush() { - unsigned char acTSbuf[TS_PACKET_SIZE]; - TsPacketHeader ts_header; - if (m_pVideo_pes->ESlen == 0) - return; - unsigned char *pucTs = acTSbuf; - if ((m_uiWritePacketNum % 40) == 0) //每40个包打一个 pat,一个pmt - { - CreatePAT(); //创建PAT - CreatePMT(); //创建PMT - } - memset(acTSbuf, 0, TS_PACKET_SIZE); - CreateTsHeader(&ts_header, TS_H264_PID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载; - TsHeader2buffer(&ts_header, acTSbuf); - pucTs += 4; - pucTs[0] = 184 - m_pVideo_pes->ESlen - 1; - pucTs[1] = 0x00; - pucTs += 2; - memset(pucTs, 0xFF, (184 - m_pVideo_pes->ESlen - 2)); - pucTs += (184 - m_pVideo_pes->ESlen - 2); - memcpy(pucTs, m_pVideo_pes->ES, m_pVideo_pes->ESlen); - m_pVideo_pes->ESlen = 0; - fwrite(acTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件 - m_uiWritePacketNum++; - return; -} - -bool TSMaker::init(const string& filename, uint32_t bufsize) { - m_strFilename = filename; - if (m_pOutVideoTs == NULL) { - m_pOutVideoTs = File::createfile_file(filename.data(), "wb"); - if (m_pOutVideoTs == NULL) { - return false; - } - } - - if (m_pcFileBuf == NULL) { - m_pcFileBuf = new char[bufsize]; - setvbuf(m_pOutVideoTs, m_pcFileBuf, _IOFBF, bufsize); - } - - return true; - -} - -void TSMaker::CreatePAT() { - TsPacketHeader ts_header; - TsPat ts_pat; - unsigned char aucPat[TS_PACKET_SIZE]; - unsigned char * pucPat; - uint32_t ui32PAT_CRC = 0xFFFFFFFF; - - memset(aucPat, 0xFF, TS_PACKET_SIZE); - pucPat = aucPat; - CreateTsHeader(&ts_header, TS_PAT_PID, 0x01, 0x01); //PID = 0x00,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x01,仅有有效负载 ; - TsHeader2buffer(&ts_header, aucPat); - pucPat[4] = 0; //自适应段的长度为0 - pucPat += 5; - ts_pat.table_id = 0x00; - ts_pat.section_syntax_indicator = 0x01; - ts_pat.zero = 0x00; - ts_pat.reserved_1 = 0x03; //设置为11; - ts_pat.section_length = 0x0d; //pat结构体长度 16个字节减去上面的3个字节 - ts_pat.transport_stream_id = 0x01; - ts_pat.reserved_2 = 0x03; //设置为11; - ts_pat.version_number = 0x00; - ts_pat.current_next_indicator = 0x01; //当前的pat 有效 - ts_pat.section_number = 0x00; - ts_pat.last_section_number = 0x00; - ts_pat.program_number = 0x01; - ts_pat.reserved_3 = 0x07; //设置为111; - ts_pat.program_map_PID = TS_PMT_PID; //PMT的PID - ts_pat.CRC_32 = ui32PAT_CRC; //传输过程中检测的一种算法值 先设定一个填充值 - - pucPat[0] = ts_pat.table_id; - pucPat[1] = ts_pat.section_syntax_indicator << 7 | ts_pat.zero << 6 - | ts_pat.reserved_1 << 4 | ((ts_pat.section_length >> 8) & 0x0F); - pucPat[2] = ts_pat.section_length & 0x00FF; - pucPat[3] = ts_pat.transport_stream_id >> 8; - pucPat[4] = ts_pat.transport_stream_id & 0x00FF; - pucPat[5] = ts_pat.reserved_2 << 6 | ts_pat.version_number << 1 - | ts_pat.current_next_indicator; - pucPat[6] = ts_pat.section_number; - pucPat[7] = ts_pat.last_section_number; - pucPat[8] = ts_pat.program_number >> 8; - pucPat[9] = ts_pat.program_number & 0x00FF; - pucPat[10] = ts_pat.reserved_3 << 5 - | ((ts_pat.program_map_PID >> 8) & 0x0F); - pucPat[11] = ts_pat.program_map_PID & 0x00FF; - pucPat += 12; - ui32PAT_CRC = Zwg_ntohl(calc_crc32(aucPat + 5, pucPat - aucPat - 5)); - memcpy(pucPat, (unsigned char *) &ui32PAT_CRC, 4); - fwrite(aucPat, 188, 1, m_pOutVideoTs); //将PAT包写入文件 - return; -} - -void TSMaker::CreatePMT() { - TsPacketHeader ts_header; - TsPmt ts_pmt; - unsigned char aucPmt[TS_PACKET_SIZE]; - unsigned char * pucPmt; - uint32_t ui32PMT_CRC = 0xFFFFFFFF; - int iLen = 0; - - memset(aucPmt, 0xFF, TS_PACKET_SIZE); //将一个包填成0xFF - pucPmt = aucPmt; - - CreateTsHeader(&ts_header, TS_PMT_PID, 0x01, 0x01); //PID = 0x00,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x01,仅有有效负载; - TsHeader2buffer(&ts_header, aucPmt); - pucPmt[4] = 0; //自适应段的长度为0 - pucPmt += 5; - ts_pmt.table_id = 0x02; - ts_pmt.section_syntax_indicator = 0x01; - ts_pmt.zero = 0x00; - ts_pmt.reserved_1 = 0x03; - ts_pmt.section_length = 0x17; //PMT结构体长度 16 + 5 + 5个字节减去上面的3个字节 - ts_pmt.program_number = 01; //只有一个节目 - ts_pmt.reserved_2 = 0x03; - ts_pmt.version_number = 0x00; - ts_pmt.current_next_indicator = 0x01; //当前的PMT有效 - ts_pmt.section_number = 0x00; - ts_pmt.last_section_number = 0x00; - ts_pmt.reserved_3 = 0x07; - ts_pmt.PCR_PID = TS_H264_PID; //视频PID - ts_pmt.reserved_4 = 0x0F; - ts_pmt.program_info_length = 0x00; //后面无 节目信息描述 - ts_pmt.stream_type_video = PMT_STREAM_TYPE_VIDEO; //视频的类型 - ts_pmt.reserved_5_video = 0x07; - ts_pmt.elementary_PID_video = TS_H264_PID; //视频的PID - ts_pmt.reserved_6_video = 0x0F; - ts_pmt.ES_info_length_video = 0x00; //视频无跟随的相关信息 - ts_pmt.stream_type_audio = PMT_STREAM_TYPE_AUDIO; //音频类型 - ts_pmt.reserved_5_audio = 0x07; - ts_pmt.elementary_PID_audio = TS_AAC_PID; //音频PID - ts_pmt.reserved_6_audio = 0x0F; - ts_pmt.ES_info_length_audio = 0x00; //音频无跟随的相关信息 - - ts_pmt.CRC_32 = ui32PMT_CRC; - - pucPmt[0] = ts_pmt.table_id; - pucPmt[1] = ts_pmt.section_syntax_indicator << 7 | ts_pmt.zero << 6 - | ts_pmt.reserved_1 << 4 | ((ts_pmt.section_length >> 8) & 0x0F); - pucPmt[2] = ts_pmt.section_length & 0x00FF; - pucPmt[3] = ts_pmt.program_number >> 8; - pucPmt[4] = ts_pmt.program_number & 0x00FF; - pucPmt[5] = ts_pmt.reserved_2 << 6 | ts_pmt.version_number << 1 - | ts_pmt.current_next_indicator; - pucPmt[6] = ts_pmt.section_number; - pucPmt[7] = ts_pmt.last_section_number; - pucPmt[8] = ts_pmt.reserved_3 << 5 | ((ts_pmt.PCR_PID >> 8) & 0x1F); - pucPmt[9] = ts_pmt.PCR_PID & 0x0FF; - pucPmt[10] = ts_pmt.reserved_4 << 4 - | ((ts_pmt.program_info_length >> 8) & 0x0F); - pucPmt[11] = ts_pmt.program_info_length & 0xFF; - pucPmt[12] = ts_pmt.stream_type_video; //视频流的stream_type - pucPmt[13] = ts_pmt.reserved_5_video << 5 - | ((ts_pmt.elementary_PID_video >> 8) & 0x1F); - pucPmt[14] = ts_pmt.elementary_PID_video & 0x00FF; - pucPmt[15] = ts_pmt.reserved_6_video << 4 - | ((ts_pmt.ES_info_length_video >> 8) & 0x0F); - pucPmt[16] = ts_pmt.ES_info_length_video & 0x0FF; - pucPmt[17] = ts_pmt.stream_type_audio; //音频流的stream_type - pucPmt[18] = ts_pmt.reserved_5_audio << 5 - | ((ts_pmt.elementary_PID_audio >> 8) & 0x1F); - pucPmt[19] = ts_pmt.elementary_PID_audio & 0x00FF; - pucPmt[20] = ts_pmt.reserved_6_audio << 4 - | ((ts_pmt.ES_info_length_audio >> 8) & 0x0F); - pucPmt[21] = ts_pmt.ES_info_length_audio & 0x0FF; - pucPmt += 22; - - iLen = pucPmt - aucPmt - 8 + 4; - iLen = iLen > 0xffff ? 0 : iLen; - *(aucPmt + 6) = 0xb0 | (iLen >> 8); - *(aucPmt + 7) = iLen; - - ui32PMT_CRC = Zwg_ntohl(calc_crc32(aucPmt + 5, pucPmt - aucPmt - 5)); - memcpy(pucPmt, (unsigned char *) &ui32PMT_CRC, 4); - fwrite(aucPmt, 188, 1, m_pOutVideoTs); //将PAT包写入文件 -} - -void TSMaker::CreateTsHeader(TsPacketHeader* pTsHeader, unsigned int uiPID, unsigned char ucPlayInit, unsigned char ucAdaFieldC) { - pTsHeader->sync_byte = TS_SYNC_BYTE; - pTsHeader->tras_error = 0x00; - pTsHeader->play_init = ucPlayInit; - pTsHeader->tras_prio = 0x00; - pTsHeader->PID = uiPID; - pTsHeader->tras_scramb = 0x00; - pTsHeader->ada_field_C = ucAdaFieldC; - - if (uiPID == TS_PAT_PID) { //这是pat的包 - pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_pat % 16); - m_continuityCounter.continuity_counter_pat++; - } else if (uiPID == TS_PMT_PID) { //这是pmt的包 - pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_pmt % 16); - m_continuityCounter.continuity_counter_pmt++; - } else if (uiPID == TS_H264_PID) { //这是H264的包 - pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_video % 16); - m_continuityCounter.continuity_counter_video++; - } else if (uiPID == TS_AAC_PID) { //这是MP3的包 - pTsHeader->conti_cter = (m_continuityCounter.continuity_counter_audio % 16); - m_continuityCounter.continuity_counter_audio++; - } else { //其他包出错,或可扩展 - WarnL << "continuity_counter error packet"; - } -} - -void TSMaker::TsHeader2buffer(TsPacketHeader* pTsHeader, unsigned char* pucBuffer) { - pucBuffer[0] = pTsHeader->sync_byte; - pucBuffer[1] = pTsHeader->tras_error << 7 | pTsHeader->play_init << 6 | - pTsHeader->tras_prio << 5 | ((pTsHeader->PID >> 8) & 0x1f); - pucBuffer[2] = (pTsHeader->PID & 0x00ff); - pucBuffer[3] = pTsHeader->tras_scramb << 6 | pTsHeader->ada_field_C << 4 | pTsHeader->conti_cter; - -} - -void TSMaker::WriteAdaptive_flags_Head( Ts_Adaptation_field * pTsAdaptationField, uint64_t ui64VideoDts) { - //填写自适应段 - pTsAdaptationField->discontinuty_indicator = 0; - pTsAdaptationField->random_access_indicator = 0; - pTsAdaptationField->elementary_stream_priority_indicator = 0; - pTsAdaptationField->PCR_flag = 1; //只用到这个 - pTsAdaptationField->OPCR_flag = 0; - pTsAdaptationField->splicing_point_flag = 0; - pTsAdaptationField->transport_private_data_flag = 0; - pTsAdaptationField->adaptation_field_extension_flag = 0; - - //需要自己算 - pTsAdaptationField->pcr = ui64VideoDts * 300; - pTsAdaptationField->adaptation_field_length = 7; //占用7位 - - pTsAdaptationField->opcr = 0; - pTsAdaptationField->splice_countdown = 0; - pTsAdaptationField->private_data_len = 0; -} - -int TSMaker::inputH264(const char* pcData, uint32_t ui32Len, uint64_t ui64Dts , uint64_t ui64Pts) { - if (m_pOutVideoTs == NULL) { - return false; - } - m_pVideo_pes->ES = const_cast(pcData); - m_pVideo_pes->ESlen = ui32Len; - Ts_Adaptation_field ts_adaptation_field_Head; - WriteAdaptive_flags_Head(&ts_adaptation_field_Head, ui64Dts); //填写自适应段标志帧头 - m_pVideo_pes->packet_start_code_prefix = 0x000001; - m_pVideo_pes->stream_id = TS_H264_STREAM_ID; //E0~EF表示是视频的,C0~DF是音频,H264-- E0 - m_pVideo_pes->marker_bit = 0x02; - m_pVideo_pes->PES_scrambling_control = 0x00; //人选字段 存在,不加扰 - m_pVideo_pes->PES_priority = 0x00; - m_pVideo_pes->data_alignment_indicator = 0x00; - m_pVideo_pes->copyright = 0x00; - m_pVideo_pes->original_or_copy = 0x00; - m_pVideo_pes->PTS_DTS_flags = 0x03; - m_pVideo_pes->ESCR_flag = 0x00; - m_pVideo_pes->ES_rate_flag = 0x00; - m_pVideo_pes->DSM_trick_mode_flag = 0x00; - m_pVideo_pes->additional_copy_info_flag = 0x00; - m_pVideo_pes->PES_CRC_flag = 0x00; - m_pVideo_pes->PES_extension_flag = 0x00; - m_pVideo_pes->PES_header_data_length = 0x0A; //后面的数据包括了PTS和 DTS所占的字节数 - PES2TS(m_pVideo_pes, TS_H264_PID, &ts_adaptation_field_Head, ui64Dts , ui64Pts); - m_pVideo_pes->ESlen = 0; - return ui32Len; -} - -int TSMaker::inputAAC(const char* pcData, uint32_t ui32Len, uint64_t ui64Dts , uint64_t ui64Pts) { - if (m_pOutVideoTs == NULL) { - return 0; - } - m_pAudio_pes->ES = const_cast(pcData); - m_pAudio_pes->ESlen = ui32Len; - Ts_Adaptation_field ts_adaptation_field_Head; - WriteAdaptive_flags_Tail(&ts_adaptation_field_Head); //填写自适应段标志帧头 - m_pAudio_pes->packet_start_code_prefix = 0x000001; - m_pAudio_pes->stream_id = TS_AAC_STREAM_ID; //E0~EF表示是视频的,C0~DF是音频,H264-- E0 - m_pAudio_pes->marker_bit = 0x02; - m_pAudio_pes->PES_scrambling_control = 0x00; //人选字段 存在,不加扰 - m_pAudio_pes->PES_priority = 0x00; - m_pAudio_pes->data_alignment_indicator = 0x00; - m_pAudio_pes->copyright = 0x00; - m_pAudio_pes->original_or_copy = 0x00; - m_pAudio_pes->PTS_DTS_flags = 0x03; - m_pAudio_pes->ESCR_flag = 0x00; - m_pAudio_pes->ES_rate_flag = 0x00; - m_pAudio_pes->DSM_trick_mode_flag = 0x00; - m_pAudio_pes->additional_copy_info_flag = 0x00; - m_pAudio_pes->PES_CRC_flag = 0x00; - m_pAudio_pes->PES_extension_flag = 0x00; - m_pAudio_pes->PES_header_data_length = 0x0A; //后面的数据包括了PTS - PES2TS(m_pAudio_pes, TS_AAC_PID, &ts_adaptation_field_Head,ui64Dts,ui64Pts); - m_pAudio_pes->ESlen = 0; - return ui32Len; -} - -void TSMaker::WriteAdaptive_flags_Tail(Ts_Adaptation_field* pTsAdaptationField) { - //填写自适应段 - pTsAdaptationField->discontinuty_indicator = 0; - pTsAdaptationField->random_access_indicator = 0; - pTsAdaptationField->elementary_stream_priority_indicator = 0; - pTsAdaptationField->PCR_flag = 0; //只用到这个 - pTsAdaptationField->OPCR_flag = 0; - pTsAdaptationField->splicing_point_flag = 0; - pTsAdaptationField->transport_private_data_flag = 0; - pTsAdaptationField->adaptation_field_extension_flag = 0; - - //需要自己算 - pTsAdaptationField->pcr = 0; - pTsAdaptationField->adaptation_field_length = 1; //占用1位标志所用的位 - - pTsAdaptationField->opcr = 0; - pTsAdaptationField->splice_countdown = 0; - pTsAdaptationField->private_data_len = 0; -} - -void TSMaker::CreateAdaptive_Ts(Ts_Adaptation_field * pTsAdaptationField, unsigned char * pucTs, unsigned int uiAdaptiveLength) { - unsigned int uiCurrentAdaptiveLength = 1; //当前已经用的自适应段长度 - unsigned char ucAdaptiveflags = 0; //自适应段的标志 - unsigned char *pucTmp = pucTs; - //填写自适应字段 - if (pTsAdaptationField->adaptation_field_length > 0) { - pucTs += 1; //自适应段的一些标志所占用的1个字节 - uiCurrentAdaptiveLength += 1; - - if (pTsAdaptationField->discontinuty_indicator) { - ucAdaptiveflags |= 0x80; - } - if (pTsAdaptationField->random_access_indicator) { - ucAdaptiveflags |= 0x40; - } - if (pTsAdaptationField->elementary_stream_priority_indicator) { - ucAdaptiveflags |= 0x20; - } - if (pTsAdaptationField->PCR_flag) { - unsigned long long pcr_base; - unsigned int pcr_ext; - - pcr_base = (pTsAdaptationField->pcr / 300); - pcr_ext = (pTsAdaptationField->pcr % 300); - - ucAdaptiveflags |= 0x10; - - pucTs[0] = (pcr_base >> 25) & 0xff; - pucTs[1] = (pcr_base >> 17) & 0xff; - pucTs[2] = (pcr_base >> 9) & 0xff; - pucTs[3] = (pcr_base >> 1) & 0xff; - pucTs[4] = pcr_base << 7 | pcr_ext >> 8 | 0x7e; - pucTs[5] = (pcr_ext) & 0xff; - pucTs += 6; - - uiCurrentAdaptiveLength += 6; - } - if (pTsAdaptationField->OPCR_flag) { - unsigned long long opcr_base; - unsigned int opcr_ext; - - opcr_base = (pTsAdaptationField->opcr / 300); - opcr_ext = (pTsAdaptationField->opcr % 300); - - ucAdaptiveflags |= 0x08; - - pucTs[0] = (opcr_base >> 25) & 0xff; - pucTs[1] = (opcr_base >> 17) & 0xff; - pucTs[2] = (opcr_base >> 9) & 0xff; - pucTs[3] = (opcr_base >> 1) & 0xff; - pucTs[4] = ((opcr_base << 7) & 0x80) - | ((opcr_ext >> 8) & 0x01); - pucTs[5] = (opcr_ext) & 0xff; - pucTs += 6; - uiCurrentAdaptiveLength += 6; - } - if (pTsAdaptationField->splicing_point_flag) { - pucTs[0] = pTsAdaptationField->splice_countdown; - - ucAdaptiveflags |= 0x04; - - pucTs += 1; - uiCurrentAdaptiveLength += 1; - } - if (pTsAdaptationField->private_data_len > 0) { - ucAdaptiveflags |= 0x02; - if (1 + pTsAdaptationField->private_data_len - > static_cast(uiAdaptiveLength - - uiCurrentAdaptiveLength)) { - WarnL << "private_data_len error !"; - return; - } else { - pucTs[0] = pTsAdaptationField->private_data_len; - pucTs += 1; - memcpy(pucTs, pTsAdaptationField->private_data, - pTsAdaptationField->private_data_len); - pucTs += pTsAdaptationField->private_data_len; - - uiCurrentAdaptiveLength += (1 - + pTsAdaptationField->private_data_len); - } - } - if (pTsAdaptationField->adaptation_field_extension_flag) { - ucAdaptiveflags |= 0x01; - pucTs[1] = 1; - pucTs[2] = 0; - uiCurrentAdaptiveLength += 2; - } - *pucTmp = ucAdaptiveflags; //将标志放入内存 - } - return; -} -void TSMaker::PES2TS(TsPes * pTsPes, unsigned int uiPID, Ts_Adaptation_field * pTsAdaptationFieldHead, uint64_t ui64Dts ,uint64_t ui64Pts) { - TsPacketHeader ts_header; - unsigned int uiAdaptiveLength = 0; //要填写0XFF的长度 - unsigned int uiFirstPacketLoadLength = 188 - 4 - 1 - pTsAdaptationFieldHead->adaptation_field_length - 19; //分片包的第一个包的负载长度 - const char * pcNeafBuf = pTsPes->ES; //分片包 总负载的指针 - unsigned char aucTSbuf[TS_PACKET_SIZE]; - unsigned char * pucTSBuf; - bool bFirstPkt = true; - while (true) { - if ((m_uiWritePacketNum++ % 40) == 0) //每40个包打一个 pat,一个pmt - { - CreatePAT(); //创建PAT - CreatePMT(); //创建PMT - } - if (bFirstPkt) { - bFirstPkt = false; - //memset(TSbuf,0,TS_PACKET_SIZE); - pucTSBuf = aucTSbuf; - CreateTsHeader(&ts_header, uiPID, 0x01, 0x03); //PID = TS_H264_PID,有效荷载单元起始指示符_play_init = 0x01, ada_field_C,0x03,含有调整字段和有效负载 ; - TsHeader2buffer(&ts_header, aucTSbuf); - pucTSBuf += 4; //写入TS 头 - if (pTsPes->ESlen > uiFirstPacketLoadLength) { - //计算分片包的第一个包的负载长度 - uiAdaptiveLength = 188 - 4 - 1 - ((pTsPes->ESlen - uiFirstPacketLoadLength) % 184); //要填写0XFF的长度,最后一个包有自适应 - pucTSBuf[0] = pTsAdaptationFieldHead->adaptation_field_length; //自适应字段的长度,自己填写的 - pucTSBuf += 1; - CreateAdaptive_Ts(pTsAdaptationFieldHead, pucTSBuf, (uiAdaptiveLength)); //填写自适应字段 - pucTSBuf += pTsAdaptationFieldHead->adaptation_field_length; //填写自适应段所需要的长度 - } else { - uiAdaptiveLength = uiFirstPacketLoadLength - pTsPes->ESlen; - pucTSBuf[0] = pTsAdaptationFieldHead->adaptation_field_length + uiAdaptiveLength; //自适应字段的长度,自己填写的 - pucTSBuf += 1; - CreateAdaptive_Ts(pTsAdaptationFieldHead, pucTSBuf, uiAdaptiveLength); //填写自适应字段 - pucTSBuf += pTsAdaptationFieldHead->adaptation_field_length; - memset(pucTSBuf, 0xFF, uiAdaptiveLength); - pucTSBuf += uiAdaptiveLength; - uiFirstPacketLoadLength = pTsPes->ESlen; - } - - pTsPes->PES_packet_length = pTsPes->ESlen + pTsPes->PES_header_data_length + 3; - if (TS_H264_PID==uiPID || pTsPes->PES_packet_length > 0xFFFF) { - pTsPes->PES_packet_length = 0; - } - pucTSBuf[0] = (pTsPes->packet_start_code_prefix >> 16) & 0xFF; - pucTSBuf[1] = (pTsPes->packet_start_code_prefix >> 8) & 0xFF; - pucTSBuf[2] = pTsPes->packet_start_code_prefix & 0xFF; - pucTSBuf[3] = pTsPes->stream_id; - pucTSBuf[4] = (pTsPes->PES_packet_length >> 8) & 0xFF; - pucTSBuf[5] = pTsPes->PES_packet_length & 0xFF; - pucTSBuf[6] = pTsPes->marker_bit << 6 - | pTsPes->PES_scrambling_control << 4 - | pTsPes->PES_priority << 3 - | pTsPes->data_alignment_indicator << 2 - | pTsPes->copyright << 1 | pTsPes->original_or_copy; - pucTSBuf[7] = pTsPes->PTS_DTS_flags << 6 | pTsPes->ESCR_flag << 5 - | pTsPes->ES_rate_flag << 4 - | pTsPes->DSM_trick_mode_flag << 3 - | pTsPes->additional_copy_info_flag << 2 - | pTsPes->PES_CRC_flag << 1 | pTsPes->PES_extension_flag; - pucTSBuf += 8; - switch (pTsPes->PTS_DTS_flags) { - case 0x03: //both pts and dts - pucTSBuf[6] = (((0x1 << 4) | ((ui64Dts >> 29) & 0x0E) | 0x01) & 0xff); - pucTSBuf[7] = (((((ui64Dts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff); - pucTSBuf[8] = ((((ui64Dts >> 14) & 0xfffe) | 0x01) & 0xff); - pucTSBuf[9] = ((((ui64Dts << 1) & 0xfffe) | 0x01) >> 8) & 0xff; - pucTSBuf[10] = (((ui64Dts << 1) & 0xfffe) | 0x01) & 0xff; - case 0x02: //pts only - pucTSBuf[1] = (((0x3 << 4) | ((ui64Pts >> 29) & 0x0E) | 0x01) & 0xff); - pucTSBuf[2] = (((((ui64Pts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff); - pucTSBuf[3] = ((((ui64Pts >> 14) & 0xfffe) | 0x01) & 0xff); - pucTSBuf[4] = (((((ui64Pts << 1) & 0xfffe) | 0x01) >> 8) & 0xff); - pucTSBuf[5] = ((((ui64Pts << 1) & 0xfffe) | 0x01) & 0xff); - break; - default: - break; - } - pucTSBuf[0] = pTsPes->PES_header_data_length; - pucTSBuf += (1 + pucTSBuf[0]); - memcpy(pucTSBuf, pcNeafBuf, uiFirstPacketLoadLength); - pcNeafBuf += uiFirstPacketLoadLength; - pTsPes->ESlen -= uiFirstPacketLoadLength; - //将包写入文件 - fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件 - continue; - } - if (pTsPes->ESlen >= 184) { - //处理中间包 - //memset(TSbuf,0,TS_PACKET_SIZE); - pucTSBuf = aucTSbuf; - CreateTsHeader(&ts_header, uiPID, 0x00, 0x01); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x01,仅有有效负载; - TsHeader2buffer(&ts_header, aucTSbuf); - pucTSBuf += 4; - memcpy(pucTSBuf, pcNeafBuf, 184); - pcNeafBuf += 184; - pTsPes->ESlen -= 184; - fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); - continue; - } - if (pTsPes->ESlen == 183) { - //memset(TSbuf,0,TS_PACKET_SIZE); - pucTSBuf = aucTSbuf; - CreateTsHeader(&ts_header, uiPID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载; - TsHeader2buffer(&ts_header, aucTSbuf); - pucTSBuf += 4; - pucTSBuf[0] = 1; - pucTSBuf[1] = 0x00; - pucTSBuf += 2; - memcpy(pucTSBuf, pcNeafBuf, 182); - pTsPes->ESlen = 1; - fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件 - - } - //memset(TSbuf,0,TS_PACKET_SIZE); - pucTSBuf = aucTSbuf; - CreateTsHeader(&ts_header, uiPID, 0x00, 0x03); //PID = TS_H264_PID,不是有效荷载单元起始指示符_play_init = 0x00, ada_field_C,0x03,含有调整字段和有效负载; - TsHeader2buffer(&ts_header, aucTSbuf); - pucTSBuf += 4; - pucTSBuf[0] = 184 - pTsPes->ESlen - 1; - pucTSBuf[1] = 0x00; - pucTSBuf += 2; - - memset(pucTSBuf, 0xFF, (184 - pTsPes->ESlen - 2)); - pucTSBuf += (184 - pTsPes->ESlen - 2); - - memcpy(pucTSBuf, pcNeafBuf, pTsPes->ESlen); //183就丢弃一字节 - pTsPes->ESlen = 0; - fwrite(aucTSbuf, 188, 1, m_pOutVideoTs); //将一包数据写入文件 - break; - } -} - diff --git a/src/MediaFile/TSMaker.h b/src/MediaFile/TSMaker.h deleted file mode 100644 index 20de570e..00000000 --- a/src/MediaFile/TSMaker.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2016 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef TSMAKER_H_ -#define TSMAKER_H_ - -#include "crc32.h" -#include -#include -#include -#include -#include -#include "Util/File.h" -using namespace std; -using namespace toolkit; - -#define TS_PACKET_SIZE 188 -#define TS_PACKET_HEADER 4 -#define TS_SYNC_BYTE 0x47 -#define TS_PAT_PID 0x00 -#define TS_PMT_PID 0xFFF -#define TS_H264_PID 0x100 -#define TS_AAC_PID 0x101 -#define TS_H264_STREAM_ID 0xE0 -#define TS_AAC_STREAM_ID 0xC0 -#define PMT_STREAM_TYPE_VIDEO 0x1B -#define PMT_STREAM_TYPE_AUDIO 0x0F - -//#define ES_BUF_SIZE 256*1024 - -//ts 包头 -typedef struct Tag_PacketHeader { - unsigned char sync_byte :8; //同步字节, 固定为0x47,表示后面的是一个TS分组 - unsigned char tras_error :1; //传输误码指示符 - unsigned char play_init :1; //有效荷载单元起始指示符 - unsigned char tras_prio :1; //传输优先, 1表示高优先级,传输机制可能用到,解码用不着 - unsigned int PID :13; //PID - unsigned char tras_scramb :2; //传输加扰控制 - unsigned char ada_field_C :2; //自适应控制 01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载,先调整字段然后有效负载。为00解码器不进行处理 - unsigned char conti_cter :4; //连续计数器 一个4bit的计数器,范围0-15 -} TsPacketHeader; - -//连续性计数器,也就是说 有多少个 pat包,几个pmt包 ,几个MP3 包,几个 h264包,0x00 - 0x0f ,然后折回到0x00 -typedef struct Tag_Continuity_Counter { - unsigned char continuity_counter_pat; - unsigned char continuity_counter_pmt; - unsigned char continuity_counter_video; - unsigned char continuity_counter_audio; -} Continuity_Counter; - -//自适应段标志 -typedef struct Tag_Ts_Adaptation_field { - unsigned char discontinuty_indicator :1; //1表明当前传送流分组的不连续状态为真 - unsigned char random_access_indicator :1; //表明下一个有相同PID的PES分组应该含有PTS字段和一个原始流访问点 - unsigned char elementary_stream_priority_indicator :1; //优先级 - unsigned char PCR_flag :1; //包含pcr字段 - unsigned char OPCR_flag :1; //包含opcr字段 - unsigned char splicing_point_flag :1; //拼接点标志 - unsigned char transport_private_data_flag :1; //私用字节 - unsigned char adaptation_field_extension_flag :1; //调整字段有扩展 - - unsigned char adaptation_field_length; //自适应段长度 - unsigned long long pcr; //自适应段中用到的的pcr - unsigned long long opcr; //自适应段中用到的的opcr - unsigned char splice_countdown; - unsigned char private_data_len; - unsigned char private_data[256]; -} Ts_Adaptation_field; - -//PAT结构体:节目相关表 -typedef struct Tag_TsPat { - unsigned char table_id :8; //固定为0x00 ,标志是该表是PAT - unsigned char section_syntax_indicator :1; //段语法标志位,固定为1 - unsigned char zero :1; //0 - unsigned char reserved_1 :2; //保留位 - unsigned int section_length :12; //表示这个字节后面有用的字节数,包括CRC32 - unsigned int transport_stream_id :16; //该传输流的ID,区别于一个网络中其它多路复用的流 - unsigned char reserved_2 :2; //保留位 - unsigned char version_number :5; //范围0-31,表示PAT的版本号 - unsigned char current_next_indicator :1; //发送的PAT是当前有效还是下一个PAT有效 - unsigned char section_number :8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段 - unsigned char last_section_number :8; //最后一个分段的号码 - unsigned int program_number :16; //节目号 - unsigned char reserved_3 :3; //保留位 - //unsigned int network_PID :13 ; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID,本例中不含有 networke_pid - unsigned int program_map_PID :13; //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个 - unsigned long long CRC_32 :32; //CRC32校验码 -} TsPat; - -//PMT结构体:节目映射表 -typedef struct Tag_TsPmt { - unsigned char table_id :8; //固定为0x02, 表示PMT表 - unsigned char section_syntax_indicator :1; //固定为0x01 - unsigned char zero :1; //0x00 - unsigned char reserved_1 :2; //0x03 - unsigned int section_length :12; //首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。 - unsigned int program_number :16; // 指出该节目对应于可应用的Program map PID - unsigned char reserved_2 :2; //0x03 - unsigned char version_number :5; //指出TS流中Program map section的版本号 - unsigned char current_next_indicator :1; //当该位置1时,当前传送的Program map section可用;当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。 - unsigned char section_number :8; //固定为0x00 - unsigned char last_section_number :8; //固定为0x00 - unsigned char reserved_3 :3; //0x07 - unsigned int PCR_PID :13; //指明TS包的PID值,该TS包含有PCR域,该PCR值对应于由节目号指定的对应节目。如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。 - unsigned char reserved_4 :4; //预留为0x0F - unsigned int program_info_length :12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。 - unsigned char stream_type_video :8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定 - unsigned char reserved_5_video :3; //0x07 - unsigned int elementary_PID_video :13; //该域指示TS包的PID值。这些TS包含有相关的节目元素 - unsigned char reserved_6_video :4; //0x0F - unsigned int ES_info_length_video :12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数 - unsigned char stream_type_audio :8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定 - unsigned char reserved_5_audio :3; //0x07 - unsigned int elementary_PID_audio :13; //该域指示TS包的PID值。这些TS包含有相关的节目元素 - unsigned char reserved_6_audio :4; //0x0F - unsigned int ES_info_length_audio :12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数 - unsigned long long CRC_32 :32; //CRC32校验码 -} TsPmt; - -//PTS_DTS结构体:本程序设置都有 “11” -typedef struct Tag_TsPtsDts { - unsigned char reserved_1 :4; - unsigned char pts_32_30 :3; //显示时间戳 - unsigned char marker_bit1 :1; - unsigned int pts_29_15 :15; - unsigned char marker_bit2 :1; - unsigned int pts_14_0 :15; - unsigned char marker_bit3 :1; - unsigned char reserved_2 :4; - unsigned char dts_32_30 :3; //解码时间戳 - unsigned char marker_bit4 :1; - unsigned int dts_29_15 :15; - unsigned char marker_bit5 :1; - unsigned int dts_14_0 :15; - unsigned char marker_bit6 :1; -} TsPtsDts; - -//PES包结构体,包括PES包头和ES数据 ,头 19 个字节 -typedef struct Tag_TsPes { - unsigned int packet_start_code_prefix :24; //起始:0x000001 - unsigned char stream_id :8; //基本流的类型和编号 - unsigned int PES_packet_length :16; //包长度,就是帧数据的长度,可能为0,要自己算,做多16位,如果超出则需要自己算 - unsigned char marker_bit :2; //必须是:'10' - unsigned char PES_scrambling_control :2; //pes包有效载荷的加扰方式 - unsigned char PES_priority :1; //有效负载的优先级 - unsigned char data_alignment_indicator :1; //如果设置为1表明PES包的头后面紧跟着视频或音频syncword开始的代码。 - unsigned char copyright :1; //1:靠版权保护,0:不靠 - unsigned char original_or_copy :1; //1;有效负载是原始的,0:有效负载时拷贝的 - unsigned char PTS_DTS_flags :2; //'10':PTS字段存在,‘11’:PTD和DTS都存在,‘00’:都没有,‘01’:禁用。 - unsigned char ESCR_flag :1; //1:escr基准字段 和 escr扩展字段均存在,0:无任何escr字段存在 - unsigned char ES_rate_flag :1; //1:es_rate字段存在,0 :不存在 - unsigned char DSM_trick_mode_flag :1; //1;8比特特接方式字段存在,0 :不存在 - unsigned char additional_copy_info_flag :1; //1:additional_copy_info存在,0: 不存在 - unsigned char PES_CRC_flag :1; //1:crc字段存在,0:不存在 - unsigned char PES_extension_flag :1; //1:扩展字段存在,0:不存在 - unsigned char PES_header_data_length :8; //后面数据的长度, - //TsPtsDts tsptsdts; //ptsdts结构体对象,10个字节 - char *ES; - unsigned long ESlen; -} TsPes; - -/*//H264一帧数据的结构体 - typedef struct Tag_NALU_t { - unsigned char forbidden_bit; //! Should always be FALSE - unsigned char nal_reference_idc; //! NALU_PRIORITY_xxxx - unsigned char nal_unit_type; //! NALU_TYPE_xxxx - unsigned int startcodeprefix_len; //! 前缀字节数 - unsigned int len; //! 包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度 - unsigned int max_size; //! 做多一个nal 的长度 - unsigned char * buf; //! 包含nal 头的nal 数据 - unsigned int lost_packets; //! 预留 - } NALU_t;*/ - -//nal类型 -typedef enum { - NALU_TYPE_SLICE = 1, - NALU_TYPE_DPA = 2, - NALU_TYPE_DPB = 3, - NALU_TYPE_DPC = 4, - NALU_TYPE_IDR = 5, - NALU_TYPE_SEI = 6, - NALU_TYPE_SPS = 7, - NALU_TYPE_PPS = 8, - NALU_TYPE_AUD = 9, - NALU_TYPE_EOSEQ = 10, - NALU_TYPE_EOSTREAM = 11, - NALU_TYPE_FILL = 12, -#if (MVC_EXTENSION_ENABLE) -NALU_TYPE_PREFIX = 14, -NALU_TYPE_SUB_SPS = 15, -NALU_TYPE_SLC_EXT = 20, -NALU_TYPE_VDRD = 24 // View and Dependency Representation Delimiter NAL Unit -#endif -} NaluType; - -/*//MP3头结构体 - typedef struct Tag_Mp3_Header { - unsigned int sync :11; //同步信息 - unsigned char version :2; //版本 - unsigned char layer :2; //层 - unsigned char error_protection :1; //CRC校验 - unsigned char bitrate_index :4; //位率 - unsigned char sampling_frequency :2; //采样频率 - unsigned char padding :1; //帧长调节 - unsigned char private_t :1; //保留字 - unsigned char mode :2; //声道模式 - unsigned char mode_extension :2; //扩展模式 - unsigned char copyright :1; //版权 - unsigned char original :1; //原版标志 - unsigned char emphasis :2; //强调模式 - } Mp3_Header;*/ - - -class TSMaker { -public: - TSMaker(); - virtual ~TSMaker(); - bool init(const string &strFilename, uint32_t ui32BufSize); - int inputH264(const char *pcData, uint32_t ui32Len, uint64_t ui64Dts , uint64_t ui64Pts); - int inputAAC(const char *pcData, uint32_t ui32Len, uint64_t ui64Dts , uint64_t ui64Pts); - void clear(); -private: - string m_strFilename; - FILE *m_pOutVideoTs; - Continuity_Counter m_continuityCounter; - TsPes *m_pVideo_pes; - TsPes *m_pAudio_pes; - unsigned int m_uiWritePacketNum; - char *m_pcFileBuf; - - void flush(); - void CreateTsHeader(TsPacketHeader * pTsHeader, unsigned int uiPID, unsigned char ucPlayInit, unsigned char ucAdaFieldC); - void TsHeader2buffer(TsPacketHeader * pTsHeader, unsigned char *pucBuffer); - void CreatePAT(); - void CreatePMT(); - - void WriteAdaptive_flags_Head(Ts_Adaptation_field * pAdaptationField, uint64_t ui64VideoPts); - void WriteAdaptive_flags_Tail(Ts_Adaptation_field * pAdaptationField); //填写自适应段标志帧尾的 - - void PES2TS(TsPes * pPes, unsigned int uiPID, Ts_Adaptation_field * pAdaptationField, uint64_t ui64Dts ,uint64_t ui64Pts ); - void CreateAdaptive_Ts(Ts_Adaptation_field * pAdaptationField, unsigned char * pcTs, unsigned int uiAdaptiveLength); -}; - -#endif /* TSMAKER_H_ */ diff --git a/src/MediaFile/TsMuxer.cpp b/src/MediaFile/TsMuxer.cpp new file mode 100644 index 00000000..999d11ea --- /dev/null +++ b/src/MediaFile/TsMuxer.cpp @@ -0,0 +1,124 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "TsMuxer.h" +#include +#include "mpeg-ts-proto.h" + +namespace mediakit { + +TsMuxer::TsMuxer() { + init(); +} + +TsMuxer::~TsMuxer() { + uninit(); +} + +void TsMuxer::addTrack(const Track::Ptr &track) { + switch (track->getCodecId()){ + case CodecH264: + _codecid_to_stream_id[CodecH264] = mpeg_ts_add_stream(_context,PSI_STREAM_H264, nullptr,0); + break; + case CodecH265: + _codecid_to_stream_id[CodecH265] = mpeg_ts_add_stream(_context,PSI_STREAM_H265, nullptr,0); + break; + case CodecAAC: + _codecid_to_stream_id[CodecAAC] = mpeg_ts_add_stream(_context,PSI_STREAM_AAC, nullptr,0); + break; + default: + break; + } +} + +void TsMuxer::inputFrame(const Frame::Ptr &frame) { + auto it = _codecid_to_stream_id.find(frame->getCodecId()); + if(it == _codecid_to_stream_id.end()){ + return; + } + switch (frame->getCodecId()){ + case CodecH265: + case CodecH264: { + //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, + if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { + Frame::Ptr back = _frameCached.back(); + Buffer::Ptr merged_frame = back; + if(_frameCached.size() != 1){ + string merged; + _frameCached.for_each([&](const Frame::Ptr &frame){ + merged.append(frame->data(),frame->size()); + }); + merged_frame = std::make_shared(std::move(merged)); + } + _timestamp = back->dts(); + mpeg_ts_write(_context, it->second, back->keyFrame() ? 0x0001 : 0, back->pts() * 90LL, back->dts() * 90LL, merged_frame->data(), merged_frame->size()); + _frameCached.clear(); + } + _frameCached.emplace_back(frame); + } + break; + default: { + _timestamp = frame->dts(); + mpeg_ts_write(_context, it->second, frame->keyFrame() ? 0x0001 : 0, frame->pts() * 90LL, frame->dts() * 90LL, frame->data(), frame->size()); + } + break; + } +} + +void TsMuxer::resetTracks() { + uninit(); + init(); +} + +void TsMuxer::init() { + static mpeg_ts_func_t s_func= { + [](void* param, size_t bytes){ + TsMuxer *muxer = (TsMuxer *)param; + assert(sizeof(TsMuxer::_tsbuf) >= bytes); + return (void *)muxer->_tsbuf; + }, + [](void* param, void* packet){ + //do nothing + }, + [](void* param, const void* packet, size_t bytes){ + TsMuxer *muxer = (TsMuxer *)param; + muxer->onTs(packet, bytes,muxer->_timestamp,0); + } + }; + if(_context == nullptr){ + _context = mpeg_ts_create(&s_func,this); + } +} + +void TsMuxer::uninit() { + if(_context){ + mpeg_ts_destroy(_context); + _context = nullptr; + } + _codecid_to_stream_id.clear(); +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/MediaFile/TsMuxer.h b/src/MediaFile/TsMuxer.h new file mode 100644 index 00000000..188de897 --- /dev/null +++ b/src/MediaFile/TsMuxer.h @@ -0,0 +1,191 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TSMUXER_H +#define TSMUXER_H + +#include +#include "mpeg-ts.h" +#include "Extension/Frame.h" +#include "Extension/Track.h" +#include "Util/File.h" +using namespace toolkit; + +namespace mediakit { + +class TsMuxer { +public: + TsMuxer(); + virtual ~TsMuxer(); + void addTrack(const Track::Ptr &track); + void inputFrame(const Frame::Ptr &frame); +protected: + virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0; + void resetTracks(); +private: + void init(); + void uninit(); +private: + void *_context = nullptr; + char *_tsbuf[188]; + uint32_t _timestamp = 0; + unordered_map _codecid_to_stream_id; + List _frameCached; +}; + + + +class TsMuxer2{ +public: + typedef function onTsCallback; + TsMuxer2(){ + init(); + } + ~TsMuxer2(){ + uninit(); + } + + bool addTrack(int track,int codec_id){ + lock_guard lck(_mtx); + auto it = _allTrackMap.find(track); + if(it != _allTrackMap.end()){ +// WarnL << "Track:" << track << "已经存在!"; + return false; + } + _allTrackMap[track] = codec_id; + resetAllTracks(); + return true; + } + + bool removeTrack(int track){ + lock_guard lck(_mtx); + auto it = _allTrackMap.find(track); + if(it == _allTrackMap.end()){ +// WarnL << "Track:" << track << "不存在!"; + return false; + } + DebugL << "删除Track:" << track; + _allTrackMap.erase(it); + resetAllTracks(); + return true; + } + + bool inputTrackData(int track, const char *data, int length, int64_t pts, int64_t dts, int flags){ + lock_guard lck(_mtx); + auto it = _track_id_to_stream_id.find(track); + if(it == _track_id_to_stream_id.end()){ + WarnL << "Track:" << track << "不存在!"; + return false; + } + mpeg_ts_write(_context,it->second,flags,pts,dts,data,length); + return true; + } + + void setOnTsCallback(const onTsCallback &cb) { + lock_guard lck(_mtx); + _onts = cb; + } + + bool saveToFile(const string &file){ + lock_guard lck(_mtx); + FILE *fp = File::createfile_file(file.data(),"ab"); + if(!fp){ + WarnL << "打开文件失败:" << file << " " << get_uv_errmsg(); + return false; + } + setvbuf(fp, _file_buf, _IOFBF, sizeof(_file_buf)); + _file.reset(fp,[](FILE *fp){ + fclose(fp); + }); + return true; + } +private: + void init() { + lock_guard lck(_mtx); + static mpeg_ts_func_t s_func= { + [](void* param, size_t bytes){ + TsMuxer2 *muxer = (TsMuxer2 *)param; + assert(sizeof(TsMuxer2::_tsbuf) >= bytes); + return (void *)muxer->_tsbuf; + }, + [](void* param, void* packet){ + //do nothing + }, + [](void* param, const void* packet, size_t bytes){ + TsMuxer2 *muxer = (TsMuxer2 *)param; + muxer->onTs(packet, bytes); + } + }; + if(_context == nullptr){ + _context = mpeg_ts_create(&s_func,this); + } + } + + void uninit() { + lock_guard lck(_mtx); + if(_context){ + mpeg_ts_destroy(_context); + _context = nullptr; + } + _track_id_to_stream_id.clear(); + } + + void resetAllTracks(){ + lock_guard lck(_mtx); + uninit(); + init(); + + //添加Track + for (auto &pr : _allTrackMap){ + InfoL << "添加Track:" << pr.first << " " << pr.second; + _track_id_to_stream_id[pr.first] = mpeg_ts_add_stream(_context,pr.second, nullptr,0); + } + } + + void onTs(const void *packet, int bytes) { + lock_guard lck(_mtx); + if(_onts){ + _onts(packet,bytes); + } + + if(_file){ + fwrite(packet,bytes,1,_file.get()); + } + } +private: + void *_context = nullptr; + char *_tsbuf[188]; + unordered_map _track_id_to_stream_id; + unordered_map _allTrackMap; + recursive_mutex _mtx; + onTsCallback _onts; + + char _file_buf[64 * 1024]; + std::shared_ptr _file; +}; + +}//namespace mediakit +#endif //TSMUXER_H diff --git a/src/MediaFile/crc32.cpp b/src/MediaFile/crc32.cpp deleted file mode 100644 index 6e781374..00000000 --- a/src/MediaFile/crc32.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "crc32.h" - -#define BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff)) -#define BSWAP32C(x) (BSWAP16C(x) << 16 | BSWAP16C((x) >> 16)) -#define BSWAP64C(x) (BSWAP32C(x) << 32 | BSWAP32C((x) >> 32)) - -static const unsigned int crc_tab[256] = { - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, - 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, - 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, - 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, - 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, - 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, - 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, - 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, - 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, - 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, - 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, - 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, - 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, - 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, - 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, - 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, - 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, - 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, - 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, - 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, - 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, - 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 -}; - -unsigned int calc_crc32 (unsigned char *data, unsigned int datalen) -{ - unsigned int i; - unsigned int crc = 0xffffffff; - - for (i=0; i> 24) ^ *data++) & 0xff]; - } - return crc; -} - -unsigned int Zwg_ntohl(unsigned int s) -{ - union - { - int i; - char buf; - }a; - a.i = 0x01; - if(a.buf) - { - // 小端 - s = BSWAP32C(s); - } - return s; -} - - - - diff --git a/src/MediaFile/crc32.h b/src/MediaFile/crc32.h deleted file mode 100644 index 8629418a..00000000 --- a/src/MediaFile/crc32.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CRC32_H_ -#define CRC32_H_ - -unsigned int calc_crc32 (unsigned char *data, unsigned int datalen); -unsigned int Zwg_ntohl(unsigned int s); - - - -#endif /* CRC32_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6301f927..6985b30f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,7 +37,7 @@ foreach(TEST_SRC ${TEST_SRC_LIST}) STRING(REGEX REPLACE "^\\./|\\.c[a-zA-Z0-9_]*$" "" TEST_EXE_NAME ${TEST_SRC}) message(STATUS "add test:${TEST_EXE_NAME}") add_executable(${TEST_EXE_NAME} ${TEST_SRC}) - target_link_libraries(${TEST_EXE_NAME} ${LINK_LIB_LIST}) + target_link_libraries(${TEST_EXE_NAME} mpeg ${LINK_LIB_LIST}) endforeach()