| @@ -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); | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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; | |||
| @@ -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[]; | |||