美文网首页iOS开发笔记
从H264/H265码流sps中获取宽、高及帧率

从H264/H265码流sps中获取宽、高及帧率

作者: 苦境名人 | 来源:发表于2018-04-25 16:47 被阅读1279次

    在做码流分析时,图像分辨率、帧率这类的基本信息,当然不可少。本文介绍如何从NAL中计算到图像宽、高,还有分辨率。由于H264和H265有相似性,就在一起写了。

    一、从码流获得宽、高

    1、H264

    宽高可从SPS字段计算得到,公式如下:
    Width = (pic_width_in_mbs_minus1+1)16;
    Height = (pic_height_in_map_units_minus1+1)
    16;

    但以上是针对宽高是16的整数倍的情况,当不是16整数倍时,frame_cropping_flag值为1,frame_mbs_only_flag为1,公式如下:
    (也可以认为统一使用下面的公式)

    1.  width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2;  
    2.  height= ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);  
    

    比如一个1080P视频的SPS信息如下:

    pic_width_in_mbs_minus1 : 119
    pic_height_in_map_units_minus1 : 67 
    frame_mbs_only_flag : 1
    mb_adaptive_frame_field_flag : 0
    direct_8x8_inference_flag : 1
    frame_cropping_flag : 1
       frame_crop_left_offset : 0
       frame_crop_right_offset : 0
       frame_crop_top_offset : 0
       frame_crop_bottom_offset : 4
    

    根据第二个公式

    width = (119+1) * 16 - 0*2 - 0*2 = 1920
    height = (2-1) * (67+1)*16 - 0*2 - 4*2 = 1088 - 8 = 1080
    

    以上公式是一年多以前在网上找的,仔细看手册,上面的公式有局限性。根据H264手册Table6-1及7.4.2.1.1,参考mkvtoolnix代码,比如稳妥的计算方法如下:

    // 宽高计算公式
        width  = (sps->pic_width_in_mbs_minus1+1) * 16;
        height = (2 - sps->frame_mbs_only_flag)* (sps->pic_height_in_map_units_minus1 +1) * 16);
        
        if(sps->frame_cropping_flag)
        {
            unsigned int crop_unit_x;
            unsigned int crop_unit_y;
            if (0 == sps->chroma_format_idc) // monochrome
            {
                crop_unit_x = 1;
                crop_unit_y = 2 - sps->frame_mbs_only_flag;
            }
            else if (1 == sps->chroma_format_idc) // 4:2:0
            {
                crop_unit_x = 2;
                crop_unit_y = 2 * (2 - sps->frame_mbs_only_flag);
            }
            else if (2 == sps->chroma_format_idc) // 4:2:2
            {
                crop_unit_x = 2;
                crop_unit_y = 2 - sps->frame_mbs_only_flag;
            }
            else // 3 == sps.chroma_format_idc   // 4:4:4
            {
                crop_unit_x = 1;
                crop_unit_y = 2 - sps->frame_mbs_only_flag;
            }
            
            width  -= crop_unit_x * (sps->frame_crop_left_offset + sps->frame_crop_right_offset);
            height -= crop_unit_y * (sps->frame_crop_top_offset  + sps->frame_crop_bottom_offset);
        }
    

    注:
    一些H264分析工具,比如CodecVisa和H264Visa,1080P的视频,分辨率为1920x1088。这是不正确的。
    此外可以参考ffmpeg的h264_ps.c

        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;
        }
    

    图像的宽和高计算:

                s->coded_width  = 16 * sps->mb_width;
                s->coded_height = 16 * sps->mb_height;
                s->width        = s->coded_width  - (sps->crop_right + sps->crop_left);
                s->height       = s->coded_height - (sps->crop_top   + sps->crop_bottom);
    

    2、H265

    H.265类似,但SPS的字段不同了。公式如下:

    width  = sps->pic_width_in_luma_samples;
    height = sps->pic_height_in_luma_samples;
    

    当窗口有裁剪时(conformance_window_flag为1),计算如下:

    sub_width_c  = ((1==chroma_format_idc)||(2 == chroma_format_idc))&&(0==separate_colour_plane_flag)?2:1;
    sub_height_c = (1==chroma_format_idc)&& (0 == separate_colour_plane_flag)?2:1;
    width  -= (sub_width_c*conf_win_right_offset + sub_width_c*conf_win_left_offset);
    height -= (sub_height_c*conf_win_bottom_offset + sub_height_c*conf_win_top_offset);
    

    上式根据H265手册Table6-1及7.4.3.2.1小节计算宽、高。注意,手册里加了1,但实际不使用。
    参考mkvtoolnix讨论:https://github.com/mbunkus/mkvtoolnix/issues/1152
    注:对于1080P的视频,H265直接用pic_width_in_luma_samples及pic_height_in_luma_samples即得到正确的值。但对于一些奇葩的分辨率,还没有测试过。

    三、帧率

    H264和H265帧率计算公式相同,如下:

    max_framerate = (float)(sps->vui.vui_time_scale) / (float)(sps->vui.vui_num_units_in_tick);
    

    使用x264编码YUV序列,设置为25fps时,time_scale为50,num_units_in_tick为1,计算得50fps,与实际不符。而x265用同样的参数编码,计算得到的帧率是正常的。

    网上有说法,当nuit_field_based_flag为1时,要再除以2。另外说x264将该值设置为0,所以得到的值不是实际值。参见:http://forum.doom9.org/showthread.php?t=153019

    转载:https://blog.csdn.net/subfate/article/details/48576445

    相关文章

      网友评论

        本文标题:从H264/H265码流sps中获取宽、高及帧率

        本文链接:https://www.haomeiwen.com/subject/lqyylftx.html