diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 8fe5b3ad75..81b41d70b3 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -376,6 +376,14 @@ The argument must be the name of a level (for example, @samp{5.1}), a
 or the special name @samp{auto} indicating that the filter should
 attempt to guess the level from the input stream properties.
 
+@item sei_user_data
+Insert a string as SEI unregistered user data.  The argument must
+be of the form @emph{UUID+string}, where the UUID is as a 32-character
+(16 bytes) hexadecimal string possibly separated by hyphens, and the
+string can be anything.
+
+For example, @samp{086f3693-b7b3-4f2c-9653-21492feee5b8+hello} will
+insert the string ``hello'' associated with the given 32-bit UUID.
 @end table
 
 @section hevc_mp4toannexb
diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c
index b3a1fda144..312545cbe6 100644
--- a/libavcodec/h265_metadata_bsf.c
+++ b/libavcodec/h265_metadata_bsf.c
@@ -16,6 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/opt.h"
 
@@ -23,6 +24,7 @@
 #include "cbs.h"
 #include "cbs_h265.h"
 #include "hevc.h"
+#include "hevc_sei.h"
 #include "h265_profile_level.h"
 
 enum {
@@ -65,6 +67,8 @@ typedef struct H265MetadataContext {
     int crop_top;
     int crop_bottom;
 
+    const char *sei_user_data;
+
     int level;
     int level_guess;
     int level_warned;
@@ -340,7 +344,7 @@ static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
 {
     H265MetadataContext *ctx = bsf->priv_data;
     CodedBitstreamFragment *au = &ctx->access_unit;
-    int err, i;
+    int err, i, j, has_sps = 0, has_vps = 0, has_keyframe = 0;
 
     err = ff_bsf_get_packet_ref(bsf, pkt);
     if (err < 0)
@@ -410,11 +414,70 @@ static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *pkt)
             err = h265_metadata_update_vps(bsf, au->units[i].content);
             if (err < 0)
                 goto fail;
+            has_vps = 1;
         }
         if (au->units[i].type == HEVC_NAL_SPS) {
             err = h265_metadata_update_sps(bsf, au->units[i].content);
             if (err < 0)
                 goto fail;
+            has_sps = 1;
+        }
+        if (au->units[i].type >= HEVC_NAL_BLA_W_LP &&
+            au->units[i].type <= HEVC_NAL_IRAP_VCL23)
+            has_keyframe = 1;
+    }
+
+    // Only insert the SEI in access units containing SPSs or VPSs or Keyframe
+    if (ctx->sei_user_data && (has_sps || has_vps || has_keyframe)) {
+        H265RawSEIPayload payload = {
+            .payload_type = HEVC_SEI_TYPE_USER_DATA_UNREGISTERED,
+        };
+        H265RawSEIUserDataUnregistered *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_h265_add_sei_prefix_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;
         }
     }
 
@@ -547,6 +610,9 @@ static const AVOption h265_metadata_options[] = {
         OFFSET(crop_bottom), AV_OPT_TYPE_INT,
         { .i64 = -1 }, -1, HEVC_MAX_HEIGHT, FLAGS },
 
+    { "sei_user_data", "Insert SEI user data (UUID+string)",
+        OFFSET(sei_user_data), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS },
+
     { "level", "Set level (tables A.6 and A.7)",
         OFFSET(level), AV_OPT_TYPE_INT,
         { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" },
