diff options
Diffstat (limited to 'libavformat/oggparsevorbis.c')
-rw-r--r-- | libavformat/oggparsevorbis.c | 113 |
1 files changed, 87 insertions, 26 deletions
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index b2f9c8b..dd44337 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; @@ -252,6 +260,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; @@ -266,14 +304,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]) @@ -288,6 +326,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; @@ -295,7 +334,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 @@ -321,9 +365,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); @@ -350,7 +392,7 @@ static int vorbis_header(AVFormatContext *s, int idx) if (!priv->vp) { av_freep(&st->codec->extradata); st->codec->extradata_size = 0; - return ret; + return AVERROR_UNKNOWN; } } @@ -362,29 +404,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; 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]; } @@ -392,20 +445,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; } |