Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH v2 1/2] avformat/flvenc: add option meta_period
@ 2023-02-02 16:07 Gyan Doshi
  2023-02-02 16:07 ` [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file Gyan Doshi
  0 siblings, 1 reply; 15+ messages in thread
From: Gyan Doshi @ 2023-02-02 16:07 UTC (permalink / raw)
  To: ffmpeg-devel

Allows to re-emit global metadata. Useful for dynamic metadata.

Accepts values:
at_start or 0, for insertion only at start
at_keyframes or 1, for insertion at each video keyframe
every_packet or 2, for insertion with all video packets
---
 doc/muxers.texi      | 20 ++++++++++++++++++++
 libavformat/flvenc.c | 22 +++++++++++++++++++---
 2 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/doc/muxers.texi b/doc/muxers.texi
index ed5341be39..02ecddf186 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -535,6 +535,26 @@ at the end of stream. (Be used to non-seekable living stream).
 @item add_keyframe_index
 Used to facilitate seeking; particularly for HTTP pseudo streaming.
 @end table
+
+@item meta_period
+Set interval at which to re-emit metadata.
+
+Possible values:
+
+@table @samp
+
+@item at_start
+Only once, in the header at the start. (@var{default})
+
+@item at_keyframes
+With each video keyframe.
+
+@item every_packet
+With every video packet.
+
+@end table
+Note that metadata will always be re-emitted if a metadata update event is signalled.
+
 @end table
 
 @anchor{framecrc}
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index 128ae8ebc0..d1c7a493d1 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -71,6 +71,12 @@ typedef enum {
     FLV_NO_DURATION_FILESIZE = (1 << 4),
 } FLVFlags;
 
+typedef enum {
+    FLV_META_ONCE_AT_START = 0,
+    FLV_META_AT_KF,
+    FLV_META_EVERY_PACKET,
+} FLVMetaPeriod;
+
 typedef struct FLVFileposition {
     int64_t keyframe_position;
     double keyframe_timestamp;
@@ -117,6 +123,7 @@ typedef struct FLVContext {
     AVCodecParameters *data_par;
 
     int flags;
+    int meta_period;
 } FLVContext;
 
 typedef struct FLVStreamContext {
@@ -822,7 +829,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
     unsigned ts;
     int size = pkt->size;
     uint8_t *data = NULL;
-    int flags = -1, flags_size, ret = 0;
+    int flags = -1, flags_size, ret = 0, meta_upd_flag;
     int64_t cur_offset = avio_tell(pb);
 
     if (par->codec_type == AVMEDIA_TYPE_AUDIO && !pkt->size) {
@@ -868,9 +875,14 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
 
     ts = pkt->dts;
 
-    if (s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) {
+    meta_upd_flag = s->event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+
+    if (meta_upd_flag ||
+        (flv->meta_period == FLV_META_AT_KF        && par->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
+        (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type == AVMEDIA_TYPE_VIDEO )) {
         write_metadata(s, ts);
-        s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+        if (meta_upd_flag)
+            s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
     }
 
     avio_write_marker(pb, av_rescale(ts, AV_TIME_BASE, 1000),
@@ -1050,6 +1062,10 @@ static const AVOption options[] = {
     { "no_metadata", "disable metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_METADATA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
     { "no_duration_filesize", "disable duration and filesize zero value metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_DURATION_FILESIZE}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
     { "add_keyframe_index", "Add keyframe index metadata", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" },
+    { "meta_period",  "metadata insertion period", offsetof(FLVContext, meta_period), AV_OPT_TYPE_INT, {.i64 = FLV_META_ONCE_AT_START}, FLV_META_ONCE_AT_START, FLV_META_EVERY_PACKET, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
+    { "at_start",      "only once at start",        0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
+    { "at_keyframes",  "with every video keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
+    { "every_packet",  "with every video packet",   0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
     { NULL },
 };
 
-- 
2.39.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-02 16:07 [FFmpeg-devel] [PATCH v2 1/2] avformat/flvenc: add option meta_period Gyan Doshi
@ 2023-02-02 16:07 ` Gyan Doshi
  2023-02-03 15:34   ` Andreas Rheinhardt
  0 siblings, 1 reply; 15+ messages in thread
From: Gyan Doshi @ 2023-02-02 16:07 UTC (permalink / raw)
  To: ffmpeg-devel

Useful, in conjuntion with option meta_period, to vary metadata during
stream.

File format is ffmetadata.
---
 configure            |   2 +-
 doc/muxers.texi      |   3 +
 libavformat/flvenc.c | 133 +++++++++++++++++++++++++++++++++----------
 3 files changed, 107 insertions(+), 31 deletions(-)

diff --git a/configure b/configure
index 9d78a244a3..de371632c4 100755
--- a/configure
+++ b/configure
@@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
 f4v_muxer_select="mov_muxer"
 fifo_muxer_deps="threads"
 flac_demuxer_select="flac_parser"
-flv_muxer_select="aac_adtstoasc_bsf"
+flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
 gxf_muxer_select="pcm_rechunk_bsf"
 hds_muxer_select="flv_muxer"
 hls_demuxer_select="adts_header ac3_parser"
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 02ecddf186..000c92b2a7 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -555,6 +555,9 @@ With every video packet.
 @end table
 Note that metadata will always be re-emitted if a metadata update event is signalled.
 
+@item meta_filename
+Specify a ffmetadata file from which to load metadata.
+
 @end table
 
 @anchor{framecrc}
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index d1c7a493d1..1332b18b41 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -122,6 +122,10 @@ typedef struct FLVContext {
     double framerate;
     AVCodecParameters *data_par;
 
+    char *meta_filename;
+    AVFormatContext *meta_ctx;
+    AVDictionary *meta_dict;
+
     int flags;
     int meta_period;
 } FLVContext;
@@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
     avio_w8(pb, !!b);
 }
 
+static int read_metadata_from_file(AVFormatContext *s, unsigned int ts, int header)
+{
+    FLVContext *flv = s->priv_data;
+    const AVInputFormat *meta_format = NULL;
+    AVDictionary *current_meta_dict = NULL;
+    float timestamp = ts/1000.f;
+    int ret;
+
+    if (!flv->meta_filename)
+        return 0;
+
+    meta_format = av_find_input_format("ffmetadata");
+    if (!meta_format) {
+        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
+        return AVERROR(ENOSYS);
+    }
+
+    avformat_close_input(&flv->meta_ctx);
+
+    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename, meta_format, NULL);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s t: %f.", flv->meta_filename, timestamp);
+        if (flv->meta_dict)
+            av_log(s, AV_LOG_ERROR, " Continuing with old metadata.");
+        av_log(s, AV_LOG_ERROR, "\n");
+        return ret;
+    }
+
+    if (flv->meta_dict) {
+        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
+        av_dict_free(&flv->meta_dict);
+    }
+
+    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s at %f seconds. Continuing with old metadata.\n", flv->meta_filename, timestamp);
+        av_dict_free(&flv->meta_dict);
+        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
+        av_dict_free(&current_meta_dict);
+        return ret;
+    }
+
+    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f seconds.\n", flv->meta_filename, header ? "in header" : "in video packet", timestamp);
+    av_dict_free(&current_meta_dict);
+    avformat_close_input(&flv->meta_ctx);
+
+    return 0;
+}
+
+static int write_user_metadata_tag(AVFormatContext *s, AVDictionaryEntry *tag, AVIOContext *pb, int *metadata_count)
+{
+
+    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count: %d\n", tag->key, tag->value, *metadata_count);
+
+    if(   !strcmp(tag->key, "width")
+        ||!strcmp(tag->key, "height")
+        ||!strcmp(tag->key, "videodatarate")
+        ||!strcmp(tag->key, "framerate")
+        ||!strcmp(tag->key, "videocodecid")
+        ||!strcmp(tag->key, "audiodatarate")
+        ||!strcmp(tag->key, "audiosamplerate")
+        ||!strcmp(tag->key, "audiosamplesize")
+        ||!strcmp(tag->key, "stereo")
+        ||!strcmp(tag->key, "audiocodecid")
+        ||!strcmp(tag->key, "duration")
+        ||!strcmp(tag->key, "onMetaData")
+        ||!strcmp(tag->key, "datasize")
+        ||!strcmp(tag->key, "lasttimestamp")
+        ||!strcmp(tag->key, "totalframes")
+        ||!strcmp(tag->key, "hasAudio")
+        ||!strcmp(tag->key, "hasVideo")
+        ||!strcmp(tag->key, "hasCuePoints")
+        ||!strcmp(tag->key, "hasMetadata")
+        ||!strcmp(tag->key, "hasKeyframes")
+    ){
+        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
+        return AVERROR(EINVAL);
+    }
+    put_amf_string(pb, tag->key);
+    avio_w8(pb, AMF_DATA_TYPE_STRING);
+    put_amf_string(pb, tag->value);
+    (*metadata_count)++;
+
+    return 0;
+}
+
 static void write_metadata(AVFormatContext *s, unsigned int ts)
 {
     AVIOContext *pb = s->pb;
@@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
     }
 
     ff_standardize_creation_time(s);
-    while ((tag = av_dict_iterate(s->metadata, tag))) {
-        if(   !strcmp(tag->key, "width")
-            ||!strcmp(tag->key, "height")
-            ||!strcmp(tag->key, "videodatarate")
-            ||!strcmp(tag->key, "framerate")
-            ||!strcmp(tag->key, "videocodecid")
-            ||!strcmp(tag->key, "audiodatarate")
-            ||!strcmp(tag->key, "audiosamplerate")
-            ||!strcmp(tag->key, "audiosamplesize")
-            ||!strcmp(tag->key, "stereo")
-            ||!strcmp(tag->key, "audiocodecid")
-            ||!strcmp(tag->key, "duration")
-            ||!strcmp(tag->key, "onMetaData")
-            ||!strcmp(tag->key, "datasize")
-            ||!strcmp(tag->key, "lasttimestamp")
-            ||!strcmp(tag->key, "totalframes")
-            ||!strcmp(tag->key, "hasAudio")
-            ||!strcmp(tag->key, "hasVideo")
-            ||!strcmp(tag->key, "hasCuePoints")
-            ||!strcmp(tag->key, "hasMetadata")
-            ||!strcmp(tag->key, "hasKeyframes")
-        ){
-            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
-            continue;
-        }
-        put_amf_string(pb, tag->key);
-        avio_w8(pb, AMF_DATA_TYPE_STRING);
-        put_amf_string(pb, tag->value);
-        metadata_count++;
-    }
+
+    while (tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
+        write_user_metadata_tag(s, tag, pb, &metadata_count);
+    while (tag = av_dict_get(flv->meta_dict, "", tag, AV_DICT_IGNORE_SUFFIX))
+        write_user_metadata_tag(s, tag, pb, &metadata_count);
 
     if (write_duration_filesize) {
         put_amf_string(pb, "filesize");
@@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
     if (flv->flags & FLV_NO_METADATA) {
         pb->seekable = 0;
     } else {
+        read_metadata_from_file(s, 0, 1);
         write_metadata(s, 0);
     }
 
@@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
     if (meta_upd_flag ||
         (flv->meta_period == FLV_META_AT_KF        && par->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
         (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type == AVMEDIA_TYPE_VIDEO )) {
+        read_metadata_from_file(s, ts, 0);
         write_metadata(s, ts);
         if (meta_upd_flag)
             s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
@@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
     }
     flv->filepositions = flv->head_filepositions = NULL;
     flv->filepositions_count = 0;
+
+    if (flv->meta_dict)
+        av_dict_free(&flv->meta_dict);
+
+    avformat_close_input(&flv->meta_ctx);
 }
 
 static const AVOption options[] = {
@@ -1066,6 +1138,7 @@ static const AVOption options[] = {
     { "at_start",      "only once at start",        0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
     { "at_keyframes",  "with every video keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
     { "every_packet",  "with every video packet",   0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
+    { "meta_filename", "ffmetadata file to import metadata", offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0, AV_OPT_FLAG_ENCODING_PARAM },
     { NULL },
 };
 
-- 
2.39.1

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-02 16:07 ` [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file Gyan Doshi
@ 2023-02-03 15:34   ` Andreas Rheinhardt
  2023-02-04 10:11     ` Gyan Doshi
  0 siblings, 1 reply; 15+ messages in thread
From: Andreas Rheinhardt @ 2023-02-03 15:34 UTC (permalink / raw)
  To: ffmpeg-devel

Gyan Doshi:
> Useful, in conjuntion with option meta_period, to vary metadata during
> stream.
> 
> File format is ffmetadata.
> ---
>  configure            |   2 +-
>  doc/muxers.texi      |   3 +
>  libavformat/flvenc.c | 133 +++++++++++++++++++++++++++++++++----------
>  3 files changed, 107 insertions(+), 31 deletions(-)
> 
> diff --git a/configure b/configure
> index 9d78a244a3..de371632c4 100755
> --- a/configure
> +++ b/configure
> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>  f4v_muxer_select="mov_muxer"
>  fifo_muxer_deps="threads"
>  flac_demuxer_select="flac_parser"
> -flv_muxer_select="aac_adtstoasc_bsf"
> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>  gxf_muxer_select="pcm_rechunk_bsf"
>  hds_muxer_select="flv_muxer"
>  hls_demuxer_select="adts_header ac3_parser"
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 02ecddf186..000c92b2a7 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -555,6 +555,9 @@ With every video packet.
>  @end table
>  Note that metadata will always be re-emitted if a metadata update event is signalled.
>  
> +@item meta_filename
> +Specify a ffmetadata file from which to load metadata.
> +
>  @end table
>  
>  @anchor{framecrc}
> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
> index d1c7a493d1..1332b18b41 100644
> --- a/libavformat/flvenc.c
> +++ b/libavformat/flvenc.c
> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>      double framerate;
>      AVCodecParameters *data_par;
>  
> +    char *meta_filename;
> +    AVFormatContext *meta_ctx;
> +    AVDictionary *meta_dict;
> +
>      int flags;
>      int meta_period;
>  } FLVContext;
> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>      avio_w8(pb, !!b);
>  }
>  
> +static int read_metadata_from_file(AVFormatContext *s, unsigned int ts, int header)
> +{
> +    FLVContext *flv = s->priv_data;
> +    const AVInputFormat *meta_format = NULL;
> +    AVDictionary *current_meta_dict = NULL;
> +    float timestamp = ts/1000.f;
> +    int ret;
> +
> +    if (!flv->meta_filename)
> +        return 0;
> +
> +    meta_format = av_find_input_format("ffmetadata");
> +    if (!meta_format) {
> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
> +        return AVERROR(ENOSYS);
> +    }
> +
> +    avformat_close_input(&flv->meta_ctx);
> +
> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename, meta_format, NULL);
> +    if (ret < 0) {
> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s t: %f.", flv->meta_filename, timestamp);
> +        if (flv->meta_dict)
> +            av_log(s, AV_LOG_ERROR, " Continuing with old metadata.");
> +        av_log(s, AV_LOG_ERROR, "\n");
> +        return ret;
> +    }
> +
> +    if (flv->meta_dict) {
> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
> +        av_dict_free(&flv->meta_dict);
> +    }
> +
> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
> +    if (ret < 0) {
> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s at %f seconds. Continuing with old metadata.\n", flv->meta_filename, timestamp);
> +        av_dict_free(&flv->meta_dict);
> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
> +        av_dict_free(&current_meta_dict);
> +        return ret;
> +    }
> +
> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f seconds.\n", flv->meta_filename, header ? "in header" : "in video packet", timestamp);
> +    av_dict_free(&current_meta_dict);
> +    avformat_close_input(&flv->meta_ctx);
> +
> +    return 0;
> +}
> +
> +static int write_user_metadata_tag(AVFormatContext *s, AVDictionaryEntry *tag, AVIOContext *pb, int *metadata_count)
> +{
> +
> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count: %d\n", tag->key, tag->value, *metadata_count);
> +
> +    if(   !strcmp(tag->key, "width")
> +        ||!strcmp(tag->key, "height")
> +        ||!strcmp(tag->key, "videodatarate")
> +        ||!strcmp(tag->key, "framerate")
> +        ||!strcmp(tag->key, "videocodecid")
> +        ||!strcmp(tag->key, "audiodatarate")
> +        ||!strcmp(tag->key, "audiosamplerate")
> +        ||!strcmp(tag->key, "audiosamplesize")
> +        ||!strcmp(tag->key, "stereo")
> +        ||!strcmp(tag->key, "audiocodecid")
> +        ||!strcmp(tag->key, "duration")
> +        ||!strcmp(tag->key, "onMetaData")
> +        ||!strcmp(tag->key, "datasize")
> +        ||!strcmp(tag->key, "lasttimestamp")
> +        ||!strcmp(tag->key, "totalframes")
> +        ||!strcmp(tag->key, "hasAudio")
> +        ||!strcmp(tag->key, "hasVideo")
> +        ||!strcmp(tag->key, "hasCuePoints")
> +        ||!strcmp(tag->key, "hasMetadata")
> +        ||!strcmp(tag->key, "hasKeyframes")
> +    ){
> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
> +        return AVERROR(EINVAL);
> +    }
> +    put_amf_string(pb, tag->key);
> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
> +    put_amf_string(pb, tag->value);
> +    (*metadata_count)++;
> +
> +    return 0;
> +}
> +
>  static void write_metadata(AVFormatContext *s, unsigned int ts)
>  {
>      AVIOContext *pb = s->pb;
> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
>      }
>  
>      ff_standardize_creation_time(s);
> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
> -        if(   !strcmp(tag->key, "width")
> -            ||!strcmp(tag->key, "height")
> -            ||!strcmp(tag->key, "videodatarate")
> -            ||!strcmp(tag->key, "framerate")
> -            ||!strcmp(tag->key, "videocodecid")
> -            ||!strcmp(tag->key, "audiodatarate")
> -            ||!strcmp(tag->key, "audiosamplerate")
> -            ||!strcmp(tag->key, "audiosamplesize")
> -            ||!strcmp(tag->key, "stereo")
> -            ||!strcmp(tag->key, "audiocodecid")
> -            ||!strcmp(tag->key, "duration")
> -            ||!strcmp(tag->key, "onMetaData")
> -            ||!strcmp(tag->key, "datasize")
> -            ||!strcmp(tag->key, "lasttimestamp")
> -            ||!strcmp(tag->key, "totalframes")
> -            ||!strcmp(tag->key, "hasAudio")
> -            ||!strcmp(tag->key, "hasVideo")
> -            ||!strcmp(tag->key, "hasCuePoints")
> -            ||!strcmp(tag->key, "hasMetadata")
> -            ||!strcmp(tag->key, "hasKeyframes")
> -        ){
> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
> -            continue;
> -        }
> -        put_amf_string(pb, tag->key);
> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
> -        put_amf_string(pb, tag->value);
> -        metadata_count++;
> -    }
> +
> +    while (tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
> +    while (tag = av_dict_get(flv->meta_dict, "", tag, AV_DICT_IGNORE_SUFFIX))
> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>  
>      if (write_duration_filesize) {
>          put_amf_string(pb, "filesize");
> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>      if (flv->flags & FLV_NO_METADATA) {
>          pb->seekable = 0;
>      } else {
> +        read_metadata_from_file(s, 0, 1);
>          write_metadata(s, 0);
>      }
>  
> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
>      if (meta_upd_flag ||
>          (flv->meta_period == FLV_META_AT_KF        && par->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>          (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type == AVMEDIA_TYPE_VIDEO )) {
> +        read_metadata_from_file(s, ts, 0);
>          write_metadata(s, ts);
>          if (meta_upd_flag)
>              s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>      }
>      flv->filepositions = flv->head_filepositions = NULL;
>      flv->filepositions_count = 0;
> +
> +    if (flv->meta_dict)
> +        av_dict_free(&flv->meta_dict);
> +
> +    avformat_close_input(&flv->meta_ctx);
>  }
>  
>  static const AVOption options[] = {
> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>      { "at_start",      "only once at start",        0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>      { "at_keyframes",  "with every video keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>      { "every_packet",  "with every video packet",   0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
> +    { "meta_filename", "ffmetadata file to import metadata", offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>      { NULL },
>  };
>  

What does this achieve that can't be achieved by other means (namely by
an API-user updating the relevant metadata dictionaries)?

- Andreas

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-03 15:34   ` Andreas Rheinhardt
@ 2023-02-04 10:11     ` Gyan Doshi
  2023-02-04 10:16       ` Paul B Mahol
  2023-02-05 10:37       ` Anton Khirnov
  0 siblings, 2 replies; 15+ messages in thread
From: Gyan Doshi @ 2023-02-04 10:11 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
> Gyan Doshi:
>> Useful, in conjuntion with option meta_period, to vary metadata during
>> stream.
>>
>> File format is ffmetadata.
>> ---
>>   configure            |   2 +-
>>   doc/muxers.texi      |   3 +
>>   libavformat/flvenc.c | 133 +++++++++++++++++++++++++++++++++----------
>>   3 files changed, 107 insertions(+), 31 deletions(-)
>>
>> diff --git a/configure b/configure
>> index 9d78a244a3..de371632c4 100755
>> --- a/configure
>> +++ b/configure
>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>   f4v_muxer_select="mov_muxer"
>>   fifo_muxer_deps="threads"
>>   flac_demuxer_select="flac_parser"
>> -flv_muxer_select="aac_adtstoasc_bsf"
>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>   gxf_muxer_select="pcm_rechunk_bsf"
>>   hds_muxer_select="flv_muxer"
>>   hls_demuxer_select="adts_header ac3_parser"
>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>> index 02ecddf186..000c92b2a7 100644
>> --- a/doc/muxers.texi
>> +++ b/doc/muxers.texi
>> @@ -555,6 +555,9 @@ With every video packet.
>>   @end table
>>   Note that metadata will always be re-emitted if a metadata update event is signalled.
>>   
>> +@item meta_filename
>> +Specify a ffmetadata file from which to load metadata.
>> +
>>   @end table
>>   
>>   @anchor{framecrc}
>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>> index d1c7a493d1..1332b18b41 100644
>> --- a/libavformat/flvenc.c
>> +++ b/libavformat/flvenc.c
>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>       double framerate;
>>       AVCodecParameters *data_par;
>>   
>> +    char *meta_filename;
>> +    AVFormatContext *meta_ctx;
>> +    AVDictionary *meta_dict;
>> +
>>       int flags;
>>       int meta_period;
>>   } FLVContext;
>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>>       avio_w8(pb, !!b);
>>   }
>>   
>> +static int read_metadata_from_file(AVFormatContext *s, unsigned int ts, int header)
>> +{
>> +    FLVContext *flv = s->priv_data;
>> +    const AVInputFormat *meta_format = NULL;
>> +    AVDictionary *current_meta_dict = NULL;
>> +    float timestamp = ts/1000.f;
>> +    int ret;
>> +
>> +    if (!flv->meta_filename)
>> +        return 0;
>> +
>> +    meta_format = av_find_input_format("ffmetadata");
>> +    if (!meta_format) {
>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
>> +        return AVERROR(ENOSYS);
>> +    }
>> +
>> +    avformat_close_input(&flv->meta_ctx);
>> +
>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename, meta_format, NULL);
>> +    if (ret < 0) {
>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s t: %f.", flv->meta_filename, timestamp);
>> +        if (flv->meta_dict)
>> +            av_log(s, AV_LOG_ERROR, " Continuing with old metadata.");
>> +        av_log(s, AV_LOG_ERROR, "\n");
>> +        return ret;
>> +    }
>> +
>> +    if (flv->meta_dict) {
>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>> +        av_dict_free(&flv->meta_dict);
>> +    }
>> +
>> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
>> +    if (ret < 0) {
>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s at %f seconds. Continuing with old metadata.\n", flv->meta_filename, timestamp);
>> +        av_dict_free(&flv->meta_dict);
>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>> +        av_dict_free(&current_meta_dict);
>> +        return ret;
>> +    }
>> +
>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f seconds.\n", flv->meta_filename, header ? "in header" : "in video packet", timestamp);
>> +    av_dict_free(&current_meta_dict);
>> +    avformat_close_input(&flv->meta_ctx);
>> +
>> +    return 0;
>> +}
>> +
>> +static int write_user_metadata_tag(AVFormatContext *s, AVDictionaryEntry *tag, AVIOContext *pb, int *metadata_count)
>> +{
>> +
>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count: %d\n", tag->key, tag->value, *metadata_count);
>> +
>> +    if(   !strcmp(tag->key, "width")
>> +        ||!strcmp(tag->key, "height")
>> +        ||!strcmp(tag->key, "videodatarate")
>> +        ||!strcmp(tag->key, "framerate")
>> +        ||!strcmp(tag->key, "videocodecid")
>> +        ||!strcmp(tag->key, "audiodatarate")
>> +        ||!strcmp(tag->key, "audiosamplerate")
>> +        ||!strcmp(tag->key, "audiosamplesize")
>> +        ||!strcmp(tag->key, "stereo")
>> +        ||!strcmp(tag->key, "audiocodecid")
>> +        ||!strcmp(tag->key, "duration")
>> +        ||!strcmp(tag->key, "onMetaData")
>> +        ||!strcmp(tag->key, "datasize")
>> +        ||!strcmp(tag->key, "lasttimestamp")
>> +        ||!strcmp(tag->key, "totalframes")
>> +        ||!strcmp(tag->key, "hasAudio")
>> +        ||!strcmp(tag->key, "hasVideo")
>> +        ||!strcmp(tag->key, "hasCuePoints")
>> +        ||!strcmp(tag->key, "hasMetadata")
>> +        ||!strcmp(tag->key, "hasKeyframes")
>> +    ){
>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
>> +        return AVERROR(EINVAL);
>> +    }
>> +    put_amf_string(pb, tag->key);
>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>> +    put_amf_string(pb, tag->value);
>> +    (*metadata_count)++;
>> +
>> +    return 0;
>> +}
>> +
>>   static void write_metadata(AVFormatContext *s, unsigned int ts)
>>   {
>>       AVIOContext *pb = s->pb;
>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s, unsigned int ts)
>>       }
>>   
>>       ff_standardize_creation_time(s);
>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>> -        if(   !strcmp(tag->key, "width")
>> -            ||!strcmp(tag->key, "height")
>> -            ||!strcmp(tag->key, "videodatarate")
>> -            ||!strcmp(tag->key, "framerate")
>> -            ||!strcmp(tag->key, "videocodecid")
>> -            ||!strcmp(tag->key, "audiodatarate")
>> -            ||!strcmp(tag->key, "audiosamplerate")
>> -            ||!strcmp(tag->key, "audiosamplesize")
>> -            ||!strcmp(tag->key, "stereo")
>> -            ||!strcmp(tag->key, "audiocodecid")
>> -            ||!strcmp(tag->key, "duration")
>> -            ||!strcmp(tag->key, "onMetaData")
>> -            ||!strcmp(tag->key, "datasize")
>> -            ||!strcmp(tag->key, "lasttimestamp")
>> -            ||!strcmp(tag->key, "totalframes")
>> -            ||!strcmp(tag->key, "hasAudio")
>> -            ||!strcmp(tag->key, "hasVideo")
>> -            ||!strcmp(tag->key, "hasCuePoints")
>> -            ||!strcmp(tag->key, "hasMetadata")
>> -            ||!strcmp(tag->key, "hasKeyframes")
>> -        ){
>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n", tag->key);
>> -            continue;
>> -        }
>> -        put_amf_string(pb, tag->key);
>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>> -        put_amf_string(pb, tag->value);
>> -        metadata_count++;
>> -    }
>> +
>> +    while (tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>> +    while (tag = av_dict_get(flv->meta_dict, "", tag, AV_DICT_IGNORE_SUFFIX))
>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>   
>>       if (write_duration_filesize) {
>>           put_amf_string(pb, "filesize");
>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>       if (flv->flags & FLV_NO_METADATA) {
>>           pb->seekable = 0;
>>       } else {
>> +        read_metadata_from_file(s, 0, 1);
>>           write_metadata(s, 0);
>>       }
>>   
>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt)
>>       if (meta_upd_flag ||
>>           (flv->meta_period == FLV_META_AT_KF        && par->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>           (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type == AVMEDIA_TYPE_VIDEO )) {
>> +        read_metadata_from_file(s, ts, 0);
>>           write_metadata(s, ts);
>>           if (meta_upd_flag)
>>               s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>       }
>>       flv->filepositions = flv->head_filepositions = NULL;
>>       flv->filepositions_count = 0;
>> +
>> +    if (flv->meta_dict)
>> +        av_dict_free(&flv->meta_dict);
>> +
>> +    avformat_close_input(&flv->meta_ctx);
>>   }
>>   
>>   static const AVOption options[] = {
>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>       { "at_start",      "only once at start",        0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>       { "at_keyframes",  "with every video keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>       { "every_packet",  "with every video packet",   0, AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>> +    { "meta_filename", "ffmetadata file to import metadata", offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>       { NULL },
>>   };
>>   
> What does this achieve that can't be achieved by other means (namely by
> an API-user updating the relevant metadata dictionaries)?

This option together with meta_period is for CLI users. Similar to 
drawtext's textfile + reload.
It's been used in production for over a year at a client. Think it will 
be broadly useful.

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:11     ` Gyan Doshi
@ 2023-02-04 10:16       ` Paul B Mahol
  2023-02-04 10:24         ` Gyan Doshi
  2023-02-05 10:37       ` Anton Khirnov
  1 sibling, 1 reply; 15+ messages in thread
From: Paul B Mahol @ 2023-02-04 10:16 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>
>
> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>> Gyan Doshi:
>>> Useful, in conjuntion with option meta_period, to vary metadata during
>>> stream.
>>>
>>> File format is ffmetadata.
>>> ---
>>>   configure            |   2 +-
>>>   doc/muxers.texi      |   3 +
>>>   libavformat/flvenc.c | 133 +++++++++++++++++++++++++++++++++----------
>>>   3 files changed, 107 insertions(+), 31 deletions(-)
>>>
>>> diff --git a/configure b/configure
>>> index 9d78a244a3..de371632c4 100755
>>> --- a/configure
>>> +++ b/configure
>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>   f4v_muxer_select="mov_muxer"
>>>   fifo_muxer_deps="threads"
>>>   flac_demuxer_select="flac_parser"
>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>   gxf_muxer_select="pcm_rechunk_bsf"
>>>   hds_muxer_select="flv_muxer"
>>>   hls_demuxer_select="adts_header ac3_parser"
>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>> index 02ecddf186..000c92b2a7 100644
>>> --- a/doc/muxers.texi
>>> +++ b/doc/muxers.texi
>>> @@ -555,6 +555,9 @@ With every video packet.
>>>   @end table
>>>   Note that metadata will always be re-emitted if a metadata update event
>>> is signalled.
>>>
>>> +@item meta_filename
>>> +Specify a ffmetadata file from which to load metadata.
>>> +
>>>   @end table
>>>
>>>   @anchor{framecrc}
>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>> index d1c7a493d1..1332b18b41 100644
>>> --- a/libavformat/flvenc.c
>>> +++ b/libavformat/flvenc.c
>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>       double framerate;
>>>       AVCodecParameters *data_par;
>>>
>>> +    char *meta_filename;
>>> +    AVFormatContext *meta_ctx;
>>> +    AVDictionary *meta_dict;
>>> +
>>>       int flags;
>>>       int meta_period;
>>>   } FLVContext;
>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>>>       avio_w8(pb, !!b);
>>>   }
>>>
>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned int ts,
>>> int header)
>>> +{
>>> +    FLVContext *flv = s->priv_data;
>>> +    const AVInputFormat *meta_format = NULL;
>>> +    AVDictionary *current_meta_dict = NULL;
>>> +    float timestamp = ts/1000.f;
>>> +    int ret;
>>> +
>>> +    if (!flv->meta_filename)
>>> +        return 0;
>>> +
>>> +    meta_format = av_find_input_format("ffmetadata");
>>> +    if (!meta_format) {
>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
>>> +        return AVERROR(ENOSYS);
>>> +    }
>>> +
>>> +    avformat_close_input(&flv->meta_ctx);
>>> +
>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>> meta_format, NULL);
>>> +    if (ret < 0) {
>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s t:
>>> %f.", flv->meta_filename, timestamp);
>>> +        if (flv->meta_dict)
>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old metadata.");
>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    if (flv->meta_dict) {
>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>> +        av_dict_free(&flv->meta_dict);
>>> +    }
>>> +
>>> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
>>> +    if (ret < 0) {
>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s at
>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>> timestamp);
>>> +        av_dict_free(&flv->meta_dict);
>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>> +        av_dict_free(&current_meta_dict);
>>> +        return ret;
>>> +    }
>>> +
>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f
>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>> packet", timestamp);
>>> +    av_dict_free(&current_meta_dict);
>>> +    avformat_close_input(&flv->meta_ctx);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int write_user_metadata_tag(AVFormatContext *s, AVDictionaryEntry
>>> *tag, AVIOContext *pb, int *metadata_count)
>>> +{
>>> +
>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count: %d\n",
>>> tag->key, tag->value, *metadata_count);
>>> +
>>> +    if(   !strcmp(tag->key, "width")
>>> +        ||!strcmp(tag->key, "height")
>>> +        ||!strcmp(tag->key, "videodatarate")
>>> +        ||!strcmp(tag->key, "framerate")
>>> +        ||!strcmp(tag->key, "videocodecid")
>>> +        ||!strcmp(tag->key, "audiodatarate")
>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>> +        ||!strcmp(tag->key, "stereo")
>>> +        ||!strcmp(tag->key, "audiocodecid")
>>> +        ||!strcmp(tag->key, "duration")
>>> +        ||!strcmp(tag->key, "onMetaData")
>>> +        ||!strcmp(tag->key, "datasize")
>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>> +        ||!strcmp(tag->key, "totalframes")
>>> +        ||!strcmp(tag->key, "hasAudio")
>>> +        ||!strcmp(tag->key, "hasVideo")
>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>> +        ||!strcmp(tag->key, "hasMetadata")
>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>> +    ){
>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>> tag->key);
>>> +        return AVERROR(EINVAL);
>>> +    }
>>> +    put_amf_string(pb, tag->key);
>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>> +    put_amf_string(pb, tag->value);
>>> +    (*metadata_count)++;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>   {
>>>       AVIOContext *pb = s->pb;
>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s,
>>> unsigned int ts)
>>>       }
>>>
>>>       ff_standardize_creation_time(s);
>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>> -        if(   !strcmp(tag->key, "width")
>>> -            ||!strcmp(tag->key, "height")
>>> -            ||!strcmp(tag->key, "videodatarate")
>>> -            ||!strcmp(tag->key, "framerate")
>>> -            ||!strcmp(tag->key, "videocodecid")
>>> -            ||!strcmp(tag->key, "audiodatarate")
>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>> -            ||!strcmp(tag->key, "stereo")
>>> -            ||!strcmp(tag->key, "audiocodecid")
>>> -            ||!strcmp(tag->key, "duration")
>>> -            ||!strcmp(tag->key, "onMetaData")
>>> -            ||!strcmp(tag->key, "datasize")
>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>> -            ||!strcmp(tag->key, "totalframes")
>>> -            ||!strcmp(tag->key, "hasAudio")
>>> -            ||!strcmp(tag->key, "hasVideo")
>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>> -            ||!strcmp(tag->key, "hasMetadata")
>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>> -        ){
>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>> tag->key);
>>> -            continue;
>>> -        }
>>> -        put_amf_string(pb, tag->key);
>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>> -        put_amf_string(pb, tag->value);
>>> -        metadata_count++;
>>> -    }
>>> +
>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>> AV_DICT_IGNORE_SUFFIX))
>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>> AV_DICT_IGNORE_SUFFIX))
>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>
>>>       if (write_duration_filesize) {
>>>           put_amf_string(pb, "filesize");
>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>       if (flv->flags & FLV_NO_METADATA) {
>>>           pb->seekable = 0;
>>>       } else {
>>> +        read_metadata_from_file(s, 0, 1);
>>>           write_metadata(s, 0);
>>>       }
>>>
>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>> AVPacket *pkt)
>>>       if (meta_upd_flag ||
>>>           (flv->meta_period == FLV_META_AT_KF        && par->codec_type
>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>           (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type
>>> == AVMEDIA_TYPE_VIDEO )) {
>>> +        read_metadata_from_file(s, ts, 0);
>>>           write_metadata(s, ts);
>>>           if (meta_upd_flag)
>>>               s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>       }
>>>       flv->filepositions = flv->head_filepositions = NULL;
>>>       flv->filepositions_count = 0;
>>> +
>>> +    if (flv->meta_dict)
>>> +        av_dict_free(&flv->meta_dict);
>>> +
>>> +    avformat_close_input(&flv->meta_ctx);
>>>   }
>>>
>>>   static const AVOption options[] = {
>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>       { "at_start",      "only once at start",        0,
>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX,
>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>       { "at_keyframes",  "with every video keyframe", 0,
>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX,
>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>       { "every_packet",  "with every video packet",   0,
>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX,
>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str = NULL},
>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>       { NULL },
>>>   };
>>>
>> What does this achieve that can't be achieved by other means (namely by
>> an API-user updating the relevant metadata dictionaries)?
>
> This option together with meta_period is for CLI users. Similar to
> drawtext's textfile + reload.
> It's been used in production for over a year at a client. Think it will
> be broadly useful.

Not everything that is useful now is correct on long term.

Muxer specific metadata handling by reading from files is big hack.

NAK.

>
> Regards,
> Gyan
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:16       ` Paul B Mahol
@ 2023-02-04 10:24         ` Gyan Doshi
  2023-02-04 10:32           ` Paul B Mahol
  0 siblings, 1 reply; 15+ messages in thread
From: Gyan Doshi @ 2023-02-04 10:24 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-04 03:46 pm, Paul B Mahol wrote:
> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>
>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>> Gyan Doshi:
>>>> Useful, in conjuntion with option meta_period, to vary metadata during
>>>> stream.
>>>>
>>>> File format is ffmetadata.
>>>> ---
>>>>    configure            |   2 +-
>>>>    doc/muxers.texi      |   3 +
>>>>    libavformat/flvenc.c | 133 +++++++++++++++++++++++++++++++++----------
>>>>    3 files changed, 107 insertions(+), 31 deletions(-)
>>>>
>>>> diff --git a/configure b/configure
>>>> index 9d78a244a3..de371632c4 100755
>>>> --- a/configure
>>>> +++ b/configure
>>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>>    f4v_muxer_select="mov_muxer"
>>>>    fifo_muxer_deps="threads"
>>>>    flac_demuxer_select="flac_parser"
>>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>>    gxf_muxer_select="pcm_rechunk_bsf"
>>>>    hds_muxer_select="flv_muxer"
>>>>    hls_demuxer_select="adts_header ac3_parser"
>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>> index 02ecddf186..000c92b2a7 100644
>>>> --- a/doc/muxers.texi
>>>> +++ b/doc/muxers.texi
>>>> @@ -555,6 +555,9 @@ With every video packet.
>>>>    @end table
>>>>    Note that metadata will always be re-emitted if a metadata update event
>>>> is signalled.
>>>>
>>>> +@item meta_filename
>>>> +Specify a ffmetadata file from which to load metadata.
>>>> +
>>>>    @end table
>>>>
>>>>    @anchor{framecrc}
>>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>>> index d1c7a493d1..1332b18b41 100644
>>>> --- a/libavformat/flvenc.c
>>>> +++ b/libavformat/flvenc.c
>>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>>        double framerate;
>>>>        AVCodecParameters *data_par;
>>>>
>>>> +    char *meta_filename;
>>>> +    AVFormatContext *meta_ctx;
>>>> +    AVDictionary *meta_dict;
>>>> +
>>>>        int flags;
>>>>        int meta_period;
>>>>    } FLVContext;
>>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>>>>        avio_w8(pb, !!b);
>>>>    }
>>>>
>>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned int ts,
>>>> int header)
>>>> +{
>>>> +    FLVContext *flv = s->priv_data;
>>>> +    const AVInputFormat *meta_format = NULL;
>>>> +    AVDictionary *current_meta_dict = NULL;
>>>> +    float timestamp = ts/1000.f;
>>>> +    int ret;
>>>> +
>>>> +    if (!flv->meta_filename)
>>>> +        return 0;
>>>> +
>>>> +    meta_format = av_find_input_format("ffmetadata");
>>>> +    if (!meta_format) {
>>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
>>>> +        return AVERROR(ENOSYS);
>>>> +    }
>>>> +
>>>> +    avformat_close_input(&flv->meta_ctx);
>>>> +
>>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>>> meta_format, NULL);
>>>> +    if (ret < 0) {
>>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s t:
>>>> %f.", flv->meta_filename, timestamp);
>>>> +        if (flv->meta_dict)
>>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old metadata.");
>>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    if (flv->meta_dict) {
>>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>>> +        av_dict_free(&flv->meta_dict);
>>>> +    }
>>>> +
>>>> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
>>>> +    if (ret < 0) {
>>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s at
>>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>>> timestamp);
>>>> +        av_dict_free(&flv->meta_dict);
>>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>>> +        av_dict_free(&current_meta_dict);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f
>>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>>> packet", timestamp);
>>>> +    av_dict_free(&current_meta_dict);
>>>> +    avformat_close_input(&flv->meta_ctx);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int write_user_metadata_tag(AVFormatContext *s, AVDictionaryEntry
>>>> *tag, AVIOContext *pb, int *metadata_count)
>>>> +{
>>>> +
>>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count: %d\n",
>>>> tag->key, tag->value, *metadata_count);
>>>> +
>>>> +    if(   !strcmp(tag->key, "width")
>>>> +        ||!strcmp(tag->key, "height")
>>>> +        ||!strcmp(tag->key, "videodatarate")
>>>> +        ||!strcmp(tag->key, "framerate")
>>>> +        ||!strcmp(tag->key, "videocodecid")
>>>> +        ||!strcmp(tag->key, "audiodatarate")
>>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>>> +        ||!strcmp(tag->key, "stereo")
>>>> +        ||!strcmp(tag->key, "audiocodecid")
>>>> +        ||!strcmp(tag->key, "duration")
>>>> +        ||!strcmp(tag->key, "onMetaData")
>>>> +        ||!strcmp(tag->key, "datasize")
>>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>>> +        ||!strcmp(tag->key, "totalframes")
>>>> +        ||!strcmp(tag->key, "hasAudio")
>>>> +        ||!strcmp(tag->key, "hasVideo")
>>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>>> +        ||!strcmp(tag->key, "hasMetadata")
>>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>>> +    ){
>>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>> tag->key);
>>>> +        return AVERROR(EINVAL);
>>>> +    }
>>>> +    put_amf_string(pb, tag->key);
>>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>> +    put_amf_string(pb, tag->value);
>>>> +    (*metadata_count)++;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>    static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>>    {
>>>>        AVIOContext *pb = s->pb;
>>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s,
>>>> unsigned int ts)
>>>>        }
>>>>
>>>>        ff_standardize_creation_time(s);
>>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>>> -        if(   !strcmp(tag->key, "width")
>>>> -            ||!strcmp(tag->key, "height")
>>>> -            ||!strcmp(tag->key, "videodatarate")
>>>> -            ||!strcmp(tag->key, "framerate")
>>>> -            ||!strcmp(tag->key, "videocodecid")
>>>> -            ||!strcmp(tag->key, "audiodatarate")
>>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>>> -            ||!strcmp(tag->key, "stereo")
>>>> -            ||!strcmp(tag->key, "audiocodecid")
>>>> -            ||!strcmp(tag->key, "duration")
>>>> -            ||!strcmp(tag->key, "onMetaData")
>>>> -            ||!strcmp(tag->key, "datasize")
>>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>>> -            ||!strcmp(tag->key, "totalframes")
>>>> -            ||!strcmp(tag->key, "hasAudio")
>>>> -            ||!strcmp(tag->key, "hasVideo")
>>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>>> -            ||!strcmp(tag->key, "hasMetadata")
>>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>>> -        ){
>>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>> tag->key);
>>>> -            continue;
>>>> -        }
>>>> -        put_amf_string(pb, tag->key);
>>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>> -        put_amf_string(pb, tag->value);
>>>> -        metadata_count++;
>>>> -    }
>>>> +
>>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>>> AV_DICT_IGNORE_SUFFIX))
>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>>> AV_DICT_IGNORE_SUFFIX))
>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>
>>>>        if (write_duration_filesize) {
>>>>            put_amf_string(pb, "filesize");
>>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>>        if (flv->flags & FLV_NO_METADATA) {
>>>>            pb->seekable = 0;
>>>>        } else {
>>>> +        read_metadata_from_file(s, 0, 1);
>>>>            write_metadata(s, 0);
>>>>        }
>>>>
>>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>>> AVPacket *pkt)
>>>>        if (meta_upd_flag ||
>>>>            (flv->meta_period == FLV_META_AT_KF        && par->codec_type
>>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>            (flv->meta_period == FLV_META_EVERY_PACKET && par->codec_type
>>>> == AVMEDIA_TYPE_VIDEO )) {
>>>> +        read_metadata_from_file(s, ts, 0);
>>>>            write_metadata(s, ts);
>>>>            if (meta_upd_flag)
>>>>                s->event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>>        }
>>>>        flv->filepositions = flv->head_filepositions = NULL;
>>>>        flv->filepositions_count = 0;
>>>> +
>>>> +    if (flv->meta_dict)
>>>> +        av_dict_free(&flv->meta_dict);
>>>> +
>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>    }
>>>>
>>>>    static const AVOption options[] = {
>>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>>        { "at_start",      "only once at start",        0,
>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX,
>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>        { "at_keyframes",  "with every video keyframe", 0,
>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX,
>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>        { "every_packet",  "with every video packet",   0,
>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX,
>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str = NULL},
>>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>>        { NULL },
>>>>    };
>>>>
>>> What does this achieve that can't be achieved by other means (namely by
>>> an API-user updating the relevant metadata dictionaries)?
>> This option together with meta_period is for CLI users. Similar to
>> drawtext's textfile + reload.
>> It's been used in production for over a year at a client. Think it will
>> be broadly useful.
> Not everything that is useful now is correct on long term.
>
> Muxer specific metadata handling by reading from files is big hack.

What would you suggest?

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:24         ` Gyan Doshi
@ 2023-02-04 10:32           ` Paul B Mahol
  2023-02-04 10:42             ` Gyan Doshi
  0 siblings, 1 reply; 15+ messages in thread
From: Paul B Mahol @ 2023-02-04 10:32 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>
>
> On 2023-02-04 03:46 pm, Paul B Mahol wrote:
>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>
>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>> Gyan Doshi:
>>>>> Useful, in conjuntion with option meta_period, to vary metadata during
>>>>> stream.
>>>>>
>>>>> File format is ffmetadata.
>>>>> ---
>>>>>    configure            |   2 +-
>>>>>    doc/muxers.texi      |   3 +
>>>>>    libavformat/flvenc.c | 133
>>>>> +++++++++++++++++++++++++++++++++----------
>>>>>    3 files changed, 107 insertions(+), 31 deletions(-)
>>>>>
>>>>> diff --git a/configure b/configure
>>>>> index 9d78a244a3..de371632c4 100755
>>>>> --- a/configure
>>>>> +++ b/configure
>>>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>>>    f4v_muxer_select="mov_muxer"
>>>>>    fifo_muxer_deps="threads"
>>>>>    flac_demuxer_select="flac_parser"
>>>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>>>    gxf_muxer_select="pcm_rechunk_bsf"
>>>>>    hds_muxer_select="flv_muxer"
>>>>>    hls_demuxer_select="adts_header ac3_parser"
>>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>>> index 02ecddf186..000c92b2a7 100644
>>>>> --- a/doc/muxers.texi
>>>>> +++ b/doc/muxers.texi
>>>>> @@ -555,6 +555,9 @@ With every video packet.
>>>>>    @end table
>>>>>    Note that metadata will always be re-emitted if a metadata update
>>>>> event
>>>>> is signalled.
>>>>>
>>>>> +@item meta_filename
>>>>> +Specify a ffmetadata file from which to load metadata.
>>>>> +
>>>>>    @end table
>>>>>
>>>>>    @anchor{framecrc}
>>>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>>>> index d1c7a493d1..1332b18b41 100644
>>>>> --- a/libavformat/flvenc.c
>>>>> +++ b/libavformat/flvenc.c
>>>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>>>        double framerate;
>>>>>        AVCodecParameters *data_par;
>>>>>
>>>>> +    char *meta_filename;
>>>>> +    AVFormatContext *meta_ctx;
>>>>> +    AVDictionary *meta_dict;
>>>>> +
>>>>>        int flags;
>>>>>        int meta_period;
>>>>>    } FLVContext;
>>>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>>>>>        avio_w8(pb, !!b);
>>>>>    }
>>>>>
>>>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned int
>>>>> ts,
>>>>> int header)
>>>>> +{
>>>>> +    FLVContext *flv = s->priv_data;
>>>>> +    const AVInputFormat *meta_format = NULL;
>>>>> +    AVDictionary *current_meta_dict = NULL;
>>>>> +    float timestamp = ts/1000.f;
>>>>> +    int ret;
>>>>> +
>>>>> +    if (!flv->meta_filename)
>>>>> +        return 0;
>>>>> +
>>>>> +    meta_format = av_find_input_format("ffmetadata");
>>>>> +    if (!meta_format) {
>>>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
>>>>> +        return AVERROR(ENOSYS);
>>>>> +    }
>>>>> +
>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>> +
>>>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>>>> meta_format, NULL);
>>>>> +    if (ret < 0) {
>>>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s
>>>>> t:
>>>>> %f.", flv->meta_filename, timestamp);
>>>>> +        if (flv->meta_dict)
>>>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old
>>>>> metadata.");
>>>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    if (flv->meta_dict) {
>>>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>>>> +        av_dict_free(&flv->meta_dict);
>>>>> +    }
>>>>> +
>>>>> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
>>>>> +    if (ret < 0) {
>>>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s
>>>>> at
>>>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>>>> timestamp);
>>>>> +        av_dict_free(&flv->meta_dict);
>>>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>>>> +        av_dict_free(&current_meta_dict);
>>>>> +        return ret;
>>>>> +    }
>>>>> +
>>>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f
>>>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>>>> packet", timestamp);
>>>>> +    av_dict_free(&current_meta_dict);
>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int write_user_metadata_tag(AVFormatContext *s,
>>>>> AVDictionaryEntry
>>>>> *tag, AVIOContext *pb, int *metadata_count)
>>>>> +{
>>>>> +
>>>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count:
>>>>> %d\n",
>>>>> tag->key, tag->value, *metadata_count);
>>>>> +
>>>>> +    if(   !strcmp(tag->key, "width")
>>>>> +        ||!strcmp(tag->key, "height")
>>>>> +        ||!strcmp(tag->key, "videodatarate")
>>>>> +        ||!strcmp(tag->key, "framerate")
>>>>> +        ||!strcmp(tag->key, "videocodecid")
>>>>> +        ||!strcmp(tag->key, "audiodatarate")
>>>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>>>> +        ||!strcmp(tag->key, "stereo")
>>>>> +        ||!strcmp(tag->key, "audiocodecid")
>>>>> +        ||!strcmp(tag->key, "duration")
>>>>> +        ||!strcmp(tag->key, "onMetaData")
>>>>> +        ||!strcmp(tag->key, "datasize")
>>>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>>>> +        ||!strcmp(tag->key, "totalframes")
>>>>> +        ||!strcmp(tag->key, "hasAudio")
>>>>> +        ||!strcmp(tag->key, "hasVideo")
>>>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>>>> +        ||!strcmp(tag->key, "hasMetadata")
>>>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>>>> +    ){
>>>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>> tag->key);
>>>>> +        return AVERROR(EINVAL);
>>>>> +    }
>>>>> +    put_amf_string(pb, tag->key);
>>>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>> +    put_amf_string(pb, tag->value);
>>>>> +    (*metadata_count)++;
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>>>    {
>>>>>        AVIOContext *pb = s->pb;
>>>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s,
>>>>> unsigned int ts)
>>>>>        }
>>>>>
>>>>>        ff_standardize_creation_time(s);
>>>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>>>> -        if(   !strcmp(tag->key, "width")
>>>>> -            ||!strcmp(tag->key, "height")
>>>>> -            ||!strcmp(tag->key, "videodatarate")
>>>>> -            ||!strcmp(tag->key, "framerate")
>>>>> -            ||!strcmp(tag->key, "videocodecid")
>>>>> -            ||!strcmp(tag->key, "audiodatarate")
>>>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>>>> -            ||!strcmp(tag->key, "stereo")
>>>>> -            ||!strcmp(tag->key, "audiocodecid")
>>>>> -            ||!strcmp(tag->key, "duration")
>>>>> -            ||!strcmp(tag->key, "onMetaData")
>>>>> -            ||!strcmp(tag->key, "datasize")
>>>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>>>> -            ||!strcmp(tag->key, "totalframes")
>>>>> -            ||!strcmp(tag->key, "hasAudio")
>>>>> -            ||!strcmp(tag->key, "hasVideo")
>>>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>>>> -            ||!strcmp(tag->key, "hasMetadata")
>>>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>>>> -        ){
>>>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>> tag->key);
>>>>> -            continue;
>>>>> -        }
>>>>> -        put_amf_string(pb, tag->key);
>>>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>> -        put_amf_string(pb, tag->value);
>>>>> -        metadata_count++;
>>>>> -    }
>>>>> +
>>>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>
>>>>>        if (write_duration_filesize) {
>>>>>            put_amf_string(pb, "filesize");
>>>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>>>        if (flv->flags & FLV_NO_METADATA) {
>>>>>            pb->seekable = 0;
>>>>>        } else {
>>>>> +        read_metadata_from_file(s, 0, 1);
>>>>>            write_metadata(s, 0);
>>>>>        }
>>>>>
>>>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>>>> AVPacket *pkt)
>>>>>        if (meta_upd_flag ||
>>>>>            (flv->meta_period == FLV_META_AT_KF        &&
>>>>> par->codec_type
>>>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>>            (flv->meta_period == FLV_META_EVERY_PACKET &&
>>>>> par->codec_type
>>>>> == AVMEDIA_TYPE_VIDEO )) {
>>>>> +        read_metadata_from_file(s, ts, 0);
>>>>>            write_metadata(s, ts);
>>>>>            if (meta_upd_flag)
>>>>>                s->event_flags &=
>>>>> ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>>>        }
>>>>>        flv->filepositions = flv->head_filepositions = NULL;
>>>>>        flv->filepositions_count = 0;
>>>>> +
>>>>> +    if (flv->meta_dict)
>>>>> +        av_dict_free(&flv->meta_dict);
>>>>> +
>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>    }
>>>>>
>>>>>    static const AVOption options[] = {
>>>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>>>        { "at_start",      "only once at start",        0,
>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX,
>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>        { "at_keyframes",  "with every video keyframe", 0,
>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX,
>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>        { "every_packet",  "with every video packet",   0,
>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX,
>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str =
>>>>> NULL},
>>>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>>>        { NULL },
>>>>>    };
>>>>>
>>>> What does this achieve that can't be achieved by other means (namely by
>>>> an API-user updating the relevant metadata dictionaries)?
>>> This option together with meta_period is for CLI users. Similar to
>>> drawtext's textfile + reload.
>>> It's been used in production for over a year at a client. Think it will
>>> be broadly useful.
>> Not everything that is useful now is correct on long term.
>>
>> Muxer specific metadata handling by reading from files is big hack.
>
> What would you suggest?

There should be way to handle this generically see Anton's patch that
adds new AVOption type.

Also those strcmp() spaghetti lines are unacceptable.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:32           ` Paul B Mahol
@ 2023-02-04 10:42             ` Gyan Doshi
  2023-02-05 10:01               ` Gyan Doshi
  0 siblings, 1 reply; 15+ messages in thread
From: Gyan Doshi @ 2023-02-04 10:42 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-04 04:02 pm, Paul B Mahol wrote:
> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>
>> On 2023-02-04 03:46 pm, Paul B Mahol wrote:
>>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>>> Gyan Doshi:
>>>>>> Useful, in conjuntion with option meta_period, to vary metadata during
>>>>>> stream.
>>>>>>
>>>>>> File format is ffmetadata.
>>>>>> ---
>>>>>>     configure            |   2 +-
>>>>>>     doc/muxers.texi      |   3 +
>>>>>>     libavformat/flvenc.c | 133
>>>>>> +++++++++++++++++++++++++++++++++----------
>>>>>>     3 files changed, 107 insertions(+), 31 deletions(-)
>>>>>>
>>>>>> diff --git a/configure b/configure
>>>>>> index 9d78a244a3..de371632c4 100755
>>>>>> --- a/configure
>>>>>> +++ b/configure
>>>>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>>>>     f4v_muxer_select="mov_muxer"
>>>>>>     fifo_muxer_deps="threads"
>>>>>>     flac_demuxer_select="flac_parser"
>>>>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>>>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>>>>     gxf_muxer_select="pcm_rechunk_bsf"
>>>>>>     hds_muxer_select="flv_muxer"
>>>>>>     hls_demuxer_select="adts_header ac3_parser"
>>>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>>>> index 02ecddf186..000c92b2a7 100644
>>>>>> --- a/doc/muxers.texi
>>>>>> +++ b/doc/muxers.texi
>>>>>> @@ -555,6 +555,9 @@ With every video packet.
>>>>>>     @end table
>>>>>>     Note that metadata will always be re-emitted if a metadata update
>>>>>> event
>>>>>> is signalled.
>>>>>>
>>>>>> +@item meta_filename
>>>>>> +Specify a ffmetadata file from which to load metadata.
>>>>>> +
>>>>>>     @end table
>>>>>>
>>>>>>     @anchor{framecrc}
>>>>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>>>>> index d1c7a493d1..1332b18b41 100644
>>>>>> --- a/libavformat/flvenc.c
>>>>>> +++ b/libavformat/flvenc.c
>>>>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>>>>         double framerate;
>>>>>>         AVCodecParameters *data_par;
>>>>>>
>>>>>> +    char *meta_filename;
>>>>>> +    AVFormatContext *meta_ctx;
>>>>>> +    AVDictionary *meta_dict;
>>>>>> +
>>>>>>         int flags;
>>>>>>         int meta_period;
>>>>>>     } FLVContext;
>>>>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, int b)
>>>>>>         avio_w8(pb, !!b);
>>>>>>     }
>>>>>>
>>>>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned int
>>>>>> ts,
>>>>>> int header)
>>>>>> +{
>>>>>> +    FLVContext *flv = s->priv_data;
>>>>>> +    const AVInputFormat *meta_format = NULL;
>>>>>> +    AVDictionary *current_meta_dict = NULL;
>>>>>> +    float timestamp = ts/1000.f;
>>>>>> +    int ret;
>>>>>> +
>>>>>> +    if (!flv->meta_filename)
>>>>>> +        return 0;
>>>>>> +
>>>>>> +    meta_format = av_find_input_format("ffmetadata");
>>>>>> +    if (!meta_format) {
>>>>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not found.\n");
>>>>>> +        return AVERROR(ENOSYS);
>>>>>> +    }
>>>>>> +
>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>> +
>>>>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>>>>> meta_format, NULL);
>>>>>> +    if (ret < 0) {
>>>>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from file %s
>>>>>> t:
>>>>>> %f.", flv->meta_filename, timestamp);
>>>>>> +        if (flv->meta_dict)
>>>>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old
>>>>>> metadata.");
>>>>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>>>>> +        return ret;
>>>>>> +    }
>>>>>> +
>>>>>> +    if (flv->meta_dict) {
>>>>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>> +    }
>>>>>> +
>>>>>> +    ret = av_dict_copy(&flv->meta_dict, flv->meta_ctx->metadata, 0);
>>>>>> +    if (ret < 0) {
>>>>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata from %s
>>>>>> at
>>>>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>>>>> timestamp);
>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>>>>> +        av_dict_free(&current_meta_dict);
>>>>>> +        return ret;
>>>>>> +    }
>>>>>> +
>>>>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s at %f
>>>>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>>>>> packet", timestamp);
>>>>>> +    av_dict_free(&current_meta_dict);
>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int write_user_metadata_tag(AVFormatContext *s,
>>>>>> AVDictionaryEntry
>>>>>> *tag, AVIOContext *pb, int *metadata_count)
>>>>>> +{
>>>>>> +
>>>>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count:
>>>>>> %d\n",
>>>>>> tag->key, tag->value, *metadata_count);
>>>>>> +
>>>>>> +    if(   !strcmp(tag->key, "width")
>>>>>> +        ||!strcmp(tag->key, "height")
>>>>>> +        ||!strcmp(tag->key, "videodatarate")
>>>>>> +        ||!strcmp(tag->key, "framerate")
>>>>>> +        ||!strcmp(tag->key, "videocodecid")
>>>>>> +        ||!strcmp(tag->key, "audiodatarate")
>>>>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>>>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>>>>> +        ||!strcmp(tag->key, "stereo")
>>>>>> +        ||!strcmp(tag->key, "audiocodecid")
>>>>>> +        ||!strcmp(tag->key, "duration")
>>>>>> +        ||!strcmp(tag->key, "onMetaData")
>>>>>> +        ||!strcmp(tag->key, "datasize")
>>>>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>>>>> +        ||!strcmp(tag->key, "totalframes")
>>>>>> +        ||!strcmp(tag->key, "hasAudio")
>>>>>> +        ||!strcmp(tag->key, "hasVideo")
>>>>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>>>>> +        ||!strcmp(tag->key, "hasMetadata")
>>>>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>>>>> +    ){
>>>>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>> tag->key);
>>>>>> +        return AVERROR(EINVAL);
>>>>>> +    }
>>>>>> +    put_amf_string(pb, tag->key);
>>>>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>> +    put_amf_string(pb, tag->value);
>>>>>> +    (*metadata_count)++;
>>>>>> +
>>>>>> +    return 0;
>>>>>> +}
>>>>>> +
>>>>>>     static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>>>>     {
>>>>>>         AVIOContext *pb = s->pb;
>>>>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext *s,
>>>>>> unsigned int ts)
>>>>>>         }
>>>>>>
>>>>>>         ff_standardize_creation_time(s);
>>>>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>>>>> -        if(   !strcmp(tag->key, "width")
>>>>>> -            ||!strcmp(tag->key, "height")
>>>>>> -            ||!strcmp(tag->key, "videodatarate")
>>>>>> -            ||!strcmp(tag->key, "framerate")
>>>>>> -            ||!strcmp(tag->key, "videocodecid")
>>>>>> -            ||!strcmp(tag->key, "audiodatarate")
>>>>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>>>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>>>>> -            ||!strcmp(tag->key, "stereo")
>>>>>> -            ||!strcmp(tag->key, "audiocodecid")
>>>>>> -            ||!strcmp(tag->key, "duration")
>>>>>> -            ||!strcmp(tag->key, "onMetaData")
>>>>>> -            ||!strcmp(tag->key, "datasize")
>>>>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>>>>> -            ||!strcmp(tag->key, "totalframes")
>>>>>> -            ||!strcmp(tag->key, "hasAudio")
>>>>>> -            ||!strcmp(tag->key, "hasVideo")
>>>>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>>>>> -            ||!strcmp(tag->key, "hasMetadata")
>>>>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>>>>> -        ){
>>>>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>> tag->key);
>>>>>> -            continue;
>>>>>> -        }
>>>>>> -        put_amf_string(pb, tag->key);
>>>>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>> -        put_amf_string(pb, tag->value);
>>>>>> -        metadata_count++;
>>>>>> -    }
>>>>>> +
>>>>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>>
>>>>>>         if (write_duration_filesize) {
>>>>>>             put_amf_string(pb, "filesize");
>>>>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>>>>         if (flv->flags & FLV_NO_METADATA) {
>>>>>>             pb->seekable = 0;
>>>>>>         } else {
>>>>>> +        read_metadata_from_file(s, 0, 1);
>>>>>>             write_metadata(s, 0);
>>>>>>         }
>>>>>>
>>>>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>>>>> AVPacket *pkt)
>>>>>>         if (meta_upd_flag ||
>>>>>>             (flv->meta_period == FLV_META_AT_KF        &&
>>>>>> par->codec_type
>>>>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>>>             (flv->meta_period == FLV_META_EVERY_PACKET &&
>>>>>> par->codec_type
>>>>>> == AVMEDIA_TYPE_VIDEO )) {
>>>>>> +        read_metadata_from_file(s, ts, 0);
>>>>>>             write_metadata(s, ts);
>>>>>>             if (meta_upd_flag)
>>>>>>                 s->event_flags &=
>>>>>> ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>>>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>>>>         }
>>>>>>         flv->filepositions = flv->head_filepositions = NULL;
>>>>>>         flv->filepositions_count = 0;
>>>>>> +
>>>>>> +    if (flv->meta_dict)
>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>> +
>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>     }
>>>>>>
>>>>>>     static const AVOption options[] = {
>>>>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>>>>         { "at_start",      "only once at start",        0,
>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, INT_MAX,
>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>         { "at_keyframes",  "with every video keyframe", 0,
>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF},         INT_MIN, INT_MAX,
>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>         { "every_packet",  "with every video packet",   0,
>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET},  INT_MIN, INT_MAX,
>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>>>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str =
>>>>>> NULL},
>>>>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>>>>         { NULL },
>>>>>>     };
>>>>>>
>>>>> What does this achieve that can't be achieved by other means (namely by
>>>>> an API-user updating the relevant metadata dictionaries)?
>>>> This option together with meta_period is for CLI users. Similar to
>>>> drawtext's textfile + reload.
>>>> It's been used in production for over a year at a client. Think it will
>>>> be broadly useful.
>>> Not everything that is useful now is correct on long term.
>>>
>>> Muxer specific metadata handling by reading from files is big hack.
>> What would you suggest?
> There should be way to handle this generically see Anton's patch that
> adds new AVOption type.
Link?

>
> Also those strcmp() spaghetti lines are unacceptable.

I didn't add those, only shifted them.

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:42             ` Gyan Doshi
@ 2023-02-05 10:01               ` Gyan Doshi
  2023-02-05 10:09                 ` Paul B Mahol
  0 siblings, 1 reply; 15+ messages in thread
From: Gyan Doshi @ 2023-02-05 10:01 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-04 04:12 pm, Gyan Doshi wrote:
>
>
> On 2023-02-04 04:02 pm, Paul B Mahol wrote:
>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>
>>> On 2023-02-04 03:46 pm, Paul B Mahol wrote:
>>>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>>>> Gyan Doshi:
>>>>>>> Useful, in conjuntion with option meta_period, to vary metadata 
>>>>>>> during
>>>>>>> stream.
>>>>>>>
>>>>>>> File format is ffmetadata.
>>>>>>> ---
>>>>>>>     configure            |   2 +-
>>>>>>>     doc/muxers.texi      |   3 +
>>>>>>>     libavformat/flvenc.c | 133
>>>>>>> +++++++++++++++++++++++++++++++++----------
>>>>>>>     3 files changed, 107 insertions(+), 31 deletions(-)
>>>>>>>
>>>>>>> diff --git a/configure b/configure
>>>>>>> index 9d78a244a3..de371632c4 100755
>>>>>>> --- a/configure
>>>>>>> +++ b/configure
>>>>>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>>>>>     f4v_muxer_select="mov_muxer"
>>>>>>>     fifo_muxer_deps="threads"
>>>>>>>     flac_demuxer_select="flac_parser"
>>>>>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>>>>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>>>>>     gxf_muxer_select="pcm_rechunk_bsf"
>>>>>>>     hds_muxer_select="flv_muxer"
>>>>>>>     hls_demuxer_select="adts_header ac3_parser"
>>>>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>>>>> index 02ecddf186..000c92b2a7 100644
>>>>>>> --- a/doc/muxers.texi
>>>>>>> +++ b/doc/muxers.texi
>>>>>>> @@ -555,6 +555,9 @@ With every video packet.
>>>>>>>     @end table
>>>>>>>     Note that metadata will always be re-emitted if a metadata 
>>>>>>> update
>>>>>>> event
>>>>>>> is signalled.
>>>>>>>
>>>>>>> +@item meta_filename
>>>>>>> +Specify a ffmetadata file from which to load metadata.
>>>>>>> +
>>>>>>>     @end table
>>>>>>>
>>>>>>>     @anchor{framecrc}
>>>>>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>>>>>> index d1c7a493d1..1332b18b41 100644
>>>>>>> --- a/libavformat/flvenc.c
>>>>>>> +++ b/libavformat/flvenc.c
>>>>>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>>>>>         double framerate;
>>>>>>>         AVCodecParameters *data_par;
>>>>>>>
>>>>>>> +    char *meta_filename;
>>>>>>> +    AVFormatContext *meta_ctx;
>>>>>>> +    AVDictionary *meta_dict;
>>>>>>> +
>>>>>>>         int flags;
>>>>>>>         int meta_period;
>>>>>>>     } FLVContext;
>>>>>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb, 
>>>>>>> int b)
>>>>>>>         avio_w8(pb, !!b);
>>>>>>>     }
>>>>>>>
>>>>>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned 
>>>>>>> int
>>>>>>> ts,
>>>>>>> int header)
>>>>>>> +{
>>>>>>> +    FLVContext *flv = s->priv_data;
>>>>>>> +    const AVInputFormat *meta_format = NULL;
>>>>>>> +    AVDictionary *current_meta_dict = NULL;
>>>>>>> +    float timestamp = ts/1000.f;
>>>>>>> +    int ret;
>>>>>>> +
>>>>>>> +    if (!flv->meta_filename)
>>>>>>> +        return 0;
>>>>>>> +
>>>>>>> +    meta_format = av_find_input_format("ffmetadata");
>>>>>>> +    if (!meta_format) {
>>>>>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not 
>>>>>>> found.\n");
>>>>>>> +        return AVERROR(ENOSYS);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>> +
>>>>>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>>>>>> meta_format, NULL);
>>>>>>> +    if (ret < 0) {
>>>>>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from 
>>>>>>> file %s
>>>>>>> t:
>>>>>>> %f.", flv->meta_filename, timestamp);
>>>>>>> +        if (flv->meta_dict)
>>>>>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old
>>>>>>> metadata.");
>>>>>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>>>>>> +        return ret;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (flv->meta_dict) {
>>>>>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    ret = av_dict_copy(&flv->meta_dict, 
>>>>>>> flv->meta_ctx->metadata, 0);
>>>>>>> +    if (ret < 0) {
>>>>>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata 
>>>>>>> from %s
>>>>>>> at
>>>>>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>>>>>> timestamp);
>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>>>>>> +        av_dict_free(&current_meta_dict);
>>>>>>> +        return ret;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s 
>>>>>>> at %f
>>>>>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>>>>>> packet", timestamp);
>>>>>>> +    av_dict_free(&current_meta_dict);
>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int write_user_metadata_tag(AVFormatContext *s,
>>>>>>> AVDictionaryEntry
>>>>>>> *tag, AVIOContext *pb, int *metadata_count)
>>>>>>> +{
>>>>>>> +
>>>>>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count:
>>>>>>> %d\n",
>>>>>>> tag->key, tag->value, *metadata_count);
>>>>>>> +
>>>>>>> +    if(   !strcmp(tag->key, "width")
>>>>>>> +        ||!strcmp(tag->key, "height")
>>>>>>> +        ||!strcmp(tag->key, "videodatarate")
>>>>>>> +        ||!strcmp(tag->key, "framerate")
>>>>>>> +        ||!strcmp(tag->key, "videocodecid")
>>>>>>> +        ||!strcmp(tag->key, "audiodatarate")
>>>>>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>>>>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>>>>>> +        ||!strcmp(tag->key, "stereo")
>>>>>>> +        ||!strcmp(tag->key, "audiocodecid")
>>>>>>> +        ||!strcmp(tag->key, "duration")
>>>>>>> +        ||!strcmp(tag->key, "onMetaData")
>>>>>>> +        ||!strcmp(tag->key, "datasize")
>>>>>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>>>>>> +        ||!strcmp(tag->key, "totalframes")
>>>>>>> +        ||!strcmp(tag->key, "hasAudio")
>>>>>>> +        ||!strcmp(tag->key, "hasVideo")
>>>>>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>>>>>> +        ||!strcmp(tag->key, "hasMetadata")
>>>>>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>>>>>> +    ){
>>>>>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>>> tag->key);
>>>>>>> +        return AVERROR(EINVAL);
>>>>>>> +    }
>>>>>>> +    put_amf_string(pb, tag->key);
>>>>>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>>> +    put_amf_string(pb, tag->value);
>>>>>>> +    (*metadata_count)++;
>>>>>>> +
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>>>>>     {
>>>>>>>         AVIOContext *pb = s->pb;
>>>>>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext 
>>>>>>> *s,
>>>>>>> unsigned int ts)
>>>>>>>         }
>>>>>>>
>>>>>>>         ff_standardize_creation_time(s);
>>>>>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>>>>>> -        if(   !strcmp(tag->key, "width")
>>>>>>> -            ||!strcmp(tag->key, "height")
>>>>>>> -            ||!strcmp(tag->key, "videodatarate")
>>>>>>> -            ||!strcmp(tag->key, "framerate")
>>>>>>> -            ||!strcmp(tag->key, "videocodecid")
>>>>>>> -            ||!strcmp(tag->key, "audiodatarate")
>>>>>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>>>>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>>>>>> -            ||!strcmp(tag->key, "stereo")
>>>>>>> -            ||!strcmp(tag->key, "audiocodecid")
>>>>>>> -            ||!strcmp(tag->key, "duration")
>>>>>>> -            ||!strcmp(tag->key, "onMetaData")
>>>>>>> -            ||!strcmp(tag->key, "datasize")
>>>>>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>>>>>> -            ||!strcmp(tag->key, "totalframes")
>>>>>>> -            ||!strcmp(tag->key, "hasAudio")
>>>>>>> -            ||!strcmp(tag->key, "hasVideo")
>>>>>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>>>>>> -            ||!strcmp(tag->key, "hasMetadata")
>>>>>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>>>>>> -        ){
>>>>>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>>> tag->key);
>>>>>>> -            continue;
>>>>>>> -        }
>>>>>>> -        put_amf_string(pb, tag->key);
>>>>>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>>> -        put_amf_string(pb, tag->value);
>>>>>>> -        metadata_count++;
>>>>>>> -    }
>>>>>>> +
>>>>>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>>>
>>>>>>>         if (write_duration_filesize) {
>>>>>>>             put_amf_string(pb, "filesize");
>>>>>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>>>>>         if (flv->flags & FLV_NO_METADATA) {
>>>>>>>             pb->seekable = 0;
>>>>>>>         } else {
>>>>>>> +        read_metadata_from_file(s, 0, 1);
>>>>>>>             write_metadata(s, 0);
>>>>>>>         }
>>>>>>>
>>>>>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>>>>>> AVPacket *pkt)
>>>>>>>         if (meta_upd_flag ||
>>>>>>>             (flv->meta_period == FLV_META_AT_KF        &&
>>>>>>> par->codec_type
>>>>>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>>>>             (flv->meta_period == FLV_META_EVERY_PACKET &&
>>>>>>> par->codec_type
>>>>>>> == AVMEDIA_TYPE_VIDEO )) {
>>>>>>> +        read_metadata_from_file(s, ts, 0);
>>>>>>>             write_metadata(s, ts);
>>>>>>>             if (meta_upd_flag)
>>>>>>>                 s->event_flags &=
>>>>>>> ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>>>>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>>>>>         }
>>>>>>>         flv->filepositions = flv->head_filepositions = NULL;
>>>>>>>         flv->filepositions_count = 0;
>>>>>>> +
>>>>>>> +    if (flv->meta_dict)
>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>> +
>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>>     }
>>>>>>>
>>>>>>>     static const AVOption options[] = {
>>>>>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>>>>>         { "at_start",      "only once at start",        0,
>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN, 
>>>>>>> INT_MAX,
>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>>         { "at_keyframes",  "with every video keyframe", 0,
>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF}, INT_MIN, INT_MAX,
>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>>         { "every_packet",  "with every video packet",   0,
>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET}, INT_MIN, 
>>>>>>> INT_MAX,
>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>>>>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str =
>>>>>>> NULL},
>>>>>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>>>>>         { NULL },
>>>>>>>     };
>>>>>>>
>>>>>> What does this achieve that can't be achieved by other means 
>>>>>> (namely by
>>>>>> an API-user updating the relevant metadata dictionaries)?
>>>>> This option together with meta_period is for CLI users. Similar to
>>>>> drawtext's textfile + reload.
>>>>> It's been used in production for over a year at a client. Think it 
>>>>> will
>>>>> be broadly useful.
>>>> Not everything that is useful now is correct on long term.
>>>>
>>>> Muxer specific metadata handling by reading from files is big hack.
>>> What would you suggest?
>> There should be way to handle this generically see Anton's patch that
>> adds new AVOption type.
> Link?

So, I only found his patch to load filter options from files, but that's 
only inside filtergraphs.

Anyway, this option is for dynamic metadata. Which means that even 
meta_period would have to be a generic lavf option.
But most muxers don't emit metadata post-header.  In fact, flvenc is the 
only muxer that makes use of
AV***_EVENT_FLAG_METADATA_UPDATED.  So, we will need a new AVFMT flag to 
avoid reloading of file metadata in all other cases.
Seems overkill for something useful in one muxer.

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-05 10:01               ` Gyan Doshi
@ 2023-02-05 10:09                 ` Paul B Mahol
  0 siblings, 0 replies; 15+ messages in thread
From: Paul B Mahol @ 2023-02-05 10:09 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

On 2/5/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>
>
> On 2023-02-04 04:12 pm, Gyan Doshi wrote:
>>
>>
>> On 2023-02-04 04:02 pm, Paul B Mahol wrote:
>>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>>
>>>> On 2023-02-04 03:46 pm, Paul B Mahol wrote:
>>>>> On 2/4/23, Gyan Doshi <ffmpeg@gyani.pro> wrote:
>>>>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>>>>> Gyan Doshi:
>>>>>>>> Useful, in conjuntion with option meta_period, to vary metadata
>>>>>>>> during
>>>>>>>> stream.
>>>>>>>>
>>>>>>>> File format is ffmetadata.
>>>>>>>> ---
>>>>>>>>     configure            |   2 +-
>>>>>>>>     doc/muxers.texi      |   3 +
>>>>>>>>     libavformat/flvenc.c | 133
>>>>>>>> +++++++++++++++++++++++++++++++++----------
>>>>>>>>     3 files changed, 107 insertions(+), 31 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/configure b/configure
>>>>>>>> index 9d78a244a3..de371632c4 100755
>>>>>>>> --- a/configure
>>>>>>>> +++ b/configure
>>>>>>>> @@ -3433,7 +3433,7 @@ eac3_demuxer_select="ac3_parser"
>>>>>>>>     f4v_muxer_select="mov_muxer"
>>>>>>>>     fifo_muxer_deps="threads"
>>>>>>>>     flac_demuxer_select="flac_parser"
>>>>>>>> -flv_muxer_select="aac_adtstoasc_bsf"
>>>>>>>> +flv_muxer_select="aac_adtstoasc_bsf ffmetadata_demuxer"
>>>>>>>>     gxf_muxer_select="pcm_rechunk_bsf"
>>>>>>>>     hds_muxer_select="flv_muxer"
>>>>>>>>     hls_demuxer_select="adts_header ac3_parser"
>>>>>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>>>>>> index 02ecddf186..000c92b2a7 100644
>>>>>>>> --- a/doc/muxers.texi
>>>>>>>> +++ b/doc/muxers.texi
>>>>>>>> @@ -555,6 +555,9 @@ With every video packet.
>>>>>>>>     @end table
>>>>>>>>     Note that metadata will always be re-emitted if a metadata
>>>>>>>> update
>>>>>>>> event
>>>>>>>> is signalled.
>>>>>>>>
>>>>>>>> +@item meta_filename
>>>>>>>> +Specify a ffmetadata file from which to load metadata.
>>>>>>>> +
>>>>>>>>     @end table
>>>>>>>>
>>>>>>>>     @anchor{framecrc}
>>>>>>>> diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
>>>>>>>> index d1c7a493d1..1332b18b41 100644
>>>>>>>> --- a/libavformat/flvenc.c
>>>>>>>> +++ b/libavformat/flvenc.c
>>>>>>>> @@ -122,6 +122,10 @@ typedef struct FLVContext {
>>>>>>>>         double framerate;
>>>>>>>>         AVCodecParameters *data_par;
>>>>>>>>
>>>>>>>> +    char *meta_filename;
>>>>>>>> +    AVFormatContext *meta_ctx;
>>>>>>>> +    AVDictionary *meta_dict;
>>>>>>>> +
>>>>>>>>         int flags;
>>>>>>>>         int meta_period;
>>>>>>>>     } FLVContext;
>>>>>>>> @@ -277,6 +281,92 @@ static void put_amf_bool(AVIOContext *pb,
>>>>>>>> int b)
>>>>>>>>         avio_w8(pb, !!b);
>>>>>>>>     }
>>>>>>>>
>>>>>>>> +static int read_metadata_from_file(AVFormatContext *s, unsigned
>>>>>>>> int
>>>>>>>> ts,
>>>>>>>> int header)
>>>>>>>> +{
>>>>>>>> +    FLVContext *flv = s->priv_data;
>>>>>>>> +    const AVInputFormat *meta_format = NULL;
>>>>>>>> +    AVDictionary *current_meta_dict = NULL;
>>>>>>>> +    float timestamp = ts/1000.f;
>>>>>>>> +    int ret;
>>>>>>>> +
>>>>>>>> +    if (!flv->meta_filename)
>>>>>>>> +        return 0;
>>>>>>>> +
>>>>>>>> +    meta_format = av_find_input_format("ffmetadata");
>>>>>>>> +    if (!meta_format) {
>>>>>>>> +        av_log(s, AV_LOG_ERROR, "ffmetadata demuxer not
>>>>>>>> found.\n");
>>>>>>>> +        return AVERROR(ENOSYS);
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>>> +
>>>>>>>> +    ret = avformat_open_input(&flv->meta_ctx, flv->meta_filename,
>>>>>>>> meta_format, NULL);
>>>>>>>> +    if (ret < 0) {
>>>>>>>> +        av_log(s, AV_LOG_ERROR, "Failed to read metadata from
>>>>>>>> file %s
>>>>>>>> t:
>>>>>>>> %f.", flv->meta_filename, timestamp);
>>>>>>>> +        if (flv->meta_dict)
>>>>>>>> +            av_log(s, AV_LOG_ERROR, " Continuing with old
>>>>>>>> metadata.");
>>>>>>>> +        av_log(s, AV_LOG_ERROR, "\n");
>>>>>>>> +        return ret;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    if (flv->meta_dict) {
>>>>>>>> +        av_dict_copy(&current_meta_dict, flv->meta_dict, 0);
>>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    ret = av_dict_copy(&flv->meta_dict,
>>>>>>>> flv->meta_ctx->metadata, 0);
>>>>>>>> +    if (ret < 0) {
>>>>>>>> +        av_log(s, AV_LOG_ERROR, "Could not transfer metadata
>>>>>>>> from %s
>>>>>>>> at
>>>>>>>> %f seconds. Continuing with old metadata.\n", flv->meta_filename,
>>>>>>>> timestamp);
>>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>>> +        av_dict_copy(&flv->meta_dict, current_meta_dict, 0);
>>>>>>>> +        av_dict_free(&current_meta_dict);
>>>>>>>> +        return ret;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    av_log(s, AV_LOG_VERBOSE, "Metadata from file %s updated %s
>>>>>>>> at %f
>>>>>>>> seconds.\n", flv->meta_filename, header ? "in header" : "in video
>>>>>>>> packet", timestamp);
>>>>>>>> +    av_dict_free(&current_meta_dict);
>>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int write_user_metadata_tag(AVFormatContext *s,
>>>>>>>> AVDictionaryEntry
>>>>>>>> *tag, AVIOContext *pb, int *metadata_count)
>>>>>>>> +{
>>>>>>>> +
>>>>>>>> +    av_log(s, AV_LOG_DEBUG, "Writing tag %s with value %s count:
>>>>>>>> %d\n",
>>>>>>>> tag->key, tag->value, *metadata_count);
>>>>>>>> +
>>>>>>>> +    if(   !strcmp(tag->key, "width")
>>>>>>>> +        ||!strcmp(tag->key, "height")
>>>>>>>> +        ||!strcmp(tag->key, "videodatarate")
>>>>>>>> +        ||!strcmp(tag->key, "framerate")
>>>>>>>> +        ||!strcmp(tag->key, "videocodecid")
>>>>>>>> +        ||!strcmp(tag->key, "audiodatarate")
>>>>>>>> +        ||!strcmp(tag->key, "audiosamplerate")
>>>>>>>> +        ||!strcmp(tag->key, "audiosamplesize")
>>>>>>>> +        ||!strcmp(tag->key, "stereo")
>>>>>>>> +        ||!strcmp(tag->key, "audiocodecid")
>>>>>>>> +        ||!strcmp(tag->key, "duration")
>>>>>>>> +        ||!strcmp(tag->key, "onMetaData")
>>>>>>>> +        ||!strcmp(tag->key, "datasize")
>>>>>>>> +        ||!strcmp(tag->key, "lasttimestamp")
>>>>>>>> +        ||!strcmp(tag->key, "totalframes")
>>>>>>>> +        ||!strcmp(tag->key, "hasAudio")
>>>>>>>> +        ||!strcmp(tag->key, "hasVideo")
>>>>>>>> +        ||!strcmp(tag->key, "hasCuePoints")
>>>>>>>> +        ||!strcmp(tag->key, "hasMetadata")
>>>>>>>> +        ||!strcmp(tag->key, "hasKeyframes")
>>>>>>>> +    ){
>>>>>>>> +        av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>>>> tag->key);
>>>>>>>> +        return AVERROR(EINVAL);
>>>>>>>> +    }
>>>>>>>> +    put_amf_string(pb, tag->key);
>>>>>>>> +    avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>>>> +    put_amf_string(pb, tag->value);
>>>>>>>> +    (*metadata_count)++;
>>>>>>>> +
>>>>>>>> +    return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>     static void write_metadata(AVFormatContext *s, unsigned int ts)
>>>>>>>>     {
>>>>>>>>         AVIOContext *pb = s->pb;
>>>>>>>> @@ -360,36 +450,11 @@ static void write_metadata(AVFormatContext
>>>>>>>> *s,
>>>>>>>> unsigned int ts)
>>>>>>>>         }
>>>>>>>>
>>>>>>>>         ff_standardize_creation_time(s);
>>>>>>>> -    while ((tag = av_dict_iterate(s->metadata, tag))) {
>>>>>>>> -        if(   !strcmp(tag->key, "width")
>>>>>>>> -            ||!strcmp(tag->key, "height")
>>>>>>>> -            ||!strcmp(tag->key, "videodatarate")
>>>>>>>> -            ||!strcmp(tag->key, "framerate")
>>>>>>>> -            ||!strcmp(tag->key, "videocodecid")
>>>>>>>> -            ||!strcmp(tag->key, "audiodatarate")
>>>>>>>> -            ||!strcmp(tag->key, "audiosamplerate")
>>>>>>>> -            ||!strcmp(tag->key, "audiosamplesize")
>>>>>>>> -            ||!strcmp(tag->key, "stereo")
>>>>>>>> -            ||!strcmp(tag->key, "audiocodecid")
>>>>>>>> -            ||!strcmp(tag->key, "duration")
>>>>>>>> -            ||!strcmp(tag->key, "onMetaData")
>>>>>>>> -            ||!strcmp(tag->key, "datasize")
>>>>>>>> -            ||!strcmp(tag->key, "lasttimestamp")
>>>>>>>> -            ||!strcmp(tag->key, "totalframes")
>>>>>>>> -            ||!strcmp(tag->key, "hasAudio")
>>>>>>>> -            ||!strcmp(tag->key, "hasVideo")
>>>>>>>> -            ||!strcmp(tag->key, "hasCuePoints")
>>>>>>>> -            ||!strcmp(tag->key, "hasMetadata")
>>>>>>>> -            ||!strcmp(tag->key, "hasKeyframes")
>>>>>>>> -        ){
>>>>>>>> -            av_log(s, AV_LOG_DEBUG, "Ignoring metadata for %s\n",
>>>>>>>> tag->key);
>>>>>>>> -            continue;
>>>>>>>> -        }
>>>>>>>> -        put_amf_string(pb, tag->key);
>>>>>>>> -        avio_w8(pb, AMF_DATA_TYPE_STRING);
>>>>>>>> -        put_amf_string(pb, tag->value);
>>>>>>>> -        metadata_count++;
>>>>>>>> -    }
>>>>>>>> +
>>>>>>>> +    while (tag = av_dict_get(s->metadata, "", tag,
>>>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>>>> +    while (tag = av_dict_get(flv->meta_dict, "", tag,
>>>>>>>> AV_DICT_IGNORE_SUFFIX))
>>>>>>>> +        write_user_metadata_tag(s, tag, pb, &metadata_count);
>>>>>>>>
>>>>>>>>         if (write_duration_filesize) {
>>>>>>>>             put_amf_string(pb, "filesize");
>>>>>>>> @@ -718,6 +783,7 @@ static int flv_write_header(AVFormatContext *s)
>>>>>>>>         if (flv->flags & FLV_NO_METADATA) {
>>>>>>>>             pb->seekable = 0;
>>>>>>>>         } else {
>>>>>>>> +        read_metadata_from_file(s, 0, 1);
>>>>>>>>             write_metadata(s, 0);
>>>>>>>>         }
>>>>>>>>
>>>>>>>> @@ -880,6 +946,7 @@ static int flv_write_packet(AVFormatContext *s,
>>>>>>>> AVPacket *pkt)
>>>>>>>>         if (meta_upd_flag ||
>>>>>>>>             (flv->meta_period == FLV_META_AT_KF        &&
>>>>>>>> par->codec_type
>>>>>>>> == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY)) ||
>>>>>>>>             (flv->meta_period == FLV_META_EVERY_PACKET &&
>>>>>>>> par->codec_type
>>>>>>>> == AVMEDIA_TYPE_VIDEO )) {
>>>>>>>> +        read_metadata_from_file(s, ts, 0);
>>>>>>>>             write_metadata(s, ts);
>>>>>>>>             if (meta_upd_flag)
>>>>>>>>                 s->event_flags &=
>>>>>>>> ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
>>>>>>>> @@ -1053,6 +1120,11 @@ static void flv_deinit(AVFormatContext *s)
>>>>>>>>         }
>>>>>>>>         flv->filepositions = flv->head_filepositions = NULL;
>>>>>>>>         flv->filepositions_count = 0;
>>>>>>>> +
>>>>>>>> +    if (flv->meta_dict)
>>>>>>>> +        av_dict_free(&flv->meta_dict);
>>>>>>>> +
>>>>>>>> +    avformat_close_input(&flv->meta_ctx);
>>>>>>>>     }
>>>>>>>>
>>>>>>>>     static const AVOption options[] = {
>>>>>>>> @@ -1066,6 +1138,7 @@ static const AVOption options[] = {
>>>>>>>>         { "at_start",      "only once at start",        0,
>>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_ONCE_AT_START}, INT_MIN,
>>>>>>>> INT_MAX,
>>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>>>         { "at_keyframes",  "with every video keyframe", 0,
>>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_AT_KF}, INT_MIN, INT_MAX,
>>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>>>         { "every_packet",  "with every video packet",   0,
>>>>>>>> AV_OPT_TYPE_CONST, {.i64 = FLV_META_EVERY_PACKET}, INT_MIN,
>>>>>>>> INT_MAX,
>>>>>>>> AV_OPT_FLAG_ENCODING_PARAM, "meta_period" },
>>>>>>>> +    { "meta_filename", "ffmetadata file to import metadata",
>>>>>>>> offsetof(FLVContext, meta_filename), AV_OPT_TYPE_STRING, {.str =
>>>>>>>> NULL},
>>>>>>>> 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
>>>>>>>>         { NULL },
>>>>>>>>     };
>>>>>>>>
>>>>>>> What does this achieve that can't be achieved by other means
>>>>>>> (namely by
>>>>>>> an API-user updating the relevant metadata dictionaries)?
>>>>>> This option together with meta_period is for CLI users. Similar to
>>>>>> drawtext's textfile + reload.
>>>>>> It's been used in production for over a year at a client. Think it
>>>>>> will
>>>>>> be broadly useful.
>>>>> Not everything that is useful now is correct on long term.
>>>>>
>>>>> Muxer specific metadata handling by reading from files is big hack.
>>>> What would you suggest?
>>> There should be way to handle this generically see Anton's patch that
>>> adds new AVOption type.
>> Link?
>
> So, I only found his patch to load filter options from files, but that's
> only inside filtergraphs.
>
> Anyway, this option is for dynamic metadata. Which means that even
> meta_period would have to be a generic lavf option.
> But most muxers don't emit metadata post-header.  In fact, flvenc is the
> only muxer that makes use of
> AV***_EVENT_FLAG_METADATA_UPDATED.  So, we will need a new AVFMT flag to
> avoid reloading of file metadata in all other cases.
> Seems overkill for something useful in one muxer.
>

Please ask Anton for opinion about this one.


> Regards,
> Gyan
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-04 10:11     ` Gyan Doshi
  2023-02-04 10:16       ` Paul B Mahol
@ 2023-02-05 10:37       ` Anton Khirnov
  2023-02-05 11:05         ` Gyan Doshi
  1 sibling, 1 reply; 15+ messages in thread
From: Anton Khirnov @ 2023-02-05 10:37 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Quoting Gyan Doshi (2023-02-04 11:11:12)
> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
> > What does this achieve that can't be achieved by other means (namely by
> > an API-user updating the relevant metadata dictionaries)?
> This option together with meta_period is for CLI users.

Options for CLI users should be in the CLI, not the libraries.


-- 
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-05 10:37       ` Anton Khirnov
@ 2023-02-05 11:05         ` Gyan Doshi
  2023-02-07  8:57           ` Gyan Doshi
  2023-02-09 15:45           ` Anton Khirnov
  0 siblings, 2 replies; 15+ messages in thread
From: Gyan Doshi @ 2023-02-05 11:05 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-05 04:07 pm, Anton Khirnov wrote:
> Quoting Gyan Doshi (2023-02-04 11:11:12)
>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>> What does this achieve that can't be achieved by other means (namely by
>>> an API-user updating the relevant metadata dictionaries)?
>> This option together with meta_period is for CLI users.
> Options for CLI users should be in the CLI, not the libraries.

Do I then add and use a AVFMT flag to avoid recurring metadata updates 
for all other muxers?

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-05 11:05         ` Gyan Doshi
@ 2023-02-07  8:57           ` Gyan Doshi
  2023-02-09 15:45           ` Anton Khirnov
  1 sibling, 0 replies; 15+ messages in thread
From: Gyan Doshi @ 2023-02-07  8:57 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-05 04:35 pm, Gyan Doshi wrote:
>
>
> On 2023-02-05 04:07 pm, Anton Khirnov wrote:
>> Quoting Gyan Doshi (2023-02-04 11:11:12)
>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>> What does this achieve that can't be achieved by other means 
>>>> (namely by
>>>> an API-user updating the relevant metadata dictionaries)?
>>> This option together with meta_period is for CLI users.
>> Options for CLI users should be in the CLI, not the libraries.
>
> Do I then add and use a AVFMT flag to avoid recurring metadata updates 
> for all other muxers?

To add to this, it's not exactly a 'CLI 'option. It allows CLI users the 
same feature that API users can manually undertake,
similar to the existence of the tee muxer itself which API users don't need.

But I'll see how this can fit inside fftools, though lavf/mux might be a 
better place.

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-05 11:05         ` Gyan Doshi
  2023-02-07  8:57           ` Gyan Doshi
@ 2023-02-09 15:45           ` Anton Khirnov
  2023-02-09 16:14             ` Gyan Doshi
  1 sibling, 1 reply; 15+ messages in thread
From: Anton Khirnov @ 2023-02-09 15:45 UTC (permalink / raw)
  To: FFmpeg development discussions and patches

Quoting Gyan Doshi (2023-02-05 12:05:19)
> 
> 
> On 2023-02-05 04:07 pm, Anton Khirnov wrote:
> > Quoting Gyan Doshi (2023-02-04 11:11:12)
> >> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
> >>> What does this achieve that can't be achieved by other means (namely by
> >>> an API-user updating the relevant metadata dictionaries)?
> >> This option together with meta_period is for CLI users.
> > Options for CLI users should be in the CLI, not the libraries.
> 
> Do I then add and use a AVFMT flag to avoid recurring metadata updates 
> for all other muxers?

I don't follow, why should there be a lavf flag for an ffmpeg CLI
option?

-- 
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file
  2023-02-09 15:45           ` Anton Khirnov
@ 2023-02-09 16:14             ` Gyan Doshi
  0 siblings, 0 replies; 15+ messages in thread
From: Gyan Doshi @ 2023-02-09 16:14 UTC (permalink / raw)
  To: ffmpeg-devel



On 2023-02-09 09:15 pm, Anton Khirnov wrote:
> Quoting Gyan Doshi (2023-02-05 12:05:19)
>>
>> On 2023-02-05 04:07 pm, Anton Khirnov wrote:
>>> Quoting Gyan Doshi (2023-02-04 11:11:12)
>>>> On 2023-02-03 09:04 pm, Andreas Rheinhardt wrote:
>>>>> What does this achieve that can't be achieved by other means (namely by
>>>>> an API-user updating the relevant metadata dictionaries)?
>>>> This option together with meta_period is for CLI users.
>>> Options for CLI users should be in the CLI, not the libraries.
>> Do I then add and use a AVFMT flag to avoid recurring metadata updates
>> for all other muxers?
> I don't follow, why should there be a lavf flag for an ffmpeg CLI
> option?

The meta period option will control how frequently the metadata file is 
read and the muxer ctx dict updated in ffttools write_packet.
Only one muxer - flvenc - can be triggered to recheck the dict in its 
.write_packet, so it seems wasteful to carry out those ops generally.
The AVFMT flag can guard that.

Regards,
Gyan

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-02-09 16:14 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-02 16:07 [FFmpeg-devel] [PATCH v2 1/2] avformat/flvenc: add option meta_period Gyan Doshi
2023-02-02 16:07 ` [FFmpeg-devel] [PATCH v2 2/2] avformat/flvenc: add option to read metadata from file Gyan Doshi
2023-02-03 15:34   ` Andreas Rheinhardt
2023-02-04 10:11     ` Gyan Doshi
2023-02-04 10:16       ` Paul B Mahol
2023-02-04 10:24         ` Gyan Doshi
2023-02-04 10:32           ` Paul B Mahol
2023-02-04 10:42             ` Gyan Doshi
2023-02-05 10:01               ` Gyan Doshi
2023-02-05 10:09                 ` Paul B Mahol
2023-02-05 10:37       ` Anton Khirnov
2023-02-05 11:05         ` Gyan Doshi
2023-02-07  8:57           ` Gyan Doshi
2023-02-09 15:45           ` Anton Khirnov
2023-02-09 16:14             ` Gyan Doshi

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git