码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-02-05 16:31:26      阅读:484      评论:0      收藏:0      [点我收藏+]

标签:ts   demux   

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 byte
		return -1;
	u_int16_t pid = to_int(ptr + 1);//get 2, 3 Byte
	u_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 PID
	pid &= 0x1fff;

	if (transport_error)
		return -2;

	//empty payload
	if (pid == 0x1fff || !payload_data_exist)
		return 0;

	ptr += 4;

	// skip adaptation field
	if (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))
	{
		// PSI
		if (payload_unit_start_indicator)
		{
			// begin of PSI table
			ptr++;

			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;
			}
			else
				end_ptr = ptr + l;
		}
		else
		{
			// next part of PSI
			if (!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)
		{
			// PAT
			ptr += 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
		{
			// PMT
			ptr += 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 streams
				if (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)));
							else
								ss.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)
		{
			// PES

			if (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:

http://download.csdn.net/detail/rootusers/8426227




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

标签:ts   demux   

原文地址:http://blog.csdn.net/rootusers/article/details/43528261

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!