#include <stdlib.h>
#include "libavutil/avstring.h"
+#include "libavutil/base64.h"
#include "libavutil/bswap.h"
#include "libavutil/dict.h"
#include "libavcodec/bytestream.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/vorbis_parser.h"
#include "avformat.h"
+#include "flac_picture.h"
#include "internal.h"
#include "oggdec.h"
#include "vorbiscomment.h"
+#include "replaygain.h"
static int ogm_chapter(AVFormatContext *as, uint8_t *key, uint8_t *val)
{
int i, cnum, h, m, s, ms, keylen = strlen(key);
AVChapter *chapter = NULL;
- if (keylen < 9 || sscanf(key, "CHAPTER%02d", &cnum) != 1)
+ if (keylen < 9 || sscanf(key, "CHAPTER%03d", &cnum) != 1)
return 0;
- if (keylen == 9) {
+ if (keylen <= 10) {
if (sscanf(val, "%02d:%02d:%02d.%03d", &h, &m, &s, &ms) < 4)
return 0;
ms + 1000 * (s + 60 * (m + 60 * h)),
AV_NOPTS_VALUE, NULL);
av_free(val);
- } else if (!strcmp(key + 9, "NAME")) {
+ } else if (!strcmp(key + keylen - 4, "NAME")) {
for (i = 0; i < as->nb_chapters; i++)
if (as->chapters[i]->id == cnum) {
chapter = as->chapters[i];
return 1;
}
+int ff_vorbis_stream_comment(AVFormatContext *as, AVStream *st,
+ const uint8_t *buf, int size)
+{
+ int updates = ff_vorbis_comment(as, &st->metadata, buf, size, 1);
+
+ if (updates > 0) {
+ st->event_flags |= AVSTREAM_EVENT_FLAG_METADATA_UPDATED;
+ }
+
+ return updates;
+}
+
int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
- const uint8_t *buf, int size)
+ const uint8_t *buf, int size,
+ int parse_picture)
{
const uint8_t *p = buf;
const uint8_t *end = buf + size;
+ int updates = 0;
unsigned n, j;
int s;
/* must have vendor_length and user_comment_list_length */
if (size < 8)
- return -1;
+ return AVERROR_INVALIDDATA;
s = bytestream_get_le32(&p);
if (end - p - 4 < s || s < 0)
- return -1;
+ return AVERROR_INVALIDDATA;
p += s;
if (!tt || !ct) {
av_freep(&tt);
av_freep(&ct);
- av_log(as, AV_LOG_WARNING,
- "out-of-memory error. skipping VorbisComment tag.\n");
- continue;
+ return AVERROR(ENOMEM);
}
for (j = 0; j < tl; j++)
memcpy(ct, v, vl);
ct[vl] = 0;
- if (!ogm_chapter(as, tt, ct))
+ /* The format in which the pictures are stored is the FLAC format.
+ * Xiph says: "The binary FLAC picture structure is base64 encoded
+ * and placed within a VorbisComment with the tag name
+ * 'METADATA_BLOCK_PICTURE'. This is the preferred and
+ * recommended way of embedding cover art within VorbisComments."
+ */
+ if (!strcmp(tt, "METADATA_BLOCK_PICTURE") && parse_picture) {
+ int ret;
+ char *pict = av_malloc(vl);
+
+ if (!pict) {
+ av_freep(&tt);
+ av_freep(&ct);
+ return AVERROR(ENOMEM);
+ }
+ if ((ret = av_base64_decode(pict, ct, vl)) > 0)
+ ret = ff_flac_parse_picture(as, pict, ret);
+ av_freep(&tt);
+ av_freep(&ct);
+ av_freep(&pict);
+ if (ret < 0) {
+ av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n");
+ continue;
+ }
+ } else if (!ogm_chapter(as, tt, ct)) {
+ updates++;
av_dict_set(m, tt, ct,
AV_DICT_DONT_STRDUP_KEY |
AV_DICT_DONT_STRDUP_VAL);
+ }
}
}
ff_metadata_conv(m, NULL, ff_vorbiscomment_metadata_conv);
- return 0;
+ return updates;
}
/*
struct oggvorbis_private {
unsigned int len[3];
unsigned char *packet[3];
- VorbisParseContext vp;
+ AVVorbisParseContext *vp;
int64_t final_pts;
int final_duration;
};
-static unsigned int fixup_vorbis_headers(AVFormatContext *as,
- struct oggvorbis_private *priv,
- uint8_t **buf)
+static int fixup_vorbis_headers(AVFormatContext *as,
+ struct oggvorbis_private *priv,
+ uint8_t **buf)
{
int i, offset, len, err;
unsigned char *ptr;
len = priv->len[0] + priv->len[1] + priv->len[2];
ptr = *buf = av_mallocz(len + len / 255 + 64);
+ if (!ptr)
+ return AVERROR(ENOMEM);
ptr[0] = 2;
offset = 1;
offset += priv->len[i];
av_freep(&priv->packet[i]);
}
- if ((err = av_reallocp(buf, offset + FF_INPUT_BUFFER_PADDING_SIZE)) < 0)
+ if ((err = av_reallocp(buf, offset + AV_INPUT_BUFFER_PADDING_SIZE)) < 0)
return err;
return offset;
}
struct ogg_stream *os = ogg->streams + idx;
struct oggvorbis_private *priv = os->private;
int i;
- if (os->private)
+ if (os->private) {
+ av_vorbis_parse_free(&priv->vp);
for (i = 0; i < 3; i++)
av_freep(&priv->packet[i]);
+ }
}
static int vorbis_header(AVFormatContext *s, int idx)
if (!os->private) {
os->private = av_mallocz(sizeof(struct oggvorbis_private));
if (!os->private)
- return 0;
+ return AVERROR(ENOMEM);
}
if (!(pkt_type & 1))
return 0;
if (os->psize < 1 || pkt_type > 5)
- return -1;
+ return AVERROR_INVALIDDATA;
priv = os->private;
if (priv->packet[pkt_type >> 1])
- return -1;
+ return AVERROR_INVALIDDATA;
if (pkt_type > 1 && !priv->packet[0] || pkt_type > 3 && !priv->packet[1])
- return -1;
+ return AVERROR_INVALIDDATA;
priv->len[pkt_type >> 1] = os->psize;
priv->packet[pkt_type >> 1] = av_mallocz(os->psize);
+ if (!priv->packet[pkt_type >> 1])
+ return AVERROR(ENOMEM);
memcpy(priv->packet[pkt_type >> 1], os->buf + os->pstart, os->psize);
if (os->buf[os->pstart] == 1) {
const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */
int srate;
if (os->psize != 30)
- return -1;
+ return AVERROR_INVALIDDATA;
if (bytestream_get_le32(&p) != 0) /* vorbis_version */
- return -1;
+ return AVERROR_INVALIDDATA;
st->codec->channels = bytestream_get_byte(&p);
srate = bytestream_get_le32(&p);
bs1 = blocksize >> 4;
if (bs0 > bs1)
- return -1;
+ return AVERROR_INVALIDDATA;
if (bs0 < 6 || bs1 > 13)
- return -1;
+ return AVERROR_INVALIDDATA;
if (bytestream_get_byte(&p) != 1) /* framing_flag */
- return -1;
+ return AVERROR_INVALIDDATA;
st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
st->codec->codec_id = AV_CODEC_ID_VORBIS;
}
} else if (os->buf[os->pstart] == 3) {
if (os->psize > 8 &&
- ff_vorbis_comment(s, &st->metadata, os->buf + os->pstart + 7,
- os->psize - 8) >= 0) {
+ ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7,
+ os->psize - 8) >= 0) {
+ unsigned new_len;
+
+ int ret = ff_replaygain_export(st, st->metadata);
+ if (ret < 0)
+ return ret;
+
// drop all metadata we parsed and which is not required by libvorbis
- unsigned new_len = 7 + 4 + AV_RL32(priv->packet[1] + 7) + 4 + 1;
+ new_len = 7 + 4 + AV_RL32(priv->packet[1] + 7) + 4 + 1;
if (new_len >= 16 && new_len < os->psize) {
AV_WL32(priv->packet[1] + new_len - 5, 0);
priv->packet[1][new_len - 1] = 1;
return ret;
}
st->codec->extradata_size = ret;
- if ((ret = avpriv_vorbis_parse_extradata(st->codec, &priv->vp))) {
+
+ priv->vp = av_vorbis_parse_init(st->codec->extradata, st->codec->extradata_size);
+ if (!priv->vp) {
av_freep(&st->codec->extradata);
st->codec->extradata_size = 0;
return ret;
struct oggvorbis_private *priv = os->private;
int duration;
+ if (!priv->vp)
+ return AVERROR_INVALIDDATA;
+
/* first packet handling
* here we parse the duration of each packet in the first page and compare
* the total duration to the page granule to find the encoder delay and
uint8_t *next_pkt = last_pkt;
int first_duration = 0;
- avpriv_vorbis_parse_reset(&priv->vp);
+ av_vorbis_parse_reset(priv->vp);
duration = 0;
for (seg = 0; seg < os->nsegs; seg++) {
if (os->segments[seg] < 255) {
- int d = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1);
+ int d = av_vorbis_parse_frame(priv->vp, last_pkt, 1);
if (d < 0) {
duration = os->granule;
break;
s->streams[idx]->duration -= s->streams[idx]->start_time;
s->streams[idx]->cur_dts = AV_NOPTS_VALUE;
priv->final_pts = AV_NOPTS_VALUE;
- avpriv_vorbis_parse_reset(&priv->vp);
+ av_vorbis_parse_reset(priv->vp);
}
/* parse packet duration */
if (os->psize > 0) {
- duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1);
+ duration = av_vorbis_parse_frame(priv->vp, os->buf + os->pstart, 1);
if (duration <= 0) {
os->pflags |= AV_PKT_FLAG_CORRUPT;
return 0;