Make live metadata changes reach the output: transcoding an
Icecast/Shoutcast stream to RTMP now emits a fresh onMetaData with the
current StreamTitle whenever the station announces one, instead of
writing metadata once into the header and never updating it.
Both halves already existed -- read_frame_internal() raises
AVFMT_EVENT_FLAG_METADATA_UPDATED when a demuxer reports changed
metadata, and flv_write_packet() re-emits onMetaData when that flag is
set on the output -- only the link between them was missing. Copy the
changed dictionary to every output still using the automatic global
metadata mapping and raise the flag there.
The update travels through InputFile rather than as
AV_PKT_DATA_METADATA_UPDATE side data so that it survives transcoding.
Signed-off-by: Przemysław Sobala <[email protected]>
---
fftools/ffmpeg.c | 2 ++
fftools/ffmpeg.h | 15 ++++++++++++++
fftools/ffmpeg_demux.c | 22 +++++++++++++++++++++
fftools/ffmpeg_mux.c | 41 +++++++++++++++++++++++++++++++++++++++
fftools/ffmpeg_mux.h | 6 ++++++
fftools/ffmpeg_mux_init.c | 2 ++
6 files changed, 88 insertions(+)
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index d38acffbdf..e53e25f934 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -108,6 +108,8 @@ AVIOContext *progress_avio = NULL;
InputFile **input_files = NULL;
int nb_input_files = 0;
+atomic_uint metadata_update_serial = 0;
+
OutputFile **output_files = NULL;
int nb_output_files = 0;
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index a45d004fdf..9a78465ee4 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -527,6 +527,18 @@ typedef struct InputFile {
/* stream groups that ffmpeg is aware of; */
InputStreamGroup **stream_groups;
int nb_stream_groups;
+
+ /* Most recent in-band metadata update seen by the demuxer thread,
e.g. an
+ * ICY StreamTitle on an Icecast/Shoutcast stream. Consumed by
muxers able
+ * to emit metadata mid-stream. Access is protected by meta_lock.
+ *
+ * Readers copy this dictionary rather than clearing it, so that every
+ * output file can pick up the same update; it therefore stays
populated
+ * after the first publish. metadata_update_serial is bumped on each
+ * publish so a reader can tell a fresh update from one it already
applied
+ * -- without that edge-detection every packet would look like an
update. */
+ pthread_mutex_t meta_lock;
+ AVDictionary *meta_updated;
} InputFile;
enum forced_keyframes_const {
@@ -716,6 +728,9 @@ typedef struct FrameData {
extern InputFile **input_files;
extern int nb_input_files;
+/* Incremented every time a demuxer publishes an
InputFile.meta_updated. */
+extern atomic_uint metadata_update_serial;
+
extern OutputFile **output_files;
extern int nb_output_files;
diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c
index 8ae5486582..2eb135dca7 100644
--- a/fftools/ffmpeg_demux.c
+++ b/fftools/ffmpeg_demux.c
@@ -860,12 +860,29 @@ static int input_thread(void *arg)
d->read_started = 1;
d->wallclock_start = av_gettime_relative();
+ /* Metadata parsed out of the header is already mapped to the
outputs at
+ * init time, so only report changes seen from here on. */
+ f->ctx->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
+
while (1) {
DemuxStream *ds;
unsigned send_flags = 0;
ret = av_read_frame(f->ctx, dt.pkt_demux);
+ /* Publish in-band metadata updates (e.g. ICY StreamTitle)
for muxers
+ * that can emit them mid-stream. Done here rather than via
packet side
+ * data so it survives transcoding. */
+ if (f->ctx->event_flags & AVFMT_EVENT_FLAG_METADATA_UPDATED) {
+ f->ctx->event_flags &= ~AVFMT_EVENT_FLAG_METADATA_UPDATED;
+
+ pthread_mutex_lock(&f->meta_lock);
+ av_dict_free(&f->meta_updated);
+ if (av_dict_copy(&f->meta_updated, f->ctx->metadata, 0) >= 0)
+ atomic_fetch_add(&metadata_update_serial, 1);
+ pthread_mutex_unlock(&f->meta_lock);
+ }
+
if (ret == AVERROR(EAGAIN)) {
av_usleep(10000);
continue;
@@ -1051,6 +1068,9 @@ void ifile_close(InputFile **pf)
av_packet_free(&d->pkt_heartbeat);
+ av_dict_free(&f->meta_updated);
+ pthread_mutex_destroy(&f->meta_lock);
+
av_freep(pf);
}
@@ -2238,6 +2258,8 @@ static Demuxer *demux_alloc(void)
d->f.class = &input_file_class;
d->f.index = nb_input_files - 1;
+ pthread_mutex_init(&d->f.meta_lock, NULL);
+
snprintf(d->log_name, sizeof(d->log_name), "in#%d", d->f.index);
return d;
diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c
index b408b02f2a..5be205fa09 100644
--- a/fftools/ffmpeg_mux.c
+++ b/fftools/ffmpeg_mux.c
@@ -201,6 +201,43 @@ static int mux_fixup_ts(Muxer *mux, MuxStream *ms,
AVPacket *pkt)
return 0;
}
+/* Forward in-band metadata updates published by the demuxers (e.g. ICY
+ * StreamTitle on Icecast/Shoutcast input) to the muxer. Muxers that
support
+ * mid-stream metadata, such as FLV/RTMP with its onMetaData script
tag, act on
+ * AVFMT_EVENT_FLAG_METADATA_UPDATED; the rest simply ignore it. */
+static int mux_metadata_update(Muxer *mux)
+{
+ AVFormatContext *s = mux->fc;
+ unsigned serial = atomic_load(&metadata_update_serial);
+ int updated = 0;
+
+ if (serial == mux->meta_serial || mux->metadata_global_manual)
+ return 0;
+ mux->meta_serial = serial;
+
+ /* The published dictionary is copied rather than consumed, so that
every
+ * output file gets it; each Muxer tracks the serial it last
applied. */
+ for (int i = 0; i < nb_input_files; i++) {
+ InputFile *f = input_files[i];
+ int ret = 0;
+
+ pthread_mutex_lock(&f->meta_lock);
+ if (f->meta_updated) {
+ ret = av_dict_copy(&s->metadata, f->meta_updated, 0);
+ updated = 1;
+ }
+ pthread_mutex_unlock(&f->meta_lock);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ if (updated)
+ s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+
+ return 0;
+}
+
static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
{
MuxStream *ms = ms_from_ost(ost);
@@ -209,6 +246,10 @@ static int write_packet(Muxer *mux, OutputStream
*ost, AVPacket *pkt)
uint64_t frame_num;
int ret;
+ ret = mux_metadata_update(mux);
+ if (ret < 0)
+ goto fail;
+
fs = filesize(s->pb);
atomic_store(&mux->last_filesize, fs);
if (fs >= mux->limit_filesize) {
diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h
index f458cc882c..b89f72179f 100644
--- a/fftools/ffmpeg_mux.h
+++ b/fftools/ffmpeg_mux.h
@@ -114,6 +114,12 @@ typedef struct Muxer {
atomic_int_least64_t last_filesize;
int header_written;
+ /* set when -map_metadata suppressed the automatic global
metadata mapping,
+ * in which case in-band metadata updates are not forwarded either */
+ int metadata_global_manual;
+ /* value of metadata_update_serial last applied to fc->metadata */
+ unsigned meta_serial;
+
SyncQueue *sq_mux;
AVPacket *sq_pkt;
} Muxer;
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 2a9146e60c..2d47abfb13 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -3138,6 +3138,8 @@ static int copy_meta(Muxer *mux, const
OptionsContext *o)
return ret;
}
+ mux->metadata_global_manual = metadata_global_manual;
+
/* copy chapters */
if (chapters_input_file >= nb_input_files) {
if (chapters_input_file == INT_MAX) {
--
2.55.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]