summaryrefslogtreecommitdiff
path: root/libavformat/oggparsevorbis.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/oggparsevorbis.c')
-rw-r--r--libavformat/oggparsevorbis.c105
1 files changed, 83 insertions, 22 deletions
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c
index 6bd1411cdb..5e34be5378 100644
--- a/libavformat/oggparsevorbis.c
+++ b/libavformat/oggparsevorbis.c
@@ -156,9 +156,10 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m,
char *pict = av_malloc(vl);
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);
@@ -171,16 +172,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);
@@ -218,12 +223,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;
@@ -250,6 +258,36 @@ static void vorbis_cleanup(AVFormatContext *s, int idx)
av_freep(&priv->packet[i]);
}
+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;
@@ -286,6 +324,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;
@@ -293,7 +332,12 @@ static int vorbis_header(AVFormatContext *s, int idx)
if (bytestream_get_le32(&p) != 0) /* vorbis_version */
return AVERROR_INVALIDDATA;
- st->codec->channels = bytestream_get_byte(&p);
+ channels = bytestream_get_byte(&p);
+ if (st->codec->channels && channels != st->codec->channels) {
+ av_log(s, AV_LOG_ERROR, "Channel change is not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ st->codec->channels = channels;
srate = bytestream_get_le32(&p);
p += 4; // skip maximum bitrate
st->codec->bit_rate = bytestream_get_le32(&p); // nominal bitrate
@@ -319,9 +363,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);
@@ -358,29 +400,40 @@ 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;
/* 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
* 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;
avpriv_vorbis_parse_reset(&priv->vp);
duration = 0;
- for (seg = 0; seg < os->nsegs; seg++) {
+ seg = os->segp;
+ d = avpriv_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 = avpriv_vorbis_parse_frame(&priv->vp, last_pkt, 1);
+ int d = avpriv_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];
}
@@ -388,20 +441,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;
avpriv_vorbis_parse_reset(&priv->vp);
}
/* parse packet duration */
if (os->psize > 0) {
- duration = avpriv_vorbis_parse_frame(&priv->vp, os->buf + os->pstart, 1);
- if (duration <= 0) {
+ duration = avpriv_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;
}