MpegTS流解复用程序实现(解复用得到PES和ES)

MpegTS基础看这几篇博文:

MpegTS基础

MpegTS之TS,PES,ES结构分析

TS流复用和解复用是一个相逆的过程。TS解复用得到的是音视频的PES裸流。一般来讲,每个TS包的长度是188个字节,也有一种204个字节的,就是在每个包后面加上16个字节的RS冗余校验信息。在这里分析188个字节的情况,其余的都类似了。

从文件中循环读取188个字节的包,然后对包进行逐字节分析,,分析流程如下:

TS包的标志是首字节以0x47开头

如下图是一个ts包:

按位解析,得到pid,flag,错误标志,负载类型,PSI, PMI等信息。

源码分析如下:该源码是从一开源工具tsDemux截取,所有的ts流的解析过程无非也就是整么一个过程了。

<span style="font-family:SimHei;font-size:18px;">int ts::demuxer::demux_ts_packet(const char* ptr){u_int32_t timecode = 0;const char* end_ptr = ptr + 188;if (ptr[0] != 0x47)// ts sync bytereturn -1;u_int16_t pid = to_int(ptr + 1);//get 2, 3 Byteu_int8_t flags = to_byte(ptr + 3);bool transport_error = pid & 0x8000;/*ts带有PES包时,1:负载是PES,0:负载不是PES ts带有PSI数据时,1:带有PSI部分的第一个字节 0:不带有PSI部分的第一个字节*/bool payload_unit_start_indicator = pid & 0x4000;bool adaptation_field_exist = flags & 0x20;bool payload_data_exist = flags & 0x10;u_int8_t continuity_counter = flags & 0x0f;//get PIDpid &= 0x1fff;if (transport_error)return -2;//empty payloadif (pid == 0x1fff || !payload_data_exist)return 0;ptr += 4;// skip adaptation fieldif (adaptation_field_exist){ptr += to_byte(ptr) + 1;if (ptr >= end_ptr)return -3;}stream& s = streams[pid];if (!pid || (s.channel != 0xffff && s.type == 0xff)){// PSIif (payload_unit_start_indicator){// begin of PSI tableptr++;if (ptr >= end_ptr)return -4;if (*ptr != 0x00 && *ptr != 0x02)return 0;if (end_ptr – ptr < 3)return -5;u_int16_t l = to_int(ptr + 1);if (l & 0x3000 != 0x3000)return -6;l &= 0x0fff;ptr += 3;int len = end_ptr – ptr;if (l > len){if (l > ts::table::max_buf_len)return -7;s.psi.reset();memcpy(s.psi.buf, ptr, len);s.psi.offset += len;s.psi.len = l;return 0;}elseend_ptr = ptr + l;}else{// next part of PSIif (!s.psi.offset)return -8;int len = end_ptr – ptr;if (len > ts::table::max_buf_len – s.psi.offset)return -9;memcpy(s.psi.buf + s.psi.offset, ptr, len);s.psi.offset += len;if (s.psi.offset < s.psi.len)return 0;else{ptr = s.psi.buf;end_ptr = ptr + s.psi.len;}}if (!pid){// PATptr += 5;if (ptr >= end_ptr)return -10;int len = end_ptr – ptr – 4;if (len < 0 || len % 4)return -11;int n = len / 4;for (int i = 0; i < n; i++, ptr += 4){u_int16_t channel = to_int(ptr);u_int16_t pid = to_int(ptr + 2);if (pid & 0xe000 != 0xe000)return -12;pid &= 0x1fff;if (!demuxer::channel || demuxer::channel == channel){stream& ss = streams[pid];ss.channel = channel;ss.type = 0xff;}}}else{// PMTptr += 7;if (ptr >= end_ptr)return -13;u_int16_t info_len = to_int(ptr) & 0x0fff;ptr += info_len + 2;end_ptr -= 4;if (ptr >= end_ptr)return -14;while (ptr < end_ptr){if (end_ptr – ptr < 5)return -15;u_int8_t type = to_byte(ptr);u_int16_t pid = to_int(ptr + 1);if (pid & 0xe000 != 0xe000)return -16;pid &= 0x1fff;info_len = to_int(ptr + 3) & 0x0fff;ptr += 5 + info_len;// ignore unknown streamsif (validate_type(type)){stream& ss = streams[pid];if (ss.channel != s.channel || ss.type != type){ss.channel = s.channel;ss.type = type;ss.id = ++s.id;if (!parse_only && !ss.file.is_opened()){if (dst.length())ss.file.open(file::out, false, "%s%c%strack_%i.%s", dst.c_str(), os_slash, prefix.c_str(), pid, get_stream_ext(get_stream_type(ss.type)));elsess.file.open(file::out, false, "%strack_%i.%s", prefix.c_str(), pid, get_stream_ext(get_stream_type(ss.type)));if (es_parse)ss.file.open(file::out, true, "%ses_strack_%i.%s", prefix.c_str(), pid, "es");}}}}if (ptr != end_ptr)return -18;}}else{ if (s.type != 0xff){// PESif (payload_unit_start_indicator)//等于true代表一个PES包的开始{s.psi.reset();s.psi.len = 9;}while (s.psi.offset<s.psi.len){int len = end_ptr – ptr;if (len <= 0)return 0;int n = s.psi.len – s.psi.offset;if (len>n)len = n;memcpy(s.psi.buf + s.psi.offset, ptr, len);s.psi.offset += len;ptr += len;if (s.psi.len == 9)s.psi.len += to_byte(s.psi.buf + 8);}if (s.psi.len){if (memcmp(s.psi.buf, "\x00\x00\x01", 3))return -19;s.stream_id = to_byte(s.psi.buf + 3);u_int8_t flags = to_byte(s.psi.buf + 7);s.frame_num++;switch (flags & 0xc0){case 0x80:// PTS only 音频包PTS和DTS相同,所以只有PTS{u_int64_t pts = decode_pts(s.psi.buf + 9);if (dump == 2)printf("%.4x: %llu\n", pid, pts);else if (dump == 3)printf("%.4x: track=%.4x.%.2i, type=%.2x, stream=%.2x, pts=%llums\n", pid, s.channel, s.id, s.type, s.stream_id, pts / 90);if (s.dts > 0 && pts > s.dts)s.frame_length = pts – s.dts;s.dts = pts;if (pts > s.last_pts)s.last_pts = pts;if (!s.first_pts)s.first_pts = pts;}break;case 0xc0:// PTS,DTS 视频包含有PTS和DTS{u_int64_t pts = decode_pts(s.psi.buf + 9);u_int64_t dts = decode_pts(s.psi.buf + 14);if (dump == 2)printf("%.4x: %llu %llu\n", pid, pts, dts);else if (dump == 3)printf("%.4x: track=%.4x.%.2i, type=%.2x, stream=%.2x, pts=%llums, dts=%llums\n", pid, s.channel, s.id, s.type, s.stream_id, pts / 90, dts / 90);if (s.dts > 0 && dts > s.dts)s.frame_length = dts – s.dts;s.dts = dts;if (pts > s.last_pts)s.last_pts = pts;if (!s.first_dts)s.first_dts = dts;}break;}if (pes_output && s.file.is_opened()){s.file.write(s.psi.buf, s.psi.len, false);//将PES包头信息存入文件}s.psi.reset();}if (s.frame_num){int len = end_ptr – ptr;if (s.file.is_opened()){s.file.write(ptr, len, true);//此处获取的是ES包}}}}return 0;}</span>

该代码是根据TSDemux工程修改,源项目只能解复用得到PES,在此基础上修改能同时获取音视频的PES,ES共4个文件。需要详细学习TS的同学可以研究下。

源代码已经上传到CSDN:

努力爱一个人。付出,不一定会有收获;

MpegTS流解复用程序实现(解复用得到PES和ES)

相关文章:

你感兴趣的文章:

标签云: