mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-26 19:01:44 +02:00
parent
d283ee085f
commit
c0fb6f963f
@ -65,7 +65,7 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m,
|
|||||||
|
|
||||||
ff_metadata_conv(m, ff_vorbiscomment_metadata_conv, NULL);
|
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))
|
if (len >= ((1<<24) - 4))
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
p0 = av_malloc(len+4);
|
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_byte(&p, last_block ? 0x84 : 0x04);
|
||||||
bytestream_put_be24(&p, len);
|
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);
|
avio_write(pb, p0, len+4);
|
||||||
av_freep(&p0);
|
av_freep(&p0);
|
||||||
|
@ -693,7 +693,7 @@ static int put_flac_codecpriv(AVFormatContext *s,
|
|||||||
snprintf(buf, sizeof(buf), "0x%"PRIx64, par->channel_layout);
|
snprintf(buf, sizeof(buf), "0x%"PRIx64, par->channel_layout);
|
||||||
av_dict_set(&dict, "WAVEFORMATEXTENSIBLE_CHANNEL_MASK", buf, 0);
|
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))
|
if (len >= ((1<<24) - 4))
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
@ -707,7 +707,7 @@ static int put_flac_codecpriv(AVFormatContext *s,
|
|||||||
AV_WB24(data + 1, len);
|
AV_WB24(data + 1, len);
|
||||||
|
|
||||||
p = data + 4;
|
p = data + 4;
|
||||||
ff_vorbiscomment_write(&p, &dict, vendor);
|
ff_vorbiscomment_write(&p, &dict, vendor, NULL, 0);
|
||||||
|
|
||||||
avio_write(pb, data, len + 4);
|
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,
|
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;
|
const char *vendor = bitexact ? "ffmpeg" : LIBAVFORMAT_IDENT;
|
||||||
int64_t size;
|
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);
|
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)
|
if (size > INT_MAX)
|
||||||
return NULL;
|
return NULL;
|
||||||
p = av_mallocz(size);
|
p = av_mallocz(size);
|
||||||
@ -308,7 +309,7 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact,
|
|||||||
p0 = p;
|
p0 = p;
|
||||||
|
|
||||||
p += offset;
|
p += offset;
|
||||||
ff_vorbiscomment_write(&p, m, vendor);
|
ff_vorbiscomment_write(&p, m, vendor, chapters, nb_chapters);
|
||||||
if (framing_bit)
|
if (framing_bit)
|
||||||
bytestream_put_byte(&p, 1);
|
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);
|
bytestream_put_buffer(&p, par->extradata, FLAC_STREAMINFO_SIZE);
|
||||||
|
|
||||||
// second packet: VorbisComment
|
// 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)
|
if (!p)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
oggstream->header[1] = p;
|
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
|
AV_WL32(&oggstream->header[0][68], 0); // set extra_headers to 0
|
||||||
|
|
||||||
// second packet: VorbisComment
|
// 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)
|
if (!p)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
oggstream->header[1] = p;
|
oggstream->header[1] = p;
|
||||||
@ -385,7 +386,8 @@ static int ogg_build_speex_headers(AVCodecParameters *par,
|
|||||||
|
|
||||||
static int ogg_build_opus_headers(AVCodecParameters *par,
|
static int ogg_build_opus_headers(AVCodecParameters *par,
|
||||||
OGGStreamContext *oggstream, int bitexact,
|
OGGStreamContext *oggstream, int bitexact,
|
||||||
AVDictionary **m)
|
AVDictionary **m, AVChapter **chapters,
|
||||||
|
unsigned int nb_chapters)
|
||||||
{
|
{
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
|
|
||||||
@ -401,7 +403,7 @@ static int ogg_build_opus_headers(AVCodecParameters *par,
|
|||||||
bytestream_put_buffer(&p, par->extradata, par->extradata_size);
|
bytestream_put_buffer(&p, par->extradata, par->extradata_size);
|
||||||
|
|
||||||
/* second packet: VorbisComment */
|
/* 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)
|
if (!p)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
oggstream->header[1] = p;
|
oggstream->header[1] = p;
|
||||||
@ -446,7 +448,7 @@ static int ogg_build_vp8_headers(AVFormatContext *s, AVStream *st,
|
|||||||
|
|
||||||
/* optional second packet: VorbisComment */
|
/* optional second packet: VorbisComment */
|
||||||
if (av_dict_get(st->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
|
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)
|
if (!p)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
oggstream->header[1] = p;
|
oggstream->header[1] = p;
|
||||||
@ -560,7 +562,7 @@ static int ogg_init(AVFormatContext *s)
|
|||||||
} else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
|
} else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) {
|
||||||
int err = ogg_build_opus_headers(st->codecpar, oggstream,
|
int err = ogg_build_opus_headers(st->codecpar, oggstream,
|
||||||
s->flags & AVFMT_FLAG_BITEXACT,
|
s->flags & AVFMT_FLAG_BITEXACT,
|
||||||
&st->metadata);
|
&st->metadata, s->chapters, s->nb_chapters);
|
||||||
if (err) {
|
if (err) {
|
||||||
av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n");
|
av_log(s, AV_LOG_ERROR, "Error writing Opus headers\n");
|
||||||
av_freep(&st->priv_data);
|
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,
|
p = ogg_write_vorbiscomment(7, s->flags & AVFMT_FLAG_BITEXACT,
|
||||||
&oggstream->header_len[1], &st->metadata,
|
&oggstream->header_len[1], &st->metadata,
|
||||||
framing_bit);
|
framing_bit, NULL, 0);
|
||||||
oggstream->header[1] = p;
|
oggstream->header[1] = p;
|
||||||
if (!p)
|
if (!p)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
|
@ -38,10 +38,21 @@ const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
|
|||||||
{ 0 }
|
{ 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;
|
int64_t len = 8;
|
||||||
len += strlen(vendor_string);
|
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) {
|
if (m) {
|
||||||
AVDictionaryEntry *tag = NULL;
|
AVDictionaryEntry *tag = NULL;
|
||||||
while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
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,
|
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_le32(p, strlen(vendor_string));
|
||||||
bytestream_put_buffer(p, vendor_string, 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) {
|
if (*m) {
|
||||||
int count = av_dict_count(*m);
|
int count = av_dict_count(*m) + cm_count;
|
||||||
AVDictionaryEntry *tag = NULL;
|
AVDictionaryEntry *tag = NULL;
|
||||||
bytestream_put_le32(p, count);
|
bytestream_put_le32(p, count);
|
||||||
while ((tag = av_dict_get(*m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
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_byte(p, '=');
|
||||||
bytestream_put_buffer(p, tag->value, len2);
|
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
|
} else
|
||||||
bytestream_put_le32(p, 0);
|
bytestream_put_le32(p, 0);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
* For no string, set to an empty string.
|
* For no string, set to an empty string.
|
||||||
* @return The length in bytes.
|
* @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
|
* 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 p The buffer in which to write.
|
||||||
* @param m The metadata struct to write.
|
* @param m The metadata struct to write.
|
||||||
* @param vendor_string The vendor string 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,
|
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[];
|
extern const AVMetadataConv ff_vorbiscomment_metadata_conv[];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user