yading@11: /* yading@11: * WAV demuxer yading@11: * Copyright (c) 2001, 2002 Fabrice Bellard yading@11: * yading@11: * Sony Wave64 demuxer yading@11: * RF64 demuxer yading@11: * Copyright (c) 2009 Daniel Verkamp yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avassert.h" yading@11: #include "libavutil/dict.h" yading@11: #include "libavutil/log.h" yading@11: #include "libavutil/mathematics.h" yading@11: #include "libavutil/opt.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #include "avio_internal.h" yading@11: #include "pcm.h" yading@11: #include "riff.h" yading@11: #include "w64.h" yading@11: #include "avio.h" yading@11: #include "metadata.h" yading@11: #include "spdif.h" yading@11: yading@11: typedef struct WAVDemuxContext { yading@11: const AVClass *class; yading@11: int64_t data_end; yading@11: int w64; yading@11: int64_t smv_data_ofs; yading@11: int smv_block_size; yading@11: int smv_frames_per_jpeg; yading@11: int smv_block; yading@11: int smv_last_stream; yading@11: int smv_eof; yading@11: int audio_eof; yading@11: int ignore_length; yading@11: int spdif; yading@11: } WAVDemuxContext; yading@11: yading@11: yading@11: #if CONFIG_WAV_DEMUXER yading@11: yading@11: static int64_t next_tag(AVIOContext *pb, uint32_t *tag) yading@11: { yading@11: *tag = avio_rl32(pb); yading@11: return avio_rl32(pb); yading@11: } yading@11: yading@11: /* return the size of the found tag */ yading@11: static int64_t find_tag(AVIOContext *pb, uint32_t tag1) yading@11: { yading@11: unsigned int tag; yading@11: int64_t size; yading@11: yading@11: for (;;) { yading@11: if (url_feof(pb)) yading@11: return -1; yading@11: size = next_tag(pb, &tag); yading@11: if (tag == tag1) yading@11: break; yading@11: avio_skip(pb, size + (size & 1)); yading@11: } yading@11: return size; yading@11: } yading@11: yading@11: static int wav_probe(AVProbeData *p) yading@11: { yading@11: /* check file header */ yading@11: if (p->buf_size <= 32) yading@11: return 0; yading@11: if (!memcmp(p->buf + 8, "WAVE", 4)) { yading@11: if (!memcmp(p->buf, "RIFF", 4)) yading@11: /* yading@11: Since ACT demuxer has standard WAV header at top of it's own, yading@11: returning score is decreased to avoid probe conflict yading@11: between ACT and WAV. yading@11: */ yading@11: return AVPROBE_SCORE_MAX - 1; yading@11: else if (!memcmp(p->buf, "RF64", 4) && yading@11: !memcmp(p->buf + 12, "ds64", 4)) yading@11: return AVPROBE_SCORE_MAX; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static void handle_stream_probing(AVStream *st) yading@11: { yading@11: if (st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) { yading@11: st->request_probe = AVPROBE_SCORE_MAX/2; yading@11: st->probe_packets = FFMIN(st->probe_packets, 4); yading@11: } yading@11: } yading@11: yading@11: static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) yading@11: { yading@11: AVIOContext *pb = s->pb; yading@11: int ret; yading@11: yading@11: /* parse fmt header */ yading@11: *st = avformat_new_stream(s, NULL); yading@11: if (!*st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: ret = ff_get_wav_header(pb, (*st)->codec, size); yading@11: if (ret < 0) yading@11: return ret; yading@11: handle_stream_probing(*st); yading@11: yading@11: (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; yading@11: yading@11: avpriv_set_pts_info(*st, 64, 1, (*st)->codec->sample_rate); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static inline int wav_parse_bext_string(AVFormatContext *s, const char *key, yading@11: int length) yading@11: { yading@11: char temp[257]; yading@11: int ret; yading@11: yading@11: av_assert0(length <= sizeof(temp)); yading@11: if ((ret = avio_read(s->pb, temp, length)) < 0) yading@11: return ret; yading@11: yading@11: temp[length] = 0; yading@11: yading@11: if (strlen(temp)) yading@11: return av_dict_set(&s->metadata, key, temp, 0); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int wav_parse_bext_tag(AVFormatContext *s, int64_t size) yading@11: { yading@11: char temp[131], *coding_history; yading@11: int ret, x; yading@11: uint64_t time_reference; yading@11: int64_t umid_parts[8], umid_mask = 0; yading@11: yading@11: if ((ret = wav_parse_bext_string(s, "description", 256)) < 0 || yading@11: (ret = wav_parse_bext_string(s, "originator", 32)) < 0 || yading@11: (ret = wav_parse_bext_string(s, "originator_reference", 32)) < 0 || yading@11: (ret = wav_parse_bext_string(s, "origination_date", 10)) < 0 || yading@11: (ret = wav_parse_bext_string(s, "origination_time", 8)) < 0) yading@11: return ret; yading@11: yading@11: time_reference = avio_rl64(s->pb); yading@11: snprintf(temp, sizeof(temp), "%"PRIu64, time_reference); yading@11: if ((ret = av_dict_set(&s->metadata, "time_reference", temp, 0)) < 0) yading@11: return ret; yading@11: yading@11: /* check if version is >= 1, in which case an UMID may be present */ yading@11: if (avio_rl16(s->pb) >= 1) { yading@11: for (x = 0; x < 8; x++) yading@11: umid_mask |= umid_parts[x] = avio_rb64(s->pb); yading@11: yading@11: if (umid_mask) { yading@11: /* the string formatting below is per SMPTE 330M-2004 Annex C */ yading@11: if (umid_parts[4] == 0 && umid_parts[5] == 0 && umid_parts[6] == 0 && umid_parts[7] == 0) { yading@11: /* basic UMID */ yading@11: snprintf(temp, sizeof(temp), "0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64, yading@11: umid_parts[0], umid_parts[1], umid_parts[2], umid_parts[3]); yading@11: } else { yading@11: /* extended UMID */ yading@11: snprintf(temp, sizeof(temp), "0x%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64 yading@11: "%016"PRIX64"%016"PRIX64"%016"PRIX64"%016"PRIX64, yading@11: umid_parts[0], umid_parts[1], umid_parts[2], umid_parts[3], yading@11: umid_parts[4], umid_parts[5], umid_parts[6], umid_parts[7]); yading@11: } yading@11: yading@11: if ((ret = av_dict_set(&s->metadata, "umid", temp, 0)) < 0) yading@11: return ret; yading@11: } yading@11: yading@11: avio_skip(s->pb, 190); yading@11: } else yading@11: avio_skip(s->pb, 254); yading@11: yading@11: if (size > 602) { yading@11: /* CodingHistory present */ yading@11: size -= 602; yading@11: yading@11: if (!(coding_history = av_malloc(size+1))) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: if ((ret = avio_read(s->pb, coding_history, size)) < 0) yading@11: return ret; yading@11: yading@11: coding_history[size] = 0; yading@11: if ((ret = av_dict_set(&s->metadata, "coding_history", coding_history, yading@11: AV_DICT_DONT_STRDUP_VAL)) < 0) yading@11: return ret; yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static const AVMetadataConv wav_metadata_conv[] = { yading@11: {"description", "comment" }, yading@11: {"originator", "encoded_by" }, yading@11: {"origination_date", "date" }, yading@11: {"origination_time", "creation_time"}, yading@11: {0}, yading@11: }; yading@11: yading@11: /* wav input */ yading@11: static int wav_read_header(AVFormatContext *s) yading@11: { yading@11: int64_t size, av_uninit(data_size); yading@11: int64_t sample_count=0; yading@11: int rf64; yading@11: uint32_t tag; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *st = NULL; yading@11: WAVDemuxContext *wav = s->priv_data; yading@11: int ret, got_fmt = 0; yading@11: int64_t next_tag_ofs, data_ofs = -1; yading@11: yading@11: wav->smv_data_ofs = -1; yading@11: yading@11: /* check RIFF header */ yading@11: tag = avio_rl32(pb); yading@11: yading@11: rf64 = tag == MKTAG('R', 'F', '6', '4'); yading@11: if (!rf64 && tag != MKTAG('R', 'I', 'F', 'F')) yading@11: return -1; yading@11: avio_rl32(pb); /* file size */ yading@11: tag = avio_rl32(pb); yading@11: if (tag != MKTAG('W', 'A', 'V', 'E')) yading@11: return -1; yading@11: yading@11: if (rf64) { yading@11: if (avio_rl32(pb) != MKTAG('d', 's', '6', '4')) yading@11: return -1; yading@11: size = avio_rl32(pb); yading@11: if (size < 24) yading@11: return -1; yading@11: avio_rl64(pb); /* RIFF size */ yading@11: data_size = avio_rl64(pb); yading@11: sample_count = avio_rl64(pb); yading@11: if (data_size < 0 || sample_count < 0) { yading@11: av_log(s, AV_LOG_ERROR, "negative data_size and/or sample_count in " yading@11: "ds64: data_size = %"PRId64", sample_count = %"PRId64"\n", yading@11: data_size, sample_count); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: avio_skip(pb, size - 24); /* skip rest of ds64 chunk */ yading@11: yading@11: } yading@11: yading@11: for (;;) { yading@11: AVStream *vst; yading@11: size = next_tag(pb, &tag); yading@11: next_tag_ofs = avio_tell(pb) + size; yading@11: yading@11: if (url_feof(pb)) yading@11: break; yading@11: yading@11: switch (tag) { yading@11: case MKTAG('f', 'm', 't', ' '): yading@11: /* only parse the first 'fmt ' tag found */ yading@11: if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { yading@11: return ret; yading@11: } else if (got_fmt) yading@11: av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n"); yading@11: yading@11: got_fmt = 1; yading@11: break; yading@11: case MKTAG('d', 'a', 't', 'a'): yading@11: if (!got_fmt) { yading@11: av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'data' tag\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: if (rf64) { yading@11: next_tag_ofs = wav->data_end = avio_tell(pb) + data_size; yading@11: } else { yading@11: data_size = size; yading@11: next_tag_ofs = wav->data_end = size ? next_tag_ofs : INT64_MAX; yading@11: } yading@11: yading@11: data_ofs = avio_tell(pb); yading@11: yading@11: /* don't look for footer metadata if we can't seek or if we don't yading@11: * know where the data tag ends yading@11: */ yading@11: if (!pb->seekable || (!rf64 && !size)) yading@11: goto break_loop; yading@11: break; yading@11: case MKTAG('f','a','c','t'): yading@11: if (!sample_count) yading@11: sample_count = avio_rl32(pb); yading@11: break; yading@11: case MKTAG('b','e','x','t'): yading@11: if ((ret = wav_parse_bext_tag(s, size)) < 0) yading@11: return ret; yading@11: break; yading@11: case MKTAG('S','M','V','0'): yading@11: if (!got_fmt) { yading@11: av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'SMV0' tag\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: // SMV file, a wav file with video appended. yading@11: if (size != MKTAG('0','2','0','0')) { yading@11: av_log(s, AV_LOG_ERROR, "Unknown SMV version found\n"); yading@11: goto break_loop; yading@11: } yading@11: av_log(s, AV_LOG_DEBUG, "Found SMV data\n"); yading@11: vst = avformat_new_stream(s, NULL); yading@11: if (!vst) yading@11: return AVERROR(ENOMEM); yading@11: avio_r8(pb); yading@11: vst->id = 1; yading@11: vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: vst->codec->codec_id = AV_CODEC_ID_MJPEG; yading@11: vst->codec->width = avio_rl24(pb); yading@11: vst->codec->height = avio_rl24(pb); yading@11: size = avio_rl24(pb); yading@11: wav->smv_data_ofs = avio_tell(pb) + (size - 5) * 3; yading@11: avio_rl24(pb); yading@11: wav->smv_block_size = avio_rl24(pb); yading@11: avpriv_set_pts_info(vst, 32, 1, avio_rl24(pb)); yading@11: vst->duration = avio_rl24(pb); yading@11: avio_rl24(pb); yading@11: avio_rl24(pb); yading@11: wav->smv_frames_per_jpeg = avio_rl24(pb); yading@11: goto break_loop; yading@11: case MKTAG('L', 'I', 'S', 'T'): yading@11: if (size < 4) { yading@11: av_log(s, AV_LOG_ERROR, "too short LIST tag\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: switch (avio_rl32(pb)) { yading@11: case MKTAG('I', 'N', 'F', 'O'): yading@11: ff_read_riff_info(s, size - 4); yading@11: } yading@11: break; yading@11: } yading@11: yading@11: /* skip padding byte */ yading@11: next_tag_ofs += (next_tag_ofs < INT64_MAX && next_tag_ofs & 1); yading@11: yading@11: /* seek to next tag unless we know that we'll run into EOF */ yading@11: if ((avio_size(pb) > 0 && next_tag_ofs >= avio_size(pb)) || yading@11: avio_seek(pb, next_tag_ofs, SEEK_SET) < 0) { yading@11: break; yading@11: } yading@11: } yading@11: break_loop: yading@11: if (data_ofs < 0) { yading@11: av_log(s, AV_LOG_ERROR, "no 'data' tag found\n"); yading@11: return AVERROR_INVALIDDATA; yading@11: } yading@11: yading@11: avio_seek(pb, data_ofs, SEEK_SET); yading@11: yading@11: if (!sample_count && st->codec->channels && av_get_bits_per_sample(st->codec->codec_id) && wav->data_end <= avio_size(pb)) yading@11: sample_count = (data_size<<3) / (st->codec->channels * (uint64_t)av_get_bits_per_sample(st->codec->codec_id)); yading@11: if (sample_count) yading@11: st->duration = sample_count; yading@11: yading@11: ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); yading@11: ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: /** Find chunk with w64 GUID by skipping over other chunks yading@11: * @return the size of the found chunk yading@11: */ yading@11: static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16]) yading@11: { yading@11: uint8_t guid[16]; yading@11: int64_t size; yading@11: yading@11: while (!url_feof(pb)) { yading@11: avio_read(pb, guid, 16); yading@11: size = avio_rl64(pb); yading@11: if (size <= 24) yading@11: return -1; yading@11: if (!memcmp(guid, guid1, 16)) yading@11: return size; yading@11: avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24); yading@11: } yading@11: return -1; yading@11: } yading@11: yading@11: #define MAX_SIZE 4096 yading@11: yading@11: static int wav_read_packet(AVFormatContext *s, yading@11: AVPacket *pkt) yading@11: { yading@11: int ret, size; yading@11: int64_t left; yading@11: AVStream *st; yading@11: WAVDemuxContext *wav = s->priv_data; yading@11: yading@11: if (CONFIG_SPDIF_DEMUXER && wav->spdif == 0 && yading@11: s->streams[0]->codec->codec_tag == 1) { yading@11: enum AVCodecID codec; yading@11: ret = ff_spdif_probe(s->pb->buffer, s->pb->buf_end - s->pb->buffer, yading@11: &codec); yading@11: if (ret > AVPROBE_SCORE_MAX / 2) { yading@11: s->streams[0]->codec->codec_id = codec; yading@11: wav->spdif = 1; yading@11: } else { yading@11: wav->spdif = -1; yading@11: } yading@11: } yading@11: if (CONFIG_SPDIF_DEMUXER && wav->spdif == 1) yading@11: return ff_spdif_read_packet(s, pkt); yading@11: yading@11: if (wav->smv_data_ofs > 0) { yading@11: int64_t audio_dts, video_dts; yading@11: smv_retry: yading@11: audio_dts = s->streams[0]->cur_dts; yading@11: video_dts = s->streams[1]->cur_dts; yading@11: if (audio_dts != AV_NOPTS_VALUE && video_dts != AV_NOPTS_VALUE) { yading@11: audio_dts = av_rescale_q(audio_dts, s->streams[0]->time_base, AV_TIME_BASE_Q); yading@11: video_dts = av_rescale_q(video_dts, s->streams[1]->time_base, AV_TIME_BASE_Q); yading@11: wav->smv_last_stream = video_dts >= audio_dts; yading@11: } yading@11: wav->smv_last_stream = !wav->smv_last_stream; yading@11: wav->smv_last_stream |= wav->audio_eof; yading@11: wav->smv_last_stream &= !wav->smv_eof; yading@11: if (wav->smv_last_stream) { yading@11: uint64_t old_pos = avio_tell(s->pb); yading@11: uint64_t new_pos = wav->smv_data_ofs + yading@11: wav->smv_block * wav->smv_block_size; yading@11: if (avio_seek(s->pb, new_pos, SEEK_SET) < 0) { yading@11: ret = AVERROR_EOF; yading@11: goto smv_out; yading@11: } yading@11: size = avio_rl24(s->pb); yading@11: ret = av_get_packet(s->pb, pkt, size); yading@11: if (ret < 0) yading@11: goto smv_out; yading@11: pkt->pos -= 3; yading@11: pkt->pts = wav->smv_block * wav->smv_frames_per_jpeg; yading@11: wav->smv_block++; yading@11: pkt->stream_index = 1; yading@11: smv_out: yading@11: avio_seek(s->pb, old_pos, SEEK_SET); yading@11: if (ret == AVERROR_EOF) { yading@11: wav->smv_eof = 1; yading@11: goto smv_retry; yading@11: } yading@11: return ret; yading@11: } yading@11: } yading@11: yading@11: st = s->streams[0]; yading@11: yading@11: left = wav->data_end - avio_tell(s->pb); yading@11: if (wav->ignore_length) yading@11: left= INT_MAX; yading@11: if (left <= 0){ yading@11: if (CONFIG_W64_DEMUXER && wav->w64) yading@11: left = find_guid(s->pb, ff_w64_guid_data) - 24; yading@11: else yading@11: left = find_tag(s->pb, MKTAG('d', 'a', 't', 'a')); yading@11: if (left < 0) { yading@11: wav->audio_eof = 1; yading@11: if (wav->smv_data_ofs > 0 && !wav->smv_eof) yading@11: goto smv_retry; yading@11: return AVERROR_EOF; yading@11: } yading@11: wav->data_end= avio_tell(s->pb) + left; yading@11: } yading@11: yading@11: size = MAX_SIZE; yading@11: if (st->codec->block_align > 1) { yading@11: if (size < st->codec->block_align) yading@11: size = st->codec->block_align; yading@11: size = (size / st->codec->block_align) * st->codec->block_align; yading@11: } yading@11: size = FFMIN(size, left); yading@11: ret = av_get_packet(s->pb, pkt, size); yading@11: if (ret < 0) yading@11: return ret; yading@11: pkt->stream_index = 0; yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: static int wav_read_seek(AVFormatContext *s, yading@11: int stream_index, int64_t timestamp, int flags) yading@11: { yading@11: WAVDemuxContext *wav = s->priv_data; yading@11: AVStream *st; yading@11: wav->smv_eof = 0; yading@11: wav->audio_eof = 0; yading@11: if (wav->smv_data_ofs > 0) { yading@11: int64_t smv_timestamp = timestamp; yading@11: if (stream_index == 0) yading@11: smv_timestamp = av_rescale_q(timestamp, s->streams[0]->time_base, s->streams[1]->time_base); yading@11: else yading@11: timestamp = av_rescale_q(smv_timestamp, s->streams[1]->time_base, s->streams[0]->time_base); yading@11: wav->smv_block = smv_timestamp / wav->smv_frames_per_jpeg; yading@11: } yading@11: yading@11: st = s->streams[0]; yading@11: switch (st->codec->codec_id) { yading@11: case AV_CODEC_ID_MP2: yading@11: case AV_CODEC_ID_MP3: yading@11: case AV_CODEC_ID_AC3: yading@11: case AV_CODEC_ID_DTS: yading@11: /* use generic seeking with dynamically generated indexes */ yading@11: return -1; yading@11: default: yading@11: break; yading@11: } yading@11: return ff_pcm_read_seek(s, stream_index, timestamp, flags); yading@11: } yading@11: yading@11: #define OFFSET(x) offsetof(WAVDemuxContext, x) yading@11: #define DEC AV_OPT_FLAG_DECODING_PARAM yading@11: static const AVOption demux_options[] = { yading@11: { "ignore_length", "Ignore length", OFFSET(ignore_length), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, DEC }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: static const AVClass wav_demuxer_class = { yading@11: .class_name = "WAV demuxer", yading@11: .item_name = av_default_item_name, yading@11: .option = demux_options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: }; yading@11: AVInputFormat ff_wav_demuxer = { yading@11: .name = "wav", yading@11: .long_name = NULL_IF_CONFIG_SMALL("WAV / WAVE (Waveform Audio)"), yading@11: .priv_data_size = sizeof(WAVDemuxContext), yading@11: .read_probe = wav_probe, yading@11: .read_header = wav_read_header, yading@11: .read_packet = wav_read_packet, yading@11: .read_seek = wav_read_seek, yading@11: .flags = AVFMT_GENERIC_INDEX, yading@11: .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, yading@11: .priv_class = &wav_demuxer_class, yading@11: }; yading@11: #endif /* CONFIG_WAV_DEMUXER */ yading@11: yading@11: yading@11: #if CONFIG_W64_DEMUXER yading@11: static int w64_probe(AVProbeData *p) yading@11: { yading@11: if (p->buf_size <= 40) yading@11: return 0; yading@11: if (!memcmp(p->buf, ff_w64_guid_riff, 16) && yading@11: !memcmp(p->buf + 24, ff_w64_guid_wave, 16)) yading@11: return AVPROBE_SCORE_MAX; yading@11: else yading@11: return 0; yading@11: } yading@11: yading@11: static int w64_read_header(AVFormatContext *s) yading@11: { yading@11: int64_t size, data_ofs = 0; yading@11: AVIOContext *pb = s->pb; yading@11: WAVDemuxContext *wav = s->priv_data; yading@11: AVStream *st; yading@11: uint8_t guid[16]; yading@11: int ret; yading@11: yading@11: avio_read(pb, guid, 16); yading@11: if (memcmp(guid, ff_w64_guid_riff, 16)) yading@11: return -1; yading@11: yading@11: if (avio_rl64(pb) < 16 + 8 + 16 + 8 + 16 + 8) /* riff + wave + fmt + sizes */ yading@11: return -1; yading@11: yading@11: avio_read(pb, guid, 16); yading@11: if (memcmp(guid, ff_w64_guid_wave, 16)) { yading@11: av_log(s, AV_LOG_ERROR, "could not find wave guid\n"); yading@11: return -1; yading@11: } yading@11: yading@11: wav->w64 = 1; yading@11: yading@11: st = avformat_new_stream(s, NULL); yading@11: if (!st) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: while (!url_feof(pb)) { yading@11: if (avio_read(pb, guid, 16) != 16) yading@11: break; yading@11: size = avio_rl64(pb); yading@11: if (size <= 24 || INT64_MAX - size < avio_tell(pb)) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: if (!memcmp(guid, ff_w64_guid_fmt, 16)) { yading@11: /* subtract chunk header size - normal wav file doesn't count it */ yading@11: ret = ff_get_wav_header(pb, st->codec, size - 24); yading@11: if (ret < 0) yading@11: return ret; yading@11: avio_skip(pb, FFALIGN(size, INT64_C(8)) - size); yading@11: yading@11: avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate); yading@11: } else if (!memcmp(guid, ff_w64_guid_fact, 16)) { yading@11: int64_t samples; yading@11: yading@11: samples = avio_rl64(pb); yading@11: if (samples > 0) yading@11: st->duration = samples; yading@11: } else if (!memcmp(guid, ff_w64_guid_data, 16)) { yading@11: wav->data_end = avio_tell(pb) + size - 24; yading@11: yading@11: data_ofs = avio_tell(pb); yading@11: if (!pb->seekable) yading@11: break; yading@11: yading@11: avio_skip(pb, size - 24); yading@11: } else if (!memcmp(guid, ff_w64_guid_summarylist, 16)) { yading@11: int64_t start, end, cur; yading@11: uint32_t count, chunk_size, i; yading@11: yading@11: start = avio_tell(pb); yading@11: end = start + size; yading@11: count = avio_rl32(pb); yading@11: yading@11: for (i = 0; i < count; i++) { yading@11: char chunk_key[5], *value; yading@11: yading@11: if (url_feof(pb) || (cur = avio_tell(pb)) < 0 || cur > end - 8 /* = tag + size */) yading@11: break; yading@11: yading@11: chunk_key[4] = 0; yading@11: avio_read(pb, chunk_key, 4); yading@11: chunk_size = avio_rl32(pb); yading@11: yading@11: value = av_mallocz(chunk_size + 1); yading@11: if (!value) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: ret = avio_get_str16le(pb, chunk_size, value, chunk_size); yading@11: avio_skip(pb, chunk_size - ret); yading@11: yading@11: av_dict_set(&s->metadata, chunk_key, value, AV_DICT_DONT_STRDUP_VAL); yading@11: } yading@11: yading@11: avio_skip(pb, end - avio_tell(pb)); yading@11: } else { yading@11: av_log(s, AV_LOG_DEBUG, "unknown guid: "FF_PRI_GUID"\n", FF_ARG_GUID(guid)); yading@11: avio_skip(pb, size - 24); yading@11: } yading@11: } yading@11: yading@11: if (!data_ofs) yading@11: return AVERROR_EOF; yading@11: yading@11: ff_metadata_conv_ctx(s, NULL, wav_metadata_conv); yading@11: ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv); yading@11: yading@11: handle_stream_probing(st); yading@11: st->need_parsing = AVSTREAM_PARSE_FULL_RAW; yading@11: yading@11: avio_seek(pb, data_ofs, SEEK_SET); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_w64_demuxer = { yading@11: .name = "w64", yading@11: .long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"), yading@11: .priv_data_size = sizeof(WAVDemuxContext), yading@11: .read_probe = w64_probe, yading@11: .read_header = w64_read_header, yading@11: .read_packet = wav_read_packet, yading@11: .read_seek = wav_read_seek, yading@11: .flags = AVFMT_GENERIC_INDEX, yading@11: .codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, 0 }, yading@11: }; yading@11: #endif /* CONFIG_W64_DEMUXER */