RTSP支持MPEG-4格式监控

1 前沿

基本定义:MPEG-4 标准是一种基于对象的编解码方式,它以帧重建技术为基础实现了影像压缩

技术要点:MPEG-4第二代的视频编码技术,提出了基于人眼生物特征的视频编码方法。提出了把人眼感兴趣的元素放在一起,即视频对象面的概念VOP 面。VOP 的分割也简单,按照一定的语义,0 表示在视频对象面内部,1 表示在视频对象面对象外部;在混合视频场景中,判断方法较复杂。

MPEG基本组成:MPEG-4 视频码流包括:视觉对象序列VS、视觉对象VO、视频对象层VOL、视频对象平面组GOV、视频对象平面VOP。其中VOP包括3种类型:帧内编码VOP(I-VOP)、预测编码VOP(P-VOP)和双向预测编码VOP(B-VOP) 。

MPEG优势:MPEG-4 标准在以下三方面具有明显优势:第一MPEG-4 压缩标准的视频传输效率比
H.264 高,且更适用于无线传输;第二基于MPEG-4 压缩标准的视频采用的是通用存储格式;第三H.264 压缩编码标准占用的CPU 和内存较大。

2 RTSP客户端支持MPEG4

2.1 rtsp协议支持

首先是在SDP协议协商时候需要提取几个关键信息:一个是协议格式:MP4V-ES;第二个是协议Payload:96;第三是分辨率等级:profile-level-id=4;当然这个还是以实际MPEG-4头解析为准;
第四是最为重要的MPEG-4头:config=000001b004000001b59113000001000000012000c888800f514043c14103;头部存放了MPEG-4分辨率信息,解码相关信息非常重要。

DESCRIBE URL RTSP/1.0
CSeq: 3
Accept: application/sdp
RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
Content-Base: URL
Server: GStreamer RTSP server
Date: Wed, 01 Sep 2021 02:45:00 GMT
Content-Length: 360
v=0
o=- 1188340656180883 1 IN IP4 IP
s=Session streamed with GStreamer
i=rtsp-server
t=0 0
a=tool:GStreamer
a=type:broadcast
a=control:*
a=range:npt=now-
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
a=rtpmap:96 MP4V-ES/90000
a=control:stream=0
a=fmtp:96 profile-level-id=4;config=000001b004000001b59113000001000000012000c888800f514043c14103

2.2 MPEG-4头部解析

这里采用的是xvidcore库中的bitestream.c进行头部解析。头部解析分为几个步骤:

1.十六进制字符串转为二进制流

static void hex_str_to_byte(char *in, int len, unsigned char *out)
{
    char *str = (char *)malloc(len);
    memset(str, 0, len);
    memcpy(str, in, len);
    for (int i = 0; i < len; i+=2)
    {
        if(str[i] >= 'a' && str[i] <= 'f') str[i] = str[i] - 0x20;
        if(str[i+1] >= 'a' && str[i] <= 'f') str[i+1] = str[i+1] - 0x20;

        if(str[i] >= 'A' && str[i] <= 'F')
            out[i/2] = (str[i]-'A'+10)<<4;
        else
            out[i/2] = (str[i] & ~0x30)<<4;
            
        if(str[i+1] >= 'A' && str[i+1] <= 'F')
            out[i/2] |= (str[i+1]-'A'+10);
        else
            out[i/2] |= (str[i+1] & ~0x30);
    }
    free(str);
}

2.第二是将数据导入xvidcore解析
解析的过程比较简单其实就是调用库函数API接口,这里只用到了流处理,即将头部赋给bs内部的流管理结构,进行初始化;然后调用头部解析,头部解析过程比较简单,就是协议的解析,分为三个部分:vos,vo,vop解析过程。
需要注意的是大小端的转换,一开始裁剪的太严重把大小端处理函数裁剪了,解析出来都是错乱的。

//这里对BitstreamReadHeaders函数参数进行了裁剪,主要是目的是为了获取视频分辨率
int mpeg4_parse(Mpeg4Context* cxt, const uint8_t* buf, size_t buf_len) 
{
	if(!cxt || !buf) return -1;
	
	Bitstream bs;
	BitstreamReset(&bs);
	BitstreamInit(&bs, buf, buf_len);
	
	BitstreamReadHeaders(&bs, &cxt->width, &cxt->height);
	printf("[%s %d]:width(%d)higth(%d)\n", __FUNCTION__, __LINE__, cxt->width, cxt->height);
	return 0;
}

//大小端处理函数
#define BSWAP(x) \
	x = ((((x) & 0xff000000) >> 24) | \
			(((x) & 0x00ff0000) >>  8) | \
			(((x) & 0x0000ff00) <<  8) | \
			(((x) & 0x000000ff) << 24))

这里采用源码进行基本介绍

int
BitstreamReadHeaders(Bitstream * bs,
					 DECODER * dec,
					 uint32_t * rounding,
					 uint32_t * reduced_resolution,
					 uint32_t * quant,
					 uint32_t * fcode_forward,
					 uint32_t * fcode_backward,
					 uint32_t * intra_dc_threshold,
					 WARPPOINTS *gmc_warp)
{
	uint32_t vol_ver_id;
	uint32_t coding_type;
	uint32_t start_code;
	uint32_t time_incr = 0;
	int32_t time_increment = 0;
	int resize = 0;

	do {

		BitstreamByteAlign(bs);
		start_code = BitstreamShowBits(bs, 32);
		//VOS起始头部解析:0x000001b0
		if (start_code == VISOBJSEQ_START_CODE) {

			int profile;

			DPRINTF(XVID_DEBUG_STARTCODE, "<visual_object_sequence>\n");

			BitstreamSkip(bs, 32);	/* visual_object_sequence_start_code */
			profile = BitstreamGetBits(bs, 8);	/* profile_and_level_indication */

			DPRINTF(XVID_DEBUG_HEADER, "profile_and_level_indication %i\n", profile);

		} else if (start_code == VISOBJSEQ_STOP_CODE) {

			BitstreamSkip(bs, 32);	/* visual_object_sequence_stop_code */

			DPRINTF(XVID_DEBUG_STARTCODE, "</visual_object_sequence>\n");
		//vo信息解析:0x000001b5
		} else if (start_code == VISOBJ_START_CODE) {
			int visobj_ver_id;

			DPRINTF(XVID_DEBUG_STARTCODE, "<visual_object>\n");

			BitstreamSkip(bs, 32);	/* visual_object_start_code */
			if (BitstreamGetBit(bs))	/* is_visual_object_identified */
			{
				visobj_ver_id = BitstreamGetBits(bs, 4);	/* visual_object_ver_id */
				DPRINTF(XVID_DEBUG_HEADER,"visobj_ver_id %i\n", visobj_ver_id);
				BitstreamSkip(bs, 3);	/* visual_object_priority */
			} else {
				visobj_ver_id = 1;
			}

			if (BitstreamShowBits(bs, 4) != VISOBJ_TYPE_VIDEO)	/* visual_object_type */
			{
				DPRINTF(XVID_DEBUG_ERROR, "visual_object_type != video\n");
				return -1;
			}
			BitstreamSkip(bs, 4);

			/* video_signal_type */

			if (BitstreamGetBit(bs))	/* video_signal_type */
			{
				DPRINTF(XVID_DEBUG_HEADER,"+ video_signal_type\n");
				BitstreamSkip(bs, 3);	/* video_format */
				BitstreamSkip(bs, 1);	/* video_range */
				if (BitstreamGetBit(bs))	/* color_description */
				{
					DPRINTF(XVID_DEBUG_HEADER,"+ color_description");
					BitstreamSkip(bs, 8);	/* color_primaries */
					BitstreamSkip(bs, 8);	/* transfer_characteristics */
					BitstreamSkip(bs, 8);	/* matrix_coefficients */
				}
			}
		} else if ((start_code & ~VIDOBJ_START_CODE_MASK) == VIDOBJ_START_CODE) {

			DPRINTF(XVID_DEBUG_STARTCODE, "<video_object>\n");
			DPRINTF(XVID_DEBUG_HEADER, "vo id %i\n", start_code & VIDOBJ_START_CODE_MASK);

			BitstreamSkip(bs, 32);	/* video_object_start_code */
		//vol解析:0x00000120
		} else if ((start_code & ~VIDOBJLAY_START_CODE_MASK) == VIDOBJLAY_START_CODE) {

			DPRINTF(XVID_DEBUG_STARTCODE, "<video_object_layer>\n");
			DPRINTF(XVID_DEBUG_HEADER, "vol id %i\n", start_code & VIDOBJLAY_START_CODE_MASK);

			BitstreamSkip(bs, 32);	/* video_object_layer_start_code */
			BitstreamSkip(bs, 1);	/* random_accessible_vol */

            BitstreamSkip(bs, 8);   /* video_object_type_indication */

			if (BitstreamGetBit(bs))	/* is_object_layer_identifier */
			{
				DPRINTF(XVID_DEBUG_HEADER, "+ is_object_layer_identifier\n");
				vol_ver_id = BitstreamGetBits(bs, 4);	/* video_object_layer_verid */
				DPRINTF(XVID_DEBUG_HEADER,"ver_id %i\n", vol_ver_id);
				BitstreamSkip(bs, 3);	/* video_object_layer_priority */
			} else {
				vol_ver_id = 1;
			}

			dec->aspect_ratio = BitstreamGetBits(bs, 4);

			if (dec->aspect_ratio == VIDOBJLAY_AR_EXTPAR)	/* aspect_ratio_info */
			{
				DPRINTF(XVID_DEBUG_HEADER, "+ aspect_ratio_info\n");
				dec->par_width = BitstreamGetBits(bs, 8);	/* par_width */
				dec->par_height = BitstreamGetBits(bs, 8);	/* par_height */
			}

			if (BitstreamGetBit(bs))	/* vol_control_parameters */
			{
				DPRINTF(XVID_DEBUG_HEADER, "+ vol_control_parameters\n");
				BitstreamSkip(bs, 2);	/* chroma_format */
				dec->low_delay = BitstreamGetBit(bs);	/* low_delay */
				DPRINTF(XVID_DEBUG_HEADER, "low_delay %i\n", dec->low_delay);
				if (BitstreamGetBit(bs))	/* vbv_parameters */
				{
					unsigned int bitrate;
					unsigned int buffer_size;
					unsigned int occupancy;

					DPRINTF(XVID_DEBUG_HEADER,"+ vbv_parameters\n");

					bitrate = BitstreamGetBits(bs,15) << 15;	/* first_half_bit_rate */
					READ_MARKER();
					bitrate |= BitstreamGetBits(bs,15);		/* latter_half_bit_rate */
					READ_MARKER();

					buffer_size = BitstreamGetBits(bs, 15) << 3;	/* first_half_vbv_buffer_size */
					READ_MARKER();
					buffer_size |= BitstreamGetBits(bs, 3);		/* latter_half_vbv_buffer_size */

					occupancy = BitstreamGetBits(bs, 11) << 15;	/* first_half_vbv_occupancy */
					READ_MARKER();
					occupancy |= BitstreamGetBits(bs, 15);	/* latter_half_vbv_occupancy */
					READ_MARKER();

					DPRINTF(XVID_DEBUG_HEADER,"bitrate %d (unit=400 bps)\n", bitrate);
					DPRINTF(XVID_DEBUG_HEADER,"buffer_size %d (unit=16384 bits)\n", buffer_size);
					DPRINTF(XVID_DEBUG_HEADER,"occupancy %d (unit=64 bits)\n", occupancy);
				}
			}else{
				dec->low_delay = dec->low_delay_default;
			}

			dec->shape = BitstreamGetBits(bs, 2);	/* video_object_layer_shape */

			DPRINTF(XVID_DEBUG_HEADER, "shape %i\n", dec->shape);
			if (dec->shape != VIDOBJLAY_SHAPE_RECTANGULAR)
			{
				DPRINTF(XVID_DEBUG_ERROR,"non-rectangular shapes are not supported\n");
			}

			if (dec->shape == VIDOBJLAY_SHAPE_GRAYSCALE && vol_ver_id != 1) {
				BitstreamSkip(bs, 4);	/* video_object_layer_shape_extension */
			}

			READ_MARKER();

			/********************** for decode B-frame time ***********************/
			dec->time_inc_resolution = BitstreamGetBits(bs, 16);	/* vop_time_increment_resolution */
			DPRINTF(XVID_DEBUG_HEADER,"vop_time_increment_resolution %i\n", dec->time_inc_resolution);

#if 0
			dec->time_inc_resolution--;
#endif

			if (dec->time_inc_resolution > 0) {
				dec->time_inc_bits = MAX(log2bin(dec->time_inc_resolution-1), 1);
			} else {
#if 0
				dec->time_inc_bits = 0;
#endif
				/* for "old" xvid compatibility, set time_inc_bits = 1 */
				dec->time_inc_bits = 1;
			}

			READ_MARKER();

			if (BitstreamGetBit(bs))	/* fixed_vop_rate */
			{
				DPRINTF(XVID_DEBUG_HEADER, "+ fixed_vop_rate\n");
				BitstreamSkip(bs, dec->time_inc_bits);	/* fixed_vop_time_increment */
			}

			if (dec->shape != VIDOBJLAY_SHAPE_BINARY_ONLY) {

				if (dec->shape == VIDOBJLAY_SHAPE_RECTANGULAR) {
					uint32_t width, height;

					READ_MARKER();
					width = BitstreamGetBits(bs, 13);	/* video_object_layer_width */
					READ_MARKER();
					height = BitstreamGetBits(bs, 13);	/* video_object_layer_height */
					READ_MARKER();
					//获取视频分辨率
					DPRINTF(XVID_DEBUG_HEADER, "width %i\n", width);
					DPRINTF(XVID_DEBUG_HEADER, "height %i\n", height);

					if (dec->width != width || dec->height != height)
					{
						if (dec->fixed_dimensions)
						{
							DPRINTF(XVID_DEBUG_ERROR, "decoder width/height does not match bitstream\n");
							return -1;
						}
						resize = 1;
						dec->width = width;
						dec->height = height;
					}
				}

				dec->interlacing = BitstreamGetBit(bs);
				DPRINTF(XVID_DEBUG_HEADER, "interlacing %i\n", dec->interlacing);

				if (!BitstreamGetBit(bs))	/* obmc_disable */
				{
					DPRINTF(XVID_DEBUG_ERROR, "obmc_disabled==false not supported\n");
					/* TODO */
					/* fucking divx4.02 has this enabled */
				}

				dec->sprite_enable = BitstreamGetBits(bs, (vol_ver_id == 1 ? 1 : 2));	/* sprite_enable */

				if (dec->sprite_enable == SPRITE_STATIC || dec->sprite_enable == SPRITE_GMC)
				{
					int low_latency_sprite_enable;

					if (dec->sprite_enable != SPRITE_GMC)
					{
						int sprite_width;
						int sprite_height;
						int sprite_left_coord;
						int sprite_top_coord;
						sprite_width = BitstreamGetBits(bs, 13);		/* sprite_width */
						READ_MARKER();
						sprite_height = BitstreamGetBits(bs, 13);	/* sprite_height */
						READ_MARKER();
						sprite_left_coord = BitstreamGetBits(bs, 13);	/* sprite_left_coordinate */
						READ_MARKER();
						sprite_top_coord = BitstreamGetBits(bs, 13);	/* sprite_top_coordinate */
						READ_MARKER();
					}
					dec->sprite_warping_points = BitstreamGetBits(bs, 6);		/* no_of_sprite_warping_points */
					dec->sprite_warping_accuracy = BitstreamGetBits(bs, 2);		/* sprite_warping_accuracy */
					dec->sprite_brightness_change = BitstreamGetBits(bs, 1);		/* brightness_change */
					if (dec->sprite_enable != SPRITE_GMC)
					{
						low_latency_sprite_enable = BitstreamGetBits(bs, 1);		/* low_latency_sprite_enable */
					}
				}

				if (vol_ver_id != 1 &&
					dec->shape != VIDOBJLAY_SHAPE_RECTANGULAR) {
					BitstreamSkip(bs, 1);	/* sadct_disable */
				}

				if (BitstreamGetBit(bs))	/* not_8_bit */
				{
					DPRINTF(XVID_DEBUG_HEADER, "not_8_bit==true (ignored)\n");
					dec->quant_bits = BitstreamGetBits(bs, 4);	/* quant_precision */
					BitstreamSkip(bs, 4);	/* bits_per_pixel */
				} else {
					dec->quant_bits = 5;
				}

				if (dec->shape == VIDOBJLAY_SHAPE_GRAYSCALE) {
					BitstreamSkip(bs, 1);	/* no_gray_quant_update */
					BitstreamSkip(bs, 1);	/* composition_method */
					BitstreamSkip(bs, 1);	/* linear_composition */
				}

				dec->quant_type = BitstreamGetBit(bs);	/* quant_type */
				DPRINTF(XVID_DEBUG_HEADER, "quant_type %i\n", dec->quant_type);

				if (dec->quant_type) {
					if (BitstreamGetBit(bs))	/* load_intra_quant_mat */
					{
						uint8_t matrix[64];

						DPRINTF(XVID_DEBUG_HEADER, "load_intra_quant_mat\n");

						bs_get_matrix(bs, matrix);
						set_intra_matrix(dec->mpeg_quant_matrices, matrix);
					} else
						set_intra_matrix(dec->mpeg_quant_matrices, get_default_intra_matrix());

					if (BitstreamGetBit(bs))	/* load_inter_quant_mat */
					{
						uint8_t matrix[64];

						DPRINTF(XVID_DEBUG_HEADER, "load_inter_quant_mat\n");

						bs_get_matrix(bs, matrix);
						set_inter_matrix(dec->mpeg_quant_matrices, matrix);
					} else
						set_inter_matrix(dec->mpeg_quant_matrices, get_default_inter_matrix());

					if (dec->shape == VIDOBJLAY_SHAPE_GRAYSCALE) {
						DPRINTF(XVID_DEBUG_ERROR, "greyscale matrix not supported\n");
						return -1;
					}

				}


				if (vol_ver_id != 1) {
					dec->quarterpel = BitstreamGetBit(bs);	/* quarter_sample */
					DPRINTF(XVID_DEBUG_HEADER,"quarterpel %i\n", dec->quarterpel);
				}
				else
					dec->quarterpel = 0;


				dec->complexity_estimation_disable = BitstreamGetBit(bs);	/* complexity estimation disable */
				if (!dec->complexity_estimation_disable)
				{
					read_vol_complexity_estimation_header(bs, dec);
				}

				BitstreamSkip(bs, 1);	/* resync_marker_disable */

				if (BitstreamGetBit(bs))	/* data_partitioned */
				{
					DPRINTF(XVID_DEBUG_ERROR, "data_partitioned not supported\n");
					BitstreamSkip(bs, 1);	/* reversible_vlc */
				}

				if (vol_ver_id != 1) {
					dec->newpred_enable = BitstreamGetBit(bs);
					if (dec->newpred_enable)	/* newpred_enable */
					{
						DPRINTF(XVID_DEBUG_HEADER, "+ newpred_enable\n");
						BitstreamSkip(bs, 2);	/* requested_upstream_message_type */
						BitstreamSkip(bs, 1);	/* newpred_segment_type */
					}
					dec->reduced_resolution_enable = BitstreamGetBit(bs);	/* reduced_resolution_vop_enable */
					DPRINTF(XVID_DEBUG_HEADER, "reduced_resolution_enable %i\n", dec->reduced_resolution_enable);
				}
				else
				{
					dec->newpred_enable = 0;
					dec->reduced_resolution_enable = 0;
				}

				dec->scalability = BitstreamGetBit(bs);	/* scalability */
				if (dec->scalability)
				{
					DPRINTF(XVID_DEBUG_ERROR, "scalability not supported\n");
					BitstreamSkip(bs, 1);	/* hierarchy_type */
					BitstreamSkip(bs, 4);	/* ref_layer_id */
					BitstreamSkip(bs, 1);	/* ref_layer_sampling_direc */
					BitstreamSkip(bs, 5);	/* hor_sampling_factor_n */
					BitstreamSkip(bs, 5);	/* hor_sampling_factor_m */
					BitstreamSkip(bs, 5);	/* vert_sampling_factor_n */
					BitstreamSkip(bs, 5);	/* vert_sampling_factor_m */
					BitstreamSkip(bs, 1);	/* enhancement_type */
					if(dec->shape == VIDOBJLAY_SHAPE_BINARY /* && hierarchy_type==0 */) {
						BitstreamSkip(bs, 1);	/* use_ref_shape */
						BitstreamSkip(bs, 1);	/* use_ref_texture */
						BitstreamSkip(bs, 5);	/* shape_hor_sampling_factor_n */
						BitstreamSkip(bs, 5);	/* shape_hor_sampling_factor_m */
						BitstreamSkip(bs, 5);	/* shape_vert_sampling_factor_n */
						BitstreamSkip(bs, 5);	/* shape_vert_sampling_factor_m */
					}
					return -1;
				}
			} else				/* dec->shape == BINARY_ONLY */
			{
				if (vol_ver_id != 1) {
					dec->scalability = BitstreamGetBit(bs); /* scalability */
					if (dec->scalability)
					{
						DPRINTF(XVID_DEBUG_ERROR, "scalability not supported\n");
						BitstreamSkip(bs, 4);	/* ref_layer_id */
						BitstreamSkip(bs, 5);	/* hor_sampling_factor_n */
						BitstreamSkip(bs, 5);	/* hor_sampling_factor_m */
						BitstreamSkip(bs, 5);	/* vert_sampling_factor_n */
						BitstreamSkip(bs, 5);	/* vert_sampling_factor_m */
						return -1;
					}
				}
				BitstreamSkip(bs, 1);	/* resync_marker_disable */

			}

			return (resize ? -3 : -2 );	/* VOL */

		} else if (start_code == GRPOFVOP_START_CODE) {

			DPRINTF(XVID_DEBUG_STARTCODE, "<group_of_vop>\n");

			BitstreamSkip(bs, 32);
			{
				int hours, minutes, seconds;

				hours = BitstreamGetBits(bs, 5);
				minutes = BitstreamGetBits(bs, 6);
				READ_MARKER();
				seconds = BitstreamGetBits(bs, 6);

				DPRINTF(XVID_DEBUG_HEADER, "time %ih%im%is\n", hours,minutes,seconds);
			}
			BitstreamSkip(bs, 1);	/* closed_gov */
			BitstreamSkip(bs, 1);	/* broken_link */

		} else if (start_code == VOP_START_CODE) {

			DPRINTF(XVID_DEBUG_STARTCODE, "<vop>\n");

			BitstreamSkip(bs, 32);	/* vop_start_code */

			coding_type = BitstreamGetBits(bs, 2);	/* vop_coding_type */
			DPRINTF(XVID_DEBUG_HEADER, "coding_type %i\n", coding_type);

			/*********************** for decode B-frame time ***********************/
			while (BitstreamGetBit(bs) != 0)	/* time_base */
				time_incr++;

			READ_MARKER();

			if (dec->time_inc_bits) {
				time_increment = (BitstreamGetBits(bs, dec->time_inc_bits));	/* vop_time_increment */
			}

			DPRINTF(XVID_DEBUG_HEADER, "time_base %i\n", time_incr);
			DPRINTF(XVID_DEBUG_HEADER, "time_increment %i\n", time_increment);

			DPRINTF(XVID_DEBUG_TIMECODE, "%c %i:%i\n",
				coding_type == I_VOP ? 'I' : coding_type == P_VOP ? 'P' : coding_type == B_VOP ? 'B' : 'S',
				time_incr, time_increment);

			if (coding_type != B_VOP) {
				dec->last_time_base = dec->time_base;
				dec->time_base += time_incr;
				dec->time = dec->time_base*dec->time_inc_resolution + time_increment;
				dec->time_pp = (int32_t)(dec->time - dec->last_non_b_time);
				dec->last_non_b_time = dec->time;
			} else {
				dec->time = (dec->last_time_base + time_incr)*dec->time_inc_resolution + time_increment;
				dec->time_bp = dec->time_pp - (int32_t)(dec->last_non_b_time - dec->time);
			}
			DPRINTF(XVID_DEBUG_HEADER,"time_pp=%i\n", dec->time_pp);
			DPRINTF(XVID_DEBUG_HEADER,"time_bp=%i\n", dec->time_bp);

			READ_MARKER();

			if (!BitstreamGetBit(bs))	/* vop_coded */
			{
				DPRINTF(XVID_DEBUG_HEADER, "vop_coded==false\n");
				return N_VOP;
			}

			if (dec->newpred_enable)
			{
				int vop_id;
				int vop_id_for_prediction;

				vop_id = BitstreamGetBits(bs, MIN(dec->time_inc_bits + 3, 15));
				DPRINTF(XVID_DEBUG_HEADER, "vop_id %i\n", vop_id);
				if (BitstreamGetBit(bs))	/* vop_id_for_prediction_indication */
				{
					vop_id_for_prediction = BitstreamGetBits(bs, MIN(dec->time_inc_bits + 3, 15));
					DPRINTF(XVID_DEBUG_HEADER, "vop_id_for_prediction %i\n", vop_id_for_prediction);
				}
				READ_MARKER();
			}



			/* fix a little bug by MinChen <chenm002@163.com> */
			if ((dec->shape != VIDOBJLAY_SHAPE_BINARY_ONLY) &&
				( (coding_type == P_VOP) || (coding_type == S_VOP && dec->sprite_enable == SPRITE_GMC) ) ) {
				*rounding = BitstreamGetBit(bs);	/* rounding_type */
				DPRINTF(XVID_DEBUG_HEADER, "rounding %i\n", *rounding);
			}

			if (dec->reduced_resolution_enable &&
				dec->shape == VIDOBJLAY_SHAPE_RECTANGULAR &&
				(coding_type == P_VOP || coding_type == I_VOP)) {

				*reduced_resolution = BitstreamGetBit(bs);
				DPRINTF(XVID_DEBUG_HEADER, "reduced_resolution %i\n", *reduced_resolution);
			}
			else
			{
				*reduced_resolution = 0;
			}

			if (dec->shape != VIDOBJLAY_SHAPE_RECTANGULAR) {
				if(!(dec->sprite_enable == SPRITE_STATIC && coding_type == I_VOP)) {

					uint32_t width, height;
					uint32_t horiz_mc_ref, vert_mc_ref;

					width = BitstreamGetBits(bs, 13);
					READ_MARKER();
					height = BitstreamGetBits(bs, 13);
					READ_MARKER();
					horiz_mc_ref = BitstreamGetBits(bs, 13);
					READ_MARKER();
					vert_mc_ref = BitstreamGetBits(bs, 13);
					READ_MARKER();

					DPRINTF(XVID_DEBUG_HEADER, "width %i\n", width);
					DPRINTF(XVID_DEBUG_HEADER, "height %i\n", height);
					DPRINTF(XVID_DEBUG_HEADER, "horiz_mc_ref %i\n", horiz_mc_ref);
					DPRINTF(XVID_DEBUG_HEADER, "vert_mc_ref %i\n", vert_mc_ref);
				}

				BitstreamSkip(bs, 1);	/* change_conv_ratio_disable */
				if (BitstreamGetBit(bs))	/* vop_constant_alpha */
				{
					BitstreamSkip(bs, 8);	/* vop_constant_alpha_value */
				}
			}

			if (dec->shape != VIDOBJLAY_SHAPE_BINARY_ONLY) {

				if (!dec->complexity_estimation_disable)
				{
					read_vop_complexity_estimation_header(bs, dec, coding_type);
				}

				/* intra_dc_vlc_threshold */
				*intra_dc_threshold =
					intra_dc_threshold_table[BitstreamGetBits(bs, 3)];

				dec->top_field_first = 0;
				dec->alternate_vertical_scan = 0;

				if (dec->interlacing) {
					dec->top_field_first = BitstreamGetBit(bs);
					DPRINTF(XVID_DEBUG_HEADER, "interlace top_field_first %i\n", dec->top_field_first);
					dec->alternate_vertical_scan = BitstreamGetBit(bs);
					DPRINTF(XVID_DEBUG_HEADER, "interlace alternate_vertical_scan %i\n", dec->alternate_vertical_scan);

				}
			}

			if ((dec->sprite_enable == SPRITE_STATIC || dec->sprite_enable== SPRITE_GMC) && coding_type == S_VOP) {

				int i;

				for (i = 0 ; i < dec->sprite_warping_points; i++)
				{
					int length;
					int x = 0, y = 0;

					/* sprite code borowed from ffmpeg; thx Michael Niedermayer <michaelni@gmx.at> */
					length = bs_get_spritetrajectory(bs);
					if(length){
						x= BitstreamGetBits(bs, length);
						if ((x >> (length - 1)) == 0) /* if MSB not set it is negative*/
							x = - (x ^ ((1 << length) - 1));
					}
					READ_MARKER();

					length = bs_get_spritetrajectory(bs);
					if(length){
						y = BitstreamGetBits(bs, length);
						if ((y >> (length - 1)) == 0) /* if MSB not set it is negative*/
							y = - (y ^ ((1 << length) - 1));
					}
					READ_MARKER();

					gmc_warp->duv[i].x = x;
					gmc_warp->duv[i].y = y;

					DPRINTF(XVID_DEBUG_HEADER,"sprite_warping_point[%i] xy=(%i,%i)\n", i, x, y);
				}

				if (dec->sprite_brightness_change)
				{
					/* XXX: brightness_change_factor() */
				}
				if (dec->sprite_enable == SPRITE_STATIC)
				{
					/* XXX: todo */
				}

			}

			if ((*quant = BitstreamGetBits(bs, dec->quant_bits)) < 1)	/* vop_quant */
				*quant = 1;
			DPRINTF(XVID_DEBUG_HEADER, "quant %i\n", *quant);

			if (coding_type != I_VOP) {
				*fcode_forward = BitstreamGetBits(bs, 3);	/* fcode_forward */
				DPRINTF(XVID_DEBUG_HEADER, "fcode_forward %i\n", *fcode_forward);
			}

			if (coding_type == B_VOP) {
				*fcode_backward = BitstreamGetBits(bs, 3);	/* fcode_backward */
				DPRINTF(XVID_DEBUG_HEADER, "fcode_backward %i\n", *fcode_backward);
			}
			if (!dec->scalability) {
				if ((dec->shape != VIDOBJLAY_SHAPE_RECTANGULAR) &&
					(coding_type != I_VOP)) {
					BitstreamSkip(bs, 1);	/* vop_shape_coding_type */
				}
			}
			return coding_type;

		} else if (start_code == USERDATA_START_CODE) {
			char tmp[256];
		    int i, version, build;
			char packed;

			BitstreamSkip(bs, 32);	/* user_data_start_code */

			tmp[0] = BitstreamShowBits(bs, 8);

			for(i = 1; i < 256; i++){
				tmp[i] = (BitstreamShowBits(bs, 16) & 0xFF);

				if(tmp[i] == 0)
					break;

				BitstreamSkip(bs, 8);
			}

			DPRINTF(XVID_DEBUG_STARTCODE, "<user_data>: %s\n", tmp);

			/* read xvid bitstream version */
			if(strncmp(tmp, "XviD", 4) == 0) {
				if (tmp[strlen(tmp)-1] == 'C') {				
					sscanf(tmp, "XviD%dC", &dec->bs_version);
					dec->cartoon_mode = 1;
				}
				else
					sscanf(tmp, "XviD%d", &dec->bs_version);

				DPRINTF(XVID_DEBUG_HEADER, "xvid bitstream version=%i\n", dec->bs_version);
			}

		    /* divx detection */
			i = sscanf(tmp, "DivX%dBuild%d%c", &version, &build, &packed);
			if (i < 2)
				i = sscanf(tmp, "DivX%db%d%c", &version, &build, &packed);

			if (i >= 2)
			{
				dec->packed_mode = (i == 3 && packed == 'p');
				DPRINTF(XVID_DEBUG_HEADER, "divx version=%i, build=%i packed=%i\n",
						version, build, dec->packed_mode);
			}

		} else					/* start_code == ? */
		{
			if (BitstreamShowBits(bs, 24) == 0x000001) {
				DPRINTF(XVID_DEBUG_STARTCODE, "<unknown: %x>\n", BitstreamShowBits(bs, 32));
			}
			BitstreamSkip(bs, 8);
		}
	}
	while ((BitstreamPos(bs) >> 3) < bs->length);

#if 0
	DPRINTF("*** WARNING: no vop_start_code found");
#endif
	return -1;					/* ignore it */
}

3 解码渲染

解码选择比较多一个是设备平台本身支持硬解,这个比较好办,直接将头部数据和第一个I帧数据绑定丢到解码器,后面的数据也是直接丢掉解码器即可;最终拿到视频YUV数据走正常视频播放渲染流程即可。

另一种比较麻烦需要自己实现软解码,也有比较多种选择,比如这个xvid-core本身支持MPEG-4的编解码,也可以采用通用的视频框架比如ffmpeg也是支持软解MPEG-4格式。很遗憾我的设备没有硬件采用的是ffmpeg进行软解。
下面介绍下ffmpeg初始化一种编码格式过程,注意不同版本存在一些差异:

//MPEG-4 codecId:AV_CODEC_ID_MPEG4
//1. 初始化获取解码器:
	avcodec_find_decoder
	//初始化环境变量
	avcodec_alloc_context3
	//启动解码器
	avcodec_open2
	//申请帧空间
	av_frame_alloc

//2. 释放帧空间
	//关闭解码器
	avcodec_close
	//释放帧空间
	av_frame_free

//3. 解码过程
	//解码视频帧
	avcodec_decode_video2

需要注意点是解码后帧如何转成你想要的YUV格式:
AVFrame结构体,其中data是一个数组,data[0]存放的是Y数据它是按行存放,data[1]存放的是UV数据,也是按行存放;linesize表示的是data一行数据大小。

static void* get_yuv420_data(int h, int w)
{
    //申请内存    
    int height = h;
    int width = w;
  
    //写入数据  
    int a=0,i;   
    for (i=0; i<height; i++)   
    {   
        memcpy(yuv_buf + a, pFrame->data[0] + i * pFrame->linesize[0], width);   
        a+=width;   
    }   
    for (i=0; i<height/2; i++)   
    {   
        memcpy(yuv_buf + a, pFrame->data[1] + i * pFrame->linesize[1], width/2);   
        a+=width/2;   
    }   
    for (i=0; i<height/2; i++)   
    {   
        memcpy(yuv_buf + a, pFrame->data[2] + i * pFrame->linesize[2], width/2);   
        a+=width/2;   
    }   
	return yuv_buf;
}

渲染这边是送到硬件平台显示不做展开。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.下载并安装rtsp-simple-server 您可以从https://github.com/aler9/rtsp-simple-server/releases 下载适用于您的操作系统的rtsp-simple-server。 对于Ubuntu或Debian用户,可以使用以下命令下载和安装: ``` wget https://github.com/aler9/rtsp-simple-server/releases/download/v0.17.2/rtsp-simple-server_v0.17.2_linux_amd64.tar.gz tar -xzvf rtsp-simple-server_v0.17.2_linux_amd64.tar.gz cd rtsp-simple-server_v0.17.2_linux_amd64 sudo cp rtsp-simple-server /usr/local/bin/ ``` 2. 启动rtsp-simple-server 在终端中执行以下命令以启动rtsp-simple-server: ``` rtsp-simple-server ``` 这将会启动rtsp-simple-server并在终端输出日志信息。 默认情况下,rtsp-simple-server 监听所有网卡上的 8554 端口。如果您需要更改端口号,可以使用以下命令: ``` rtsp-simple-server --rtsp-port=[PORT_NUMBER] ``` 3. 使用rtsp-simple-server 启动rtsp-simple-server后,您可以通过网络独立地连接和使用RTSP流,并将其传输到其他客户端。 例如,您可以使用VLC打开RTSP连接并查看视频流: - 启动VLC并选择“文件”>“打开网络...” - 在“URL”字段中输入:rtsp://[RTSP_SERVER_IP]:8554/stream - 点击“播放”并等待视频流的加载。 请确保替换 [RTSP_SERVER_IP] 为rtsp-simple-server所在的IP地址。 4. 指定视频源 rtsp-simple-server需要知道视频从哪里流入,因此您需要指定一个或多个输入源。 您可以使用以下命令启动示例源进行测试: ``` rtsp-simple-server --rtsp-port=8554 --publish-tester-video ``` 此命令将使用测试视频建立一个默认的rtsp://localhost:8554/stream输入源。 您还可以将自己的数据流作为输入源传输到rtsp-simple-server,例如: - 使用ffmpeg将视频文件流式传输到rtsp-simple-server ``` ffmpeg -re -i [LOCAL_VIDEO_FILE] -f rtsp rtsp://[RTSP_SERVER_IP]:8554/stream ``` - 使用 gstreamer 将相机视频流传输到 rtsp-simple-server。 ``` gst-launch-1.0 v4l2src ! videoconvert ! x264enc tune=zerolatency ! rtph264pay ! udpsink host=[RTSP_SERVER_IP] port=8554 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值