不使用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;
}