一.h264中的I、P和B帧
I-帧:也成为关键帧,I-帧完全自我指涉的,并且不使用任何其他帧的信息。它在三种帧中占最大的比例,并且具有最高的质量,但是压缩效率是最低的。
P-帧:P-帧是所谓的“预测”帧。当创建了一个P-帧时,编码器可以向后查看I-帧或者P-帧中冗余的图片信息。P-帧比I-帧效率高,但是没有B-帧的效率高。
B-帧:B-帧是双向预测帧,这意味着当我们创建B-帧,编码器可以同时向前和向后查找冗余的图片信息。这使得B-帧在三种帧中具备最佳的效率。注意,B-帧在使用Baseline方式生产视频的时候是不可用的。
I-帧与IDR帧
具体说来,视频编码序列指的是“图片组”,或者简称为GOP,它是组成H.264流的组成部分,每个H.264流都是由很 多个静态的GOP组成的。每个GOP都是由一个I-帧开始的,幷包含了所有的帧,但是并不包含下一个I-帧。具体说来,视频编码序列指的是“图片组”,或者简称为GOP,它是组成H.264流的组成部分,每个H.264流都是由很 多个静态的GOP组成的。每个GOP都是由一个I-帧开始的,幷包含了所有的帧,但是并不包含下一个I-帧。
简单的说,H.264规格使用两种类型的I-帧:普通I-帧和IDR帧。对于IDR帧来说,在IDR帧之后的所有帧都不能引用任何IDR帧之前的帧的内容,与此相反,对于普通的I-帧来说,位于其之后的B-和P-帧可以引用位于普通I-帧之前的I-帧。
从随机存取的视频流中,播放器永远可以从一个IDR帧播放,因为在它之后没有任何帧引用之前的帧。 但是,不能在一个没有IDR帧的视频中从任意点开始播放,因为后面的帧总是会引用前面的帧。
参考文献:https://www.adobe.com/devnet/adobe-media-server/articles/h264_encoding.html
结合参考文献中的描述和前文中关于nal_unit_type的解释,可以推断出,nalType=5是IDR帧;nalType=1是普通I帧、P帧或B帧,至于具体是哪一种得去到slice层利用熵编码数据来判断。判断方法如下:
https://www.cnblogs.com/Dreaming-in-Gottingen/p/13799066.html
为防止原作者一时冲动删除文章和源码,这里备份一下。
/**
* H.264 MediaData Detector
*
* Author: [email protected]
* Date: 2020-10-11
* https://www.cnblogs.com/Dreaming-in-Gottingen/
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "bs_read.h"
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
} NaluType;
typedef enum {
FRAME_TYPE_I = 15,
FRAME_TYPE_P = 16,
FRAME_TYPE_B = 17
} FrameType;
typedef enum {
NALU_PRIORITY_DISPOSABLE = 0,
NALU_PRIRITY_LOW = 1,
NALU_PRIORITY_HIGH = 2,
NALU_PRIORITY_HIGHEST = 3
} NaluPriority;
typedef struct {
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
unsigned char *buf; //! contains the first byte followed by the EBSP
unsigned char frame_type; //! nalu frame type (I/P/B)
} NALU_t;
//! Boolean Type
#ifdef FALSE
# define Boolean unsigned int
#else
typedef enum {
FALSE,
TRUE
} Boolean;
#endif
#define MAXIMUMVALUEOFcpb_cnt 32
typedef struct {
unsigned cpb_cnt; // ue(v)
unsigned bit_rate_scale; // u(4)
unsigned cpb_size_scale; // u(4)
//for (SchedSelIdx=0; SchedSelIdx<=cpb_cnt; SchedSelIdx++)
unsigned bit_rate_value [MAXIMUMVALUEOFcpb_cnt]; // ue(v)
unsigned cpb_size_value[MAXIMUMVALUEOFcpb_cnt]; // ue(v)
unsigned vbr_cbr_flag[MAXIMUMVALUEOFcpb_cnt]; // u(1)
unsigned initial_cpb_removal_delay_length_minus1; // u(5)
unsigned cpb_removal_delay_length_minus1; // u(5)
unsigned dpb_output_delay_length_minus1; // u(5)
unsigned time_offset_length; // u(5)
} hrd_parameters_t;
typedef struct {
Boolean aspect_ratio_info_present_flag; // u(1)
unsigned aspect_ratio_idc; // u(8)
unsigned sar_width; // u(16)
unsigned sar_height; // u(16)
Boolean overscan_info_present_flag; // u(1)
Boolean overscan_appropriate_flag; // u(1)
Boolean video_signal_type_present_flag; // u(1)
unsigned video_format; // u(3)
Boolean video_full_range_flag; // u(1)
Boolean colour_description_present_flag; // u(1)
unsigned colour_primaries; // u(8)
unsigned transfer_characteristics; // u(8)
unsigned matrix_coefficients; // u(8)
Boolean chroma_location_info_present_flag; // u(1)
unsigned chroma_location_frame; // ue(v)
unsigned chroma_location_field; // ue(v)
Boolean timing_info_present_flag; // u(1)
unsigned num_units_in_tick; // u(32)
unsigned time_scale; // u(32)
Boolean fixed_frame_rate_flag; // u(1)
Boolean nal_hrd_parameters_present_flag; // u(1)
hrd_parameters_t nal_hrd_parameters; // hrd_paramters_t
Boolean vcl_hrd_parameters_present_flag; // u(1)
hrd_parameters_t vcl_hrd_parameters; // hrd_paramters_t
// if ((nal_hrd_parameters_present_flag || (vcl_hrd_parameters_present_flag))
Boolean low_delay_hrd_flag; // u(1)
Boolean pic_struct_present_flag; // u(1)
Boolean bitstream_restriction_flag; // u(1)
Boolean motion_vectors_over_pic_boundaries_flag; // u(1)
unsigned max_bytes_per_pic_denom; // ue(v)
unsigned max_bits_per_mb_denom; // ue(v)
unsigned log2_max_mv_length_vertical; // ue(v)
unsigned log2_max_mv_length_horizontal; // ue(v)
unsigned max_dec_frame_reordering; // ue(v)
unsigned max_dec_frame_buffering; // ue(v)
} vui_seq_parameters_t;
#define MAXnum_ref_frames_in_pic_order_cnt_cycle 256
typedef struct {
Boolean Valid; // indicates the parameter set is valid
unsigned profile_idc; // u(8)
Boolean constrained_set0_flag; // u(1)
Boolean constrained_set1_flag; // u(1)
Boolean constrained_set2_flag; // u(1)
unsigned level_idc; // u(8)
unsigned seq_parameter_set_id; // ue(v)
// if( profile_idc == 100 || profile_idc == 110 ||
// profile_idc == 122 || profile_idc == 144 )
unsigned chroma_format_idc; // ue(v)
// if( chroma_format_idc == 3 )
Boolean residual_colour_transform_flag; // u(1)
unsigned bit_depth_luma_minus8; // ue(v)
unsigned bit_depth_chroma_minus8; // ue(v)
Boolean qpprime_y_zero_transform_bypass_flag; // u(1)
Boolean seq_scaling_matrix_present_flag; // u(1)
// if( seq_scaling_matrix_present_flag )
// for( i=0; i<8; i++)
Boolean seq_scaling_list_present_flag[8]; // u(1)
unsigned log2_max_frame_num_minus4; // ue(v)
unsigned pic_order_cnt_type;
// if( pic_order_cnt_type == 0 )
unsigned log2_max_pic_order_cnt_lsb_minus4; // ue(v)
// else if( pic_order_cnt_type == 1 )
Boolean delta_pic_order_always_zero_flag; // u(1)
int offset_for_non_ref_pic; // se(v)
int offset_for_top_to_bottom_field; // se(v)
unsigned num_ref_frames_in_pic_order_cnt_cycle; // ue(v)
// for( i=0; i<num_ref_frames_in_pic_order_cnt_cycle; i++ )
int offset_for_ref_frame[MAXnum_ref_frames_in_pic_order_cnt_cycle]; // se(v)
unsigned num_ref_frames; // ue(v)
Boolean gaps_in_frame_num_value_allowed_flag; // u(1)
unsigned pic_width_in_mbs_minus1; // ue(v)
unsigned pic_height_in_map_units_minus1; // ue(v)
Boolean frame_mbs_only_flag; // u(1)
// if( !frame_mbs_only_flag )
Boolean mb_adaptive_frame_field_flag; // u(1)
Boolean direct_8x8_inference_flag; // u(1)
Boolean frame_cropping_flag; // u(1)
// if( frame_cropping_flag )
unsigned frame_cropping_rect_left_offset; // ue(v)
unsigned frame_cropping_rect_right_offset; // ue(v)
unsigned frame_cropping_rect_top_offset; // ue(v)
unsigned frame_cropping_rect_bottom_offset; // ue(v)
Boolean vui_parameters_present_flag; // u(1)
// if( vui_parameters_present_flag )
vui_seq_parameters_t vui_seq_parameters; //
} seq_parameter_set_rbsp_t;
#define MAXnum_slice_groups_minus1 8
typedef struct {
Boolean Valid; // indicates the parameter set is valid
unsigned pic_parameter_set_id; // ue(v)
unsigned seq_parameter_set_id; // ue(v)
Boolean entropy_coding_mode_flag; // u(1)
// if( pic_order_cnt_type < 2 ) in the sequence parameter set
Boolean pic_order_present_flag; // u(1)
unsigned num_slice_groups_minus1; // ue(v)
// if( num_slice_groups_minus1 > 0 )
unsigned slice_group_map_type; // ue(v)
// if( slice_group_map_type == 0 )
unsigned run_length_minus1[MAXnum_slice_groups_minus1]; // ue(v)
// else if( slice_group_map_type == 2 )
unsigned top_left[MAXnum_slice_groups_minus1]; // ue(v)
unsigned bottom_right[MAXnum_slice_groups_minus1]; // ue(v)
// else if( slice_group_map_type == 3 || 4 || 5 )
Boolean slice_group_change_direction_flag; // u(1)
unsigned slice_group_change_rate_minus1; // ue(v)
// else if( slice_group_map_type == 6 )
unsigned num_slice_group_map_units_minus1; // ue(v)
unsigned *slice_group_id; // complete MBAmap u(v)
unsigned num_ref_idx_l0_active_minus1; // ue(v)
unsigned num_ref_idx_l1_active_minus1; // ue(v)
Boolean weighted_pred_flag; // u(1)
unsigned weighted_bipred_idc; // u(2)
int pic_init_qp_minus26; // se(v)
int pic_init_qs_minus26; // se(v)
int chroma_qp_index_offset; // se(v)
Boolean deblocking_filter_control_present_flag; // u(1)
Boolean constrained_intra_pred_flag; // u(1)
Boolean redundant_pic_cnt_present_flag; // u(1)
} pic_parameter_set_rbsp_t;
FILE *h264bitstream = NULL; // the bit stream file
static inline int FindStartCodeLen3 (unsigned char *Buf){
if(Buf[0]==0 && Buf[1]==0 && Buf[2]==1)
return 1; //0x000001
else
return 0; //others
}
static inline int FindStartCodeLen4 (unsigned char *Buf){
if(Buf[0]==0 && Buf[1]==0 && Buf[2]==0 && Buf[3]==1)
return 1; //0x00000001
else
return 0;
}
/**
* @return data_len(startcode_len + nalu_len)
*/
int GetAnnexbNALU (NALU_t *nalu)
{
int len3=0, len4=0;
int pos = 0;
int StartCodeFound, rewind;
unsigned char *Buf = nalu->buf;
nalu->startcodeprefix_len=3;
// find current startcode
if (3 != fread (Buf, 1, 3, h264bitstream)) {
return 0;
}
len3 = FindStartCodeLen3 (Buf);
if (len3 == 0) {
if(1 != fread(Buf+3, 1, 1, h264bitstream)){
return -1;
}
len4 = FindStartCodeLen4 (Buf);
if (len4 == 0) { // unknown
return -1;
} else { // 0x00,0x00,0x00,0x01
pos = 4;
nalu->startcodeprefix_len = 4;
}
} else { // 0x00,0x00,0x01
nalu->startcodeprefix_len = 3;
pos = 3;
}
StartCodeFound = 0;
len3 = 0;
len4 = 0;
// find another startcode
while (!StartCodeFound) {
if (feof (h264bitstream)){
nalu->len = (pos-1)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
return pos-1;
}
Buf[pos++] = fgetc (h264bitstream);
len4 = FindStartCodeLen4(&Buf[pos-4]);
if(len4 != 1)
len3 = FindStartCodeLen3(&Buf[pos-3]);
StartCodeFound = (len3 == 1 || len4 == 1);
}
// Here, we have found another start code (and read length of startcode bytes more than we should
// have. Hence, go back in the file
rewind = (len4 == 1)? -4 : -3;
if (0 != fseek (h264bitstream, rewind, SEEK_CUR)){
printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
}
// Here the Start code, the complete NALU, and the next start code is in the Buf.
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code
nalu->len = (pos+rewind)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); // move backward for startcodeprefix_len
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
return (pos+rewind);
}
static int GetFrameType(NALU_t * nal)
{
bs_t s;
int frame_type = 0;
bs_init(&s, nal->buf+1, nal->len - 1);
if (nal->nal_unit_type == NALU_TYPE_SLICE || nal->nal_unit_type == NALU_TYPE_IDR)
{
/* i_first_mb */
bs_read_ue(&s);
/* picture type */
frame_type = bs_read_ue(&s);
switch(frame_type)
{
case 0: case 5: /* P */
nal->frame_type = FRAME_TYPE_P;
break;
case 1: case 6: /* B */
nal->frame_type = FRAME_TYPE_B;
break;
case 3: case 8: /* SP */
nal->frame_type = FRAME_TYPE_P;
break;
case 2: case 7: /* I */
nal->frame_type = FRAME_TYPE_I;
break;
case 4: case 9: /* SI */
nal->frame_type = FRAME_TYPE_I;
break;
default:
printf("unknown frame type! nalu_data[%#x,%#x,%#x,%#x]\n", nal->buf[0], nal->buf[1], nal->buf[2], nal->buf[3]);
break;
}
}
else
{
nal->frame_type = nal->nal_unit_type;
}
return 0;
}
static int ParseAndDumpSPSInfo(NALU_t * nal)
{
bs_t s;
seq_parameter_set_rbsp_t sps;
FILE *myout=stdout;
bs_init(&s, nal->buf+1, nal->len - 1);
printf("---------------------SPS info--------------------------\n");
sps.profile_idc = bs_read(&s, 8);
fprintf(myout, "profile_idc: %d\n", sps.profile_idc);
sps.constrained_set0_flag = (Boolean)bs_read1(&s);
fprintf(myout, "constrained_set0_flag: %d\n", sps.constrained_set0_flag);
sps.constrained_set1_flag = (Boolean)bs_read1(&s);
fprintf(myout, "constrained_set1_flag: %d\n", sps.constrained_set1_flag);
sps.constrained_set2_flag = (Boolean)bs_read1(&s);
fprintf(myout, "constrained_set2_flag: %d\n", sps.constrained_set2_flag);
int reserved_zero = bs_read(&s, 5);
fprintf(myout, "reserved_zero: %d\n", reserved_zero);
assert(reserved_zero == 0);
sps.level_idc = bs_read(&s, 8);
fprintf(myout, "level_idc: %d\n", sps.level_idc);
sps.seq_parameter_set_id = bs_read_ue(&s);
fprintf(myout, "seq_parameter_set_id: %d\n", sps.seq_parameter_set_id);
if (sps.profile_idc==100 || sps.profile_idc==110 || sps.profile_idc==122 || sps.profile_idc==144) {
sps.chroma_format_idc = bs_read_ue(&s);
fprintf(myout, " chroma_format_idc: %d\n", sps.chroma_format_idc);
if (sps.chroma_format_idc == 3) {
sps.residual_colour_transform_flag = (Boolean)bs_read1(&s);
fprintf(myout, " residual_colour_transform_flag: %d\n", sps.residual_colour_transform_flag);
}
sps.bit_depth_luma_minus8 = bs_read_ue(&s);
fprintf(myout, " bit_depth_luma_minus8: %d\n", sps.bit_depth_luma_minus8);
sps.bit_depth_chroma_minus8 = bs_read_ue(&s);
fprintf(myout, " bit_depth_chroma_minus8: %d\n", sps.bit_depth_chroma_minus8);
sps.qpprime_y_zero_transform_bypass_flag = (Boolean)bs_read1(&s);
fprintf(myout, " qpprime_y_zero_transform_bypass_flag: %d\n", sps.qpprime_y_zero_transform_bypass_flag);
sps.seq_scaling_matrix_present_flag = (Boolean)bs_read1(&s);
fprintf(myout, " seq_scaling_matrix_present_flag: %d\n", sps.seq_scaling_matrix_present_flag);
if (sps.seq_scaling_matrix_present_flag) {
for (int i=0; i<8; i++) {
sps.seq_scaling_list_present_flag[i] = (Boolean)bs_read1(&s);
fprintf(myout, " seq_scaling_list_present_flag: %d\n", sps.seq_scaling_list_present_flag[i]);
}
}
}
sps.log2_max_frame_num_minus4 = bs_read_ue(&s);
fprintf(myout, "log2_max_frame_num_minus4: %d\n", sps.log2_max_frame_num_minus4);
sps.pic_order_cnt_type = bs_read_ue(&s);
fprintf(myout, "pic_order_cnt_type: %d\n", sps.pic_order_cnt_type);
if (sps.pic_order_cnt_type == 0) {
sps.log2_max_pic_order_cnt_lsb_minus4 = bs_read_ue(&s);
fprintf(myout, " log2_max_pic_order_cnt_lsb_minus4: %d\n", sps.log2_max_pic_order_cnt_lsb_minus4);
} else if (sps.pic_order_cnt_type == 1) {
sps.delta_pic_order_always_zero_flag = (Boolean)bs_read1(&s);
sps.offset_for_non_ref_pic = bs_read_se(&s);
sps.offset_for_top_to_bottom_field = bs_read_se(&s);
sps.num_ref_frames_in_pic_order_cnt_cycle = bs_read_ue(&s);
for (int i=0; i<sps.num_ref_frames_in_pic_order_cnt_cycle; i++)
sps.offset_for_ref_frame[i] = bs_read_se(&s);
}
sps.num_ref_frames = bs_read_ue(&s);
fprintf(myout, "num_ref_frames: %d\n", sps.num_ref_frames);
sps.gaps_in_frame_num_value_allowed_flag = (Boolean)bs_read1(&s);
fprintf(myout, "gaps_in_frame_num_value_allowed_flag: %d\n", sps.gaps_in_frame_num_value_allowed_flag);
sps.pic_width_in_mbs_minus1 = bs_read_ue(&s);
fprintf(myout, "pic_width_in_mbs_minus1: %d\n", sps.pic_width_in_mbs_minus1);
sps.pic_height_in_map_units_minus1 = bs_read_ue(&s);
fprintf(myout, "pic_height_in_map_units_minus1: %d\n", sps.pic_height_in_map_units_minus1);
sps.frame_mbs_only_flag = (Boolean)bs_read1(&s);
fprintf(myout, "frame_mbs_only_flag: %d\n", sps.frame_mbs_only_flag);
if (!sps.frame_mbs_only_flag) {
sps.mb_adaptive_frame_field_flag = (Boolean)bs_read1(&s);
fprintf(myout, " mb_adaptive_frame_field_flag: %d\n", sps.mb_adaptive_frame_field_flag);
}
sps.direct_8x8_inference_flag = (Boolean)bs_read1(&s);
fprintf(myout, "direct_8x8_inference_flag: %d\n", sps.direct_8x8_inference_flag);
sps.frame_cropping_flag = (Boolean)bs_read1(&s);
fprintf(myout, "frame_cropping_flag: %d\n", sps.frame_cropping_flag);
if (sps.frame_cropping_flag) {
sps.frame_cropping_rect_left_offset = bs_read_ue(&s);
fprintf(myout, " frame_cropping_rect_left_offset: %d\n", sps.frame_cropping_rect_left_offset);
sps.frame_cropping_rect_right_offset = bs_read_ue(&s);
fprintf(myout, " frame_cropping_rect_right_offset: %d\n", sps.frame_cropping_rect_right_offset);
sps.frame_cropping_rect_top_offset = bs_read_ue(&s);
fprintf(myout, " frame_cropping_rect_top_offset: %d\n", sps.frame_cropping_rect_top_offset);
sps.frame_cropping_rect_bottom_offset = bs_read_ue(&s);
fprintf(myout, " frame_cropping_rect_bottom_offset: %d\n", sps.frame_cropping_rect_bottom_offset);
}
// vui
sps.vui_parameters_present_flag = (Boolean)bs_read1(&s);
fprintf(myout, "vui_parameters_present_flag: %d\n", sps.vui_parameters_present_flag);
if (sps.vui_parameters_present_flag) {
printf ("VUI sequence parameters present but not supported, ignored, proceeding to next NALU\n");
}
sps.Valid = TRUE;
printf("-------------------------------------------------------\n");
return 0;
}
static int ParseAndDumpPPSInfo(NALU_t * nal)
{
bs_t s;
pic_parameter_set_rbsp_t pps;
FILE *myout = stdout;
bs_init(&s, nal->buf+1, nal->len - 1);
printf("---------------------PPS info--------------------------\n");
pps.pic_parameter_set_id = bs_read_ue(&s);
fprintf(myout, "pic_parameter_set_id: %u\n", pps.pic_parameter_set_id);
pps.seq_parameter_set_id = bs_read_ue(&s);
fprintf(myout, "seq_parameter_set_id: %u\n", pps.seq_parameter_set_id);
pps.entropy_coding_mode_flag = (Boolean)bs_read1(&s);
fprintf(myout, "entropy_coding_mode_flag: %u\n", pps.entropy_coding_mode_flag);
pps.pic_order_present_flag = (Boolean)bs_read1(&s);
fprintf(myout, "pic_order_present_flag: %u\n", pps.pic_order_present_flag);
pps.num_slice_groups_minus1 = bs_read_ue(&s);
fprintf(myout, "num_slice_groups_minus1: %u\n", pps.num_slice_groups_minus1);
if (pps.num_slice_groups_minus1 > 0) {
pps.slice_group_map_type = bs_read_ue(&s);
unsigned type = pps.slice_group_map_type;
if (type == 0) {
for (int i=0; i<MAXnum_slice_groups_minus1; i++)
pps.run_length_minus1[i] = bs_read_ue(&s);
} else if (type == 2) {
for (int i=0; i<MAXnum_slice_groups_minus1; i++) {
pps.top_left[i] = bs_read_ue(&s);
pps.bottom_right[i] = bs_read_ue(&s);
}
} else if (type==3 || type==4 || type==5) {
pps.slice_group_change_direction_flag = (Boolean)bs_read1(&s);
pps.slice_group_change_rate_minus1 = bs_read_ue(&s);
} else if (type == 6) {
pps.num_slice_group_map_units_minus1 = bs_read_ue(&s);
pps.slice_group_id = NULL; // need to be Fixed
}
}
pps.num_ref_idx_l0_active_minus1 = bs_read_ue(&s);
fprintf(myout, "num_ref_idx_l0_active_minus1: %u\n", pps.num_ref_idx_l0_active_minus1);
pps.num_ref_idx_l1_active_minus1 = bs_read_ue(&s);
fprintf(myout, "num_ref_idx_l1_active_minus1: %u\n", pps.num_ref_idx_l1_active_minus1);
pps.weighted_pred_flag = (Boolean)bs_read1(&s);
fprintf(myout, "weighted_pred_flag: %u\n", pps.weighted_pred_flag);
pps.weighted_bipred_idc = bs_read(&s, 2);
fprintf(myout, "weighted_bipred_idc: %u\n", pps.weighted_bipred_idc);
pps.pic_init_qp_minus26 = bs_read_se(&s);
fprintf(myout, "pic_init_qp_minus26: %2d\n", pps.pic_init_qp_minus26);
pps.pic_init_qs_minus26 = bs_read_se(&s);
fprintf(myout, "pic_init_qs_minus26: %2d\n", pps.pic_init_qs_minus26);
pps.chroma_qp_index_offset = bs_read_se(&s);
fprintf(myout, "chroma_qp_index_offset: %2d\n", pps.chroma_qp_index_offset);
pps.deblocking_filter_control_present_flag = (Boolean)bs_read1(&s);
fprintf(myout, "deblocking_filter_control_present_flag:%u\n", pps.deblocking_filter_control_present_flag);
pps.constrained_intra_pred_flag = (Boolean)bs_read1(&s);
fprintf(myout, "constrained_intra_pred_flag: %u\n", pps.constrained_intra_pred_flag);
pps.redundant_pic_cnt_present_flag = (Boolean)bs_read1(&s);
fprintf(myout, "redundant_pic_cnt_present_flag:%u\n", pps.redundant_pic_cnt_present_flag);
pps.Valid = TRUE;
printf("-------------------------------------------------------\n");
return 0;
}
static int ParseAndDumpSEIInfo(NALU_t * nal)
{
return 0;
}
/**
* Analysis H.264 Bitstream
* @param url Location of input H.264 bitstream file.
*/
int simplest_h264_parser(char *url){
NALU_t *nalu;
int buffersize = 1000000; // max bs size: 1MB
//FILE *myout=fopen("output_log.txt","wb+");
FILE *myout=stdout;
h264bitstream=fopen(url, "rb+");
if (h264bitstream==NULL){
printf("Open file error\n");
return -1;
}
nalu = (NALU_t*)calloc (1, sizeof (NALU_t));
if (nalu == NULL){
printf("Alloc NALU fail!\n");
return -1;
}
nalu->max_size=buffersize;
nalu->buf = (unsigned char*)calloc (buffersize, sizeof (char));
if (nalu->buf == NULL) {
free(nalu);
printf ("Alloc NALU buf fail!\n");
return 0;
}
int data_offset=0;
int nal_num=0;
printf("------+---------+-- NALU Table -----+---------+-------+\n");
printf("| NUM | POS | IDC | NAL_TYPE | LEN | FRAME |\n");
printf("+-----+---------+--------+----------+---------+-------+\n");
while(!feof(h264bitstream))
{
int data_lenth;
data_lenth=GetAnnexbNALU(nalu);
GetFrameType(nalu);
char nal_type_str[16]={0};
switch(nalu->nal_unit_type) {
case NALU_TYPE_SLICE:sprintf(nal_type_str,"SLICE");break;
case NALU_TYPE_DPA:sprintf(nal_type_str,"DPA");break;
case NALU_TYPE_DPB:sprintf(nal_type_str,"DPB");break;
case NALU_TYPE_DPC:sprintf(nal_type_str,"DPC");break;
case NALU_TYPE_IDR:sprintf(nal_type_str,"IDR");break;
case NALU_TYPE_SEI:sprintf(nal_type_str,"SEI");break;
case NALU_TYPE_SPS:sprintf(nal_type_str,"SPS");break;
case NALU_TYPE_PPS:sprintf(nal_type_str,"PPS");break;
case NALU_TYPE_AUD:sprintf(nal_type_str,"AUD");break;
case NALU_TYPE_EOSEQ:sprintf(nal_type_str,"EOSEQ");break;
case NALU_TYPE_EOSTREAM:sprintf(nal_type_str,"EOSTREAM");break;
case NALU_TYPE_FILL:sprintf(nal_type_str,"FILL");break;
}
char idc_str[16]={0};
switch(nalu->nal_reference_idc>>5) {
case NALU_PRIORITY_DISPOSABLE:sprintf(idc_str,"DISPOS");break;
case NALU_PRIRITY_LOW:sprintf(idc_str,"LOW");break;
case NALU_PRIORITY_HIGH:sprintf(idc_str,"HIGH");break;
case NALU_PRIORITY_HIGHEST:sprintf(idc_str,"HIGHEST");break;
}
char frame_type_str[16]={0};
switch(nalu->frame_type) {
case FRAME_TYPE_I:sprintf(frame_type_str,"I");break;
case FRAME_TYPE_P:sprintf(frame_type_str,"P");break;
case FRAME_TYPE_B:sprintf(frame_type_str,"B");break;
default:sprintf(frame_type_str,"");break;
}
fprintf(myout,"|%5d| %#8x| %7s| %9s| %8d| %5s |\n", nal_num, data_offset, idc_str, nal_type_str, nalu->len, frame_type_str);
data_offset = data_offset + data_lenth;
nal_num++;
// parse sps & pps & sei
switch(nalu->nal_unit_type) {
case NALU_TYPE_SPS:
ParseAndDumpSPSInfo(nalu);
break;
case NALU_TYPE_PPS:
ParseAndDumpPPSInfo(nalu);
break;
case NALU_TYPE_SEI:
ParseAndDumpSEIInfo(nalu);
break;
}
}
if (nalu) {
if (nalu->buf) {
free(nalu->buf);
nalu->buf=NULL;
}
free(nalu);
}
fclose(h264bitstream);
return 0;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
puts("error input! usage: cmd url");
return -1;
}
simplest_h264_parser(argv[1]);
return 0;
}
下面是代码执行结果判断:
那为什么在解析摄像机视频流数据时,时间戳是单调递增的,也就是说没有B帧,这是因为B帧压缩和解压耗时,对硬件要求高;而且B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别,换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。由于这两个原因,必然会导致延迟的产生,这对于摄像机监控类的实时系统是不太友好的。
但是播放视频文件时,对延时不敏感,因此为了加大压缩率,视频文件中一般都含义B帧,如果要测试处理包含B帧的视频流,用VLC推一个视频文件就可以了。
H264有四种画质级别,分别是BP、EP、MP、HP
BP-Baseline Profile:基本画质。支持I/P帧,只支持无交错(Progressive)和CAVLC;
EP-Extended Profile:进阶画质。支持I/P/B/SP/SI帧,只支持无交错(Progressive)和CAVLC;
MP-Main Profile:主流画质。提供I/P/B帧,支持无交错(Progressive)和交错(Interlaced),也支持CAVLC和CABAC的支持;
HP-High Profile:高级画质。在Main Profile基础上增加了8×8内部预测、自定义量化、无损视频编码和更多的YUV格式;
在相同配置情况下,HP可以比MP节省10%的码流量,比MPEG-2节省60%码流量,具有更好的编码性能。根据应用领域的不同,Baseline Profile多应用于实时通信领域,Main Profile多应用于流媒体领域,High Profile则多应用于广电和存储领域。
二.保活机制
当我们采用RTP over UDP/TCP方式在拉流的过程中,RTSPClient(live555)可能长时间都在从RTSPServer(IPC/NVR)拉流,但却没有向RTSPServer发送保活报文,如果服务器端启动了Session保活检测机制(参看live555中RTSPServer中noteliveness实现),服务器端长时间没有收到客户端发送的数据包,就会认为客户端连接是假连接,主动断开与客户端的连接(不管客户端是否正在拉流)。
所以,为了解决上述的问题,本客户端都会定时向RTSPServer发送GET_PARAMETER命令,类似于保活报文,这样服务器端就能正常地发送直播数据流了。
void keepAliveTimerHandler(void *clientData)
{
QHRtspClient *rtspClient = (QHRtspClient*)clientData;
UsageEnvironment &env = rtspClient->envir();
QHRtspState &rtspState = rtspClient->rtspState();
rtspClient->sendGetParameterCommand(*rtspState.session, nullptr, nullptr);
rtspState.keepAliveTimerTask=env.taskScheduler().scheduleDelayedTask(rtspState.interval*1000000, (TaskFunc*)keepAliveTimerHandler, rtspClient);
}
三.send......Command 发送完命令后如何判断超时
发送完sendDescribeCommand等命令后,应该立刻启动一个延时任务,在timeout秒后触发。如果live555在timeout秒内返回了正确答复,就停止该任务,否则就调用该任务异绑定的常处理函数interruptRtspHandler。
void waitLive555Response(void *clientData,std::string taskType,bool startOrStop)
{
QHRtspClient *rtspClient = (QHRtspClient*)clientData;
UsageEnvironment &env = rtspClient->envir();
QHRtspState &rtspState = rtspClient->rtspState();
if(startOrStop)
{
TaskToken task=env.taskScheduler().scheduleDelayedTask(rtspState.waitTime*1000000, (TaskFunc*)interruptRtspHandler, rtspClient);
rtspState.interruptRtspTasks.insert(std::pair<std::string, TaskToken>(taskType,task));
}
else
{
std::map<std::string,TaskToken>::iterator iter=rtspState.interruptRtspTasks.find(taskType);
if(iter != rtspState.interruptRtspTasks.end())
{
env.taskScheduler().unscheduleDelayedTask(iter->second);
}
}
}
四.建立连接时间较长
对某些具有鉴权功能的IPC,发送完sendDescribeCommand后隔10s左右才收到回应,原因是我们的RTSP的url可能是这种形式的rtsp:\\usr:[email protected]\streamname,也就是说url是包含用户名和密码的。
参考链接:https://blog.csdn.net/m0_37684310/article/details/89710717
bool QHRtspSession::openURL(UsageEnvironment &env, const char *progName, const char *url)
{
// 先将rtsp://username:[email protected]/streamname解析一下.
// unAuthorizedPath 的格式为rtsp://192.xxx.xxx.xxx/streamname.
char *userName = nullptr;
char *password = nullptr;
char *unAuthorizedPath = nullptr;
QHCommon::parseRtspUrl(url, userName, password, unAuthorizedPath);
if (userName != nullptr && password != nullptr)
{
m_rtspClient = QHRtspClient::createNew(this, env, unAuthorizedPath, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
}
else
{
m_rtspClient = QHRtspClient::createNew(this, env, url, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
}
if (!m_rtspClient)
{
env << "Failed to create a RTSP client for URL \"" << url << "\": " << env.getResultMsg() << "\n";
return false;
}
else
{
if (userName != nullptr && password != nullptr)
{
Authenticator authenticator(userName, password, false);
m_rtspClient->sendDescribeCommand(continueAfterDESCRIBE, &authenticator);
}
else
{
m_rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}
waitLive555Response(m_rtspClient,"describe",true);
}
return true;
}
原创不易,转载请标明出处:https://blog.csdn.net/caoshangpa/article/details/112123953