=================================================================
音视频入门基础:H.264专题系列文章:
音视频入门基础:H.264专题(1)——H.264官方文档下载
音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件
音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB
音视频入门基础:H.264专题(4)——NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介
音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析
音视频入门基础:H.264专题(6)——FFmpeg源码:从H.264码流中提取NALU Header、EBSP、RBSP和SODB
音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现
音视频入门基础:H.264专题(8)——H.264官方文档的描述符
音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析
音视频入门基础:H.264专题(11)——计算视频分辨率的公式
音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现
音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现
音视频入门基础:H.264专题(14)——计算视频帧率的公式
音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现
音视频入门基础:H.264专题(16)——FFmpeg源码中,判断某文件是否为H.264裸流文件的实现
音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程
=================================================================
在上一节《音视频入门基础:H.264专题(11)——计算视频分辨率的公式》中,讲述了通过SPS中的属性计算H.264编码的视频的分辨率的公式。本文讲解FFmpeg源码中计算视频分辨率的实现。
从文章《音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析》中,我们可以知道,FFmpeg源码中通过ff_h264_decode_seq_parameter_set函数解码SPS,从而拿到SPS中的属性。
在ff_h264_decode_seq_parameter_set函数中有如下代码,通过下面的这部分代码拿到计算视频分辨率所需的属性:
- int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
- H264ParamSets *ps, int ignore_truncation)
- {
- //...
-
- 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;
-
- //...
-
- 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;
- }
-
- //...
- }
然后在FFmpeg源码的源文件libavcodec/h264_parser.c的parse_nal_units函数中,有如下代码:
- static inline int parse_nal_units(AVCodecParserContext *s,
- AVCodecContext *avctx,
- const uint8_t * const buf, int buf_size)
- {
- //...
-
- for (;;) {
- switch (nal.type) {
- case H264_NAL_SPS:
- ff_h264_decode_seq_parameter_set(&nal.gb, avctx, &p->ps, 0);
- break;
-
- //...
-
- case H264_NAL_IDR_SLICE:
-
- //...
-
- 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);
- if (s->width <= 0 || s->height <= 0) {
- s->width = s->coded_width;
- s->height = s->coded_height;
- }
- //...
- }
- //...
- }
- }
可以看到parse_nal_units函数中最终是通过下面的语句拿到视频分辨率的:
- s->width = s->coded_width - (sps->crop_right + sps->crop_left);
- s->height = s->coded_height - (sps->crop_top + sps->crop_bottom);
可以看到FFmpeg源码中计算视频分辨率的实现跟文章《音视频入门基础:H.264专题(11)——计算视频分辨率的公式》中描述的公式是一致的。