整体替换HLS生成代码,支持265

This commit is contained in:
xiongziliang 2019-04-01 12:57:33 +08:00
parent d1f722939d
commit 33eb7ef320
18 changed files with 664 additions and 1182 deletions

5
.gitmodules vendored
View File

@ -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

1
3rdpart/media-server Submodule

@ -0,0 +1 @@
Subproject commit 6ae6ece439e54252116e418179bc84e6a0189f67

View File

@ -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)

View File

@ -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<FILE> 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 (auto &tp : _seg_dur_list) {
PRINT("#EXTINF:%.3f,\n%s\n", std::get<0>(tp) / 1000.0, std::get<1>(tp).data());
}
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 (eof) {
PRINT("#EXT-X-ENDLIST\n");
}
}
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;
onWriteHls(file_content, file_size);
}
void HLSMaker::inputH264(const Frame::Ptr &frame) {
auto dts = frame->dts();
if(_ui32LastStamp == 0){
_ui32LastStamp = dts;
}
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;
void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp) {
addNewFile(timestamp);
onWriteFile((char *) data, len);
}
void HLSMaker::inputAAC(const Frame::Ptr &frame) {
_ts.inputAAC(frame->data(), frame->size(), frame->dts() * 90LL , frame->pts() * 90LL);
}
void HlsMaker::delOldFile() {
//在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致
if (_file_index >= _seg_number + 2) {
_seg_dur_list.pop_front();
}
bool HLSMaker::removets() {
if (_ui64TsCnt < _ui32NumSegments + 2) {
return false;
}
File::delete_file((StrPrinter << _strOutputPrefix << "-"
<< _ui64TsCnt - _ui32NumSegments - 2
<< ".ts" << endl).data());
return true;
}
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;
//但是实际保存的切片个数比m3u8所述多两个,这样做的目的是防止播放器在切片删除前能下载完毕
if (_file_index >= _seg_number + 4) {
onDelFile(_file_index - _seg_number - 4);
}
}
} /* namespace mediakit */
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

View File

@ -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 <deque>
#include "TSMaker.h"
#include <tuple>
#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();
/**
* @param seg_duration
* @param seg_number
*/
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:
/**
* Track输出frameonAllTrackReady触发后才会调用此方法
* @param frame
* ts切片文件回调
* @param index
* @return
*/
void onTrackFrame(const Frame::Ptr &frame) override ;
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<int> _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<tuple<int,string> > _seg_dur_list;
};
} /* namespace mediakit */
#endif /* HLSMAKER_H_ */
}//namespace mediakit
#endif //HLSMAKER_H

View File

@ -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 &params,
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<FILE> HlsMakerImp::makeFile(const string &file,bool setbuf) {
auto ret= shared_ptr<FILE>(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

View File

@ -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 <memory>
#include <string>
#include <stdlib.h>
#include "HlsMaker.h"
using namespace std;
namespace mediakit {
class HlsMakerImp : public HlsMaker{
public:
HlsMakerImp(const string &m3u8_file,
const string &params,
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<FILE> makeFile(const string &file,bool setbuf = false);
private:
std::shared_ptr<FILE> _file;
std::shared_ptr<char> _file_buf;
string _path_prefix;
string _path_hls;
string _params;
int _buf_size;
};
}//namespace mediakit
#endif //HLSMAKERIMP_H

View File

@ -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<typename ...ArgsType>
HlsRecorder(ArgsType &&...args):HlsMakerImp(std::forward<ArgsType>(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

View File

@ -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

View File

@ -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> _hlsMaker;
std::shared_ptr<HlsRecorder> _hlsMaker;
#ifdef ENABLE_MP4V2
std::shared_ptr<Mp4Maker> _mp4Maker;
#endif //ENABLE_MP4V2

View File

@ -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<char *>(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<char *>(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<unsigned char>(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;
}
}

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <iostream>
#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字段存在11PTD和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_ */

124
src/MediaFile/TsMuxer.cpp Normal file
View File

@ -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 <assert.h>
#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<BufferString>(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

191
src/MediaFile/TsMuxer.h Normal file
View File

@ -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 <unordered_map>
#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<int,int > _codecid_to_stream_id;
List<Frame::Ptr> _frameCached;
};
class TsMuxer2{
public:
typedef function<void(const void *packet, int bytes)> onTsCallback;
TsMuxer2(){
init();
}
~TsMuxer2(){
uninit();
}
bool addTrack(int track,int codec_id){
lock_guard<recursive_mutex> 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<recursive_mutex> 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<recursive_mutex> 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<recursive_mutex> lck(_mtx);
_onts = cb;
}
bool saveToFile(const string &file){
lock_guard<recursive_mutex> 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<recursive_mutex> 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<recursive_mutex> lck(_mtx);
if(_context){
mpeg_ts_destroy(_context);
_context = nullptr;
}
_track_id_to_stream_id.clear();
}
void resetAllTracks(){
lock_guard<recursive_mutex> 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<recursive_mutex> lck(_mtx);
if(_onts){
_onts(packet,bytes);
}
if(_file){
fwrite(packet,bytes,1,_file.get());
}
}
private:
void *_context = nullptr;
char *_tsbuf[188];
unordered_map<int,int > _track_id_to_stream_id;
unordered_map<int,int > _allTrackMap;
recursive_mutex _mtx;
onTsCallback _onts;
char _file_buf[64 * 1024];
std::shared_ptr<FILE> _file;
};
}//namespace mediakit
#endif //TSMUXER_H

View File

@ -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<datalen; i++)
{
crc = (crc << 8) ^ crc_tab[((crc >> 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;
}

View File

@ -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_ */

View File

@ -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()