Quoting Mark Thompson (2017-07-29 23:16:32) > This is able to modify some header metadata found in the SPS/VUI, > and can also add/remove AUDs and insert user data in SEI NAL units. > --- > * Adds cropping parameters. > * SEI user data insert code improved as suggested by Jun Zhao. > > > doc/bitstream_filters.texi | 57 +++++ > libavcodec/Makefile | 2 + > libavcodec/bitstream_filters.c | 1 + > libavcodec/h264_metadata_bsf.c | 490 > +++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 550 insertions(+) > create mode 100644 libavcodec/h264_metadata_bsf.c > > diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi > index af14b67ad..34f0af922 100644 > --- a/doc/bitstream_filters.texi > +++ b/doc/bitstream_filters.texi > @@ -39,6 +39,63 @@ When this option is enabled, the long-term headers are > removed from the > bitstream after extraction. > @end table > > +@section h264_metadata > + > +Modify metadata embedded in an H.264 stream. > + > +@table @option > +@item aud > +Insert or remove AUD NAL units in all access units of the stream. > + > +@table @samp > +@item insert > +@item remove > +@end table > + > +@item sample_aspect_ratio > +Set the sample aspect ratio in the stream in the VUI parameters. > + > +@item video_format > +@item video_full_range_flag > +Set the video format in the stream (see H.264 section E.2.1 and > +table E-2). > + > +@item colour_primaries > +@item transfer_characteristics > +@item matrix_coefficients > +Set the colour description in the stream (see H.264 section E.2.1 > +and tables E-3, E-4 and E-5). > + > +@item chroma_sample_loc_type > +Set the chroma sample location in the stream (see H.264 section > +E.2.1 and figure E-1). > + > +@item frame_rate > +@item fixed_frame_rate_flag > +Set the frame rate in the VUI parameters (num_units_in_tick / > +time_scale). Note that this is likely to be overridden by container > +parameters when the stream is in a container. > + > +@item crop_left > +@item crop_right > +@item crop_top > +@item crop_bottom > +Set the frame cropping offsets in the SPS. These values will replace > +the current ones if the stream is already cropped. > + > +The units of these fields are dependent on the chroma subsampling > +and whether the stream is interlaced (see H.264 section 7.4.2.1.1).
It would be more user-friendly to have crop_* set the values in pixels, with validity checking and all that, and perhaps in addition crop_*_raw that set the raw bitstream values. Or maybe just rename those to _raw if you don't feel like implementing this right now. > +static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) > +{ > + H264MetadataContext *ctx = bsf->priv_data; > + AVPacket *in = NULL; > + CodedBitstreamFragment *au = &ctx->access_unit; > + int err, i, j, has_sps; > + char *sei_udu_string = NULL; > + > + err = ff_bsf_get_packet(bsf, &in); > + if (err < 0) > + goto fail; > + > + 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 == H264_NAL_AUD) { > + if (ctx->aud == REMOVE) > + ff_cbs_delete_unit(&ctx->cbc, au, 0); > + } else { > + if (ctx->aud == INSERT) { > + static const int primary_pic_type_table[] = { > + 0x084, // 2, 7 > + 0x0a5, // 0, 2, 5, 7 > + 0x0e7, // 0, 1, 2, 5, 6, 7 > + 0x210, // 4, 9 > + 0x318, // 3, 4, 8, 9 > + 0x294, // 2, 4, 7, 9 > + 0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9 > + 0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 > + }; > + int primary_pic_type_mask = 0xff; > + H264RawAUD *aud = &ctx->aud_nal; > + > + for (i = 0; i < au->nb_units; i++) { > + if (au->units[i].type == H264_NAL_SLICE || > + au->units[i].type == H264_NAL_IDR_SLICE) { > + H264RawSlice *slice = au->units[i].content; > + for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); > j++) { > + if (!(primary_pic_type_table[j] & > + (1 << slice->header.slice_type))) > + primary_pic_type_mask &= ~(1 << j); > + } > + } > + } > + for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) > + if (primary_pic_type_mask & (1 << j)) > + break; > + if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) { > + av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: " > + "invalid slice types?\n"); > + err = AVERROR_INVALIDDATA; > + goto fail; > + } > + > + aud->nal_unit_header.nal_unit_type = H264_NAL_AUD; > + aud->primary_pic_type = j; > + > + err = ff_cbs_insert_unit_content(&ctx->cbc, au, > + 0, H264_NAL_AUD, aud); > + if (err) { > + av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); > + goto fail; > + } > + } > + } > + > + has_sps = 0; > + for (i = 0; i < au->nb_units; i++) { > + if (au->units[i].type == H264_NAL_SPS) { > + h264_metadata_update_sps(bsf, au->units[i].content); > + has_sps = 1; > + } > + } > + > + // Only insert the SEI in access units containing SPSs. > + if (has_sps && ctx->sei_user_data) { > + H264RawSEI *sei; > + H264RawSEIPayload *payload; > + H264RawSEIUserDataUnregistered *udu; > + int sei_pos; > + > + for (i = 0; i < au->nb_units; i++) { > + if (au->units[i].type == H264_NAL_SEI || > + au->units[i].type == H264_NAL_SLICE || > + au->units[i].type == H264_NAL_IDR_SLICE) > + break; > + } > + sei_pos = i; > + > + if (sei_pos < au->nb_units && > + au->units[sei_pos].type == H264_NAL_SEI) { > + sei = au->units[sei_pos].content; > + } else { > + sei = &ctx->sei_nal; > + memset(sei, 0, sizeof(*sei)); > + > + sei->nal_unit_header.nal_unit_type = H264_NAL_SEI; > + > + err = ff_cbs_insert_unit_content(&ctx->cbc, au, > + sei_pos, H264_NAL_SEI, sei); > + if (err < 0) { > + av_log(bsf, AV_LOG_ERROR, "Failed to insert SEI.\n"); > + goto fail; > + } > + } > + > + payload = &sei->payload[sei->payload_count]; > + > + payload->payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED; > + udu = &payload->payload.user_data_unregistered; > + > + for (i = j = 0; j < 32 && ctx->sei_user_data[i]; i++) { > + int c, v; > + c = ctx->sei_user_data[i]; > + if (c == '-') { > + continue; > + } else if (av_isxdigit(c)) { > + c = av_tolower(c); > + v = (c <= '9' ? c - '0' : c - 'a' + 10); > + } else { > + goto invalid_user_data; weird indentation Otherwise ok -- Anton Khirnov _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel