diff mbox

[FFmpeg-devel,v1,2/2] avcodec/h265_metadata_bsf: add option to insert a string as SEI unregistered user data

Message ID 20191211150204.16965-2-lance.lmwang@gmail.com
State New
Headers show

Commit Message

Limin Wang Dec. 11, 2019, 3:02 p.m. UTC
From: Limin Wang <lance.lmwang@gmail.com>

Signed-off-by: Limin Wang <lance.lmwang@gmail.com>
---
 doc/bitstream_filters.texi     |  8 ++++++
 libavcodec/h265_metadata_bsf.c | 65 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 72 insertions(+), 1 deletion(-)

Comments

Limin Wang Dec. 17, 2019, 10:29 a.m. UTC | #1
On Wed, Dec 11, 2019 at 11:02:04PM +0800, lance.lmwang@gmail.com wrote:
> From: Limin Wang <lance.lmwang@gmail.com>
> 
> Signed-off-by: Limin Wang <lance.lmwang@gmail.com>
> ---
>  doc/bitstream_filters.texi     |  8 ++++++
>  libavcodec/h265_metadata_bsf.c | 65 +++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 72 insertions(+), 1 deletion(-)
> 
> diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
> index 8fe5b3a..81b41d7 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

Please use below patchset to check the result:
https://patchwork.ffmpeg.org/patch/16854/


>  
>  @section hevc_mp4toannexb
> diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c
> index b3a1fda..f32ddf0 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;
>  
>      err = ff_bsf_get_packet_ref(bsf, pkt);
>      if (err < 0)
> @@ -410,11 +414,67 @@ 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;
> +        }
> +    }
> +
> +    // Only insert the SEI in access units containing SPSs or VPSs
> +    if (ctx->sei_user_data && (has_sps || has_vps)) {
> +        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 (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_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 +607,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" },
> -- 
> 2.9.5
>
diff mbox

Patch

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 8fe5b3a..81b41d7 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 b3a1fda..f32ddf0 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;
 
     err = ff_bsf_get_packet_ref(bsf, pkt);
     if (err < 0)
@@ -410,11 +414,67 @@  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;
+        }
+    }
+
+    // Only insert the SEI in access units containing SPSs or VPSs
+    if (ctx->sei_user_data && (has_sps || has_vps)) {
+        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 (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_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 +607,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" },