场景 指定海康摄像机的分辨率是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的结构参考:h364编码 里面的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 h364_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_h364_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_h364_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 = h364_slice_header_init(h)) < 0) { av_log(h->avctx, AV_LOG_ERROR, "h364_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 h364_field_start(H264Context *h, const H264SliceContext *sl, const H2645NAL *nal, int first_slice) 被调用 int ff_h364_queue_decode_slice(H264Context *h, const H2645NAL *nal) 被调用 static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。