1. 程式人生 > 其它 >QT 不使用ffmpeg將MPEG2-TS解封裝為ES

QT 不使用ffmpeg將MPEG2-TS解封裝為ES

技術標籤: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; }