diff --git a/libavcodec/avpacket.c b/libavcodec/avpacket.c
index 033f2d8f26..a530dc6779 100644
--- a/libavcodec/avpacket.c
+++ b/libavcodec/avpacket.c
@@ -395,6 +395,7 @@ const char *av_packet_side_data_name(enum AVPacketSideDataType type)
     case AV_PKT_DATA_A53_CC:                     return "A53 Closed Captions";
     case AV_PKT_DATA_ENCRYPTION_INIT_INFO:       return "Encryption initialization data";
     case AV_PKT_DATA_ENCRYPTION_INFO:            return "Encryption info";
+    case AV_PKT_DATA_SEI_USER:                   return "SEI unregistered data";
     case AV_PKT_DATA_AFD:                        return "Active Format Description data";
     case AV_PKT_DATA_PRFT:                       return "Producer Reference Time";
     case AV_PKT_DATA_ICC_PROFILE:                return "ICC Profile";
diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c
index 99017653d0..e90b82793b 100644
--- a/libavcodec/h264_metadata_bsf.c
+++ b/libavcodec/h264_metadata_bsf.c
@@ -276,6 +276,65 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
     return 0;
 }
 
+static int write_sei_user_data(AVBSFContext *bsf, const uint8_t *data, int size)
+{
+    H264MetadataContext *ctx = bsf->priv_data;
+    CodedBitstreamFragment *au = &ctx->access_unit;
+    int err = 0, i, j;
+
+    H264RawSEIPayload payload = {
+        .payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED,
+    };
+    H264RawSEIUserDataUnregistered *udu =
+        &payload.payload.user_data_unregistered;
+
+    for (i = j = 0; j < 32 && data[i]; i++) {
+        int c, v;
+        c = 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 && data[i] == '+') {
+        size_t len = size - i - 1;
+
+        udu->data_ref = av_buffer_alloc(len + 1);
+        if (!udu->data_ref) {
+            err = AVERROR(ENOMEM);
+            return err;
+        }
+
+        udu->data        = udu->data_ref->data;
+        udu->data_length = len + 1;
+        memcpy(udu->data, 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");
+            return err;
+        }
+
+    } else {
+    invalid_user_data:
+        av_log(bsf, AV_LOG_ERROR, "Invalid user data: "
+               "must be \"UUID+string\".\n");
+        err = AVERROR(EINVAL);
+        return err;
+    }
+    return err;
+}
+
 static int h264_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt)
 {
     H264MetadataContext *ctx = bsf->priv_data;
@@ -412,56 +471,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
     // 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 (j & 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;
-        }
+      write_sei_user_data(bsf, ctx->sei_user_data, strlen(ctx->sei_user_data));
     }
 
     if (ctx->delete_filler) {
@@ -604,6 +614,12 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
         }
     }
 
+    uint8_t *data;
+    int size;
+    if (data = av_packet_get_side_data(pkt, AV_PKT_DATA_SEI_USER, &size)) {
+        write_sei_user_data(bsf, data, size);
+    }
+
     err = ff_cbs_write_packet(ctx->cbc, pkt, au);
     if (err < 0) {
         av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n");
diff --git a/libavcodec/packet.h b/libavcodec/packet.h
index 41485f4527..48e0ccbaf0 100644
--- a/libavcodec/packet.h
+++ b/libavcodec/packet.h
@@ -282,6 +282,11 @@ enum AVPacketSideDataType {
      */
     AV_PKT_DATA_DOVI_CONF,
 
+    /**
+     * This side data contains SEI unregistered Data.
+     */
+    AV_PKT_DATA_SEI_USER,
+
     /**
      * The number of side data types.
      * This is not part of the public API/ABI in the sense that it may
