summaryrefslogtreecommitdiff
path: root/libavformat/oggparsevorbis.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/oggparsevorbis.c')
-rw-r--r--libavformat/oggparsevorbis.c122
1 files changed, 92 insertions, 30 deletions
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index 95fe082949..65b1998a02 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -153,18 +153,20 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
* recommended way of embedding cover art within VorbisComments."
*/
if (!strcmp(tt, "METADATA_BLOCK_PICTURE") && parse_picture) {
- int ret;
- char *pict = av_malloc(vl);
+ int ret, len = AV_BASE64_DECODE_SIZE(vl);
+ char *pict = av_malloc(len);
if (!pict) {
+ av_log(as, AV_LOG_WARNING, "out-of-memory error. Skipping cover art block.\n");
av_freep(&tt);
av_freep(&ct);
- return AVERROR(ENOMEM);
+ continue;
}
- if ((ret = av_base64_decode(pict, ct, vl)) > 0)
- ret = ff_flac_parse_picture(as, pict, ret);
+ ret = av_base64_decode(pict, ct, len);
av_freep(&tt);
av_freep(&ct);
+ if (ret > 0)
+ ret = ff_flac_parse_picture(as, pict, ret);
av_freep(&pict);
if (ret < 0) {
av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n");
@@ -172,16 +174,20 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
}
} else if (!ogm_chapter(as, tt, ct)) {
updates++;
+ if (av_dict_get(*m, tt, NULL, 0)) {
+ av_dict_set(m, tt, ";", AV_DICT_APPEND);
+ }
av_dict_set(m, tt, ct,
AV_DICT_DONT_STRDUP_KEY |
- AV_DICT_DONT_STRDUP_VAL);
+ AV_DICT_APPEND);
+ av_freep(&ct);
}
}
}
if (p != end)
av_log(as, AV_LOG_INFO,
- "%ti bytes of comment header remain\n", end - p);
+ "%"PTRDIFF_SPECIFIER" bytes of comment header remain\n", end - p);
if (n > 0)
av_log(as, AV_LOG_INFO,
"truncated comment header, %i comments not found\n", n);
@@ -219,12 +225,15 @@ static int fixup_vorbis_headers(AVFormatContext *as,
uint8_t **buf)
{
int i, offset, len, err;
+ int buf_len;
unsigned char *ptr;
len = priv->len[0] + priv->len[1] + priv->len[2];
- ptr = *buf = av_mallocz(len + len / 255 + 64);
+ buf_len = len + len / 255 + 64;
+ ptr = *buf = av_realloc(NULL, buf_len);
if (!ptr)
return AVERROR(ENOMEM);
+ memset(*buf, '\0', buf_len);
ptr[0] = 2;
offset = 1;
@@ -253,6 +262,36 @@ static void vorbis_cleanup(AVFormatContext *s, int idx)
}
}
+static int vorbis_update_metadata(AVFormatContext *s, int idx)
+{
+ struct ogg *ogg = s->priv_data;
+ struct ogg_stream *os = ogg->streams + idx;
+ AVStream *st = s->streams[idx];
+ int ret;
+
+ if (os->psize <= 8)
+ return 0;
+
+ /* New metadata packet; release old data. */
+ av_dict_free(&st->metadata);
+ ret = ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7,
+ os->psize - 8);
+ if (ret < 0)
+ return ret;
+
+ /* Update the metadata if possible. */
+ av_freep(&os->new_metadata);
+ if (st->metadata) {
+ os->new_metadata = av_packet_pack_dictionary(st->metadata, &os->new_metadata_size);
+ /* Send an empty dictionary to indicate that metadata has been cleared. */
+ } else {
+ os->new_metadata = av_malloc(1);
+ os->new_metadata_size = 0;
+ }
+
+ return ret;
+}
+
static int vorbis_header(AVFormatContext *s, int idx)
{
struct ogg *ogg = s->priv_data;
@@ -267,14 +306,14 @@ static int vorbis_header(AVFormatContext *s, int idx)
return AVERROR(ENOMEM);
}
+ priv = os->private;
+
if (!(pkt_type & 1))
- return 0;
+ return priv->vp ? 0 : AVERROR_INVALIDDATA;
if (os->psize < 1 || pkt_type > 5)
return AVERROR_INVALIDDATA;
- priv = os->private;
-
if (priv->packet[pkt_type >> 1])
return AVERROR_INVALIDDATA;
if (pkt_type > 1 && !priv->packet[0] || pkt_type > 3 && !priv->packet[1])
@@ -289,6 +328,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
const uint8_t *p = os->buf + os->pstart + 7; /* skip "\001vorbis" tag */
unsigned blocksize, bs0, bs1;
int srate;
+ int channels;
if (os->psize != 30)
return AVERROR_INVALIDDATA;
@@ -296,7 +336,12 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (bytestream_get_le32(&p) != 0) /* vorbis_version */
return AVERROR_INVALIDDATA;
- st->codecpar->channels = bytestream_get_byte(&p);
+ channels = bytestream_get_byte(&p);
+ if (st->codecpar->channels && channels != st->codecpar->channels) {
+ av_log(s, AV_LOG_ERROR, "Channel change is not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ st->codecpar->channels = channels;
srate = bytestream_get_le32(&p);
p += 4; // skip maximum bitrate
st->codecpar->bit_rate = bytestream_get_le32(&p); // nominal bitrate
@@ -322,9 +367,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
avpriv_set_pts_info(st, 64, 1, srate);
}
} else if (os->buf[os->pstart] == 3) {
- if (os->psize > 8 &&
- ff_vorbis_stream_comment(s, st, os->buf + os->pstart + 7,
- os->psize - 8) >= 0) {
+ if (vorbis_update_metadata(s, idx) >= 0 && priv->len[1] > 10) {
unsigned new_len;
int ret = ff_replaygain_export(st, st->metadata);
@@ -351,7 +394,7 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (!priv->vp) {
av_freep(&st->codecpar->extradata);
st->codecpar->extradata_size = 0;
- return ret;
+ return AVERROR_UNKNOWN;
}
}
@@ -363,7 +406,7 @@ static int vorbis_packet(AVFormatContext *s, int idx)
struct ogg *ogg = s->priv_data;
struct ogg_stream *os = ogg->streams + idx;
struct oggvorbis_private *priv = os->private;
- int duration;
+ int duration, flags = 0;
if (!priv->vp)
return AVERROR_INVALIDDATA;
@@ -372,23 +415,34 @@ static int vorbis_packet(AVFormatContext *s, int idx)
* 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
* set the first timestamp */
- if (!os->lastpts) {
- int seg;
+ if ((!os->lastpts || os->lastpts == AV_NOPTS_VALUE) && !(os->flags & OGG_FLAG_EOS) && (int64_t)os->granule>=0) {
+ int seg, d;
uint8_t *last_pkt = os->buf + os->pstart;
uint8_t *next_pkt = last_pkt;
- int first_duration = 0;
av_vorbis_parse_reset(priv->vp);
duration = 0;
- for (seg = 0; seg < os->nsegs; seg++) {
+ seg = os->segp;
+ d = av_vorbis_parse_frame_flags(priv->vp, last_pkt, 1, &flags);
+ if (d < 0) {
+ os->pflags |= AV_PKT_FLAG_CORRUPT;
+ return 0;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
+ }
+ duration += d;
+ last_pkt = next_pkt = next_pkt + os->psize;
+ for (; seg < os->nsegs; seg++) {
if (os->segments[seg] < 255) {
- int d = av_vorbis_parse_frame(priv->vp, last_pkt, 1);
+ int d = av_vorbis_parse_frame_flags(priv->vp, last_pkt, 1, &flags);
if (d < 0) {
duration = os->granule;
break;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
}
- if (!duration)
- first_duration = d;
duration += d;
last_pkt = next_pkt + os->segments[seg];
}
@@ -396,20 +450,28 @@ static int vorbis_packet(AVFormatContext *s, int idx)
}
os->lastpts =
os->lastdts = os->granule - duration;
- s->streams[idx]->start_time = os->lastpts + first_duration;
- if (s->streams[idx]->duration)
- s->streams[idx]->duration -= s->streams[idx]->start_time;
- s->streams[idx]->cur_dts = AV_NOPTS_VALUE;
+
+ if (!os->granule && duration) //hack to deal with broken files (Ticket3710)
+ os->lastpts = os->lastdts = AV_NOPTS_VALUE;
+
+ if (s->streams[idx]->start_time == AV_NOPTS_VALUE) {
+ s->streams[idx]->start_time = FFMAX(os->lastpts, 0);
+ if (s->streams[idx]->duration != AV_NOPTS_VALUE)
+ s->streams[idx]->duration -= s->streams[idx]->start_time;
+ }
priv->final_pts = AV_NOPTS_VALUE;
av_vorbis_parse_reset(priv->vp);
}
/* parse packet duration */
if (os->psize > 0) {
- duration = av_vorbis_parse_frame(priv->vp, os->buf + os->pstart, 1);
- if (duration <= 0) {
+ duration = av_vorbis_parse_frame_flags(priv->vp, os->buf + os->pstart, 1, &flags);
+ if (duration < 0) {
os->pflags |= AV_PKT_FLAG_CORRUPT;
return 0;
+ } else if (flags & VORBIS_FLAG_COMMENT) {
+ vorbis_update_metadata(s, idx);
+ flags = 0;
}
os->pduration = duration;
}