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;
}
渲染这边是送到硬件平台显示不做展开。