diff options
Diffstat (limited to 'libavformat/oggparsevorbis.c')
-rw-r--r-- | libavformat/oggparsevorbis.c | 108 |
1 files changed, 85 insertions, 23 deletions
diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index 58cb4a67fc..8103d9dbb1 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -143,9 +143,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); @@ -156,16 +157,21 @@ int ff_vorbis_comment(AVFormatContext *as, AVDictionary **m, av_log(as, AV_LOG_WARNING, "Failed to parse cover art block.\n"); continue; } - } else if (!ogm_chapter(as, tt, ct)) + } else if (!ogm_chapter(as, tt, ct)) { + 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); @@ -203,12 +209,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; @@ -235,6 +244,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_comment(s, &st->metadata, os->buf + os->pstart + 7, + os->psize - 8, 1); + 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; @@ -271,6 +310,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; @@ -278,7 +318,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 @@ -304,9 +349,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_comment(s, &st->metadata, os->buf + os->pstart + 7, - os->psize - 8, 1) >= 0) { + if (vorbis_update_metadata(s, idx) >= 0 && priv->len[1] > 10) { unsigned new_len; int ret = ff_replaygain_export(st, st->metadata); @@ -343,29 +386,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]; } @@ -373,20 +427,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; } |