X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fh265_metadata_bsf.c;h=d841839762b7aa15e3ee0d8e7619af3020b07569;hb=e83717e63eab1f1b78dc0990e5b8e927097fca29;hp=0683cc2f9d5207f20b6e689ca780d5a85d4bd72b;hpb=ace96d2e693d804a0ed16aebc1b1027cfff2c527;p=ffmpeg diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c index 0683cc2f9d5..d841839762b 100644 --- a/libavcodec/h265_metadata_bsf.c +++ b/libavcodec/h265_metadata_bsf.c @@ -21,20 +21,18 @@ #include "bsf.h" #include "cbs.h" +#include "cbs_bsf.h" #include "cbs_h265.h" #include "hevc.h" +#include "h265_profile_level.h" enum { - PASS, - INSERT, - REMOVE, + LEVEL_UNSET = -2, + LEVEL_AUTO = -1, }; typedef struct H265MetadataContext { - const AVClass *class; - - CodedBitstreamContext *cbc; - CodedBitstreamFragment access_unit; + CBSBSFContext common; H265RawAUD aud_nal; @@ -58,9 +56,105 @@ typedef struct H265MetadataContext { int crop_right; int crop_top; int crop_bottom; + + int level; + int level_guess; + int level_warned; } H265MetadataContext; +static void h265_metadata_guess_level(AVBSFContext *bsf, + const CodedBitstreamFragment *au) +{ + H265MetadataContext *ctx = bsf->priv_data; + const H265LevelDescriptor *desc; + const H265RawProfileTierLevel *ptl = NULL; + const H265RawHRDParameters *hrd = NULL; + int64_t bit_rate = 0; + int width = 0, height = 0; + int tile_cols = 0, tile_rows = 0; + int max_dec_pic_buffering = 0; + int i; + + for (i = 0; i < au->nb_units; i++) { + const CodedBitstreamUnit *unit = &au->units[i]; + + if (unit->type == HEVC_NAL_VPS) { + const H265RawVPS *vps = unit->content; + + ptl = &vps->profile_tier_level; + max_dec_pic_buffering = vps->vps_max_dec_pic_buffering_minus1[0] + 1; + + if (vps->vps_num_hrd_parameters > 0) + hrd = &vps->hrd_parameters[0]; + + } else if (unit->type == HEVC_NAL_SPS) { + const H265RawSPS *sps = unit->content; + + ptl = &sps->profile_tier_level; + max_dec_pic_buffering = sps->sps_max_dec_pic_buffering_minus1[0] + 1; + + width = sps->pic_width_in_luma_samples; + height = sps->pic_height_in_luma_samples; + + if (sps->vui.vui_hrd_parameters_present_flag) + hrd = &sps->vui.hrd_parameters; + + } else if (unit->type == HEVC_NAL_PPS) { + const H265RawPPS *pps = unit->content; + + if (pps->tiles_enabled_flag) { + tile_cols = pps->num_tile_columns_minus1 + 1; + tile_rows = pps->num_tile_rows_minus1 + 1; + } + } + } + + if (hrd) { + if (hrd->nal_hrd_parameters_present_flag) { + bit_rate = (hrd->nal_sub_layer_hrd_parameters[0].bit_rate_value_minus1[0] + 1) * + (INT64_C(1) << hrd->bit_rate_scale + 6); + } else if (hrd->vcl_hrd_parameters_present_flag) { + bit_rate = (hrd->vcl_sub_layer_hrd_parameters[0].bit_rate_value_minus1[0] + 1) * + (INT64_C(1) << hrd->bit_rate_scale + 6); + // Adjust for VCL vs. NAL limits. + bit_rate = bit_rate * 11 / 10; + } + } + + desc = ff_h265_guess_level(ptl, bit_rate, width, height, + 0, tile_rows, tile_cols, + max_dec_pic_buffering); + if (desc) { + av_log(bsf, AV_LOG_DEBUG, "Stream appears to conform to " + "level %s.\n", desc->name); + ctx->level_guess = desc->level_idc; + } +} + +static void h265_metadata_update_level(AVBSFContext *bsf, + uint8_t *level_idc) +{ + H265MetadataContext *ctx = bsf->priv_data; + + if (ctx->level != LEVEL_UNSET) { + if (ctx->level == LEVEL_AUTO) { + if (ctx->level_guess) { + *level_idc = ctx->level_guess; + } else { + if (!ctx->level_warned) { + av_log(bsf, AV_LOG_WARNING, "Unable to determine level " + "of stream: using level 8.5.\n"); + ctx->level_warned = 1; + } + *level_idc = 255; + } + } else { + *level_idc = ctx->level; + } + } +} + static int h265_metadata_update_vps(AVBSFContext *bsf, H265RawVPS *vps) { @@ -86,6 +180,8 @@ static int h265_metadata_update_vps(AVBSFContext *bsf, } } + h265_metadata_update_level(bsf, &vps->profile_tier_level.general_level_idc); + return 0; } @@ -227,38 +323,23 @@ static int h265_metadata_update_sps(AVBSFContext *bsf, if (need_vui) sps->vui_parameters_present_flag = 1; + h265_metadata_update_level(bsf, &sps->profile_tier_level.general_level_idc); + return 0; } -static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *out) +static int h265_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt, + CodedBitstreamFragment *au) { H265MetadataContext *ctx = bsf->priv_data; - AVPacket *in = NULL; - CodedBitstreamFragment *au = &ctx->access_unit; int err, i; - err = ff_bsf_get_packet(bsf, &in); - if (err < 0) - return err; - - err = ff_cbs_read_packet(ctx->cbc, au, in); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); - goto fail; - } - - if (au->nb_units == 0) { - av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); - err = AVERROR_INVALIDDATA; - goto fail; - } - // If an AUD is present, it must be the first NAL unit. - if (au->units[0].type == HEVC_NAL_AUD) { - if (ctx->aud == REMOVE) - ff_cbs_delete_unit(ctx->cbc, au, 0); + if (au->nb_units && au->units[0].type == HEVC_NAL_AUD) { + if (ctx->aud == BSF_ELEMENT_REMOVE) + ff_cbs_delete_unit(au, 0); } else { - if (ctx->aud == INSERT) { + if (pkt && ctx->aud == BSF_ELEMENT_INSERT) { H265RawAUD *aud = &ctx->aud_nal; int pic_type = 0, temporal_id = 8, layer_id = 0; @@ -288,112 +369,50 @@ static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *out) }; aud->pic_type = pic_type; - err = ff_cbs_insert_unit_content(ctx->cbc, au, - 0, HEVC_NAL_AUD, aud, NULL); - if (err) { + err = ff_cbs_insert_unit_content(au, 0, HEVC_NAL_AUD, aud, NULL); + if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); - goto fail; + return err; } } } + if (ctx->level == LEVEL_AUTO && !ctx->level_guess) + h265_metadata_guess_level(bsf, au); + for (i = 0; i < au->nb_units; i++) { if (au->units[i].type == HEVC_NAL_VPS) { err = h265_metadata_update_vps(bsf, au->units[i].content); if (err < 0) - goto fail; + return err; } if (au->units[i].type == HEVC_NAL_SPS) { err = h265_metadata_update_sps(bsf, au->units[i].content); if (err < 0) - goto fail; + return err; } } - err = ff_cbs_write_packet(ctx->cbc, out, au); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); - goto fail; - } - - err = av_packet_copy_props(out, in); - if (err < 0) - goto fail; - - err = 0; -fail: - ff_cbs_fragment_reset(ctx->cbc, au); - - if (err < 0) - av_packet_unref(out); - av_packet_free(&in); - - return err; + return 0; } -static int h265_metadata_init(AVBSFContext *bsf) -{ - H265MetadataContext *ctx = bsf->priv_data; - CodedBitstreamFragment *au = &ctx->access_unit; - int err, i; - - err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_HEVC, bsf); - if (err < 0) - return err; - - if (bsf->par_in->extradata) { - err = ff_cbs_read_extradata(ctx->cbc, au, bsf->par_in); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); - goto fail; - } - - for (i = 0; i < au->nb_units; i++) { - if (au->units[i].type == HEVC_NAL_VPS) { - err = h265_metadata_update_vps(bsf, au->units[i].content); - if (err < 0) - goto fail; - } - if (au->units[i].type == HEVC_NAL_SPS) { - err = h265_metadata_update_sps(bsf, au->units[i].content); - if (err < 0) - goto fail; - } - } - - err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, au); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); - goto fail; - } - } - - err = 0; -fail: - ff_cbs_fragment_reset(ctx->cbc, au); - return err; -} +static const CBSBSFType h265_metadata_type = { + .codec_id = AV_CODEC_ID_HEVC, + .fragment_name = "access unit", + .unit_name = "NAL unit", + .update_fragment = &h265_metadata_update_fragment, +}; -static void h265_metadata_close(AVBSFContext *bsf) +static int h265_metadata_init(AVBSFContext *bsf) { - H265MetadataContext *ctx = bsf->priv_data; - - ff_cbs_fragment_free(ctx->cbc, &ctx->access_unit); - ff_cbs_close(&ctx->cbc); + return ff_cbs_bsf_generic_init(bsf, &h265_metadata_type); } #define OFFSET(x) offsetof(H265MetadataContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) static const AVOption h265_metadata_options[] = { - { "aud", "Access Unit Delimiter NAL units", - OFFSET(aud), AV_OPT_TYPE_INT, - { .i64 = PASS }, PASS, REMOVE, FLAGS, "aud" }, - { "pass", NULL, 0, AV_OPT_TYPE_CONST, - { .i64 = PASS }, .flags = FLAGS, .unit = "aud" }, - { "insert", NULL, 0, AV_OPT_TYPE_CONST, - { .i64 = INSERT }, .flags = FLAGS, .unit = "aud" }, - { "remove", NULL, 0, AV_OPT_TYPE_CONST, - { .i64 = REMOVE }, .flags = FLAGS, .unit = "aud" }, + BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units", + aud, FLAGS), { "sample_aspect_ratio", "Set sample aspect ratio (table E-1)", OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, @@ -441,6 +460,30 @@ static const AVOption h265_metadata_options[] = { OFFSET(crop_bottom), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS }, + { "level", "Set level (tables A.6 and A.7)", + OFFSET(level), AV_OPT_TYPE_INT, + { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" }, + { "auto", "Attempt to guess level from stream properties", + 0, AV_OPT_TYPE_CONST, + { .i64 = LEVEL_AUTO }, .flags = FLAGS, .unit = "level" }, +#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = value }, .flags = FLAGS, .unit = "level" + { LEVEL("1", 30) }, + { LEVEL("2", 60) }, + { LEVEL("2.1", 63) }, + { LEVEL("3", 90) }, + { LEVEL("3.1", 93) }, + { LEVEL("4", 120) }, + { LEVEL("4.1", 123) }, + { LEVEL("5", 150) }, + { LEVEL("5.1", 153) }, + { LEVEL("5.2", 156) }, + { LEVEL("6", 180) }, + { LEVEL("6.1", 183) }, + { LEVEL("6.2", 186) }, + { LEVEL("8.5", 255) }, +#undef LEVEL + { NULL } }; @@ -460,7 +503,7 @@ const AVBitStreamFilter ff_hevc_metadata_bsf = { .priv_data_size = sizeof(H265MetadataContext), .priv_class = &h265_metadata_class, .init = &h265_metadata_init, - .close = &h265_metadata_close, - .filter = &h265_metadata_filter, + .close = &ff_cbs_bsf_generic_close, + .filter = &ff_cbs_bsf_generic_filter, .codec_ids = h265_metadata_codec_ids, };