音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析

news2024/12/26 23:09:00

一、引言

FFmpeg源码对AnnexB包装的H.264码流解码过程中,通过ff_h2645_extract_rbsp函数拿到该H.264码流中的某个NALU的NALU Header + RBSP后(具体可以参考:《FFmpeg源码:ff_h2645_extract_rbsp函数分析》),如果从其NALU Header判断出该NALU的类型为SPS,会调用ff_h264_decode_seq_parameter_set函数对其进行解码,把解码出来的SPS属性放到uint8_t *指针指向的缓冲区中。之后对该缓冲区进行结构体指针强转(const SPS*)可以拿到该SPS的属性。本文讲解SPS结构体和ff_h264_decode_seq_parameter_set函数的内部实现。

二、SPS结构体的声明

SPS结构体的声明在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3)的头文件libavcodec/h264_ps.h中:

/**
 * Sequence parameter set
 */
typedef struct SPS {
    unsigned int sps_id;
    int profile_idc;
    int level_idc;
    int chroma_format_idc;
    int transform_bypass;              ///< qpprime_y_zero_transform_bypass_flag
    int log2_max_frame_num;            ///< log2_max_frame_num_minus4 + 4
    int poc_type;                      ///< pic_order_cnt_type
    int log2_max_poc_lsb;              ///< log2_max_pic_order_cnt_lsb_minus4
    int delta_pic_order_always_zero_flag;
    int offset_for_non_ref_pic;
    int offset_for_top_to_bottom_field;
    int poc_cycle_length;              ///< num_ref_frames_in_pic_order_cnt_cycle
    int ref_frame_count;               ///< num_ref_frames
    int gaps_in_frame_num_allowed_flag;
    int mb_width;                      ///< pic_width_in_mbs_minus1 + 1
    ///< (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag)
    int mb_height;
    int frame_mbs_only_flag;
    int mb_aff;                        ///< mb_adaptive_frame_field_flag
    int direct_8x8_inference_flag;
    int crop;                          ///< frame_cropping_flag

    /* those 4 are already in luma samples */
    unsigned int crop_left;            ///< frame_cropping_rect_left_offset
    unsigned int crop_right;           ///< frame_cropping_rect_right_offset
    unsigned int crop_top;             ///< frame_cropping_rect_top_offset
    unsigned int crop_bottom;          ///< frame_cropping_rect_bottom_offset
    int vui_parameters_present_flag;
    AVRational sar;
    int video_signal_type_present_flag;
    int full_range;
    int colour_description_present_flag;
    enum AVColorPrimaries color_primaries;
    enum AVColorTransferCharacteristic color_trc;
    enum AVColorSpace colorspace;
    enum AVChromaLocation chroma_location;

    int timing_info_present_flag;
    uint32_t num_units_in_tick;
    uint32_t time_scale;
    int fixed_frame_rate_flag;
    int32_t offset_for_ref_frame[256];
    int bitstream_restriction_flag;
    int num_reorder_frames;
    int scaling_matrix_present;
    uint8_t scaling_matrix4[6][16];
    uint8_t scaling_matrix8[6][64];
    int nal_hrd_parameters_present_flag;
    int vcl_hrd_parameters_present_flag;
    int pic_struct_present_flag;
    int time_offset_length;
    int cpb_cnt;                          ///< See H.264 E.1.2
    int initial_cpb_removal_delay_length; ///< initial_cpb_removal_delay_length_minus1 + 1
    int cpb_removal_delay_length;         ///< cpb_removal_delay_length_minus1 + 1
    int dpb_output_delay_length;          ///< dpb_output_delay_length_minus1 + 1
    int bit_depth_luma;                   ///< bit_depth_luma_minus8 + 8
    int bit_depth_chroma;                 ///< bit_depth_chroma_minus8 + 8
    int residual_color_transform_flag;    ///< residual_colour_transform_flag
    int constraint_set_flags;             ///< constraint_set[0-3]_flag
    uint8_t data[4096];
    size_t data_size;
} SPS;

SPS结构体中,成员数组data存放该SPS的“NALU Header + RBSP”。成员data_size为该SPS“NALU Header + RBSP”的长度,单位为字节。其它成员变量存放“通过ff_h264_decode_seq_parameter_set函数从该SPS解码出来的属性”。可以看到SPS结构体的成员变量跟H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第44页到45页中描述的SPS的属性是一一对应的:

三、ff_h264_decode_seq_parameter_set函数的声明

ff_h264_decode_seq_parameter_set函数声明在FFmpeg源码的头文件libavcodec/h264_ps.h中:

typedef struct H264ParamSets {
    AVBufferRef *sps_list[MAX_SPS_COUNT];
    AVBufferRef *pps_list[MAX_PPS_COUNT];

    AVBufferRef *pps_ref;
    /* currently active parameters sets */
    const PPS *pps;
    const SPS *sps;

    int overread_warning_printed[2];
} H264ParamSets;

/**
 * Decode SPS
 */
int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
                                     H264ParamSets *ps, int ignore_truncation);

该函数作用是:把形参gb指向的缓冲区中的二进制SPS数据中的每个属性提取出来,存放到形参ps->sps_list[sps_id]->data指向的缓冲区中(sps_id是该SPS的seq_parameter_set_id)。

形参gb:既是输入型参数也是输出型参数,为GetBitContext类型,用来对“位”进行操作(具体可以参考:《FFmpeg中位操作相关的源码:GetBitContext结构体,init_get_bits函数、get_bits1函数和get_bits函数分析》)。

执行ff_h264_decode_seq_parameter_set函数前:

gb->buffer为:指向某个缓冲区的指针。该缓冲区存放 SPS的“NALU Header + RBSP”

gb->buffer_end为:指向该SPS的RBSP的最后一个字节。

gb->index为:8。表示读取完了该SPS的第一个字节(表示读取完了8位的NALU Header)

gb->size_in_bit的值等于:该SPS “NALU Header + SODB的位数”,单位为bit(1个byte等于8bit)。

gb->size_in_bits_plus8的值等于:gb->size_in_bit + 8。

执行ff_h264_decode_seq_parameter_set函数后:

gb->index的值会增加该SPS存贮所占的位数,表示已经读取完了整个SPS。gb的其它成员变量的值保持不变。

形参avctx:输入型参数。用来在ff_h264_decode_seq_parameter_set函数内部打印日志,可忽略。

形参ps:输出型参数。执行ff_h264_decode_seq_parameter_set函数后,ps->sps_list[sps_id]->data指向的缓冲区会存放从SPS中提取出来的每个属性。(sps_id是该SPS的seq_parameter_set_id)。这样在ff_h264_decode_seq_parameter_set函数外部就可以通过“C语言的结构体指针强转”(const SPS*)ps->sps_list[sps_id]->data 获取结构体SPS中的成员,从而拿到SPS中的每个属性:

形参ignore_truncation:输入型参数。值一般为0,可忽略。

四、ff_h264_decode_seq_parameter_set函数的定义

ff_h264_decode_seq_parameter_set函数定义在FFmpeg源码的源文件libavcodec/h264_ps.c中:

int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
                                     H264ParamSets *ps, int ignore_truncation)
{
    AVBufferRef *sps_buf;
    int profile_idc, level_idc, constraint_set_flags = 0;
    unsigned int sps_id;
    int i, log2_max_frame_num_minus4;
    SPS *sps;
    int ret;

    sps_buf = av_buffer_allocz(sizeof(*sps));
    if (!sps_buf)
        return AVERROR(ENOMEM);
    sps = (SPS*)sps_buf->data;

    sps->data_size = gb->buffer_end - gb->buffer;
    if (sps->data_size > sizeof(sps->data)) {
        av_log(avctx, AV_LOG_DEBUG, "Truncating likely oversized SPS\n");
        sps->data_size = sizeof(sps->data);
    }
    memcpy(sps->data, gb->buffer, sps->data_size);

    profile_idc           = get_bits(gb, 8);
    constraint_set_flags |= get_bits1(gb) << 0;   // constraint_set0_flag
    constraint_set_flags |= get_bits1(gb) << 1;   // constraint_set1_flag
    constraint_set_flags |= get_bits1(gb) << 2;   // constraint_set2_flag
    constraint_set_flags |= get_bits1(gb) << 3;   // constraint_set3_flag
    constraint_set_flags |= get_bits1(gb) << 4;   // constraint_set4_flag
    constraint_set_flags |= get_bits1(gb) << 5;   // constraint_set5_flag
    skip_bits(gb, 2);                             // reserved_zero_2bits
    level_idc = get_bits(gb, 8);
    sps_id    = get_ue_golomb_31(gb);

    if (sps_id >= MAX_SPS_COUNT) {
        av_log(avctx, AV_LOG_ERROR, "sps_id %u out of range\n", sps_id);
        goto fail;
    }

    sps->sps_id               = sps_id;
    sps->time_offset_length   = 24;
    sps->profile_idc          = profile_idc;
    sps->constraint_set_flags = constraint_set_flags;
    sps->level_idc            = level_idc;
    sps->full_range           = -1;

    memset(sps->scaling_matrix4, 16, sizeof(sps->scaling_matrix4));
    memset(sps->scaling_matrix8, 16, sizeof(sps->scaling_matrix8));
    sps->scaling_matrix_present = 0;
    sps->colorspace = 2; //AVCOL_SPC_UNSPECIFIED

    if (sps->profile_idc == 100 ||  // High profile
        sps->profile_idc == 110 ||  // High10 profile
        sps->profile_idc == 122 ||  // High422 profile
        sps->profile_idc == 244 ||  // High444 Predictive profile
        sps->profile_idc ==  44 ||  // Cavlc444 profile
        sps->profile_idc ==  83 ||  // Scalable Constrained High profile (SVC)
        sps->profile_idc ==  86 ||  // Scalable High Intra profile (SVC)
        sps->profile_idc == 118 ||  // Stereo High profile (MVC)
        sps->profile_idc == 128 ||  // Multiview High profile (MVC)
        sps->profile_idc == 138 ||  // Multiview Depth High profile (MVCD)
        sps->profile_idc == 144) {  // old High444 profile
        sps->chroma_format_idc = get_ue_golomb_31(gb);
        if (sps->chroma_format_idc > 3U) {
            avpriv_request_sample(avctx, "chroma_format_idc %u",
                                  sps->chroma_format_idc);
            goto fail;
        } else if (sps->chroma_format_idc == 3) {
            sps->residual_color_transform_flag = get_bits1(gb);
            if (sps->residual_color_transform_flag) {
                av_log(avctx, AV_LOG_ERROR, "separate color planes are not supported\n");
                goto fail;
            }
        }
        sps->bit_depth_luma   = get_ue_golomb_31(gb) + 8;
        sps->bit_depth_chroma = get_ue_golomb_31(gb) + 8;
        if (sps->bit_depth_chroma != sps->bit_depth_luma) {
            avpriv_request_sample(avctx,
                                  "Different chroma and luma bit depth");
            goto fail;
        }
        if (sps->bit_depth_luma   < 8 || sps->bit_depth_luma   > 14 ||
            sps->bit_depth_chroma < 8 || sps->bit_depth_chroma > 14) {
            av_log(avctx, AV_LOG_ERROR, "illegal bit depth value (%d, %d)\n",
                   sps->bit_depth_luma, sps->bit_depth_chroma);
            goto fail;
        }
        sps->transform_bypass = get_bits1(gb);
        ret = decode_scaling_matrices(gb, sps, NULL, 1,
                                      sps->scaling_matrix4, sps->scaling_matrix8);
        if (ret < 0)
            goto fail;
        sps->scaling_matrix_present |= ret;
    } else {
        sps->chroma_format_idc = 1;
        sps->bit_depth_luma    = 8;
        sps->bit_depth_chroma  = 8;
    }

    log2_max_frame_num_minus4 = get_ue_golomb_31(gb);
    if (log2_max_frame_num_minus4 < MIN_LOG2_MAX_FRAME_NUM - 4 ||
        log2_max_frame_num_minus4 > MAX_LOG2_MAX_FRAME_NUM - 4) {
        av_log(avctx, AV_LOG_ERROR,
               "log2_max_frame_num_minus4 out of range (0-12): %d\n",
               log2_max_frame_num_minus4);
        goto fail;
    }
    sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4;

    sps->poc_type = get_ue_golomb_31(gb);

    if (sps->poc_type == 0) { // FIXME #define
        unsigned t = get_ue_golomb_31(gb);
        if (t>12) {
            av_log(avctx, AV_LOG_ERROR, "log2_max_poc_lsb (%d) is out of range\n", t);
            goto fail;
        }
        sps->log2_max_poc_lsb = t + 4;
    } else if (sps->poc_type == 1) { // FIXME #define
        sps->delta_pic_order_always_zero_flag = get_bits1(gb);
        sps->offset_for_non_ref_pic           = get_se_golomb_long(gb);
        sps->offset_for_top_to_bottom_field   = get_se_golomb_long(gb);

        if (   sps->offset_for_non_ref_pic         == INT32_MIN
            || sps->offset_for_top_to_bottom_field == INT32_MIN
        ) {
            av_log(avctx, AV_LOG_ERROR,
                   "offset_for_non_ref_pic or offset_for_top_to_bottom_field is out of range\n");
            goto fail;
        }

        sps->poc_cycle_length                 = get_ue_golomb(gb);

        if ((unsigned)sps->poc_cycle_length >=
            FF_ARRAY_ELEMS(sps->offset_for_ref_frame)) {
            av_log(avctx, AV_LOG_ERROR,
                   "poc_cycle_length overflow %d\n", sps->poc_cycle_length);
            goto fail;
        }

        for (i = 0; i < sps->poc_cycle_length; i++) {
            sps->offset_for_ref_frame[i] = get_se_golomb_long(gb);
            if (sps->offset_for_ref_frame[i] == INT32_MIN) {
                av_log(avctx, AV_LOG_ERROR,
                       "offset_for_ref_frame is out of range\n");
                goto fail;
            }
        }
    } else if (sps->poc_type != 2) {
        av_log(avctx, AV_LOG_ERROR, "illegal POC type %d\n", sps->poc_type);
        goto fail;
    }

    sps->ref_frame_count = get_ue_golomb_31(gb);
    if (avctx->codec_tag == MKTAG('S', 'M', 'V', '2'))
        sps->ref_frame_count = FFMAX(2, sps->ref_frame_count);
    if (sps->ref_frame_count > MAX_DELAYED_PIC_COUNT) {
        av_log(avctx, AV_LOG_ERROR,
               "too many reference frames %d\n", sps->ref_frame_count);
        goto fail;
    }
    sps->gaps_in_frame_num_allowed_flag = get_bits1(gb);
    sps->mb_width                       = get_ue_golomb(gb) + 1;
    sps->mb_height                      = get_ue_golomb(gb) + 1;

    sps->frame_mbs_only_flag = get_bits1(gb);

    if (sps->mb_height >= INT_MAX / 2U) {
        av_log(avctx, AV_LOG_ERROR, "height overflow\n");
        goto fail;
    }
    sps->mb_height *= 2 - sps->frame_mbs_only_flag;

    if (!sps->frame_mbs_only_flag)
        sps->mb_aff = get_bits1(gb);
    else
        sps->mb_aff = 0;

    if ((unsigned)sps->mb_width  >= INT_MAX / 16 ||
        (unsigned)sps->mb_height >= INT_MAX / 16 ||
        av_image_check_size(16 * sps->mb_width,
                            16 * sps->mb_height, 0, avctx)) {
        av_log(avctx, AV_LOG_ERROR, "mb_width/height overflow\n");
        goto fail;
    }

    sps->direct_8x8_inference_flag = get_bits1(gb);

#ifndef ALLOW_INTERLACE
    if (sps->mb_aff)
        av_log(avctx, AV_LOG_ERROR,
               "MBAFF support not included; enable it at compile-time.\n");
#endif
    sps->crop = get_bits1(gb);
    if (sps->crop) {
        unsigned int crop_left   = get_ue_golomb(gb);
        unsigned int crop_right  = get_ue_golomb(gb);
        unsigned int crop_top    = get_ue_golomb(gb);
        unsigned int crop_bottom = get_ue_golomb(gb);
        int width  = 16 * sps->mb_width;
        int height = 16 * sps->mb_height;

        if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) {
            av_log(avctx, AV_LOG_DEBUG, "discarding sps cropping, original "
                                           "values are l:%d r:%d t:%d b:%d\n",
                   crop_left, crop_right, crop_top, crop_bottom);

            sps->crop_left   =
            sps->crop_right  =
            sps->crop_top    =
            sps->crop_bottom = 0;
        } else {
            int vsub   = (sps->chroma_format_idc == 1) ? 1 : 0;
            int hsub   = (sps->chroma_format_idc == 1 ||
                          sps->chroma_format_idc == 2) ? 1 : 0;
            int step_x = 1 << hsub;
            int step_y = (2 - sps->frame_mbs_only_flag) << vsub;

            if (crop_left  > (unsigned)INT_MAX / 4 / step_x ||
                crop_right > (unsigned)INT_MAX / 4 / step_x ||
                crop_top   > (unsigned)INT_MAX / 4 / step_y ||
                crop_bottom> (unsigned)INT_MAX / 4 / step_y ||
                (crop_left + crop_right ) * step_x >= width ||
                (crop_top  + crop_bottom) * step_y >= height
            ) {
                av_log(avctx, AV_LOG_ERROR, "crop values invalid %d %d %d %d / %d %d\n", crop_left, crop_right, crop_top, crop_bottom, width, height);
                goto fail;
            }

            sps->crop_left   = crop_left   * step_x;
            sps->crop_right  = crop_right  * step_x;
            sps->crop_top    = crop_top    * step_y;
            sps->crop_bottom = crop_bottom * step_y;
        }
    } else {
        sps->crop_left   =
        sps->crop_right  =
        sps->crop_top    =
        sps->crop_bottom =
        sps->crop        = 0;
    }

    sps->vui_parameters_present_flag = get_bits1(gb);
    if (sps->vui_parameters_present_flag) {
        int ret = decode_vui_parameters(gb, avctx, sps);
        if (ret < 0)
            goto fail;
    }

    if (get_bits_left(gb) < 0) {
        av_log_once(avctx, ignore_truncation ? AV_LOG_WARNING : AV_LOG_ERROR, AV_LOG_DEBUG,
                    &ps->overread_warning_printed[sps->vui_parameters_present_flag],
               "Overread %s by %d bits\n", sps->vui_parameters_present_flag ? "VUI" : "SPS", -get_bits_left(gb));
        if (!ignore_truncation)
            goto fail;
    }

    /* if the maximum delay is not stored in the SPS, derive it based on the
     * level */
    if (!sps->bitstream_restriction_flag &&
        (sps->ref_frame_count || avctx->strict_std_compliance >= FF_COMPLIANCE_STRICT)) {
        sps->num_reorder_frames = MAX_DELAYED_PIC_COUNT - 1;
        for (i = 0; i < FF_ARRAY_ELEMS(level_max_dpb_mbs); i++) {
            if (level_max_dpb_mbs[i][0] == sps->level_idc) {
                sps->num_reorder_frames = FFMIN(level_max_dpb_mbs[i][1] / (sps->mb_width * sps->mb_height),
                                                sps->num_reorder_frames);
                break;
            }
        }
    }

    if (!sps->sar.den)
        sps->sar.den = 1;

    if (avctx->debug & FF_DEBUG_PICT_INFO) {
        static const char csp[4][5] = { "Gray", "420", "422", "444" };
        av_log(avctx, AV_LOG_DEBUG,
               "sps:%u profile:%d/%d poc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s %"PRId32"/%"PRId32" b%d reo:%d\n",
               sps_id, sps->profile_idc, sps->level_idc,
               sps->poc_type,
               sps->ref_frame_count,
               sps->mb_width, sps->mb_height,
               sps->frame_mbs_only_flag ? "FRM" : (sps->mb_aff ? "MB-AFF" : "PIC-AFF"),
               sps->direct_8x8_inference_flag ? "8B8" : "",
               sps->crop_left, sps->crop_right,
               sps->crop_top, sps->crop_bottom,
               sps->vui_parameters_present_flag ? "VUI" : "",
               csp[sps->chroma_format_idc],
               sps->timing_info_present_flag ? sps->num_units_in_tick : 0,
               sps->timing_info_present_flag ? sps->time_scale : 0,
               sps->bit_depth_luma,
               sps->bitstream_restriction_flag ? sps->num_reorder_frames : -1
               );
    }

    /* check if this is a repeat of an already parsed SPS, then keep the
     * original one.
     * otherwise drop all PPSes that depend on it */
    if (ps->sps_list[sps_id] &&
        !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) {
        av_buffer_unref(&sps_buf);
    } else {
        remove_sps(ps, sps_id);
        ps->sps_list[sps_id] = sps_buf;
    }

    return 0;

fail:
    av_buffer_unref(&sps_buf);
    return AVERROR_INVALIDDATA;
}

ff_h264_decode_seq_parameter_set函数内部,首先通过语句sps_buf = av_buffer_allocz(sizeof(*sps)) 给指针sps_buf指向的AVBufferRef结构体和其成员buf和data分配内存,字节归零,给sps_buf->data分配的内存块的大小为sizeof(*sps),也就是sizeof(SPS)个字节。关于av_buffer_allocz函数的用法可以参考:《FFmpeg源码:buffer_create、av_buffer_create、av_buffer_default_free、av_buffer_alloc、av_buffer_allocz函数分析》

通过语句sps = (SPS*)sps_buf->data 让接下来解码得到的SPS属性都存放到缓冲区sps_buf->data中。

通过语句sps->data_size = gb->buffer_end - gb->buffer 得到该SPS“NALU Header + RBSP”的长度,单位为字节。

由于sps->data数组的长度为4096字节,sizeof(sps->data)为4096。如果sps->data_size > sizeof(sps->data)表示该SPS的“NALU Header + RBSP” 大于数组长度,会导致数组越界访问,也就是超出数组合法空间的访问,所以要加如下判断语句:

if (sps->data_size > sizeof(sps->data)) {
        av_log(avctx, AV_LOG_DEBUG, "Truncating likely oversized SPS\n");
        sps->data_size = sizeof(sps->data);
    }

通过语句memcpy(sps->data, gb->buffer, sps->data_size),将SPS的“NALU Header + RBSP”拷贝到sps->data指向的缓冲区中。

通过下面语句,按位读取描述符为u(n),也就是n位无符号整数的SPS属性。由于执行函数ff_h264_decode_seq_parameter_set之前,gb->index的值为8,表示已经读取完了该SPS的NALU Header。所以执行下面的语句可以直接读取该SPS的属性:

profile_idc           = get_bits(gb, 8);
constraint_set_flags |= get_bits1(gb) << 0;   // constraint_set0_flag
constraint_set_flags |= get_bits1(gb) << 1;   // constraint_set1_flag
constraint_set_flags |= get_bits1(gb) << 2;   // constraint_set2_flag
constraint_set_flags |= get_bits1(gb) << 3;   // constraint_set3_flag
constraint_set_flags |= get_bits1(gb) << 4;   // constraint_set4_flag
constraint_set_flags |= get_bits1(gb) << 5;   // constraint_set5_flag
skip_bits(gb, 2);                             // reserved_zero_2bits
level_idc = get_bits(gb, 8);

关于get_bits1和get_bits函数的用法可以参考:《FFmpeg中位操作相关的源码:GetBitContext结构体,init_get_bits函数、get_bits1函数和get_bits函数分析》

通过下面语句,读取无符号指数哥伦布编码的SPS属性:

sps_id    = get_ue_golomb_31(gb);

关于get_ue_golomb_31函数的用法可以参考:《音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现》

H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第74页,写着seq_parameter_set_id属性的取值范围为0 ~ 31(包括0 ~ 31)

所以有如下判断语句:

    if (sps_id >= MAX_SPS_COUNT) {
        av_log(avctx, AV_LOG_ERROR, "sps_id %u out of range\n", sps_id);
        goto fail;
    }

宏定义MAX_SPS_COUNT 定义在FFmpeg源码的头文件libavcodec/h264_ps.h中:

#define MAX_SPS_COUNT          32


 

if (sps->profile_idc == 100 ||  // High profile
        sps->profile_idc == 110 ||  // High10 profile
        sps->profile_idc == 122 ||  // High422 profile
        sps->profile_idc == 244 ||  // High444 Predictive profile
        sps->profile_idc ==  44 ||  // Cavlc444 profile
        sps->profile_idc ==  83 ||  // Scalable Constrained High profile (SVC)
        sps->profile_idc ==  86 ||  // Scalable High Intra profile (SVC)
        sps->profile_idc == 118 ||  // Stereo High profile (MVC)
        sps->profile_idc == 128 ||  // Multiview High profile (MVC)
        sps->profile_idc == 138 ||  // Multiview Depth High profile (MVCD)
        sps->profile_idc == 144) {  // old High444 profile
//...
}

上面的判断语句对应H.264官方文档中的:

可以看到FFmpeg源码这里的判断语句跟H.264官方文档中的不一样,这里应该以H.264官方文档中的为准。所以如果看到FFmpeg解码某个H.264码流后拿到的SPS属性不正确,可以看看其SPS中的profile_idc是不是有问题。
 

然后不断通过get_bits1、get_bits、get_ue_golomb等函数不断将SPS的属性读取出来,放到指针sps指向的结构体中。


 

最后通过下面语句,将sps属性存贮到ps->sps_list[sps_id]中。

    /* check if this is a repeat of an already parsed SPS, then keep the
     * original one.
     * otherwise drop all PPSes that depend on it */
    if (ps->sps_list[sps_id] &&
        !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) {
        av_buffer_unref(&sps_buf);
    } else {
        remove_sps(ps, sps_id);
        ps->sps_list[sps_id] = sps_buf;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1908625.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7月5日,自然保护地总体规划智能编制系统,线上宣讲会(腾讯会议:638-228-003)

7月5日&#xff08;本周五&#xff09;下午2:30&#xff0c;国家林草局林草调查规划院胡理乐研究员&#xff0c;介绍自然保护地总体规划智能编制系统&#xff0c;欢迎大家线上参加&#xff01;&#xff08;腾讯会议号&#xff1a;638-228-003&#xff09; 系统主要特色&#x…

内存与硬盘(笔记)

文章目录 1. 内存① 笔记② 相关软件 2. 硬盘① 笔记② 相关软件 3. 区别&#xff1a;4. 推荐 1. 内存 ① 笔记 ① 笔记本的内存条和台式机的内存条是不互通的 ② 我们常说的内存其实指的是运行内存(前台后台同时能运行多少APP) ③ 下图来自京东&#xff1a; 解析&#xff1…

ShardingSphereConnection@4691d] will not be managed by Spring

boot整合分库分表后启动执行&#xff0c;提示链接不被spring管理&#xff0c; 应该看自己需要&#xff0c; 解决&#xff1a;

(十一) Docker compose 部署 Mysql 和 其它容器

文章目录 1、前言1.1、部署 MySQL 容器的 3 种类型1.2、M2芯片类型问题 2、具体实现2.1、单独部署 mysql 供宿主机访问2.1.1、文件夹结构2.1.2、docker-compose.yml 内容2.1.3、运行 2.2、单独部署 mysql 容器供其它容器访问&#xff08;以 apollo 为例&#xff09;2.2.1、文件…

近红外光谱脑功能成像(fNIRS):2.实验设计、指标计算与多重比较

一、实验设计的策略与方法 近红外光谱成像&#xff08;INIRS&#xff09;作为一种非侵入性脑功能成像技术&#xff0c;为研究大脑活动提供了一种高效、生态效度高的方法。然而&#xff0c;为了充分利用INIRS技术并确保实验结果的准确性和可靠性&#xff0c;研究者必须精心设计实…

【密码学】流密码的基本概念

在介绍流密码之前&#xff0c;我们先来弄明白一个基础前置知识点——异或运算。 一、异或运算&#xff08;XOR&#xff09; 运算规则&#xff1a;相同为0&#xff0c;不同为1 特点&#xff1a;一个比特进行两次异或运算&#xff0c;可以恢复成原来的比特。 明文&#xff1a;110…

【沐风老师】3DMAX建筑体块生成插件BuildingBlocks使用方法详解

BuildingBlocks建筑体块生成插件使用方法详解 听说你还在手动建配景楼&#xff1f;有了BuildingBlocks这个插件&#xff0c;一分钟搞定喔&#xff01; 3DMAX建筑体块生成插件BuildingBlocks&#xff0c;用于快速自定义街道及生成配景楼区块。 【适用版本】 3dMax2019及更高版…

DataExcelServer局域网文件共享服务器增加两个函数

1、PFSUM合并指定路径下单元格ID的值 PFSUM("/103采购/8月采购名细","amount") 第一个参数为路径&#xff0c;第二个参数为单元格的ID 2、PFQuery 查询路径下 单元格ID值的列表 PFQuery("/103采购/8月采购名细","amount") 查询/103采…

LLM- 注意力机制

一&#xff1a;什么是注意力机制&#xff0c;以及产生背景&#xff1f; &#xff08;1&#xff09;&#xff1a;RNN模型[RNN模型]的缺点&#xff1a;下图是例如RNN模型解决机器翻译的例子&#xff0c;从这个例子可以看到Encoder最后一个向量&#xff08;eos&#xff09;送给了…

经常用借呗和花呗对征信有影响吗?

说起支付宝里的花呗和借呗&#xff0c;大伙儿肯定都不陌生&#xff0c;它们俩就像是支付宝里的信用贷款双胞胎&#xff0c;名字相近&#xff0c;性格却大相径庭。现在&#xff0c;这俩兄弟都乖乖地接入了央行的征信大家庭&#xff0c;你的每一次使用&#xff0c;都会被记录得清…

Oracle AWR报告快速分析工具

一、背景 详细大家都遇到过需要分析Oracle AWR报告的场景&#xff0c;分析AWR对于专业DBA不是什么问题&#xff0c;但是对于一些业务后台研发确实有些困难&#xff0c;很多业务研发人员看的就是条目太多&#xff0c;无从下手。 不过最近我在使用墨天轮浏览国产信创数据库时&am…

红外光气体检测:1.分子振动与红外吸收、检测系统的基本模型和红外敏感元件

分子振动与红外吸收 分子偶极矩的变化频率与分子内原子振动状态有关&#xff1a;μqd&#xff0c;其中μ是偶极矩&#xff0c;q是电荷&#xff0c;d是正负电荷中心距离。 分子在…

【AI大模型】赋能儿童安全:楼层与室内定位实践与未来发展

文章目录 引言第一章&#xff1a;AI与室内定位技术1.1 AI技术概述1.2 室内定位技术概述1.3 楼层定位的挑战与解决方案 第二章&#xff1a;儿童定位与安全监控的需求2.1 儿童安全问题的现状2.2 智能穿戴设备的兴起 第三章&#xff1a;技术实现细节3.1 硬件设计与选择传感器选择与…

如何在Excel中对一个或多个条件求和?

在Excel中&#xff0c;基于一个或多个条件的求和值是我们大多数人的常见任务&#xff0c;SUMIF函数可以帮助我们根据一个条件快速求和&#xff0c;而SUMIFS函数可以帮助我们对多个条件求和。 本文&#xff0c;我将描述如何在Excel中对一个或多个条件求和&#xff1f; 在Excel中…

【详细教程】PowerDesigner导出表结构word文档

&#x1f4d6;【详细教程】PowerDesigner导出表结构word文档 ✅第一步&#xff1a;新建报告✅第二步&#xff1a;配置导出的参数✅第三步&#xff1a;导出 ✅第一步&#xff1a;新建报告 ✅第二步&#xff1a;配置导出的参数 如果你只需要导出纯粹的表结构&#xff0c;那么下面…

html——VSCode的使用

快捷键 快速生成标签&#xff1a;标签名tab 保存文件&#xff1a;CtrlS 设置自动保存【文件】→【自动保存】 快速查看网页效果&#xff1a;右击→Open in Default Browser 快捷键&#xff1a;altb 注意&#xff1a;必须安装了open in brows…

【C语言】return 关键字详解

在C语言中&#xff0c;return是一个关键字&#xff0c;用于从函数中返回值或者结束函数的执行。它是函数的重要组成部分&#xff0c;负责将函数的计算结果返回给调用者&#xff0c;并可以提前终止函数的执行。 主要用途和原理&#xff1a; 返回值给调用者&#xff1a; 当函数执…

YOLOv10改进 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv10的重大改进&#xff0c;特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体&#xff0c;如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU&#xff0c;还融合了“Focus”思想&#xff0c;创造了一系列新的损失函数。这些组合形式的…

充分利用视觉信息多问多答合成数据,提升多模态大模型数学推理能力

©PaperWeekly 原创 作者 | 史文浩 单位 | 电子科技大学 论文题目&#xff1a; Math-LLaVA: Bootstrapping Mathematical Reasoning for Multimodal Large Language Models 论文链接&#xff1a; https://arxiv.org/abs/2406.17294 开源链接&#xff1a; https://github.c…

鸿蒙开发HarmonyOS NEXT (三) 熟悉ArkTs (上)

一、自定义组件 1、自定义组件 自定义组件&#xff0c;最基础的结构如下&#xff1a; Component struct Header {build() {} } 提取头部标题部分的代码&#xff0c;写成自定义组件。 1、新建ArkTs文件&#xff0c;把Header内容写好。 2、在需要用到的地方&#xff0c;导入…