博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FFmpeg Reinit context to 1920x1088问题描述
阅读量:6295 次
发布时间:2019-06-22

本文共 4825 字,大约阅读时间需要 16 分钟。

场景

    指定海康摄像机的分辨率是1920*1080P,I帧间隔是25,视频帧率是25fps。查看AVFormatContext结构体中streams数组中的第一个元素codec

    codec_id=AV_CODEC_ID_H264, width=1920, height=1080, coded_with=1920, coded_height=1088, pix_fmt=AV_PIX_FMT_YUVJ420P


疑问

    coded_height为什么不是1080


解答

当前SPS的帧的宽 = (sps_info.pic_width_in_mbs_minus1 + 1) * 16

当前SPS的帧的高 = (sps_info.pic_height_in_map_units_minus1 + 1) * 16

通过如下代码计算得到的宽高(1888 x 1920)却不完全等同于源视频的宽高(1920 x 1080)。

根据编码规则计算到的宽高是1920x1088,而MP4中读取的是1920x1080。那么,如何FFmpeg是如何修正SPS里的宽高计算呢?

实际上,SPS还有crop系列字段,由crop表示帧是否被裁剪、crop_left、crop_right、crop_top和crop_bottom表示要裁剪的值得到正确的宽高值。



相关知识

sps和pps的结构参考:h264编码 里面的Sequence parameter set RBSP syntax

0x00000001或者0x000001是起始码,0x67是sps的开头,0x68是pps的开头。

0x42代表profile_idc,后面八位是constraint_set0_flag和reserved_zero_4bits,都设为0,0x0a是level_idc,

接着后面为图方便能用0表示的都用了。这里要注意是ue(v)表示该域是可变位,使用的指数-哥伦布编码 

我的目的主要是设置正确帧高度和帧高度,所以只要填充   pic_width_in_mbs_minus1和  pic_height_in_map_units_minus1,

将它们的十六进制数写入sps_pps,

如果宽度和高度不是16的倍数,要填frame_cropping_flag


参考

http://m.blog.csdn.net/stn_lcd/article/details/74390636

http://m.blog.csdn.net/leixiaohua1020/article/details/45001033


static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_slice)

{

    const SPS *sps;

    int needs_reinit = 0, must_reinit, ret;


    if (first_slice) {

        av_buffer_unref(&h->ps.pps_ref);

        h->ps.pps = NULL;

        h->ps.pps_ref = av_buffer_ref(h->ps.pps_list[sl->pps_id]);

        if (!h->ps.pps_ref)

            return AVERROR(ENOMEM);

        h->ps.pps = (const PPS*)h->ps.pps_ref->data;

    }


    if (h->ps.sps != (const SPS*)h->ps.sps_list[h->ps.pps->sps_id]->data) {

        av_buffer_unref(&h->ps.sps_ref);

        h->ps.sps = NULL;

        h->ps.sps_ref = av_buffer_ref(h->ps.sps_list[h->ps.pps->sps_id]);

        if (!h->ps.sps_ref)

            return AVERROR(ENOMEM);

        h->ps.sps = (const SPS*)h->ps.sps_ref->data;


        if (h->mb_width  != h->ps.sps->mb_width ||

            h->mb_height != h->ps.sps->mb_height ||

            h->cur_bit_depth_luma    != h->ps.sps->bit_depth_luma ||

            h->cur_chroma_format_idc != h->ps.sps->chroma_format_idc

        )

            needs_reinit = 1;


        if (h->bit_depth_luma    != h->ps.sps->bit_depth_luma ||

            h->chroma_format_idc != h->ps.sps->chroma_format_idc)

            needs_reinit         = 1;

    }

    sps = h->ps.sps;


    must_reinit = (h->context_initialized &&

                    (   16*sps->mb_width != h->avctx->coded_width

                     || 16*sps->mb_height != h->avctx->coded_height

                     || h->cur_bit_depth_luma    != sps->bit_depth_luma

                     || h->cur_chroma_format_idc != sps->chroma_format_idc

                     || h->mb_width  != sps->mb_width

                     || h->mb_height != sps->mb_height

                    ));

    if (h->avctx->pix_fmt == AV_PIX_FMT_NONE

        || (non_j_pixfmt(h->avctx->pix_fmt) != non_j_pixfmt(get_pixel_format(h, 0))))

        must_reinit = 1;


    if (first_slice && av_cmp_q(sps->sar, h->avctx->sample_aspect_ratio))

        must_reinit = 1;


    if (!h->setup_finished) {

        h->avctx->profile = ff_h264_get_profile(sps);

        h->avctx->level   = sps->level_idc;

        h->avctx->refs    = sps->ref_frame_count;


        h->mb_width  = sps->mb_width;

        h->mb_height = sps->mb_height;

        h->mb_num    = h->mb_width * h->mb_height;

        h->mb_stride = h->mb_width + 1;


        h->b_stride = h->mb_width * 4;


        h->chroma_y_shift = sps->chroma_format_idc <= 1; // 400 uses yuv420p


        h->width  = 16 * h->mb_width;

        h->height = 16 * h->mb_height;


        ret = init_dimensions(h);

        if (ret < 0)

            return ret;


        if (sps->video_signal_type_present_flag) {

            h->avctx->color_range = sps->full_range > 0 ? AVCOL_RANGE_JPEG

                                                        : AVCOL_RANGE_MPEG;

            if (sps->colour_description_present_flag) {

                if (h->avctx->colorspace != sps->colorspace)

                    needs_reinit = 1;

                h->avctx->color_primaries = sps->color_primaries;

                h->avctx->color_trc       = sps->color_trc;

                h->avctx->colorspace      = sps->colorspace;

            }

        }

    }


    if (!h->context_initialized || must_reinit || needs_reinit) {

        int flush_changes = h->context_initialized;

        h->context_initialized = 0;

        if (sl != h->slice_ctx) {

            av_log(h->avctx, AV_LOG_ERROR,

                   "changing width %d -> %d / height %d -> %d on "

                   "slice %d\n",

                   h->width, h->avctx->coded_width,

                   h->height, h->avctx->coded_height,

                   h->current_slice + 1);

            return AVERROR_INVALIDDATA;

        }


        av_assert1(first_slice);


        if (flush_changes)

            ff_h264_flush_change(h);


        if ((ret = get_pixel_format(h, 1)) < 0)

            return ret;

        h->avctx->pix_fmt = ret;


        av_log(h->avctx, AV_LOG_VERBOSE, "Reinit context to %dx%d, "

               "pix_fmt: %s\n", h->width, h->height, av_get_pix_fmt_name(h->avctx->pix_fmt));


        if ((ret = h264_slice_header_init(h)) < 0) {

            av_log(h->avctx, AV_LOG_ERROR,

                   "h264_slice_header_init() failed\n");

            return ret;

        }

    }


    return 0;

}


/* This function is called right after decoding the slice header for a first

 * slice in a field (or a frame). It decides whether we are decoding a new frame

 * or a second field in a pair and does the necessary setup.

 */

static int h264_field_start(H264Context *h, const H264SliceContext *sl, const H2645NAL *nal, int first_slice)

被调用

int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)

被调用

static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)

     本文转自fengyuzaitu 51CTO博客,原文链接:http://blog.51cto.com/fengyuzaitu/2055607,如需转载请自行联系原作者

你可能感兴趣的文章
第十三章 RememberMe——《跟我学Shiro》
查看>>
mysql 时间函数 时间戳转为日期
查看>>
索引失效 ORA-01502
查看>>
Oracle取月份,不带前面的0
查看>>
Linux Network Device Name issue
查看>>
IP地址的划分实例解答
查看>>
如何查看Linux命令源码
查看>>
运维基础命令
查看>>
入门到进阶React
查看>>
SVN 命令笔记
查看>>
检验手机号码
查看>>
重叠(Overlapped)IO模型
查看>>
Git使用教程
查看>>
使用shell脚本自动监控后台进程,并能自动重启
查看>>
Flex&Bison手册
查看>>
solrCloud+tomcat+zookeeper集群配置
查看>>
/etc/fstab,/etc/mtab,和 /proc/mounts
查看>>
Apache kafka 简介
查看>>
socket通信Demo
查看>>
技术人员的焦虑
查看>>