mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2026-06-13 19:51:49 +08:00
fix MedaiFile to MediaFile
This commit is contained in:
159
src/MediaFile/HLSMaker.cpp
Normal file
159
src/MediaFile/HLSMaker.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* HLSMaker.cpp
|
||||
*
|
||||
* Created on: 2013-6-24
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#include "HLSMaker.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
HLSMaker::HLSMaker(const string& strM3u8File, const string& strHttpUrl,
|
||||
uint32_t ui32BufSize, uint32_t ui32Duration, uint32_t ui32Num) {
|
||||
if (ui32BufSize < 16 * 1024) {
|
||||
ui32BufSize = 16 * 1024;
|
||||
}
|
||||
m_ui32BufSize = ui32BufSize;
|
||||
if (ui32Duration < 5) {
|
||||
ui32Duration = 5;
|
||||
}
|
||||
if (ui32Num < 2) {
|
||||
ui32Num = 2;
|
||||
}
|
||||
|
||||
m_ui64TsCnt = 0;
|
||||
m_strM3u8File = strM3u8File;
|
||||
m_strHttpUrl = strHttpUrl.substr(0, strHttpUrl.find_last_of('/') + 1);
|
||||
m_ui32NumSegments = ui32Num;
|
||||
m_ui32SegmentDuration = ui32Duration;
|
||||
|
||||
m_strOutputPrefix = strM3u8File.substr(0, strM3u8File.find_last_of('.'));
|
||||
m_strFileName = m_strOutputPrefix.substr(m_strOutputPrefix.find_last_of('/') + 1);
|
||||
m_strTmpFileName = m_strOutputPrefix + "-0.ts";
|
||||
m_ts.init(m_strTmpFileName, m_ui32BufSize);
|
||||
}
|
||||
|
||||
|
||||
HLSMaker::~HLSMaker() {
|
||||
m_ts.clear();
|
||||
string strDir = m_strOutputPrefix.substr(0,m_strOutputPrefix.find_last_of('/'));
|
||||
File::delete_file(strDir.data());
|
||||
}
|
||||
|
||||
int HLSMaker::write_index_file(int iFirstSegment, unsigned int uiLastSegment,
|
||||
int iEnd) {
|
||||
FILE *pIndexFp;
|
||||
char *pcWriteBuf;
|
||||
const char *pcTmpM3u8File = (m_strM3u8File).c_str();
|
||||
pIndexFp = File::createfile_file(pcTmpM3u8File, "w");
|
||||
if (pIndexFp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (iFirstSegment < 0) {
|
||||
iFirstSegment = 0;
|
||||
}
|
||||
if (!pIndexFp) {
|
||||
WarnL << "Could not open temporary m3u8 index file (" << pcTmpM3u8File
|
||||
<< "), no index file will be created";
|
||||
return -1;
|
||||
}
|
||||
|
||||
pcWriteBuf = (char *) malloc(sizeof(char) * 1024);
|
||||
if (!pcWriteBuf) {
|
||||
WarnL << "Could not allocate write buffer for index file, index file will be invalid";
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m_ui32NumSegments) {
|
||||
snprintf(pcWriteBuf, 1024,
|
||||
"#EXTM3U\n#EXT-X-TARGETDURATION:%u\n#EXT-X-MEDIA-SEQUENCE:%u\n",
|
||||
m_ui32SegmentDuration, iFirstSegment);
|
||||
} else {
|
||||
snprintf(pcWriteBuf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n",
|
||||
m_ui32SegmentDuration);
|
||||
}
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write to m3u8 index file, will not continue writing to index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned int i = iFirstSegment; i < uiLastSegment; i++) {
|
||||
snprintf(pcWriteBuf, 1024, "#EXTINF:%u,\n%s%s-%u.ts\n",
|
||||
m_ui32SegmentDuration, m_strHttpUrl.c_str(),
|
||||
m_strFileName.c_str(), i);
|
||||
//printf(options.output_prefix);
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write to m3u8 index file, will not continue writing to index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (iEnd) {
|
||||
snprintf(pcWriteBuf, 1024, "#EXT-X-ENDLIST\n");
|
||||
if (fwrite(pcWriteBuf, strlen(pcWriteBuf), 1, pIndexFp) != 1) {
|
||||
WarnL << "Could not write last file and endlist tag to m3u8 index file";
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(pcWriteBuf);
|
||||
fclose(pIndexFp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void HLSMaker::inputH264(void *data, uint32_t length, uint32_t timeStamp,
|
||||
int type) {
|
||||
switch (type) {
|
||||
case 7: //SPS
|
||||
if (m_Timer.elapsedTime() >= m_ui32SegmentDuration * 1000) {
|
||||
m_ts.clear();
|
||||
m_strTmpFileName = StrPrinter << m_strOutputPrefix << '-' << (++m_ui64TsCnt) << ".ts" << endl;
|
||||
if (!m_ts.init(m_strTmpFileName, m_ui32BufSize)) {
|
||||
//创建文件失败
|
||||
return;
|
||||
}
|
||||
m_Timer.resetTime();
|
||||
removets();
|
||||
if (write_index_file(m_ui64TsCnt - m_ui32NumSegments, m_ui64TsCnt, 0) == -1) {
|
||||
WarnL << "write_index_file error :" << strerror(errno);
|
||||
}
|
||||
}
|
||||
case 1: //P
|
||||
//insert aud frame before p and SPS frame
|
||||
m_ts.inputH264("\x0\x0\x0\x1\x9\xf0", 6, timeStamp);
|
||||
case 5: //IDR
|
||||
case 8: //PPS
|
||||
m_ts.inputH264((char *) data, length, timeStamp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HLSMaker::inputAAC(void *data, uint32_t length, uint32_t timeStamp) {
|
||||
m_ts.inputAAC((char *) data, length, timeStamp);
|
||||
}
|
||||
|
||||
void HLSMaker::removets() {
|
||||
if (m_ui64TsCnt <= m_ui32NumSegments) {
|
||||
return;
|
||||
}
|
||||
File::delete_file( (StrPrinter << m_strOutputPrefix << "-"
|
||||
<< m_ui64TsCnt - m_ui32NumSegments - 1
|
||||
<< ".ts" << endl).data());
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
63
src/MediaFile/HLSMaker.h
Normal file
63
src/MediaFile/HLSMaker.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* HLSMaker.h
|
||||
*
|
||||
* Created on: 2013-6-24
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef HLSMAKER_H_
|
||||
#define HLSMAKER_H_
|
||||
|
||||
#include "TSMaker.h"
|
||||
#include "config.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/logger.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class HLSMaker {
|
||||
public:
|
||||
HLSMaker(const string &strM3u8File,
|
||||
const string &strHttpUrl,
|
||||
uint32_t ui32BufSize = 64 * 1024,
|
||||
uint32_t ui32Duration = 5,
|
||||
uint32_t ui32Num = 3);
|
||||
|
||||
virtual ~HLSMaker();
|
||||
|
||||
//时间戳:参考频率90000
|
||||
void inputH264( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp,
|
||||
int iType);
|
||||
|
||||
//时间戳:参考频率90000
|
||||
void inputAAC( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp);
|
||||
private:
|
||||
TSMaker m_ts;
|
||||
string m_strM3u8File;
|
||||
string m_strHttpUrl;
|
||||
string m_strFileName;
|
||||
string m_strOutputPrefix;
|
||||
string m_strTmpFileName;
|
||||
uint32_t m_ui32SegmentDuration;
|
||||
uint32_t m_ui32NumSegments;
|
||||
uint64_t m_ui64TsCnt;
|
||||
uint32_t m_ui32BufSize;
|
||||
Ticker m_Timer;
|
||||
|
||||
int write_index_file(int iFirstSegment, unsigned int uiLastSegment, int iEnd);
|
||||
void removets();
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* HLSMAKER_H_ */
|
||||
349
src/MediaFile/MediaReader.cpp
Normal file
349
src/MediaFile/MediaReader.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* MediaReader.cpp
|
||||
*
|
||||
* Created on: 2016年12月14日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "MediaReader.h"
|
||||
#include "config.h"
|
||||
#include "Util/mini.h"
|
||||
#include "Http/HttpSession.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
MediaReader::MediaReader(const string &strApp, const string &strId) {
|
||||
static string recordPath = mINI::Instance()[Config::Record::kFilePath];
|
||||
auto strFileName = recordPath + "/" + strApp + "/" + strId;
|
||||
|
||||
m_hMP4File = MP4Read(strFileName.data());
|
||||
if(m_hMP4File == MP4_INVALID_FILE_HANDLE){
|
||||
throw runtime_error(StrPrinter << "打开MP4文件失败:" << strFileName << endl);
|
||||
}
|
||||
m_video_trId = MP4FindTrackId(m_hMP4File, 0, MP4_VIDEO_TRACK_TYPE, 0);
|
||||
if(m_video_trId != MP4_INVALID_TRACK_ID){
|
||||
if(strcmp(MP4GetTrackMediaDataName(m_hMP4File, m_video_trId),"avc1") ==0){
|
||||
auto m_video_timescale = MP4GetTrackTimeScale(m_hMP4File, m_video_trId);
|
||||
auto m_video_duration = MP4GetTrackDuration(m_hMP4File, m_video_trId);
|
||||
m_video_num_samples = MP4GetTrackNumberOfSamples(m_hMP4File, m_video_trId);
|
||||
m_video_sample_max_size = MP4GetTrackMaxSampleSize(m_hMP4File, m_video_trId);
|
||||
m_video_width = MP4GetTrackVideoWidth(m_hMP4File, m_video_trId);
|
||||
m_video_height = MP4GetTrackVideoHeight(m_hMP4File, m_video_trId);
|
||||
m_video_framerate = MP4GetTrackVideoFrameRate(m_hMP4File, m_video_trId);
|
||||
m_pcVideoSample = std::shared_ptr<uint8_t> (new uint8_t[m_video_sample_max_size],[](uint8_t *ptr){
|
||||
delete [] ptr;
|
||||
});
|
||||
uint8_t **seqheader;
|
||||
uint8_t **pictheader;
|
||||
uint32_t *pictheadersize;
|
||||
uint32_t *seqheadersize;
|
||||
uint32_t ix;
|
||||
if(MP4GetTrackH264SeqPictHeaders(m_hMP4File, m_video_trId, &seqheader, &seqheadersize, &pictheader, &pictheadersize)){
|
||||
for (ix = 0; seqheadersize[ix] != 0; ix++) {
|
||||
m_strSps.assign((char *)(seqheader[ix]), seqheadersize[ix]);
|
||||
float framerate;
|
||||
getAVCInfo(m_strSps, (int &)m_video_width, (int &)m_video_height, framerate);
|
||||
m_video_framerate = framerate;
|
||||
m_strSps = string("\x0\x0\x0\x1",4) + m_strSps;
|
||||
free(seqheader[ix]);
|
||||
}
|
||||
free(seqheader);
|
||||
free(seqheadersize);
|
||||
for (ix = 0; pictheadersize[ix] != 0; ix++) {
|
||||
m_strPps.assign("\x0\x0\x0\x1",4);
|
||||
m_strPps.append((char *)(pictheader[ix]), pictheadersize[ix]);
|
||||
free(pictheader[ix]);
|
||||
}
|
||||
free(pictheader);
|
||||
free(pictheadersize);
|
||||
}
|
||||
m_video_ms = 1000.0 * m_video_duration / m_video_timescale;
|
||||
/*InfoL << "\r\n"
|
||||
<< m_video_ms << "\r\n"
|
||||
<< m_video_num_samples << "\r\n"
|
||||
<< m_video_framerate << "\r\n"
|
||||
<< m_video_width << "\r\n"
|
||||
<< m_video_height << "\r\n";*/
|
||||
} else {
|
||||
//如果不是h264,则忽略
|
||||
m_video_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_audio_trId = MP4FindTrackId(m_hMP4File, 0, MP4_AUDIO_TRACK_TYPE, 0);
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
if (strcmp(MP4GetTrackMediaDataName(m_hMP4File, m_audio_trId), "mp4a") == 0) {
|
||||
m_audio_sample_rate = MP4GetTrackTimeScale(m_hMP4File, m_audio_trId);
|
||||
auto m_audio_duration = MP4GetTrackDuration(m_hMP4File, m_audio_trId);
|
||||
m_audio_num_samples = MP4GetTrackNumberOfSamples(m_hMP4File,m_audio_trId);
|
||||
m_audio_num_channels = MP4GetTrackAudioChannels(m_hMP4File, m_audio_trId);
|
||||
m_audio_sample_max_size = MP4GetTrackMaxSampleSize(m_hMP4File,m_audio_trId);
|
||||
uint8_t *ppConfig;
|
||||
uint32_t pConfigSize;
|
||||
if(MP4GetTrackESConfiguration(m_hMP4File,m_audio_trId,&ppConfig,&pConfigSize)){
|
||||
m_strAacCfg.assign((char *)ppConfig, pConfigSize);
|
||||
makeAdtsHeader(m_strAacCfg, m_adts);
|
||||
writeAdtsHeader(m_adts,m_adts.data);
|
||||
getAACInfo(m_adts, (int &)m_audio_sample_rate, (int &)m_audio_num_channels);
|
||||
free(ppConfig);
|
||||
}
|
||||
m_audio_ms = 1000.0 * m_audio_duration / m_audio_sample_rate;
|
||||
/*InfoL << "\r\n"
|
||||
<< m_audio_ms << "\r\n"
|
||||
<< m_audio_num_samples << "\r\n"
|
||||
<< m_audio_num_channels << "\r\n"
|
||||
<< m_audio_sample_rate << "\r\n";*/
|
||||
}else{
|
||||
m_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
}
|
||||
}
|
||||
if(m_audio_trId == MP4_INVALID_TRACK_ID && m_video_trId == MP4_INVALID_TRACK_ID){
|
||||
MP4Close(m_hMP4File);
|
||||
m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
throw runtime_error(StrPrinter << "该MP4文件音视频格式不支持:" << strFileName << endl);
|
||||
}
|
||||
|
||||
m_iDuration = MAX(m_video_ms,m_audio_ms);
|
||||
m_pChn.reset(new DevChannel(strApp.data(),strId.data(),m_iDuration/1000.0,false));
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
AudioInfo info;
|
||||
info.iChannel = m_audio_num_channels;
|
||||
info.iSampleBit = 16;
|
||||
info.iSampleRate = m_audio_sample_rate;
|
||||
m_pChn->initAudio(info);
|
||||
}
|
||||
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
VideoInfo info;
|
||||
info.iFrameRate = m_video_framerate;
|
||||
info.iWidth = m_video_width;
|
||||
info.iHeight = m_video_height;
|
||||
m_pChn->initVideo(info);
|
||||
}
|
||||
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_pChn->inputAAC((char *)m_adts.data, 7, 0);
|
||||
}
|
||||
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
//m_pChn->initVideo(info);
|
||||
m_pChn->inputH264((char *) m_strSps.data(), m_strSps.size(), 0);
|
||||
m_pChn->inputH264((char *) m_strPps.data(), m_strPps.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MediaReader::~MediaReader() {
|
||||
if (m_hMP4File != MP4_INVALID_FILE_HANDLE) {
|
||||
MP4Close(m_hMP4File);
|
||||
m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MediaReader::startReadMP4() {
|
||||
auto strongSelf = shared_from_this();
|
||||
static uint32_t sampleMS = mINI::Instance()[Config::Record::kSampleMS].as<uint32_t>();
|
||||
|
||||
AsyncTaskThread::Instance().DoTaskDelay(reinterpret_cast<uint64_t>(this), sampleMS, [strongSelf](){
|
||||
return strongSelf->readSample();
|
||||
});
|
||||
weak_ptr<MediaReader> weakSelf = strongSelf;
|
||||
m_pChn->setOnSeek([weakSelf](uint32_t ui32Stamp){
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf){
|
||||
return false;
|
||||
}
|
||||
strongSelf->seek(ui32Stamp);
|
||||
return true;
|
||||
});
|
||||
m_pChn->setOnStamp([weakSelf]() -> uint32_t {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if(!strongSelf) {
|
||||
return 0;
|
||||
}
|
||||
return strongSelf-> m_iSeekTime + strongSelf->m_ticker.elapsedTime();
|
||||
});
|
||||
}
|
||||
|
||||
bool MediaReader::readSample(int iTimeInc) {
|
||||
TimeTicker();
|
||||
lock_guard<recursive_mutex> lck(m_mtx);
|
||||
auto bFlag0 = readVideoSample(iTimeInc);//数据没读完
|
||||
auto bFlag1 = readAudioSample(iTimeInc);//数据没读完
|
||||
auto bFlag2 = m_pChn->readerCount() > 0;//读取者大于0
|
||||
if((bFlag0 || bFlag1) && bFlag2){
|
||||
m_alive.resetTime();
|
||||
}
|
||||
//DebugL << "alive ...";
|
||||
//3秒延时关闭
|
||||
return m_alive.elapsedTime() < 3 * 1000;
|
||||
}
|
||||
inline bool MediaReader::readVideoSample(int iTimeInc) {
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getVideoSampleId(iTimeInc);
|
||||
MP4SampleId iIdx = m_video_current;
|
||||
for (iIdx = m_video_current; iIdx < iNextSample; iIdx++) {
|
||||
uint8_t *pBytes = m_pcVideoSample.get();
|
||||
uint32_t numBytes = m_video_sample_max_size;
|
||||
if(MP4ReadSample(m_hMP4File, m_video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,NULL,&m_bSyncSample)){
|
||||
if (!iTimeInc) {
|
||||
uint32_t iOffset = 0;
|
||||
while (iOffset < numBytes) {
|
||||
uint32_t iFrameLen;
|
||||
memcpy(&iFrameLen,pBytes + iOffset,4);
|
||||
iFrameLen = ntohl(iFrameLen);
|
||||
if(iFrameLen + iOffset + 4> numBytes){
|
||||
break;
|
||||
}
|
||||
memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4);
|
||||
writeH264(pBytes + iOffset, iFrameLen + 4, (double) m_video_ms * iIdx / m_video_num_samples);
|
||||
iOffset += (iFrameLen + 4);
|
||||
}
|
||||
}else if(m_bSyncSample){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
FatalL << "读取视频失败:" << iIdx + 1;
|
||||
}
|
||||
}
|
||||
m_video_current = iIdx;
|
||||
return m_video_current < m_video_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool MediaReader::readAudioSample(int iTimeInc) {
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
auto iNextSample = getAudioSampleId(iTimeInc);
|
||||
for (auto i = m_audio_current; i < iNextSample; i++) {
|
||||
uint32_t numBytes = m_audio_sample_max_size;
|
||||
uint8_t *pBytes = m_adts.data + 7;
|
||||
if(MP4ReadSample(m_hMP4File, m_audio_trId, i + 1, &pBytes, &numBytes)){
|
||||
if (!iTimeInc) {
|
||||
m_adts.aac_frame_length = 7 + numBytes;
|
||||
writeAdtsHeader(m_adts, m_adts.data);
|
||||
writeAAC(m_adts.data, m_adts.aac_frame_length, (double) m_audio_ms * i / m_audio_num_samples);
|
||||
}
|
||||
}else{
|
||||
FatalL << "读取音频失败:" << i+ 1;
|
||||
}
|
||||
}
|
||||
m_audio_current = iNextSample;
|
||||
return m_audio_current < m_audio_num_samples;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void MediaReader::writeH264(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||
m_pChn->inputH264((char *)pucData, iLen, uiStamp);
|
||||
}
|
||||
|
||||
inline void MediaReader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) {
|
||||
m_pChn->inputAAC((char *)pucData, iLen, uiStamp);
|
||||
}
|
||||
|
||||
inline MP4SampleId MediaReader::getVideoSampleId(int iTimeInc ) {
|
||||
MP4SampleId video_current = (double)m_video_num_samples * (m_iSeekTime + m_ticker.elapsedTime() + iTimeInc) / m_video_ms;
|
||||
video_current = MAX(0,MIN(m_video_num_samples, video_current));
|
||||
return video_current;
|
||||
|
||||
}
|
||||
|
||||
inline MP4SampleId MediaReader::getAudioSampleId(int iTimeInc) {
|
||||
MP4SampleId audio_current = (double)m_audio_num_samples * (m_iSeekTime + m_ticker.elapsedTime() + iTimeInc) / m_audio_ms ;
|
||||
audio_current = MAX(0,MIN(m_audio_num_samples,audio_current));
|
||||
return audio_current;
|
||||
}
|
||||
inline void MediaReader::setSeekTime(int iSeekTime){
|
||||
m_iSeekTime = MAX(0, MIN(iSeekTime,m_iDuration));
|
||||
m_ticker.resetTime();
|
||||
if (m_audio_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_audio_current = getAudioSampleId();
|
||||
}
|
||||
if (m_video_trId != MP4_INVALID_TRACK_ID) {
|
||||
m_video_current = getVideoSampleId();
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t MediaReader::getVideoCurrentTime(){
|
||||
return (double)m_video_current * m_video_ms /m_video_num_samples;
|
||||
}
|
||||
void MediaReader::seek(int iSeekTime,bool bReStart){
|
||||
lock_guard<recursive_mutex> lck(m_mtx);
|
||||
if(iSeekTime == 0 || m_video_trId == MP4_INVALID_TRACK_ID){
|
||||
setSeekTime(iSeekTime);
|
||||
}else{
|
||||
setSeekTime(iSeekTime - 5000);
|
||||
//在之后的10秒查找关键帧
|
||||
readVideoSample(10000);
|
||||
if (m_bSyncSample) {
|
||||
//找到关键帧
|
||||
auto iIdr = m_video_current;
|
||||
setSeekTime(getVideoCurrentTime());
|
||||
m_video_current = iIdr;
|
||||
}else{
|
||||
//未找到关键帧
|
||||
setSeekTime(iSeekTime);
|
||||
}
|
||||
}
|
||||
m_pChn->updateTimeStamp(m_iSeekTime);
|
||||
|
||||
if(bReStart){
|
||||
AsyncTaskThread::Instance().CancelTask(reinterpret_cast<uint64_t>(this));
|
||||
startReadMP4();
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
RtspMediaSource::Ptr MediaReader::onMakeRtsp(const string &strApp, const string &strId) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
if (strApp != appName) {
|
||||
return nullptr;
|
||||
}
|
||||
try{
|
||||
MediaReader::Ptr pReader(new MediaReader(strApp,strId));
|
||||
pReader->startReadMP4();
|
||||
return RtspMediaSource::find(strApp, strId, false);
|
||||
}catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
}
|
||||
|
||||
RtmpMediaSource::Ptr MediaReader::onMakeRtmp(const string &strApp, const string &strId) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
if (strApp != appName) {
|
||||
return nullptr;
|
||||
}
|
||||
try {
|
||||
MediaReader::Ptr pReader(new MediaReader(strApp, strId));
|
||||
pReader->startReadMP4();
|
||||
return RtmpMediaSource::find(strApp, strId, false);
|
||||
} catch (std::exception &ex) {
|
||||
WarnL << ex.what();
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
85
src/MediaFile/MediaReader.h
Normal file
85
src/MediaFile/MediaReader.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* MediaReader.h
|
||||
*
|
||||
* Created on: 2016年12月14日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_MEDIAFILE_MEDIAREADER_H_
|
||||
#define SRC_MEDIAFILE_MEDIAREADER_H_
|
||||
|
||||
#include "Device/Device.h"
|
||||
#include "Rtsp/RtspMediaSource.h"
|
||||
#include "Rtmp/RtmpMediaSource.h"
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
using namespace ZL::DEV;
|
||||
using namespace ZL::Rtsp;
|
||||
using namespace ZL::Rtmp;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class MediaReader : public std::enable_shared_from_this<MediaReader>{
|
||||
public:
|
||||
typedef std::shared_ptr<MediaReader> Ptr;
|
||||
MediaReader(const string &strApp, const string &strId);
|
||||
virtual ~MediaReader();
|
||||
static RtspMediaSource::Ptr onMakeRtsp(const string &strApp, const string &strId);
|
||||
static RtmpMediaSource::Ptr onMakeRtmp(const string &strApp, const string &strId);
|
||||
private:
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
MP4FileHandle m_hMP4File = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId m_video_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t m_video_ms = 0;
|
||||
uint32_t m_video_num_samples = 0;
|
||||
uint32_t m_video_sample_max_size = 0;
|
||||
uint32_t m_video_width = 0;
|
||||
uint32_t m_video_height = 0;
|
||||
uint32_t m_video_framerate = 0;
|
||||
string m_strPps;
|
||||
string m_strSps;
|
||||
bool m_bSyncSample = false;
|
||||
|
||||
MP4TrackId m_audio_trId = MP4_INVALID_TRACK_ID;
|
||||
uint32_t m_audio_ms = 0;
|
||||
uint32_t m_audio_num_samples = 0;
|
||||
uint32_t m_audio_sample_max_size = 0;
|
||||
uint32_t m_audio_sample_rate = 0;
|
||||
uint32_t m_audio_num_channels = 0;
|
||||
string m_strAacCfg;
|
||||
AdtsFrame m_adts;
|
||||
|
||||
int m_iDuration = 0;
|
||||
DevChannel::Ptr m_pChn;
|
||||
MP4SampleId m_video_current = 0;
|
||||
MP4SampleId m_audio_current = 0;
|
||||
std::shared_ptr<uint8_t> m_pcVideoSample;
|
||||
|
||||
int m_iSeekTime = 0 ;
|
||||
Ticker m_ticker;
|
||||
Ticker m_alive;
|
||||
recursive_mutex m_mtx;
|
||||
|
||||
void seek(int iSeekTime,bool bReStart = true);
|
||||
inline void setSeekTime(int iSeekTime);
|
||||
inline uint32_t getVideoCurrentTime();
|
||||
void startReadMP4();
|
||||
inline MP4SampleId getVideoSampleId(int iTimeInc = 0);
|
||||
inline MP4SampleId getAudioSampleId(int iTimeInc = 0);
|
||||
bool readSample(int iTimeInc = 0);
|
||||
inline bool readVideoSample(int iTimeInc = 0);
|
||||
inline bool readAudioSample(int iTimeInc = 0);
|
||||
inline void writeH264(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
inline void writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp);
|
||||
#endif
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_MEDIAFILE_MEDIAREADER_H_ */
|
||||
61
src/MediaFile/MediaRecorder.cpp
Normal file
61
src/MediaFile/MediaRecorder.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* MediaRecorder.cpp
|
||||
*
|
||||
* Created on: 2016年12月8日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#include "MediaRecorder.h"
|
||||
#include "config.h"
|
||||
#include "Http/HttpSession.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/mini.h"
|
||||
#include "Network/sockutil.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Network;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
MediaRecorder::MediaRecorder(const string &strApp,const string &strId,const std::shared_ptr<PlayerBase> &pPlayer) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
static string hlsPrefix = mINI::Instance()[Config::Http::kHttpPrefix];
|
||||
static string hlsPath = mINI::Instance()[Config::Hls::kFilePath];
|
||||
static uint32_t hlsBufSize = mINI::Instance()[Config::Hls::kFileBufSize].as<uint32_t>();
|
||||
static uint32_t hlsDuration = mINI::Instance()[Config::Hls::kSegmentDuration].as<uint32_t>();
|
||||
static uint32_t hlsNum = mINI::Instance()[Config::Hls::kSegmentNum].as<uint32_t>();
|
||||
|
||||
static string recordPath = mINI::Instance()[Config::Record::kFilePath];
|
||||
static string recordAppName = mINI::Instance()[Config::Record::kAppName];
|
||||
|
||||
|
||||
m_hlsMaker.reset(new HLSMaker(hlsPath + "/" + strApp + "/" + strId + "/hls.m3u8",
|
||||
hlsPrefix + "/" + strApp + "/" + strId + "/",
|
||||
hlsBufSize,hlsDuration,hlsNum));
|
||||
|
||||
m_mp4Maker.reset(new Mp4Maker(recordPath + "/" + recordAppName + "/" + strApp + "/" + strId + "/",
|
||||
strApp,strId,pPlayer));
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
MediaRecorder::~MediaRecorder() {
|
||||
}
|
||||
|
||||
void MediaRecorder::inputH264(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
m_hlsMaker->inputH264(pData, ui32Length, ui32TimeStamp * 90, iType);
|
||||
m_mp4Maker->inputH264(pData, ui32Length, ui32TimeStamp, iType);
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
void MediaRecorder::inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32TimeStamp) {
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
m_hlsMaker->inputAAC(pData, ui32Length, ui32TimeStamp * 90);
|
||||
m_mp4Maker->inputAAC(pData, ui32Length, ui32TimeStamp);
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
50
src/MediaFile/MediaRecorder.h
Normal file
50
src/MediaFile/MediaRecorder.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* MediaRecorder.h
|
||||
*
|
||||
* Created on: 2016年12月8日
|
||||
* Author: xzl
|
||||
*/
|
||||
|
||||
#ifndef SRC_MEDIAFILE_MEDIARECORDER_H_
|
||||
#define SRC_MEDIAFILE_MEDIARECORDER_H_
|
||||
|
||||
#include <memory>
|
||||
#include "Player/PlayerBase.h"
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
#include "Mp4Maker.h"
|
||||
#include "HLSMaker.h"
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Player;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
|
||||
class MediaRecorder {
|
||||
public:
|
||||
typedef std::shared_ptr<MediaRecorder> Ptr;
|
||||
MediaRecorder(const string &strApp,const string &strId,const std::shared_ptr<PlayerBase> &pPlayer);
|
||||
virtual ~MediaRecorder();
|
||||
|
||||
void inputH264( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp,
|
||||
int iType);
|
||||
|
||||
void inputAAC( void *pData,
|
||||
uint32_t ui32Length,
|
||||
uint32_t ui32TimeStamp);
|
||||
private:
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
std::shared_ptr<HLSMaker> m_hlsMaker;
|
||||
std::shared_ptr<Mp4Maker> m_mp4Maker;
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif /* SRC_MEDIAFILE_MEDIARECORDER_H_ */
|
||||
190
src/MediaFile/Mp4Maker.cpp
Normal file
190
src/MediaFile/Mp4Maker.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/stat.h>
|
||||
#include "config.h"
|
||||
#include "Mp4Maker.h"
|
||||
#include "MediaRecorder.h"
|
||||
#include "Util/File.h"
|
||||
#include "Util/mini.h"
|
||||
#include "Util/NoticeCenter.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
string timeStr(const char *fmt) {
|
||||
std::tm tm_snapshot;
|
||||
auto time = ::time(NULL);
|
||||
#if defined(WIN32)
|
||||
localtime_s(&tm_snapshot, &time); // thread-safe?
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(const_cast<std::tm*>(&tm_snapshot), fmt);
|
||||
return oss.str();
|
||||
#else
|
||||
localtime_r(&time, &tm_snapshot); // POSIX
|
||||
const size_t size = 1024;
|
||||
char buffer[size];
|
||||
auto success = std::strftime(buffer, size, fmt, &tm_snapshot);
|
||||
if (0 == success)
|
||||
return string(fmt);
|
||||
return buffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
Mp4Maker::Mp4Maker(const string& strPath,const string &strApp,const string &strStreamId, const PlayerBase::Ptr &pPlayer) {
|
||||
DebugL << strPath;
|
||||
m_pPlayer = pPlayer;
|
||||
m_strPath = strPath;
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.strAppName = strApp;
|
||||
m_info.strStreamId = strStreamId;
|
||||
m_info.strFolder = strPath;
|
||||
//----record 业务逻辑----//
|
||||
}
|
||||
Mp4Maker::~Mp4Maker() {
|
||||
closeFile();
|
||||
}
|
||||
|
||||
void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType){
|
||||
switch (iType) {
|
||||
case 1: //P
|
||||
case 5: { //IDR
|
||||
if (m_strLastVideo.size()) {
|
||||
_inputH264((char *) m_strLastVideo.data(), m_strLastVideo.size(), ui32TimeStamp - m_ui32LastVideoTime, m_iLastVideoType);
|
||||
}
|
||||
//m_strLastVideo.assign(("\x0\x0\x0\x2\x9\xf0"), 6);
|
||||
uint32_t *p = (uint32_t *) pData;
|
||||
*p = htonl(ui32Length - 4);
|
||||
m_strLastVideo.assign((char *) pData, ui32Length);
|
||||
memcpy(pData, "\x00\x00\x00\x01", 4);
|
||||
|
||||
m_ui32LastVideoTime = ui32TimeStamp;
|
||||
m_iLastVideoType = iType;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp){
|
||||
if (m_strLastAudio.size()) {
|
||||
_inputAAC((char *)m_strLastAudio.data(), m_strLastAudio.size(), ui32TimeStamp - m_ui32LastAudioTime);
|
||||
}
|
||||
m_strLastAudio.assign((char *)pData, ui32Length);
|
||||
m_ui32LastAudioTime = ui32TimeStamp;
|
||||
}
|
||||
|
||||
void Mp4Maker::_inputH264(void* pData, uint32_t ui32Length, uint32_t ui32Duration, int iType) {
|
||||
static uint32_t recordMS = 1000 * mINI::Instance()[Config::Record::kFileSecond].as<uint32_t>();
|
||||
if(iType == 5 && (m_hMp4 == MP4_INVALID_FILE_HANDLE || m_ticker.elapsedTime() > recordMS)){
|
||||
//在I帧率处新建MP4文件
|
||||
//如果文件未创建或者文件超过10分钟则创建新文件
|
||||
createFile();
|
||||
}
|
||||
if (m_hVideo != MP4_INVALID_TRACK_ID) {
|
||||
MP4WriteSample(m_hMp4, m_hVideo, (uint8_t *) pData, ui32Length,ui32Duration * 90,0,iType == 5);
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::_inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32Duration) {
|
||||
static uint32_t recordMS = 1000 * mINI::Instance()[Config::Record::kFileSecond].as<uint32_t>();
|
||||
if (!m_pPlayer->containVideo() && (m_hMp4 == MP4_INVALID_FILE_HANDLE || m_ticker.elapsedTime() > recordMS)) {
|
||||
//在I帧率处新建MP4文件
|
||||
//如果文件未创建或者文件超过10分钟则创建新文件
|
||||
createFile();
|
||||
}
|
||||
if (m_hAudio != MP4_INVALID_TRACK_ID) {
|
||||
auto duration = ui32Duration * m_pPlayer->getAudioSampleRate() /1000.0;
|
||||
MP4WriteSample(m_hMp4, m_hAudio, (uint8_t*)pData + 7, ui32Length - 7,duration,0,false);
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::createFile() {
|
||||
if(!m_pPlayer->isInited()){
|
||||
return;
|
||||
}
|
||||
closeFile();
|
||||
|
||||
|
||||
auto strDate = timeStr("%Y-%m-%d");
|
||||
auto strTime = timeStr("%H-%M-%S");
|
||||
auto strFileTmp = m_strPath + strDate + "/." + strTime + ".mp4";
|
||||
auto strFile = m_strPath + strDate + "/" + strTime + ".mp4";
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.ui64StartedTime = ::time(NULL);
|
||||
m_info.strFileName = strTime + ".mp4";
|
||||
m_info.strFilePath = strFile;
|
||||
static string appName = mINI::Instance()[Config::Record::kAppName];
|
||||
m_info.strUrl = appName + "/"
|
||||
+ m_info.strAppName + "/"
|
||||
+ m_info.strStreamId + "/"
|
||||
+ strDate + "/"
|
||||
+ strTime + ".mp4";
|
||||
//----record 业务逻辑----//
|
||||
|
||||
File::createfile_path(strFileTmp.data(), S_IRWXO | S_IRWXG | S_IRWXU);
|
||||
m_hMp4 = MP4Create(strFileTmp.data());
|
||||
if (m_hMp4 == MP4_INVALID_FILE_HANDLE) {
|
||||
WarnL << "创建MP4文件失败:" << strFileTmp;
|
||||
return;
|
||||
}
|
||||
//MP4SetTimeScale(m_hMp4, 90000);
|
||||
m_strFileTmp = strFileTmp;
|
||||
m_strFile = strFile;
|
||||
m_ticker.resetTime();
|
||||
if(m_pPlayer->containVideo()){
|
||||
auto &sps = m_pPlayer->getSps();
|
||||
auto &pps = m_pPlayer->getPps();
|
||||
m_hVideo = MP4AddH264VideoTrack(m_hMp4, 90000, MP4_INVALID_DURATION,
|
||||
m_pPlayer->getVideoWidth(), m_pPlayer->getVideoHeight(),
|
||||
sps[5], sps[6], sps[7], 3);
|
||||
if(m_hVideo !=MP4_INVALID_TRACK_ID){
|
||||
MP4AddH264SequenceParameterSet(m_hMp4, m_hVideo, (uint8_t *)sps.data() + 4, sps.size() - 4);
|
||||
MP4AddH264PictureParameterSet(m_hMp4, m_hVideo, (uint8_t *)pps.data() + 4, pps.size() - 4);
|
||||
}else{
|
||||
WarnL << "添加视频通道失败:" << strFileTmp;
|
||||
}
|
||||
}
|
||||
if(m_pPlayer->containAudio()){
|
||||
m_hAudio = MP4AddAudioTrack(m_hMp4, m_pPlayer->getAudioSampleRate(), MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE);
|
||||
if (m_hAudio != MP4_INVALID_TRACK_ID) {
|
||||
auto &cfg = m_pPlayer->getAudioCfg();
|
||||
MP4SetTrackESConfiguration(m_hMp4, m_hAudio,(uint8_t *)cfg.data(), cfg.size());
|
||||
}else{
|
||||
WarnL << "添加音频通道失败:" << strFileTmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mp4Maker::closeFile() {
|
||||
if (m_hMp4 != MP4_INVALID_FILE_HANDLE) {
|
||||
{
|
||||
TimeTicker();
|
||||
MP4Close(m_hMp4,MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
|
||||
}
|
||||
rename(m_strFileTmp.data(),m_strFile.data());
|
||||
m_hMp4 = MP4_INVALID_FILE_HANDLE;
|
||||
m_hVideo = MP4_INVALID_TRACK_ID;
|
||||
m_hAudio = MP4_INVALID_TRACK_ID;
|
||||
|
||||
/////record 业务逻辑//////
|
||||
m_info.ui64TimeLen = ::time(NULL) - m_info.ui64StartedTime;
|
||||
//获取文件大小
|
||||
struct stat fileData;
|
||||
stat(m_strFile.data(), &fileData);
|
||||
m_info.ui64FileSize = fileData.st_size;
|
||||
//----record 业务逻辑----//
|
||||
NoticeCenter::Instance().emitEvent(Config::Broadcast::kBroadcastRecordMP4,(const Mp4Info &)m_info);
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
|
||||
#endif //ENABLE_MEDIAFILE
|
||||
82
src/MediaFile/Mp4Maker.h
Normal file
82
src/MediaFile/Mp4Maker.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Mp4Maker.h
|
||||
*
|
||||
* Created on: 2013-9-18
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#ifndef MP4MAKER_H_
|
||||
#define MP4MAKER_H_
|
||||
|
||||
#ifdef ENABLE_MEDIAFILE
|
||||
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <mp4v2/mp4v2.h>
|
||||
#include "Player/PlayerBase.h"
|
||||
#include "Util/util.h"
|
||||
#include "Util/logger.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
#include "Util/TimeTicker.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace ZL::Util;
|
||||
using namespace ZL::Player;
|
||||
|
||||
namespace ZL {
|
||||
namespace MediaFile {
|
||||
|
||||
class Mp4Info
|
||||
{
|
||||
public:
|
||||
time_t ui64StartedTime; //GMT标准时间,单位秒
|
||||
time_t ui64TimeLen;//录像长度,单位秒
|
||||
__off_t ui64FileSize;//文件大小,单位BYTE
|
||||
string strFilePath;//文件路径
|
||||
string strFileName;//文件名称
|
||||
string strFolder;//文件夹路径
|
||||
string strUrl;//播放路径
|
||||
string strAppName;//应用名称
|
||||
string strStreamId;//流ID
|
||||
};
|
||||
class Mp4Maker {
|
||||
public:
|
||||
typedef std::shared_ptr<Mp4Maker> Ptr;
|
||||
Mp4Maker(const string &strPath,const string &strApp,const string &strStreamId, const PlayerBase::Ptr &pPlayer);
|
||||
virtual ~Mp4Maker();
|
||||
//时间戳:参考频率1000
|
||||
void inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType);
|
||||
//时间戳:参考频率1000
|
||||
void inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
|
||||
private:
|
||||
MP4FileHandle m_hMp4 = MP4_INVALID_FILE_HANDLE;
|
||||
MP4TrackId m_hVideo = MP4_INVALID_TRACK_ID;
|
||||
MP4TrackId m_hAudio = MP4_INVALID_TRACK_ID;
|
||||
PlayerBase::Ptr m_pPlayer;
|
||||
string m_strPath;
|
||||
string m_strFile;
|
||||
string m_strFileTmp;
|
||||
Ticker m_ticker;
|
||||
SmoothTicker m_mediaTicker[2];
|
||||
|
||||
void createFile();
|
||||
void closeFile();
|
||||
void _inputH264(void *pData, uint32_t ui32Length, uint32_t ui64Duration, int iType);
|
||||
void _inputAAC(void *pData, uint32_t ui32Length, uint32_t ui64Duration);
|
||||
|
||||
string m_strLastVideo;
|
||||
string m_strLastAudio;
|
||||
|
||||
uint32_t m_ui32LastVideoTime = 0;
|
||||
uint32_t m_ui32LastAudioTime = 0;
|
||||
int m_iLastVideoType = 0;
|
||||
|
||||
Mp4Info m_info;
|
||||
};
|
||||
|
||||
} /* namespace MediaFile */
|
||||
} /* namespace ZL */
|
||||
|
||||
#endif ///ENABLE_MEDIAFILE
|
||||
|
||||
#endif /* MP4MAKER_H_ */
|
||||
585
src/MediaFile/TSMaker.cpp
Normal file
585
src/MediaFile/TSMaker.cpp
Normal file
@@ -0,0 +1,585 @@
|
||||
/*
|
||||
* TSMaker.cpp
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#include "TSMaker.h"
|
||||
#include <sys/time.h>
|
||||
#include "Util/logger.h"
|
||||
|
||||
using namespace ZL::Util;
|
||||
|
||||
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.c_str(), "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 ui64VideoPts) {
|
||||
//填写自适应段
|
||||
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 = ui64VideoPts * 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 ui64Time) {
|
||||
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, ui64Time); //填写自适应段标志帧头
|
||||
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, ui64Time);
|
||||
m_pVideo_pes->ESlen = 0;
|
||||
return ui32Len;
|
||||
}
|
||||
|
||||
int TSMaker::inputAAC(const char* pcData, uint32_t ui32Len, 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, 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) {
|
||||
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 ui64Dts
|
||||
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) | ((ui64Dts >> 29) & 0x0E) | 0x01) & 0xff);
|
||||
pucTSBuf[2] = (((((ui64Dts >> 14) & 0xfffe) | 0x01) >> 8) & 0xff);
|
||||
pucTSBuf[3] = ((((ui64Dts >> 14) & 0xfffe) | 0x01) & 0xff);
|
||||
pucTSBuf[4] = (((((ui64Dts << 1) & 0xfffe) | 0x01) >> 8) & 0xff);
|
||||
pucTSBuf[5] = ((((ui64Dts << 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;
|
||||
}
|
||||
}
|
||||
|
||||
249
src/MediaFile/TSMaker.h
Normal file
249
src/MediaFile/TSMaker.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* TSMaker.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#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 ZL::Util;
|
||||
|
||||
#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 ui64Time);
|
||||
int inputAAC(const char *pcData, uint32_t ui32Len, uint64_t ui64Time);
|
||||
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 ui64Pts);
|
||||
void CreateAdaptive_Ts(Ts_Adaptation_field * pAdaptationField, unsigned char * pcTs, unsigned int uiAdaptiveLength);
|
||||
};
|
||||
|
||||
#endif /* TSMAKER_H_ */
|
||||
97
src/MediaFile/crc32.cpp
Normal file
97
src/MediaFile/crc32.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* crc32.cpp
|
||||
*
|
||||
* Created on: 2013-9-11
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
/*
|
||||
* crc32.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
16
src/MediaFile/crc32.h
Normal file
16
src/MediaFile/crc32.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* crc32.h
|
||||
*
|
||||
* Created on: 2013-6-21
|
||||
* Author: root
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
Reference in New Issue
Block a user