X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fh264_metadata_bsf.c;h=eb1503159b275d6e62bb030c64988df7c320c749;hb=53a4620fbae20b640d7fd75a64a6d3ff6dcc8b43;hp=90ad4aad98b205302b7c853971beba83bfb84322;hpb=03210fe138f3b3bd7f5272fe29aca810cf517329;p=ffmpeg diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c index 90ad4aad98b..eb1503159b2 100644 --- a/libavcodec/h264_metadata_bsf.c +++ b/libavcodec/h264_metadata_bsf.c @@ -22,9 +22,11 @@ #include "libavutil/opt.h" #include "bsf.h" +#include "bsf_internal.h" #include "cbs.h" #include "cbs_h264.h" #include "h264.h" +#include "h264_levels.h" #include "h264_sei.h" enum { @@ -39,10 +41,16 @@ enum { FLIP_VERTICAL = 2, }; +enum { + LEVEL_UNSET = -2, + LEVEL_AUTO = -1, +}; + typedef struct H264MetadataContext { const AVClass *class; - CodedBitstreamContext *cbc; + CodedBitstreamContext *input; + CodedBitstreamContext *output; CodedBitstreamFragment access_unit; int done_first_au; @@ -51,6 +59,8 @@ typedef struct H264MetadataContext { AVRational sample_aspect_ratio; + int overscan_appropriate_flag; + int video_format; int video_full_range_flag; int colour_primaries; @@ -74,6 +84,8 @@ typedef struct H264MetadataContext { int display_orientation; double rotate; int flip; + + int level; } H264MetadataContext; @@ -114,47 +126,39 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, need_vui = 1; } -#define SET_OR_INFER(field, value, present_flag, infer) do { \ - if (value >= 0) { \ - field = value; \ +#define SET_VUI_FIELD(field) do { \ + if (ctx->field >= 0) { \ + sps->vui.field = ctx->field; \ need_vui = 1; \ - } else if (!present_flag) \ - field = infer; \ + } \ } while (0) + if (ctx->overscan_appropriate_flag >= 0) { + SET_VUI_FIELD(overscan_appropriate_flag); + sps->vui.overscan_info_present_flag = 1; + } + if (ctx->video_format >= 0 || ctx->video_full_range_flag >= 0 || ctx->colour_primaries >= 0 || ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0) { - SET_OR_INFER(sps->vui.video_format, ctx->video_format, - sps->vui.video_signal_type_present_flag, 5); + SET_VUI_FIELD(video_format); - SET_OR_INFER(sps->vui.video_full_range_flag, - ctx->video_full_range_flag, - sps->vui.video_signal_type_present_flag, 0); + SET_VUI_FIELD(video_full_range_flag); if (ctx->colour_primaries >= 0 || ctx->transfer_characteristics >= 0 || ctx->matrix_coefficients >= 0) { - SET_OR_INFER(sps->vui.colour_primaries, - ctx->colour_primaries, - sps->vui.colour_description_present_flag, 2); - - SET_OR_INFER(sps->vui.transfer_characteristics, - ctx->transfer_characteristics, - sps->vui.colour_description_present_flag, 2); - - SET_OR_INFER(sps->vui.matrix_coefficients, - ctx->matrix_coefficients, - sps->vui.colour_description_present_flag, 2); + SET_VUI_FIELD(colour_primaries); + SET_VUI_FIELD(transfer_characteristics); + SET_VUI_FIELD(matrix_coefficients); sps->vui.colour_description_present_flag = 1; } sps->vui.video_signal_type_present_flag = 1; - need_vui = 1; } if (ctx->chroma_sample_loc_type >= 0) { @@ -178,9 +182,7 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, sps->vui.timing_info_present_flag = 1; need_vui = 1; } - SET_OR_INFER(sps->vui.fixed_frame_rate_flag, - ctx->fixed_frame_rate_flag, - sps->vui.timing_info_present_flag, 0); + SET_VUI_FIELD(fixed_frame_rate_flag); if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) { crop_unit_x = 1; @@ -208,27 +210,132 @@ static int h264_metadata_update_sps(AVBSFContext *bsf, CROP(bottom, crop_unit_y); #undef CROP + if (ctx->level != LEVEL_UNSET) { + int level_idc; + + if (ctx->level == LEVEL_AUTO) { + const H264LevelDescriptor *desc; + int64_t bit_rate; + int width, height, dpb_frames; + int framerate; + + if (sps->vui.nal_hrd_parameters_present_flag) { + bit_rate = (sps->vui.nal_hrd_parameters.bit_rate_value_minus1[0] + 1) * + (INT64_C(1) << (sps->vui.nal_hrd_parameters.bit_rate_scale + 6)); + } else if (sps->vui.vcl_hrd_parameters_present_flag) { + bit_rate = (sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] + 1) * + (INT64_C(1) << (sps->vui.vcl_hrd_parameters.bit_rate_scale + 6)); + // Adjust for VCL vs. NAL limits. + bit_rate = bit_rate * 6 / 5; + } else { + bit_rate = 0; + } + + // Don't use max_dec_frame_buffering if it is only inferred. + dpb_frames = sps->vui.bitstream_restriction_flag ? + sps->vui.max_dec_frame_buffering : H264_MAX_DPB_FRAMES; + + width = 16 * (sps->pic_width_in_mbs_minus1 + 1); + height = 16 * (sps->pic_height_in_map_units_minus1 + 1) * + (2 - sps->frame_mbs_only_flag); + + if (sps->vui.timing_info_present_flag) + framerate = sps->vui.time_scale / sps->vui.num_units_in_tick / 2; + else + framerate = 0; + + desc = ff_h264_guess_level(sps->profile_idc, bit_rate, framerate, + width, height, dpb_frames); + if (desc) { + level_idc = desc->level_idc; + } else { + av_log(bsf, AV_LOG_WARNING, "Stream does not appear to " + "conform to any level: using level 6.2.\n"); + level_idc = 62; + } + } else { + level_idc = ctx->level; + } + + if (level_idc == 9) { + if (sps->profile_idc == 66 || + sps->profile_idc == 77 || + sps->profile_idc == 88) { + sps->level_idc = 11; + sps->constraint_set3_flag = 1; + } else { + sps->level_idc = 9; + } + } else { + sps->level_idc = level_idc; + } + } + if (need_vui) sps->vui_parameters_present_flag = 1; return 0; } -static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) +static int h264_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt) +{ + H264MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + uint8_t *side_data; + int side_data_size; + int err, i; + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, + &side_data_size); + if (!side_data_size) + return 0; + + err = ff_cbs_read(ctx->input, au, side_data, side_data_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata from packet side data.\n"); + return err; + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SPS) { + err = h264_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + return err; + } + } + + err = ff_cbs_write_fragment_data(ctx->output, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata into packet side data.\n"); + return err; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, au->data_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, au->data, au->data_size); + + ff_cbs_fragment_reset(au); + + return 0; +} + +static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) { H264MetadataContext *ctx = bsf->priv_data; - AVPacket *in = NULL; CodedBitstreamFragment *au = &ctx->access_unit; int err, i, j, has_sps; H264RawAUD aud; - uint8_t *displaymatrix_side_data = NULL; - size_t displaymatrix_side_data_size = 0; - err = ff_bsf_get_packet(bsf, &in); + err = ff_bsf_get_packet_ref(bsf, pkt); if (err < 0) return err; - err = ff_cbs_read_packet(ctx->cbc, au, in); + err = h264_metadata_update_side_data(bsf, pkt); + if (err < 0) + goto fail; + + err = ff_cbs_read_packet(ctx->input, au, pkt); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); goto fail; @@ -243,7 +350,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) // If an AUD is present, it must be the first NAL unit. if (au->units[0].type == H264_NAL_AUD) { if (ctx->aud == REMOVE) - ff_cbs_delete_unit(ctx->cbc, au, 0); + ff_cbs_delete_unit(au, 0); } else { if (ctx->aud == INSERT) { static const int primary_pic_type_table[] = { @@ -284,7 +391,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) .primary_pic_type = j, }; - err = ff_cbs_insert_unit_content(ctx->cbc, au, + err = ff_cbs_insert_unit_content(au, 0, H264_NAL_AUD, &aud, NULL); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); @@ -323,7 +430,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) } else { goto invalid_user_data; } - if (i & 1) + if (j & 1) udu->uuid_iso_iec_11578[j / 2] |= v; else udu->uuid_iso_iec_11578[j / 2] = v << 4; @@ -342,7 +449,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) udu->data_length = len + 1; memcpy(udu->data, ctx->sei_user_data + i + 1, len + 1); - err = ff_cbs_h264_add_sei_message(ctx->cbc, au, &payload); + err = ff_cbs_h264_add_sei_message(au, &payload); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI " "message to access unit.\n"); @@ -359,16 +466,9 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) } if (ctx->delete_filler) { - for (i = 0; i < au->nb_units; i++) { + for (i = au->nb_units - 1; i >= 0; i--) { if (au->units[i].type == H264_NAL_FILLER_DATA) { - // Filler NAL units. - err = ff_cbs_delete_unit(ctx->cbc, au, i); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to delete " - "filler NAL.\n"); - goto fail; - } - --i; + ff_cbs_delete_unit(au, i); continue; } @@ -376,34 +476,23 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) // Filler SEI messages. H264RawSEI *sei = au->units[i].content; - for (j = 0; j < sei->payload_count; j++) { + for (j = sei->payload_count - 1; j >= 0; j--) { if (sei->payload[j].payload_type == - H264_SEI_TYPE_FILLER_PAYLOAD) { - err = ff_cbs_h264_delete_sei_message(ctx->cbc, au, - &au->units[i], j); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to delete " - "filler SEI message.\n"); - goto fail; - } - // Renumbering might have happened, start again at - // the same NAL unit position. - --i; - break; - } + H264_SEI_TYPE_FILLER_PAYLOAD) + ff_cbs_h264_delete_sei_message(au, &au->units[i], j); } } } } if (ctx->display_orientation != PASS) { - for (i = 0; i < au->nb_units; i++) { + for (i = au->nb_units - 1; i >= 0; i--) { H264RawSEI *sei; if (au->units[i].type != H264_NAL_SEI) continue; sei = au->units[i].content; - for (j = 0; j < sei->payload_count; j++) { + for (j = sei->payload_count - 1; j >= 0; j--) { H264RawSEIDisplayOrientation *disp; int32_t *matrix; @@ -414,18 +503,11 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) if (ctx->display_orientation == REMOVE || ctx->display_orientation == INSERT) { - err = ff_cbs_h264_delete_sei_message(ctx->cbc, au, - &au->units[i], j); - if (err < 0) { - av_log(bsf, AV_LOG_ERROR, "Failed to delete " - "display orientation SEI message.\n"); - goto fail; - } - --i; - break; + ff_cbs_h264_delete_sei_message(au, &au->units[i], j); + continue; } - matrix = av_mallocz(9 * sizeof(int32_t)); + matrix = av_malloc(9 * sizeof(int32_t)); if (!matrix) { err = AVERROR(ENOMEM); goto fail; @@ -437,11 +519,17 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip); // If there are multiple display orientation messages in an - // access unit then ignore all but the last one. - av_freep(&displaymatrix_side_data); - - displaymatrix_side_data = (uint8_t*)matrix; - displaymatrix_side_data_size = 9 * sizeof(int32_t); + // access unit, then the last one added to the packet (i.e. + // the first one in the access unit) will prevail. + err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX, + (uint8_t*)matrix, + 9 * sizeof(int32_t)); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted " + "displaymatrix side data to packet.\n"); + av_free(matrix); + goto fail; + } } } } @@ -455,7 +543,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) int size; int write = 0; - data = av_packet_get_side_data(in, AV_PKT_DATA_DISPLAYMATRIX, &size); + data = av_packet_get_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX, &size); if (data && size >= 9 * sizeof(int32_t)) { int32_t matrix[9]; int hflip, vflip; @@ -506,7 +594,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) if (write) { disp->display_orientation_repetition_period = 1; - err = ff_cbs_h264_add_sei_message(ctx->cbc, au, &payload); + err = ff_cbs_h264_add_sei_message(au, &payload); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation " "SEI message to access unit.\n"); @@ -515,38 +603,20 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) } } - err = ff_cbs_write_packet(ctx->cbc, out, au); + err = ff_cbs_write_packet(ctx->output, pkt, 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; - - if (displaymatrix_side_data) { - err = av_packet_add_side_data(out, AV_PKT_DATA_DISPLAYMATRIX, - displaymatrix_side_data, - displaymatrix_side_data_size); - if (err) { - av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted " - "displaymatrix side data to packet.\n"); - goto fail; - } - displaymatrix_side_data = NULL; - } - ctx->done_first_au = 1; err = 0; fail: - ff_cbs_fragment_uninit(ctx->cbc, au); - av_freep(&displaymatrix_side_data); + ff_cbs_fragment_reset(au); if (err < 0) - av_packet_unref(out); - av_packet_free(&in); + av_packet_unref(pkt); return err; } @@ -557,12 +627,15 @@ static int h264_metadata_init(AVBSFContext *bsf) CodedBitstreamFragment *au = &ctx->access_unit; int err, i; - err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_H264, bsf); + err = ff_cbs_init(&ctx->input, AV_CODEC_ID_H264, bsf); + if (err < 0) + return err; + err = ff_cbs_init(&ctx->output, AV_CODEC_ID_H264, bsf); if (err < 0) return err; if (bsf->par_in->extradata) { - err = ff_cbs_read_extradata(ctx->cbc, au, bsf->par_in); + err = ff_cbs_read_extradata(ctx->input, au, bsf->par_in); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); goto fail; @@ -576,7 +649,7 @@ static int h264_metadata_init(AVBSFContext *bsf) } } - err = ff_cbs_write_extradata(ctx->cbc, bsf->par_out, au); + err = ff_cbs_write_extradata(ctx->output, bsf->par_out, au); if (err < 0) { av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); goto fail; @@ -585,14 +658,17 @@ static int h264_metadata_init(AVBSFContext *bsf) err = 0; fail: - ff_cbs_fragment_uninit(ctx->cbc, au); + ff_cbs_fragment_reset(au); return err; } static void h264_metadata_close(AVBSFContext *bsf) { H264MetadataContext *ctx = bsf->priv_data; - ff_cbs_close(&ctx->cbc); + + ff_cbs_fragment_free(&ctx->access_unit); + ff_cbs_close(&ctx->input); + ff_cbs_close(&ctx->output); } #define OFFSET(x) offsetof(H264MetadataContext, x) @@ -612,6 +688,10 @@ static const AVOption h264_metadata_options[] = { OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, { .dbl = 0.0 }, 0, 65535, FLAGS }, + { "overscan_appropriate_flag", "Set VUI overscan appropriate flag", + OFFSET(overscan_appropriate_flag), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 1, FLAGS }, + { "video_format", "Set video format (table E-2)", OFFSET(video_format), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 7, FLAGS}, @@ -683,6 +763,36 @@ static const AVOption h264_metadata_options[] = { 0, AV_OPT_TYPE_CONST, { .i64 = FLIP_VERTICAL }, .flags = FLAGS, .unit = "flip" }, + { "level", "Set level (table A-1)", + 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", 10) }, + { LEVEL("1b", 9) }, + { LEVEL("1.1", 11) }, + { LEVEL("1.2", 12) }, + { LEVEL("1.3", 13) }, + { LEVEL("2", 20) }, + { LEVEL("2.1", 21) }, + { LEVEL("2.2", 22) }, + { LEVEL("3", 30) }, + { LEVEL("3.1", 31) }, + { LEVEL("3.2", 32) }, + { LEVEL("4", 40) }, + { LEVEL("4.1", 41) }, + { LEVEL("4.2", 42) }, + { LEVEL("5", 50) }, + { LEVEL("5.1", 51) }, + { LEVEL("5.2", 52) }, + { LEVEL("6", 60) }, + { LEVEL("6.1", 61) }, + { LEVEL("6.2", 62) }, +#undef LEVEL + { NULL } };