X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fwavdec.c;h=0c8b8bf85d07af2d1a0689aee02eb5f37dd14f7e;hb=bc70684e74a185d7b80c8b80bdedda659cb581b8;hp=e280be4d44e650895db7a5ef7473960c9a4efa61;hpb=1a8be90adbaf86faa3053ff98118004ad7711c8c;p=ffmpeg diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index e280be4d44e..0c8b8bf85d0 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -6,6 +6,8 @@ * RF64 demuxer * Copyright (c) 2009 Daniel Verkamp * + * BW64 demuxer + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -25,6 +27,7 @@ #include +#include "config.h" #include "libavutil/avassert.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" @@ -34,6 +37,7 @@ #include "avformat.h" #include "avio.h" #include "avio_internal.h" +#include "id3v2.h" #include "internal.h" #include "metadata.h" #include "pcm.h" @@ -45,6 +49,7 @@ typedef struct WAVDemuxContext { const AVClass *class; int64_t data_end; int w64; + AVStream *vst; int64_t smv_data_ofs; int smv_block_size; int smv_frames_per_jpeg; @@ -53,13 +58,24 @@ typedef struct WAVDemuxContext { int smv_eof; int audio_eof; int ignore_length; + int max_size; int spdif; - int smv_cur_pt; int smv_given_first; int unaligned; // e.g. if an odd number of bytes ID3 tag was prepended int rifx; // RIFX: integer byte order for parameters is big endian } WAVDemuxContext; +#define OFFSET(x) offsetof(WAVDemuxContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption demux_options[] = { +#define W64_DEMUXER_OPTIONS_OFFSET (1 * CONFIG_WAV_DEMUXER) +#if CONFIG_WAV_DEMUXER + { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, +#endif + { "max_size", "max size of single packet", OFFSET(max_size), AV_OPT_TYPE_INT, { .i64 = 4096 }, 1024, 1 << 22, DEC }, + { NULL }, +}; + static void set_spdif(AVFormatContext *s, WAVDemuxContext *wav) { if (CONFIG_SPDIF_DEMUXER && s->streams[0]->codecpar->codec_tag == 1) { @@ -68,7 +84,7 @@ static void set_spdif(AVFormatContext *s, WAVDemuxContext *wav) int ret = ffio_ensure_seekback(s->pb, len); if (ret >= 0) { - uint8_t *buf = av_malloc(len); + uint8_t *buf = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE); if (!buf) { ret = AVERROR(ENOMEM); } else { @@ -128,7 +144,7 @@ static int64_t find_tag(WAVDemuxContext * wav, AVIOContext *pb, uint32_t tag1) return size; } -static int wav_probe(AVProbeData *p) +static int wav_probe(const AVProbeData *p) { /* check file header */ if (p->buf_size <= 32) @@ -139,7 +155,8 @@ static int wav_probe(AVProbeData *p) * its own, the returned score is decreased to avoid a probe * conflict between ACT and WAV. */ return AVPROBE_SCORE_MAX - 1; - else if (!memcmp(p->buf, "RF64", 4) && + else if ((!memcmp(p->buf, "RF64", 4) || + !memcmp(p->buf, "BW64", 4)) && !memcmp(p->buf + 12, "ds64", 4)) return AVPROBE_SCORE_MAX; } @@ -149,49 +166,41 @@ static int wav_probe(AVProbeData *p) static void handle_stream_probing(AVStream *st) { if (st->codecpar->codec_id == AV_CODEC_ID_PCM_S16LE) { - st->request_probe = AVPROBE_SCORE_EXTENSION; + st->internal->request_probe = AVPROBE_SCORE_EXTENSION; st->probe_packets = FFMIN(st->probe_packets, 32); } } -static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) +static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream *st) { AVIOContext *pb = s->pb; WAVDemuxContext *wav = s->priv_data; int ret; /* parse fmt header */ - *st = avformat_new_stream(s, NULL); - if (!*st) - return AVERROR(ENOMEM); - - ret = ff_get_wav_header(s, pb, (*st)->codecpar, size, wav->rifx); + ret = ff_get_wav_header(s, pb, st->codecpar, size, wav->rifx); if (ret < 0) return ret; - handle_stream_probing(*st); + handle_stream_probing(st); - (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; - avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate); + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); return 0; } -static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st) +static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream *st) { AVIOContext *pb = s->pb; - int version, num_streams, i, channels = 0; + int version, num_streams, i, channels = 0, ret; if (size < 36) return AVERROR_INVALIDDATA; - *st = avformat_new_stream(s, NULL); - if (!*st) - return AVERROR(ENOMEM); - - (*st)->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - (*st)->codecpar->codec_id = AV_CODEC_ID_XMA2; - (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_XMA2; + st->need_parsing = AVSTREAM_PARSE_FULL_RAW; version = avio_r8(pb); if (version != 3 && version != 4) @@ -200,28 +209,27 @@ static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st) if (size != (32 + ((version==3)?0:8) + 4*num_streams)) return AVERROR_INVALIDDATA; avio_skip(pb, 10); - (*st)->codecpar->sample_rate = avio_rb32(pb); + st->codecpar->sample_rate = avio_rb32(pb); if (version == 4) avio_skip(pb, 8); avio_skip(pb, 4); - (*st)->duration = avio_rb32(pb); + st->duration = avio_rb32(pb); avio_skip(pb, 8); for (i = 0; i < num_streams; i++) { channels += avio_r8(pb); avio_skip(pb, 3); } - (*st)->codecpar->channels = channels; + st->codecpar->channels = channels; - if ((*st)->codecpar->channels <= 0 || (*st)->codecpar->sample_rate <= 0) + if (st->codecpar->channels <= 0 || st->codecpar->sample_rate <= 0) return AVERROR_INVALIDDATA; - avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate); + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); avio_seek(pb, -size, SEEK_CUR); - av_freep(&(*st)->codecpar->extradata); - if (ff_get_extradata(s, (*st)->codecpar, pb, size) < 0) - return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, pb, size)) < 0) + return ret; return 0; } @@ -232,9 +240,9 @@ static inline int wav_parse_bext_string(AVFormatContext *s, const char *key, char temp[257]; int ret; - av_assert0(length <= sizeof(temp)); - if ((ret = avio_read(s->pb, temp, length)) < 0) - return ret; + av_assert0(length < sizeof(temp)); + if ((ret = avio_read(s->pb, temp, length)) != length) + return ret < 0 ? ret : AVERROR_INVALIDDATA; temp[length] = 0; @@ -303,8 +311,10 @@ static int wav_parse_bext_tag(AVFormatContext *s, int64_t size) if (!(coding_history = av_malloc(size + 1))) return AVERROR(ENOMEM); - if ((ret = avio_read(s->pb, coding_history, size)) < 0) - return ret; + if ((ret = avio_read(s->pb, coding_history, size)) != size) { + av_free(coding_history); + return ret < 0 ? ret : AVERROR_INVALIDDATA; + } coding_history[size] = 0; if ((ret = av_dict_set(&s->metadata, "coding_history", coding_history, @@ -328,7 +338,7 @@ static int wav_read_header(AVFormatContext *s) { int64_t size, av_uninit(data_size); int64_t sample_count = 0; - int rf64 = 0; + int rf64 = 0, bw64 = 0; uint32_t tag; AVIOContext *pb = s->pb; AVStream *st = NULL; @@ -351,6 +361,9 @@ static int wav_read_header(AVFormatContext *s) case MKTAG('R', 'F', '6', '4'): rf64 = 1; break; + case MKTAG('B', 'W', '6', '4'): + bw64 = 1; + break; default: av_log(s, AV_LOG_ERROR, "invalid start code %s in RIFF header\n", av_fourcc2str(tag)); @@ -366,7 +379,7 @@ static int wav_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - if (rf64) { + if (rf64 || bw64) { if (avio_rl32(pb) != MKTAG('d', 's', '6', '4')) return AVERROR_INVALIDDATA; size = avio_rl32(pb); @@ -387,6 +400,11 @@ static int wav_read_header(AVFormatContext *s) } + /* Create the audio stream now so that its index is always zero */ + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + for (;;) { AVStream *vst; size = next_tag(pb, &tag, wav->rifx); @@ -398,7 +416,7 @@ static int wav_read_header(AVFormatContext *s) switch (tag) { case MKTAG('f', 'm', 't', ' '): /* only parse the first 'fmt ' tag found */ - if (!got_xma2 && !got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { + if (!got_xma2 && !got_fmt && (ret = wav_parse_fmt_tag(s, size, st)) < 0) { return ret; } else if (got_fmt) av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n"); @@ -407,7 +425,7 @@ static int wav_read_header(AVFormatContext *s) break; case MKTAG('X', 'M', 'A', '2'): /* only parse the first 'XMA2' tag found */ - if (!got_fmt && !got_xma2 && (ret = wav_parse_xma2_tag(s, size, &st)) < 0) { + if (!got_fmt && !got_xma2 && (ret = wav_parse_xma2_tag(s, size, st)) < 0) { return ret; } else if (got_xma2) av_log(s, AV_LOG_WARNING, "found more than one 'XMA2' tag\n"); @@ -421,7 +439,7 @@ static int wav_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } - if (rf64) { + if (rf64 || bw64) { next_tag_ofs = wav->data_end = avio_tell(pb) + data_size; } else if (size != 0xFFFFFFFF) { data_size = size; @@ -438,7 +456,7 @@ static int wav_read_header(AVFormatContext *s) /* don't look for footer metadata if we can't seek or if we don't * know where the data tag ends */ - if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || (!rf64 && !size)) + if (!(pb->seekable & AVIO_SEEKABLE_NORMAL) || (!(rf64 && !bw64) && !size)) goto break_loop; break; case MKTAG('f', 'a', 'c', 't'): @@ -464,15 +482,16 @@ static int wav_read_header(AVFormatContext *s) vst = avformat_new_stream(s, NULL); if (!vst) return AVERROR(ENOMEM); + wav->vst = vst; avio_r8(pb); vst->id = 1; vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; vst->codecpar->codec_id = AV_CODEC_ID_SMVJPEG; vst->codecpar->width = avio_rl24(pb); vst->codecpar->height = avio_rl24(pb); - if (ff_alloc_extradata(vst->codecpar, 4)) { + if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0) { av_log(s, AV_LOG_ERROR, "Could not allocate extradata.\n"); - return AVERROR(ENOMEM); + return ret; } size = avio_rl24(pb); wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3; @@ -488,9 +507,9 @@ static int wav_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } AV_WL32(vst->codecpar->extradata, wav->smv_frames_per_jpeg); - wav->smv_cur_pt = 0; goto break_loop; case MKTAG('L', 'I', 'S', 'T'): + case MKTAG('l', 'i', 's', 't'): if (size < 4) { av_log(s, AV_LOG_ERROR, "too short LIST tag\n"); return AVERROR_INVALIDDATA; @@ -498,6 +517,66 @@ static int wav_read_header(AVFormatContext *s) switch (avio_rl32(pb)) { case MKTAG('I', 'N', 'F', 'O'): ff_read_riff_info(s, size - 4); + break; + case MKTAG('a', 'd', 't', 'l'): + if (s->nb_chapters > 0) { + while (avio_tell(pb) < next_tag_ofs && + !avio_feof(pb)) { + char cue_label[512]; + unsigned id, sub_size; + + if (avio_rl32(pb) != MKTAG('l', 'a', 'b', 'l')) + break; + + sub_size = avio_rl32(pb); + if (sub_size < 5) + break; + id = avio_rl32(pb); + avio_get_str(pb, sub_size - 4, cue_label, sizeof(cue_label)); + avio_skip(pb, avio_tell(pb) & 1); + + for (int i = 0; i < s->nb_chapters; i++) { + if (s->chapters[i]->id == id) { + av_dict_set(&s->chapters[i]->metadata, "title", cue_label, 0); + break; + } + } + } + } + break; + } + break; + case MKTAG('I', 'D', '3', ' '): + case MKTAG('i', 'd', '3', ' '): { + ID3v2ExtraMeta *id3v2_extra_meta; + ff_id3v2_read_dict(pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); + if (id3v2_extra_meta) { + ff_id3v2_parse_apic(s, id3v2_extra_meta); + ff_id3v2_parse_chapters(s, id3v2_extra_meta); + ff_id3v2_parse_priv(s, id3v2_extra_meta); + } + ff_id3v2_free_extra_meta(&id3v2_extra_meta); + } + break; + case MKTAG('c', 'u', 'e', ' '): + if (size >= 4 && got_fmt && st->codecpar->sample_rate > 0) { + AVRational tb = {1, st->codecpar->sample_rate}; + unsigned nb_cues = avio_rl32(pb); + + if (size >= nb_cues * 24LL + 4LL) { + for (int i = 0; i < nb_cues; i++) { + unsigned offset, id = avio_rl32(pb); + + if (avio_feof(pb)) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 16); + offset = avio_rl32(pb); + + if (!avpriv_new_chapter(s, id, tb, offset, AV_NOPTS_VALUE, NULL)) + return AVERROR(ENOMEM); + } + } } break; } @@ -575,6 +654,9 @@ break_loop: } else if (st->codecpar->codec_id == AV_CODEC_ID_XMA1 || st->codecpar->codec_id == AV_CODEC_ID_XMA2) { st->codecpar->block_align = 2048; + } else if (st->codecpar->codec_id == AV_CODEC_ID_ADPCM_MS && st->codecpar->channels > 2 && + st->codecpar->block_align < INT_MAX / st->codecpar->channels) { + st->codecpar->block_align *= st->codecpar->channels; } ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); @@ -597,7 +679,7 @@ static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16]) while (!avio_feof(pb)) { avio_read(pb, guid, 16); size = avio_rl64(pb); - if (size <= 24) + if (size <= 24 || size > INT64_MAX - 8) return AVERROR_INVALIDDATA; if (!memcmp(guid, guid1, 16)) return size; @@ -606,29 +688,28 @@ static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16]) return AVERROR_EOF; } -#define MAX_SIZE 4096 - static int wav_read_packet(AVFormatContext *s, AVPacket *pkt) { int ret, size; int64_t left; - AVStream *st; WAVDemuxContext *wav = s->priv_data; + AVStream *st = s->streams[0]; if (CONFIG_SPDIF_DEMUXER && wav->spdif == 1) return ff_spdif_read_packet(s, pkt); if (wav->smv_data_ofs > 0) { int64_t audio_dts, video_dts; + AVStream *vst = wav->vst; smv_retry: - audio_dts = (int32_t)s->streams[0]->cur_dts; - video_dts = (int32_t)s->streams[1]->cur_dts; + audio_dts = (int32_t)st->cur_dts; + video_dts = (int32_t)vst->cur_dts; if (audio_dts != AV_NOPTS_VALUE && video_dts != AV_NOPTS_VALUE) { /*We always return a video frame first to get the pixel format first*/ wav->smv_last_stream = wav->smv_given_first ? - av_compare_ts(video_dts, s->streams[1]->time_base, - audio_dts, s->streams[0]->time_base) > 0 : 0; + av_compare_ts(video_dts, vst->time_base, + audio_dts, st->time_base) > 0 : 0; wav->smv_given_first = 1; } wav->smv_last_stream = !wav->smv_last_stream; @@ -647,14 +728,11 @@ smv_retry: if (ret < 0) goto smv_out; pkt->pos -= 3; - pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg + wav->smv_cur_pt; - wav->smv_cur_pt++; - if (wav->smv_frames_per_jpeg > 0) - wav->smv_cur_pt %= wav->smv_frames_per_jpeg; - if (!wav->smv_cur_pt) - wav->smv_block++; - - pkt->stream_index = 1; + pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg; + pkt->duration = wav->smv_frames_per_jpeg; + wav->smv_block++; + + pkt->stream_index = vst->index; smv_out: avio_seek(s->pb, old_pos, SEEK_SET); if (ret == AVERROR_EOF) { @@ -665,8 +743,6 @@ smv_out: } } - st = s->streams[0]; - left = wav->data_end - avio_tell(s->pb); if (wav->ignore_length) left = INT_MAX; @@ -684,7 +760,7 @@ smv_out: wav->data_end = avio_tell(s->pb) + left; } - size = MAX_SIZE; + size = wav->max_size; if (st->codecpar->block_align > 1) { if (size < st->codecpar->block_align) size = st->codecpar->block_align; @@ -703,23 +779,24 @@ static int wav_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { WAVDemuxContext *wav = s->priv_data; - AVStream *st; + AVStream *ast = s->streams[0], *vst = wav->vst; wav->smv_eof = 0; wav->audio_eof = 0; + + if (stream_index != 0 && (!vst || stream_index != vst->index)) + return AVERROR(EINVAL); if (wav->smv_data_ofs > 0) { int64_t smv_timestamp = timestamp; if (stream_index == 0) - smv_timestamp = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[1]->time_base); + smv_timestamp = av_rescale_q(timestamp, ast->time_base, vst->time_base); else - timestamp = av_rescale_q(smv_timestamp, s->streams[1]->time_base, s->streams[0]->time_base); + timestamp = av_rescale_q(smv_timestamp, vst->time_base, ast->time_base); if (wav->smv_frames_per_jpeg > 0) { wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg; - wav->smv_cur_pt = smv_timestamp % wav->smv_frames_per_jpeg; } } - st = s->streams[0]; - switch (st->codecpar->codec_id) { + switch (ast->codecpar->codec_id) { case AV_CODEC_ID_MP2: case AV_CODEC_ID_MP3: case AV_CODEC_ID_AC3: @@ -730,23 +807,16 @@ static int wav_read_seek(AVFormatContext *s, default: break; } - return ff_pcm_read_seek(s, stream_index, timestamp, flags); + return ff_pcm_read_seek(s, 0, timestamp, flags); } -#define OFFSET(x) offsetof(WAVDemuxContext, x) -#define DEC AV_OPT_FLAG_DECODING_PARAM -static const AVOption demux_options[] = { - { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, - { NULL }, -}; - static const AVClass wav_demuxer_class = { .class_name = "WAV demuxer", .item_name = av_default_item_name, .option = demux_options, .version = LIBAVUTIL_VERSION_INT, }; -AVInputFormat ff_wav_demuxer = { +const AVInputFormat ff_wav_demuxer = { .name = "wav", .long_name = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"), .priv_data_size = sizeof(WAVDemuxContext), @@ -755,13 +825,13 @@ AVInputFormat ff_wav_demuxer = { .read_packet = wav_read_packet, .read_seek = wav_read_seek, .flags = AVFMT_GENERIC_INDEX, - .codec_tag = (const AVCodecTag * const []) { ff_codec_wav_tags, 0 }, + .codec_tag = ff_wav_codec_tags_list, .priv_class = &wav_demuxer_class, }; #endif /* CONFIG_WAV_DEMUXER */ #if CONFIG_W64_DEMUXER -static int w64_probe(AVProbeData *p) +static int w64_probe(const AVProbeData *p) { if (p->buf_size <= 40) return 0; @@ -834,6 +904,7 @@ static int w64_read_header(AVFormatContext *s) } else if (!memcmp(guid, ff_w64_guid_summarylist, 16)) { int64_t start, end, cur; uint32_t count, chunk_size, i; + int64_t filesize = avio_size(s->pb); start = avio_tell(pb); end = start + FFALIGN(size, INT64_C(8)) - 24; @@ -848,14 +919,18 @@ static int w64_read_header(AVFormatContext *s) chunk_key[4] = 0; avio_read(pb, chunk_key, 4); chunk_size = avio_rl32(pb); - if (chunk_size == UINT32_MAX) + if (chunk_size == UINT32_MAX || (filesize >= 0 && chunk_size > filesize)) return AVERROR_INVALIDDATA; - value = av_mallocz(chunk_size + 1); + value = av_malloc(chunk_size + 1); if (!value) return AVERROR(ENOMEM); ret = avio_get_str16le(pb, chunk_size, value, chunk_size); + if (ret < 0) { + av_free(value); + return ret; + } avio_skip(pb, chunk_size - ret); av_dict_set(&s->metadata, chunk_key, value, AV_DICT_DONT_STRDUP_VAL); @@ -884,7 +959,14 @@ static int w64_read_header(AVFormatContext *s) return 0; } -AVInputFormat ff_w64_demuxer = { +static const AVClass w64_demuxer_class = { + .class_name = "W64 demuxer", + .item_name = av_default_item_name, + .option = &demux_options[W64_DEMUXER_OPTIONS_OFFSET], + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVInputFormat ff_w64_demuxer = { .name = "w64", .long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"), .priv_data_size = sizeof(WAVDemuxContext), @@ -893,6 +975,7 @@ AVInputFormat ff_w64_demuxer = { .read_packet = wav_read_packet, .read_seek = wav_read_seek, .flags = AVFMT_GENERIC_INDEX, - .codec_tag = (const AVCodecTag * const []) { ff_codec_wav_tags, 0 }, + .codec_tag = ff_wav_codec_tags_list, + .priv_class = &w64_demuxer_class, }; #endif /* CONFIG_W64_DEMUXER */