On 2023-02-04 03:46 pm, Paul B Mahol wrote:
On 2/4/23, Gyan Doshi <ffm...@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-requ...@ffmpeg.org with subject "unsubscribe".

Reply via email to