summaryrefslogtreecommitdiff
path: root/libavformat
diff options
context:
space:
mode:
authorPaul B Mahol <onemda@gmail.com>2018-12-16 21:50:08 +0100
committerPaul B Mahol <onemda@gmail.com>2018-12-18 19:45:59 +0100
commitc0fb6f963fd7ee555ba2fdc254444e208bb0bc46 (patch)
tree20cf209d035ef7039bfed1acc4ee97610248b5ac /libavformat
parentd283ee085f9aac44b5de1b09e3cab62920fa4a9a (diff)
avformat/vorbiscomment: add support for writing chapters
Fixes #7532.
Diffstat (limited to 'libavformat')
-rw-r--r--libavformat/flacenc.c4
-rw-r--r--libavformat/matroskaenc.c4
-rw-r--r--libavformat/oggenc.c22
-rw-r--r--libavformat/vorbiscomment.c60
-rw-r--r--libavformat/vorbiscomment.h8
5 files changed, 79 insertions, 19 deletions
diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c
index 617bccdc84..a07260f426 100644
--- a/libavformat/flacenc.c
+++ b/libavformat/flacenc.c
@@ -65,7 +65,7 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
- len = ff_vorbiscomment_length(*m, vendor);
+ len = ff_vorbiscomment_length(*m, vendor, NULL, 0);
if (len >= ((1<<24) - 4))
return AVERROR(EINVAL);
p0 = av_malloc(len+4);
@@ -75,7 +75,7 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
bytestream_put_byte(&p, last_block ? 0x84 : 0x04);
bytestream_put_be24(&p, len);
- ff_vorbiscomment_write(&p, m, vendor);
+ ff_vorbiscomment_write(&p, m, vendor, NULL, 0);
avio_write(pb, p0, len+4);
av_freep(&p0);
diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c
index aed83aef70..f0e4c60b93 100644
--- a/libavformat/matroskaenc.c
+++ b/libavformat/matroskaenc.c
@@ -693,7 +693,7 @@ static int put_flac_codecpriv(AVFormatContext *s,
snprintf(buf, sizeof(buf), "0x%"PRIx64, par->channel_layout);
av_dict_set(&dict, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", buf, 0);
- len = ff_vorbiscomment_length(dict, vendor);
+ len = ff_vorbiscomment_length(dict, vendor, NULL, 0);
if (len >= ((1<<24) - 4))
return AVERROR(EINVAL);
@@ -707,7 +707,7 @@ static int put_flac_codecpriv(AVFormatContext *s,
AV_WB24(data + 1, len);
p = data + 4;
- ff_vorbiscomment_write(&p, &dict, vendor);
+ ff_vorbiscomment_write(&p, &dict, vendor, NULL, 0);
avio_write(pb, data, len + 4);
diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c
index 10c4eda062..06021c4f4b 100644
--- a/libavformat/oggenc.c
+++ b/libavformat/oggenc.c
@@ -291,7 +291,8 @@ static int ogg_buffer_data(AVFormatContext *s, AVStream *st,
}
static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact,
- int *header_len, AVDictionary **m, int framing_bit)
+ int *header_len, AVDictionary **m, int framing_bit,
+ AVChapter **chapters, unsigned int nb_chapters)
{
const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
int64_t size;
@@ -299,7 +300,7 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact,
ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
- size = offset + ff_vorbiscomment_length(*m, vendor) + framing_bit;
+ size = offset + ff_vorbiscomment_length(*m, vendor, chapters, nb_chapters) + framing_bit;
if (size > INT_MAX)
return NULL;
p = av_mallocz(size);
@@ -308,7 +309,7 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact,
p0 = p;
p += offset;
- ff_vorbiscomment_write(&p, m, vendor);
+ ff_vorbiscomment_write(&p, m, vendor, chapters, nb_chapters);
if (framing_bit)
bytestream_put_byte(&p, 1);
@@ -342,7 +343,7 @@ static int ogg_build_flac_headers(AVCodecParameters *par,
bytestream_put_buffer(&p, par->extradata, FLAC_STREAMINFO_SIZE);
// second packet: VorbisComment
- p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1], m, 0);
+ p = ogg_write_vorbiscomment(4, bitexact, &oggstream->header_len[1], m, 0, NULL, 0);
if (!p)
return AVERROR(ENOMEM);
oggstream->header[1] = p;
@@ -373,7 +374,7 @@ static int ogg_build_speex_headers(AVCodecParameters *par,
AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0
// second packet: VorbisComment
- p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, 0);
+ p = ogg_write_vorbiscomment(0, bitexact, &oggstream->header_len[1], m, 0, NULL, 0);
if (!p)
return AVERROR(ENOMEM);
oggstream->header[1] = p;
@@ -385,7 +386,8 @@ static int ogg_build_speex_headers(AVCodecParameters *par,
static int ogg_build_opus_headers(AVCodecParameters *par,
OGGStreamContext *oggstream, int bitexact,
- AVDictionary **m)
+ AVDictionary **m, AVChapter **chapters,
+ unsigned int nb_chapters)
{
uint8_t *p;
@@ -401,7 +403,7 @@ static int ogg_build_opus_headers(AVCodecParameters *par,
bytestream_put_buffer(&p, par->extradata, par->extradata_size);
/* second packet: VorbisComment */
- p = ogg_write_vorbiscomment(8, bitexact, &oggstream->header_len[1], m, 0);
+ p = ogg_write_vorbiscomment(8, bitexact, &oggstream->header_len[1], m, 0, chapters, nb_chapters);
if (!p)
return AVERROR(ENOMEM);
oggstream->header[1] = p;
@@ -446,7 +448,7 @@ static int ogg_build_vp8_headers(AVFormatContext *s, AVStream *st,
/* optional second packet: VorbisComment */
if (av_dict_get(st->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
- p = ogg_write_vorbiscomment(7, bitexact, &oggstream->header_len[1], &st->metadata, 0);
+ p = ogg_write_vorbiscomment(7, bitexact, &oggstream->header_len[1], &st->metadata, 0, NULL, 0);
if (!p)
return AVERROR(ENOMEM);
oggstream->header[1] = p;
@@ -560,7 +562,7 @@ static int ogg_init(AVFormatContext *s)
} else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
int err = ogg_build_opus_headers(st->codecpar, oggstream,
s->flags & AVFMT_FLAG_BITEXACT,
- &st->metadata);
+ &st->metadata, s->chapters, s->nb_chapters);
if (err) {
av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n");
av_freep(&st->priv_data);
@@ -590,7 +592,7 @@ static int ogg_init(AVFormatContext *s)
p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT,
&oggstream->header_len[1], &st->metadata,
- framing_bit);
+ framing_bit, NULL, 0);
oggstream->header[1] = p;
if (!p)
return AVERROR(ENOMEM);
diff --git a/libavformat/vorbiscomment.c b/libavformat/vorbiscomment.c
index 575dd13328..fb5c655a23 100644
--- a/libavformat/vorbiscomment.c
+++ b/libavformat/vorbiscomment.c
@@ -38,10 +38,21 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
{ 0 }
};
-int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string)
+int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string,
+ AVChapter **chapters, unsigned int nb_chapters)
{
int64_t len = 8;
len += strlen(vendor_string);
+ if (chapters && nb_chapters) {
+ for (int i = 0; i < nb_chapters; i++) {
+ AVDictionaryEntry *tag = NULL;
+ len += 4 + 12 + 1 + 10;
+ while ((tag = av_dict_get(chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+ int64_t len1 = !strcmp(tag->key, "title") ? 4 : strlen(tag->key);
+ len += 4 + 10 + len1 + 1 + strlen(tag->value);
+ }
+ }
+ }
if (m) {
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
@@ -52,12 +63,19 @@ int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string)
}
int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m,
- const char *vendor_string)
+ const char *vendor_string,
+ AVChapter **chapters, unsigned int nb_chapters)
{
+ int cm_count = 0;
bytestream_put_le32(p, strlen(vendor_string));
bytestream_put_buffer(p, vendor_string, strlen(vendor_string));
+ if (chapters && nb_chapters) {
+ for (int i = 0; i < nb_chapters; i++) {
+ cm_count += av_dict_count(chapters[i]->metadata) + 1;
+ }
+ }
if (*m) {
- int count = av_dict_count(*m);
+ int count = av_dict_count(*m) + cm_count;
AVDictionaryEntry *tag = NULL;
bytestream_put_le32(p, count);
while ((tag = av_dict_get(*m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
@@ -70,6 +88,42 @@ int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m,
bytestream_put_byte(p, '=');
bytestream_put_buffer(p, tag->value, len2);
}
+ for (int i = 0; i < nb_chapters; i++) {
+ AVChapter *chp = chapters[i];
+ char chapter_time[13];
+ char chapter_number[4];
+ int h, m, s, ms;
+
+ s = av_rescale(chp->start, chp->time_base.num, chp->time_base.den);
+ h = s / 3600;
+ m = (s / 60) % 60;
+ ms = av_rescale_q(chp->start, chp->time_base, av_make_q( 1, 1000)) % 1000;
+ s = s % 60;
+ snprintf(chapter_number, sizeof(chapter_number), "%03d", i);
+ snprintf(chapter_time, sizeof(chapter_time), "%02d:%02d:%02d.%03d", h, m, s, ms);
+ bytestream_put_le32(p, 10+1+12);
+ bytestream_put_buffer(p, "CHAPTER", 7);
+ bytestream_put_buffer(p, chapter_number, 3);
+ bytestream_put_byte(p, '=');
+ bytestream_put_buffer(p, chapter_time, 12);
+
+ tag = NULL;
+ while ((tag = av_dict_get(chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+ int64_t len1 = !strcmp(tag->key, "title") ? 4 : strlen(tag->key);
+ int64_t len2 = strlen(tag->value);
+ if (len1+1+len2+10 > UINT32_MAX)
+ return AVERROR(EINVAL);
+ bytestream_put_le32(p, 10+len1+1+len2);
+ bytestream_put_buffer(p, "CHAPTER", 7);
+ bytestream_put_buffer(p, chapter_number, 3);
+ if (!strcmp(tag->key, "title"))
+ bytestream_put_buffer(p, "NAME", 4);
+ else
+ bytestream_put_buffer(p, tag->key, len1);
+ bytestream_put_byte(p, '=');
+ bytestream_put_buffer(p, tag->value, len2);
+ }
+ }
} else
bytestream_put_le32(p, 0);
return 0;
diff --git a/libavformat/vorbiscomment.h b/libavformat/vorbiscomment.h
index e0d30b14a7..4ff3dd6c27 100644
--- a/libavformat/vorbiscomment.h
+++ b/libavformat/vorbiscomment.h
@@ -34,7 +34,8 @@
* For no string, set to an empty string.
* @return The length in bytes.
*/
-int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string);
+int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string,
+ AVChapter **chapters, unsigned int nb_chapters);
/**
* Write a VorbisComment into a buffer. The buffer, p, must have enough
@@ -45,9 +46,12 @@ int64_t ff_vorbiscomment_length(AVDictionary *m, const char *vendor_string);
* @param p The buffer in which to write.
* @param m The metadata struct to write.
* @param vendor_string The vendor string to write.
+ * @param chapters The chapters to write.
+ * @param nb_chapters The number of chapters to write.
*/
int ff_vorbiscomment_write(uint8_t **p, AVDictionary **m,
- const char *vendor_string);
+ const char *vendor_string,
+ AVChapter **chapters, unsigned int nb_chapters);
extern const AVMetadataConv ff_vorbiscomment_metadata_conv[];