PR #23021 opened by Vladimir Pantelic (av500) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23021 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23021.patch
avformat/wavenc: add support for writing chapters as 'cue ' chunks, also write chapter titles as 'adtl' chunks if they exist since 2a14d55a there is support for reading 'cue ' and 'adtl' chunks into chapters and titles from WAV files, this commit adds support for writing them back. issues raised in https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22909 have been addressed: - loop index is now `unsigned int` - `has_titles` logic has been fixed - cue IDs are now just the loop index `i` - `dyn_buf` usage has been replaced by simple `strlen(...) + 1` Signed-off-by: Vladimir Pantelic [email protected] >From 3b8c8b63f5bb61a49927be6508af71efd5c143e1 Mon Sep 17 00:00:00 2001 From: Vladimir Pantelic <[email protected]> Date: Fri, 24 Apr 2026 16:01:00 +0200 Subject: [PATCH] avformat/wavenc: add support for writing chapters as 'cue ' chunks also write chapter titles as 'adtl' chunks if they exist Signed-off-by: Vladimir Pantelic <[email protected]> --- libavformat/wavenc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c index 01fffaafe5..edd266f4f5 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -299,6 +299,51 @@ static int peak_write_chunk(AVFormatContext *s) return 0; } +static void wav_write_cue_chunk(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVRational scale = {1, s->streams[0]->codecpar->sample_rate}; + int has_titles = 0; + + int64_t start = ff_start_tag(pb, "cue "); /* chunk ID */ + avio_wl32(pb, s->nb_chapters); /* number of cue points */ + + /* write cue points */ + for (unsigned int i = 0; i < s->nb_chapters; i++) { + AVChapter *c = s->chapters[i]; + int64_t samples = av_rescale_q(c->start, c->time_base, scale); + if(av_dict_get(c->metadata, "title", NULL, 0)) + has_titles = 1; + + avio_wl32(pb, i); /* cue point ID */ + avio_wl32(pb, 0); /* position, only relevant with PLST chunk */ + ffio_wfourcc(pb, "data"); /* data chunk ID, only used with WAVL chunk */ + avio_wl32(pb, 0); /* chunk start, only used with WAVL chunk */ + avio_wl32(pb, 0); /* block start, only used with WAVL chunk*/ + avio_wl32(pb, samples); /* sample start (in # of samples) */ + } + ff_end_tag(pb, start); + + if(has_titles) { /* do not write an empty list */ + start = ff_start_tag(pb, "LIST"); + ffio_wfourcc(pb, "adtl"); /* associated data list chunk */ + + /* write cue point names, if they exist */ + for (unsigned int i = 0; i < s->nb_chapters; i++) { + AVChapter *c = s->chapters[i]; + AVDictionaryEntry *t = av_dict_get(c->metadata, "title", NULL, 0); + if(t) { + int len = strlen(t->value); + int64_t labl = ff_start_tag(pb, "labl"); /* sub chunk ID */ + avio_wl32(pb, i); /* cue point ID */ + avio_write(s->pb, t->value, len + 1); /* title string data */ + ff_end_tag(pb, labl); + } + } + ff_end_tag(pb, start); + } +} + static int wav_write_header(AVFormatContext *s) { WAVMuxContext *wav = s->priv_data; @@ -354,6 +399,9 @@ static int wav_write_header(AVFormatContext *s) if (wav->write_bext) bwf_write_bext_chunk(s); + if (s->nb_chapters) + wav_write_cue_chunk(s); + if (wav->write_peak) { int ret; if ((ret = peak_init_writer(s)) < 0) -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
