]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/h264_metadata_bsf.c
avutil/buffer: Switch AVBuffer API to size_t
[ffmpeg] / libavcodec / h264_metadata_bsf.c
index a17987a06cc4d48c42eed9b302e64e894a709b2a..ef74cba56037a73512bb1552967a000a8ec34984 100644 (file)
 
 #include "bsf.h"
 #include "cbs.h"
+#include "cbs_bsf.h"
 #include "cbs_h264.h"
 #include "h264.h"
 #include "h264_levels.h"
 #include "h264_sei.h"
 
-enum {
-    PASS,
-    INSERT,
-    REMOVE,
-    EXTRACT,
-};
-
 enum {
     FLIP_HORIZONTAL = 1,
     FLIP_VERTICAL   = 2,
@@ -46,17 +40,17 @@ enum {
 };
 
 typedef struct H264MetadataContext {
-    const AVClass *class;
-
-    CodedBitstreamContext *cbc;
-    CodedBitstreamFragment access_unit;
+    CBSBSFContext common;
 
     int done_first_au;
 
     int aud;
+    H264RawAUD aud_nal;
 
     AVRational sample_aspect_ratio;
 
+    int overscan_appropriate_flag;
+
     int video_format;
     int video_full_range_flag;
     int colour_primaries;
@@ -74,17 +68,72 @@ typedef struct H264MetadataContext {
     int crop_bottom;
 
     const char *sei_user_data;
+    SEIRawUserDataUnregistered sei_user_data_payload;
 
     int delete_filler;
 
     int display_orientation;
     double rotate;
     int flip;
+    H264RawSEIDisplayOrientation display_orientation_payload;
 
     int level;
 } H264MetadataContext;
 
 
+static int h264_metadata_insert_aud(AVBSFContext *bsf,
+                                    CodedBitstreamFragment *au)
+{
+    H264MetadataContext *ctx = bsf->priv_data;
+    int primary_pic_type_mask = 0xff;
+    int err, i, j;
+
+    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
+    };
+
+    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");
+        return AVERROR_INVALIDDATA;
+    }
+
+    ctx->aud_nal = (H264RawAUD) {
+        .nal_unit_header.nal_unit_type = H264_NAL_AUD,
+        .primary_pic_type = j,
+    };
+
+    err = ff_cbs_insert_unit_content(au, 0, H264_NAL_AUD,
+                                     &ctx->aud_nal, NULL);
+    if (err < 0) {
+        av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n");
+        return err;
+    }
+
+    return 0;
+}
+
 static int h264_metadata_update_sps(AVBSFContext *bsf,
                                     H264RawSPS *sps)
 {
@@ -122,47 +171,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) {
@@ -186,9 +227,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;
@@ -223,6 +262,7 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
             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) *
@@ -244,7 +284,12 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
             height = 16 * (sps->pic_height_in_map_units_minus1 + 1) *
                 (2 - sps->frame_mbs_only_flag);
 
-            desc = ff_h264_guess_level(sps->profile_idc, bit_rate,
+            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;
@@ -277,267 +322,94 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
     return 0;
 }
 
-static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out)
+static int h264_metadata_handle_display_orientation(AVBSFContext *bsf,
+                                                    AVPacket *pkt,
+                                                    CodedBitstreamFragment *au,
+                                                    int seek_point)
 {
     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);
-    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 == 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;
-
-            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 = (H264RawAUD) {
-                .nal_unit_header.nal_unit_type = H264_NAL_AUD,
-                .primary_pic_type = j,
-            };
-
-            err = ff_cbs_insert_unit_content(ctx->cbc, au,
-                                             0, H264_NAL_AUD, &aud, NULL);
-            if (err < 0) {
-                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) {
-            err = h264_metadata_update_sps(bsf, au->units[i].content);
-            if (err < 0)
-                goto fail;
-            has_sps = 1;
-        }
-    }
-
-    // Only insert the SEI in access units containing SPSs, and also
-    // unconditionally in the first access unit we ever see.
-    if (ctx->sei_user_data && (has_sps || !ctx->done_first_au)) {
-        H264RawSEIPayload payload = {
-            .payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED,
-        };
-        H264RawSEIUserDataUnregistered *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;
-            }
-            if (i & 1)
-                udu->uuid_iso_iec_11578[j / 2] |= v;
-            else
-                udu->uuid_iso_iec_11578[j / 2] = v << 4;
-            ++j;
-        }
-        if (j == 32 && ctx->sei_user_data[i] == '+') {
-            size_t len = strlen(ctx->sei_user_data + i + 1);
-
-            udu->data_ref = av_buffer_alloc(len + 1);
-            if (!udu->data_ref) {
-                err = AVERROR(ENOMEM);
-                goto fail;
-            }
-
-            udu->data        = udu->data_ref->data;
-            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);
-            if (err < 0) {
-                av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI "
-                       "message to access unit.\n");
-                goto fail;
-            }
-
-        } else {
-        invalid_user_data:
-            av_log(bsf, AV_LOG_ERROR, "Invalid user data: "
-                   "must be \"UUID+string\".\n");
-            err = AVERROR(EINVAL);
-            goto fail;
+    SEIRawMessage *message;
+    int err;
+
+    message = NULL;
+    while (ff_cbs_sei_find_message(ctx->common.output, au,
+                                   SEI_TYPE_DISPLAY_ORIENTATION,
+                                   &message) == 0) {
+        H264RawSEIDisplayOrientation *disp = message->payload;
+        int32_t *matrix;
+
+        matrix = av_malloc(9 * sizeof(int32_t));
+        if (!matrix)
+            return AVERROR(ENOMEM);
+
+        av_display_rotation_set(matrix,
+                                disp->anticlockwise_rotation *
+                                180.0 / 65536.0);
+        av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip);
+
+        // If there are multiple display orientation messages in an
+        // 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);
+            return AVERROR(ENOMEM);
         }
     }
 
-    if (ctx->delete_filler) {
-        for (i = 0; i < au->nb_units; 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;
-                continue;
-            }
-
-            if (au->units[i].type == H264_NAL_SEI) {
-                // Filler SEI messages.
-                H264RawSEI *sei = au->units[i].content;
-
-                for (j = 0; j < sei->payload_count; 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;
-                    }
-                }
-            }
-        }
+    if (ctx->display_orientation == BSF_ELEMENT_REMOVE ||
+        ctx->display_orientation == BSF_ELEMENT_INSERT) {
+        ff_cbs_sei_delete_message_type(ctx->common.output, au,
+                                       SEI_TYPE_DISPLAY_ORIENTATION);
     }
 
-    if (ctx->display_orientation != PASS) {
-        for (i = 0; i < au->nb_units; 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++) {
-                H264RawSEIDisplayOrientation *disp;
-                int32_t *matrix;
-
-                if (sei->payload[j].payload_type !=
-                    H264_SEI_TYPE_DISPLAY_ORIENTATION)
-                    continue;
-                disp = &sei->payload[j].payload.display_orientation;
-
-                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;
-                }
-
-                matrix = av_mallocz(9 * sizeof(int32_t));
-                if (!matrix) {
-                    err = AVERROR(ENOMEM);
-                    goto fail;
-                }
-
-                av_display_rotation_set(matrix,
-                                        disp->anticlockwise_rotation *
-                                        180.0 / 65536.0);
-                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);
-            }
-        }
-    }
-    if (ctx->display_orientation == INSERT) {
-        H264RawSEIPayload payload = {
-            .payload_type = H264_SEI_TYPE_DISPLAY_ORIENTATION,
-        };
+    if (ctx->display_orientation == BSF_ELEMENT_INSERT) {
         H264RawSEIDisplayOrientation *disp =
-            &payload.payload.display_orientation;
+            &ctx->display_orientation_payload;
         uint8_t *data;
-        int size;
+        size_t 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;
-            double angle;
+            double dmatrix[9];
+            int hflip, vflip, i;
+            double scale_x, scale_y, angle;
 
             memcpy(matrix, data, sizeof(matrix));
 
-            hflip = vflip = 0;
-            if (matrix[0] < 0 && matrix[4] > 0)
-                hflip = 1;
-            else if (matrix[0] > 0 && matrix[4] < 0)
-                vflip = 1;
-            av_display_matrix_flip(matrix, hflip, vflip);
+            for (i = 0; i < 9; i++)
+                dmatrix[i] = matrix[i] / 65536.0;
+
+            // Extract scale factors.
+            scale_x = hypot(dmatrix[0], dmatrix[3]);
+            scale_y = hypot(dmatrix[1], dmatrix[4]);
+
+            // Select flips to make the main diagonal positive.
+            hflip = dmatrix[0] < 0.0;
+            vflip = dmatrix[4] < 0.0;
+            if (hflip)
+                scale_x = -scale_x;
+            if (vflip)
+                scale_y = -scale_y;
+
+            // Rescale.
+            for (i = 0; i < 9; i += 3) {
+                dmatrix[i]     /= scale_x;
+                dmatrix[i + 1] /= scale_y;
+            }
 
-            angle = av_display_rotation_get(matrix);
+            // Extract rotation.
+            angle = atan2(dmatrix[3], dmatrix[0]);
 
-            if (!(angle >= -180.0 && angle <= 180.0 /* also excludes NaN */) ||
-                matrix[2] != 0 || matrix[5] != 0 ||
-                matrix[6] != 0 || matrix[7] != 0) {
+            if (!(angle >= -M_PI && angle <= M_PI) ||
+                matrix[2] != 0.0 || matrix[5] != 0.0 ||
+                matrix[6] != 0.0 || matrix[7] != 0.0) {
                 av_log(bsf, AV_LOG_WARNING, "Input display matrix is not "
                        "representable in H.264 parameters.\n");
             } else {
@@ -545,13 +417,13 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out)
                 disp->ver_flip = vflip;
                 disp->anticlockwise_rotation =
                     (uint16_t)rint((angle >= 0.0 ? angle
-                                                 : angle + 360.0) *
-                                   65536.0 / 360.0);
+                                                 : angle + 2 * M_PI) *
+                                   32768.0 / M_PI);
                 write = 1;
             }
         }
 
-        if (has_sps || !ctx->done_first_au) {
+        if (seek_point) {
             if (!isnan(ctx->rotate)) {
                 disp->anticlockwise_rotation =
                     (uint16_t)rint((ctx->rotate >= 0.0 ? ctx->rotate
@@ -569,114 +441,157 @@ 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_sei_add_message(ctx->common.output, au, 1,
+                                         SEI_TYPE_DISPLAY_ORIENTATION,
+                                         disp, NULL);
             if (err < 0) {
                 av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation "
                        "SEI message to access unit.\n");
-                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;
+    return 0;
+}
+
+static int h264_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt,
+                                         CodedBitstreamFragment *au)
+{
+    H264MetadataContext *ctx = bsf->priv_data;
+    int err, i, has_sps, seek_point;
+
+    // If an AUD is present, it must be the first NAL unit.
+    if (au->nb_units && au->units[0].type == H264_NAL_AUD) {
+        if (ctx->aud == BSF_ELEMENT_REMOVE)
+            ff_cbs_delete_unit(au, 0);
+    } else {
+        if (pkt && ctx->aud == BSF_ELEMENT_INSERT) {
+            err = h264_metadata_insert_aud(bsf, au);
+            if (err < 0)
+                return err;
+        }
     }
 
-    err = av_packet_copy_props(out, in);
-    if (err < 0)
-        goto fail;
+    has_sps = 0;
+    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;
+            has_sps = 1;
+        }
+    }
 
-    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;
+    if (pkt) {
+        // The current packet should be treated as a seek point for metadata
+        // insertion if any of:
+        // - It is the first packet in the stream.
+        // - It contains an SPS, indicating that a sequence might start here.
+        // - It is marked as containing a key frame.
+        seek_point = !ctx->done_first_au || has_sps ||
+            (pkt->flags & AV_PKT_FLAG_KEY);
+    } else {
+        seek_point = 0;
+    }
+
+    if (ctx->sei_user_data && seek_point) {
+        err = ff_cbs_sei_add_message(ctx->common.output, au, 1,
+                                     SEI_TYPE_USER_DATA_UNREGISTERED,
+                                     &ctx->sei_user_data_payload, NULL);
+        if (err < 0) {
+            av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI "
+                   "message to access unit.\n");
+            return err;
         }
-        displaymatrix_side_data = NULL;
     }
 
-    ctx->done_first_au = 1;
+    if (ctx->delete_filler) {
+        for (i = au->nb_units - 1; i >= 0; i--) {
+            if (au->units[i].type == H264_NAL_FILLER_DATA) {
+                ff_cbs_delete_unit(au, i);
+                continue;
+            }
+        }
 
-    err = 0;
-fail:
-    ff_cbs_fragment_reset(ctx->cbc, au);
-    av_freep(&displaymatrix_side_data);
+        ff_cbs_sei_delete_message_type(ctx->common.output, au,
+                                       SEI_TYPE_FILLER_PAYLOAD);
+    }
 
-    if (err < 0)
-        av_packet_unref(out);
-    av_packet_free(&in);
+    if (pkt && ctx->display_orientation != BSF_ELEMENT_PASS) {
+        err = h264_metadata_handle_display_orientation(bsf, pkt, au,
+                                                       seek_point);
+        if (err < 0)
+            return err;
+    }
+
+    if (pkt)
+        ctx->done_first_au = 1;
 
-    return err;
+    return 0;
 }
 
+static const CBSBSFType h264_metadata_type = {
+    .codec_id        = AV_CODEC_ID_H264,
+    .fragment_name   = "access unit",
+    .unit_name       = "NAL unit",
+    .update_fragment = &h264_metadata_update_fragment,
+};
+
 static int h264_metadata_init(AVBSFContext *bsf)
 {
     H264MetadataContext *ctx = bsf->priv_data;
-    CodedBitstreamFragment *au = &ctx->access_unit;
-    int err, i;
 
-    err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_H264, bsf);
-    if (err < 0)
-        return err;
+    if (ctx->sei_user_data) {
+        SEIRawUserDataUnregistered *udu = &ctx->sei_user_data_payload;
+        int i, j;
 
-    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 == H264_NAL_SPS) {
-                err = h264_metadata_update_sps(bsf, au->units[i].content);
-                if (err < 0)
-                    goto fail;
+        // Parse UUID.  It must be a hex string of length 32, possibly
+        // containing '-'s between hex digits (which we ignore).
+        for (i = j = 0; j < 32 && i < 64 && 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 {
+                break;
             }
+            if (j & 1)
+                udu->uuid_iso_iec_11578[j / 2] |= v;
+            else
+                udu->uuid_iso_iec_11578[j / 2] = v << 4;
+            ++j;
         }
-
-        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;
+        if (j == 32 && ctx->sei_user_data[i] == '+') {
+            udu->data = (uint8_t*)ctx->sei_user_data + i + 1;
+            udu->data_length = strlen(udu->data) + 1;
+        } else {
+            av_log(bsf, AV_LOG_ERROR, "Invalid user data: "
+                   "must be \"UUID+string\".\n");
+            return AVERROR(EINVAL);
         }
     }
 
-    err = 0;
-fail:
-    ff_cbs_fragment_reset(ctx->cbc, au);
-    return err;
-}
-
-static void h264_metadata_close(AVBSFContext *bsf)
-{
-    H264MetadataContext *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, &h264_metadata_type);
 }
 
 #define OFFSET(x) offsetof(H264MetadataContext, x)
 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM)
 static const AVOption h264_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,
         { .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},
@@ -723,17 +638,9 @@ static const AVOption h264_metadata_options[] = {
     { "delete_filler", "Delete all filler (both NAL and SEI)",
         OFFSET(delete_filler), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS},
 
-    { "display_orientation", "Display orientation SEI",
-        OFFSET(display_orientation), AV_OPT_TYPE_INT,
-        { .i64 = PASS }, PASS, EXTRACT, FLAGS, "disp_or" },
-    { "pass",    NULL, 0, AV_OPT_TYPE_CONST,
-        { .i64 = PASS    }, .flags = FLAGS, .unit = "disp_or" },
-    { "insert",  NULL, 0, AV_OPT_TYPE_CONST,
-        { .i64 = INSERT  }, .flags = FLAGS, .unit = "disp_or" },
-    { "remove",  NULL, 0, AV_OPT_TYPE_CONST,
-        { .i64 = REMOVE  }, .flags = FLAGS, .unit = "disp_or" },
-    { "extract", NULL, 0, AV_OPT_TYPE_CONST,
-        { .i64 = EXTRACT }, .flags = FLAGS, .unit = "disp_or" },
+    BSF_ELEMENT_OPTIONS_PIRE("display_orientation",
+                             "Display orientation SEI",
+                             display_orientation, FLAGS),
 
     { "rotate", "Set rotation in display orientation SEI (anticlockwise angle in degrees)",
         OFFSET(rotate), AV_OPT_TYPE_DOUBLE,
@@ -797,7 +704,7 @@ const AVBitStreamFilter ff_h264_metadata_bsf = {
     .priv_data_size = sizeof(H264MetadataContext),
     .priv_class     = &h264_metadata_class,
     .init           = &h264_metadata_init,
-    .close          = &h264_metadata_close,
-    .filter         = &h264_metadata_filter,
+    .close          = &ff_cbs_bsf_generic_close,
+    .filter         = &ff_cbs_bsf_generic_filter,
     .codec_ids      = h264_metadata_codec_ids,
 };