Support writing chapters as 'cue ' and 'adtl' chunks, if no chapter titles are present, it will create 'Cue #' titles using the chapter number
Signed-off-by: Vladimir Pantelic <[email protected]> --- libavformat/wavenc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/libavformat/wavenc.c b/libavformat/wavenc.c index a515f4e2a2..61a048f641 100644 --- a/libavformat/wavenc.c +++ b/libavformat/wavenc.c @@ -88,6 +88,7 @@ typedef struct WAVMuxContext { int peak_block_pos; int peak_ppv; int peak_bps; + int write_cue; } WAVMuxContext; #if CONFIG_WAV_MUXER @@ -299,6 +300,70 @@ static int peak_write_chunk(AVFormatContext *s) return 0; } +static int wav_write_cue_chunk(AVFormatContext *s, int sample_rate) +{ + AVIOContext *dyn_buf; + AVIOContext *pb = s->pb; + AVRational scale = {1, sample_rate}; + int ret; + + int64_t start = ff_start_tag(pb, "cue "); /* chunk ID */ + + avio_wl32(pb, s->nb_chapters); /* number of cue points */ + + /* write cue points */ + for (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); + + avio_wl32(pb, i + 1); /* 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 ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) + return ret; + + start = ff_start_tag(pb, "LIST"); + + ffio_wfourcc(pb, "adtl"); /* associated data list chunk */ + + /* write cue point names */ + for (int i = 0; i < s->nb_chapters; i++) { + uint8_t *buf; + int len; + AVChapter *c = s->chapters[i]; + int64_t label = ff_start_tag(pb, "labl"); /* sub chunk ID */ + AVDictionaryEntry *t = av_dict_get(c->metadata, "title", NULL, 0); + avio_wl32(pb, i + 1); /* cue point ID */ + if(t) { + avio_put_str(dyn_buf, t->value); + } else { + /* no chapter title: use "Cue xyz" */ + char *title; + if((title = av_asprintf("Cue %d", i + 1)) == NULL) + return AVERROR(ENOMEM); + avio_put_str(dyn_buf, title); + av_free(title); + } + len = avio_get_dyn_buf(dyn_buf, &buf); + avio_write(pb, buf, len); /* title string data */ + ffio_reset_dyn_buf(dyn_buf); + + ff_end_tag(pb, label); + } + + ff_end_tag(pb, start); + ffio_free_dyn_buf(&dyn_buf); + + return 0; +} + static int wav_write_header(AVFormatContext *s) { WAVMuxContext *wav = s->priv_data; @@ -344,6 +409,12 @@ static int wav_write_header(AVFormatContext *s) if (wav->write_bext) bwf_write_bext_chunk(s); + if (wav->write_cue) { + int ret; + if((ret = wav_write_cue_chunk(s, s->streams[0]->codecpar->sample_rate)) < 0) + return ret; + } + if (wav->write_peak) { int ret; if ((ret = peak_init_writer(s)) < 0) @@ -494,6 +565,7 @@ static const AVOption options[] = { { "peak_block_size", "Number of audio samples used to generate each peak frame.", OFFSET(peak_block_size), AV_OPT_TYPE_INT, { .i64 = 256 }, 0, 65536, ENC }, { "peak_format", "The format of the peak envelope data (1: uint8, 2: uint16).", OFFSET(peak_format), AV_OPT_TYPE_INT, { .i64 = PEAK_FORMAT_UINT16 }, PEAK_FORMAT_UINT8, PEAK_FORMAT_UINT16, ENC }, { "peak_ppv", "Number of peak points per peak value (1 or 2).", OFFSET(peak_ppv), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 2, ENC }, + { "write_cue", "Write CUE/ADTL chunk.", OFFSET(write_cue), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC }, { NULL }, }; -- 2.51.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
