QT 不使用ffmpeg將MPEG2-TS解封裝為ES
阿新 • • 發佈:2021-02-06
技術標籤:QT+FFMPEG
不使用ffmpeg將MPEG2-TS解封裝為ES,直接根據協議解。
解封裝的同時還解碼為YUV
此程式為驗證功能,沒有考慮效率和程式碼規範。
測試程式可用性:輸入一個mpeg2-ts
檔案,輸出一個.mpeg2
和一個.yuv
檔案
最終結果如下:
標頭檔案
#ifndef TS2H264_C_H
#define TS2H264_C_H
#include <QObject>
#include "ffmpegheader.h"
#include <vector>
class ts2H264_C
{
public:
ts2H264_C ();
int _start();
void insertVector(unsigned char *buff,int len);
int decode();
std::vector<unsigned char> vOneFullPacket;
};
#endif // TS2H264_C_H
CPP檔案
#include "ts2h264_c.h"
ts2H264_C::ts2H264_C()
{
vOneFullPacket.clear();
}
static int nCountFrame = 0;
static bool gif_I_Frame = false;//第一個I幀是否出現
static FILE *out_yuv = fopen("out.yuv","wb+");
typedef struct _TS_head
{
unsigned char sync_byte :8; //就是0x47,這是DVB TS規定的同步位元組,固定是0x47.
unsigned char transport_error_indicator :1; // 0 表示當前包沒有發生傳輸錯誤.
unsigned char payload_unit_start_indicator : 1; // 含義參考ISO13818-1標準文件
unsigned char transport_priority :1; // 表示當前包是低優先順序.
unsigned short PID :13;
unsigned char transport_scrambling_control :2; //表示節目沒有加密
unsigned char adaptation_field_control :2; // 即0x01,具體含義請參考ISO13818-1
unsigned char continuity_counte :4; //即0x02,表示當前傳送的相同型別的包是第3個
}TS_head;
typedef struct TS_PAT_Program
{
unsigned program_number : 16; //節目號
unsigned reserved_3 : 3; // 保留位
unsigned program_map_PID_network_PID : 13; // 節目對映表的PID,節目號大於0時對應的PID,每個節目對應一個
}TS_PAT_Program;
typedef struct TS_PAT
{
unsigned table_id : 8; //固定為0x00 ,標誌是該表是PAT表
unsigned section_syntax_indicator : 1; //段語法標誌位,固定為1
unsigned zero : 1; //0
unsigned reserved_1 : 2; // 保留位
unsigned section_length : 12; //表示從下一個欄位開始到CRC32(含)之間有用的位元組數
unsigned transport_stream_id : 16; //該傳輸流的ID,區別於一個網路中其它多路複用的流
unsigned reserved_2 : 2;// 保留位
unsigned version_number : 5; //範圍0-31,表示PAT的版本號
unsigned current_next_indicator : 1; //傳送的PAT是當前有效還是下一個PAT有效
unsigned section_number : 8; //分段的號碼。PAT可能分為多段傳輸,第一段為00,以後每個分段加1,最多可能有256個分段
unsigned last_section_number : 8; //最後一個分段的號碼
std::vector<TS_PAT_Program> vProgram;
//unsigned reserved_3 : 3; // 保留位
//unsigned network_PID : 13; //網路資訊表(NIT)的PID,節目號為0時對應的PID為network_PID
unsigned CRC_32 : 32; //CRC32校驗碼
} TS_PAT;
typedef struct TS_PMT_Stream
{
unsigned stream_type : 8; //指示特定PID的節目元素包的型別。該處PID由elementary PID指定
unsigned elementary_PID : 13; //該域指示TS包的PID值。這些TS包含有相關的節目元素
unsigned ES_info_length : 12; //前兩位bit為00。該域指示跟隨其後的描述相關節目元素的byte數
unsigned descriptor;
}TS_PMT_Stream;
static std::vector<TS_PAT_Program> g_vTS_program;
//PMT 表結構體
typedef struct TS_PMT
{
unsigned table_id : 8; //固定為0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定為0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12;//首先兩位bit置為00,它指示段的byte數,由段長度域開始,包含CRC。
unsigned program_number : 16;// 指出該節目對應於可應用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本號
unsigned current_next_indicator : 1; //當該位置1時,當前傳送的Program map section可用;
//當該位置0時,指示當前傳送的Program map section不可用,下一個TS流的Program map section有效。
unsigned section_number : 8; //固定為0x00
unsigned last_section_number : 8; //固定為0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,該TS包含有PCR域,
//該PCR值對應於由節目號指定的對應節目。
//如果對於私有資料流的節目定義與PCR無關,這個域的值將為0x1FFF。
unsigned reserved_4 : 4; //預留為0x0F
unsigned program_info_length : 12; //前兩位bit為00。該域指出跟隨其後對節目資訊的描述的byte數。
std::vector<TS_PMT_Stream> vPMT_Stream; //每個元素包含8位, 指示特定PID的節目元素包的型別。該處PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
} TS_PMT;
static TS_PAT gPAT;
static TS_PMT gPMT;
static void get_ts_header(TS_head &ts_head,unsigned char *buff)
{
ts_head.sync_byte = (unsigned char)buff[0];
ts_head.transport_error_indicator = buff[1] >>7;
ts_head.payload_unit_start_indicator = buff[1] >>6 & 0x1;
ts_head.transport_priority = buff[1] >>5 & 0x1;
ts_head.PID = ((buff[1] & 0x1F)<< 8) | buff[2];
ts_head.transport_scrambling_control = buff[3]>>6 & 0x3;
ts_head.adaptation_field_control = buff[3]>>4 & 0x3;
ts_head.continuity_counte = buff[3] & 0xF;
}
static void adjust_PAT_table( TS_PAT * ts_pat, unsigned char * buffer)
{
ts_pat->table_id = buffer[0];
ts_pat->section_syntax_indicator = buffer[1] >> 7;
ts_pat->zero = buffer[1] >> 6 & 0x1;
ts_pat->reserved_1 = buffer[1] >> 4 & 0x3;
ts_pat->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
ts_pat->transport_stream_id = buffer[3] << 8 | buffer[4];
ts_pat->reserved_2 = buffer[5] >> 6;
ts_pat->version_number = buffer[5] >> 1 & 0x1F;
ts_pat->current_next_indicator = buffer[5] & 0x1;
ts_pat->section_number = buffer[6];
ts_pat->last_section_number = buffer[7];
int len = 0;
len = 3 + ts_pat->section_length;
ts_pat->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);
int n = 0;
TS_PAT_Program PAT_program;
for ( n = 0; n < ts_pat->section_length - 12; n += 4 )
{
unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ];
//ts_pat->reserved_3 = buffer[10 + n ] >> 5;
//ts_pat->network_PID = 0x00;
PAT_program.reserved_3 = buffer[10 + n ] >> 5;
if ( program_num == 0x00)
{
//ts_pat->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
PAT_program.program_map_PID_network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
PAT_program.program_number = program_num;
//TS_network_Pid = packet->network_PID; //記錄該TS流的網路PID
g_vTS_program.push_back( PAT_program );//向全域性PAT節目陣列中新增PAT節目資訊
printf(" TS stream PID %0x \n\n", PAT_program.program_map_PID_network_PID );
}
else
{
PAT_program.program_map_PID_network_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];
PAT_program.program_number = program_num;
ts_pat->vProgram.push_back( PAT_program );
g_vTS_program.push_back( PAT_program );//向全域性PAT節目陣列中新增PAT節目資訊
printf(" TS programe PID %0x \n\n", PAT_program.program_map_PID_network_PID );
}
}
}
static void adjust_PMT_table ( TS_PMT * ts_pmt, unsigned char * buffer )
{
ts_pmt->table_id = buffer[0];
ts_pmt->section_syntax_indicator = buffer[1] >> 7;
ts_pmt->zero = buffer[1] >> 6 & 0x01;
ts_pmt->reserved_1 = buffer[1] >> 4 & 0x03;
ts_pmt->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
ts_pmt->program_number = buffer[3] << 8 | buffer[4];
ts_pmt->reserved_2 = buffer[5] >> 6;
ts_pmt->version_number = buffer[5] >> 1 & 0x1F;
ts_pmt->current_next_indicator = (buffer[5] << 7) >> 7;
ts_pmt->section_number = buffer[6];
ts_pmt->last_section_number = buffer[7];
ts_pmt->reserved_3 = buffer[8] >> 5;
ts_pmt->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;
//PCRID = packet->PCR_PID;
ts_pmt->reserved_4 = buffer[10] >> 4;
ts_pmt->program_info_length = (buffer[10] & 0x0F) << 8 | buffer[11];
// Get CRC_32
int len = 0;
len = ts_pmt->section_length + 3;
ts_pmt->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);
int pos = 12;
// program info descriptor
if ( ts_pmt->program_info_length != 0 )
pos += ts_pmt->program_info_length;
// Get stream type and PID
for ( ; pos <= (ts_pmt->section_length + 2 ) - 4; )
{
TS_PMT_Stream pmt_stream;
pmt_stream.stream_type = buffer[pos];
ts_pmt->reserved_5 = buffer[pos+1] >> 5;
pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;
ts_pmt->reserved_6 = buffer[pos+3] >> 4;
pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];
pmt_stream.descriptor = 0x00;
if (pmt_stream.ES_info_length != 0)
{
pmt_stream.descriptor = buffer[pos + 5];
for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )
{
pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];
}
pos += pmt_stream.ES_info_length;
}
pos += 5;
ts_pmt->vPMT_Stream.push_back( pmt_stream );
//TS_Stream_type.push_back( pmt_stream );
}
}
static bool bPESHead = false;
static uint64_t size = 0;
static uint64_t size_count = 0;
static uint8_t *data = nullptr;
static bool bFlag_ifGetPMT = false;
static bool getPESHead(unsigned char * buffer)
{
if( buffer[0] == 0x00
&& buffer[1] == 0x00
&& buffer[2] == 0x01 )
{
return true;
}
else
{
return false;
}
}
static AVPacket pkt;
static AVFrame *frame = av_frame_alloc();
static AVCodecContext* gDecodeCtx = nullptr;
static AVCodec *gCodec = nullptr;
static AVCodecParserContext *parser = nullptr;
static int openDecoder()
{
/* find the MPEG-1 video decoder *///AV_CODEC_ID_MPEG2VIDEO
gCodec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
if (!gCodec)
{
fprintf(stderr, "Codec not found\n");
exit(1);
}
// 獲取裸流的解析器 AVCodecParserContext(資料) + AVCodecParser(方法)
parser = av_parser_init(gCodec->id);
if (!parser) {
fprintf(stderr, "Parser not found\n");
exit(1);
}
gDecodeCtx = avcodec_alloc_context3(gCodec);
if (!gDecodeCtx)
{
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
if (avcodec_open2(gDecodeCtx, gCodec, nullptr) < 0)
{
fprintf(stderr, "Could not open codec\n");
exit(1);
}
}
int ts2H264_C::decode()
{
//printf_hex(pkt.data,a);
int nRet = avcodec_send_packet(gDecodeCtx, &pkt);
while( 0 == nRet)
{
int got_picture =avcodec_receive_frame(gDecodeCtx, frame);
if(0 == got_picture)//
{
//寫入資料
int picSize = frame->height *frame->width;
int newSize = picSize * 1.5;
//申請記憶體
unsigned char *buf = new unsigned char[newSize];
int a=0,i;
for (i=0; i<frame->height; i++)
{
memcpy(buf+a,frame->data[0] + i * frame->linesize[0], frame->width);
a+=frame->width;
}
for (i=0; i<frame->height/2; i++)
{
memcpy(buf+a,frame->data[1] + i * frame->linesize[1], frame->width/2);
a+=frame->width/2;
}
for (i=0; i<frame->height/2; i++)
{
memcpy(buf+a,frame->data[2] + i * frame->linesize[2], frame->width/2);
a+=frame->width/2;
}
fwrite(buf, 1, newSize, out_yuv);
fflush(out_yuv);
nCountFrame++;
printf("yuv frame %d \n",nCountFrame);
}
else
{
break;
}
}
av_packet_unref(&pkt);
return 0;
}
void ts2H264_C::insertVector(unsigned char *buff,int len)
{
for(int i = 0; i < len; i++)
{
vOneFullPacket.push_back(buff[i]);
}
}
static bool first_I_frame(unsigned char* buff,int len)
{
for (int i = 0; i < len; i++)
{
if( buff[i] == 0x00
&& buff[i+1] == 0x00
&& buff[i+2] == 0x01
&& buff[i+3] == 0x00)
{
if(0x1 == (buff[i+5]&0x38)>>3)//I幀
{
return true;
}
}
}
return false;
}
int ts2H264_C::_start()
{
int nRet = 0;
unsigned char temp[1316];
FILE *in = fopen("in.ts","rb");
FILE *out = fopen("out.mpeg2","wb+");
openDecoder();
//如果未到檔案結尾
int ret = 0;
uint8_t *data_pkt = nullptr;
uint8_t last_pes_head_len = 0;
while (!feof(in))
{
if(size_count >= 4096)
{
unsigned char OneFullPacket[size_count];
memset(OneFullPacket,0,size_count);
memcpy(OneFullPacket,&vOneFullPacket[0],size_count);
data_pkt = OneFullPacket;
while(size_count > 0 )
{
frame = av_frame_alloc();
ret = av_parser_parse2(parser, gDecodeCtx, &pkt.data, &pkt.size,
data_pkt, size_count,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0)
{
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data_pkt += ret; // 跳過已經解析的資料
size_count -= ret; // 對應的快取大小也做相應減小
vOneFullPacket.erase(vOneFullPacket.begin(),vOneFullPacket.begin()+ret);
if (pkt.size)
{
decode();
}
}
}
memset(temp,0,sizeof (temp));
fread(temp, 1, 1316, in);
TS_head ts_head;
//每次讀7個TS包
int nTsPacket = 0;
while( nTsPacket < 7)
{
get_ts_header(ts_head,(unsigned char*)&temp[nTsPacket*188]);
if(ts_head.sync_byte != 0x47)
{
printf("同步位元組錯誤 \n");
return 1;
}
if(ts_head.PID == 0x0000)//PAT
{
gPAT.vProgram.clear();
adjust_PAT_table(&gPAT,(unsigned char*)&temp[5+nTsPacket*188]);
if(bFlag_ifGetPMT == true)
{
gPMT.vPMT_Stream.clear();
bFlag_ifGetPMT = false;
}
}
else
{
if( false == bFlag_ifGetPMT )
{
auto iter = gPAT.vProgram.begin();
for ( ;iter != gPAT.vProgram.end(); iter++)
{
if(0x0001 == (*iter).program_number //PMT
&& ts_head.PID == (*iter).program_map_PID_network_PID) //PID一致
{
adjust_PMT_table( &gPMT, &temp[5+nTsPacket*188]);
bFlag_ifGetPMT = true;
}
}
}
else
{
auto iter = gPMT.vPMT_Stream.begin();
for ( ;iter != gPMT.vPMT_Stream.end(); iter++)
{
if((*iter).elementary_PID == ts_head.PID)
{
if(0x1 == ts_head.payload_unit_start_indicator)//PES包頭
{
if( ts_head.adaptation_field_control == 2
|| ts_head.adaptation_field_control == 3)//自適應欄位
{
if(bPESHead == false)
{
bPESHead = getPESHead(&temp[ nTsPacket*188+4 + 1 + temp[4+nTsPacket*188]]);
}
if(bPESHead == true)
{
last_pes_head_len = (uint8_t)temp[nTsPacket*188 + 4 + 1 + (uint8_t)temp[4+nTsPacket*188] + 9+1];
last_pes_head_len = last_pes_head_len & 0x0f;
size = 188-4-1-temp[4+nTsPacket*188]-9-last_pes_head_len;
data = (uint8_t *)&temp[nTsPacket*188 + 4 + 1 + temp[4+nTsPacket*188] + 9+last_pes_head_len];
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
else
{
gif_I_Frame = first_I_frame(data,size);//I幀
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
}
}
}
else
{
if(bPESHead == false)
{
bPESHead = getPESHead(&temp[ nTsPacket*188+4 + 1 + temp[4+nTsPacket*188]]);
}
if(bPESHead == true)
{
last_pes_head_len = (uint8_t)temp[nTsPacket*188 + 4 + 1 + 9+1];
last_pes_head_len = last_pes_head_len & 0x0f;
size = 188-4-1-9-last_pes_head_len;
data = (uint8_t *)&temp[nTsPacket*188+4 + 1 + 9+last_pes_head_len];
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
else
{
gif_I_Frame = first_I_frame(data,size);
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
}
}
}
}
else
{
if( ts_head.adaptation_field_control == 2
|| ts_head.adaptation_field_control == 3)
{
if(bPESHead == false)
{
bPESHead = getPESHead(&temp[nTsPacket*188+4]);
}
if(bPESHead == true)
{
size = 188-4-1-temp[4+nTsPacket*188];
data = (uint8_t *)&temp[nTsPacket*188 + 4 + 1 + temp[4+nTsPacket*188]];
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
else
{
gif_I_Frame = first_I_frame(data,size);
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
}
}
}
else
{
if(bPESHead == false)
{
bPESHead = getPESHead(&temp[nTsPacket*188+4]);
}
if(bPESHead == true)
{
size = 188-4;
data = (uint8_t *)&temp[nTsPacket*188+4];
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
else
{
gif_I_Frame = first_I_frame(data,size);
if(gif_I_Frame == true)
{
fwrite(data,1,size,out);
fflush(out);
size_count+=size;
insertVector(data,size);
}
}
}
}
}
}
}
}
}
nTsPacket++;
}
}
/* 沖刷解碼器 */
pkt.data = NULL; // 讓其進入drain mode
pkt.size = 0;
decode();
return nRet;
}