#include <stdint.h>
#include "config_components.h"
#include "libavutil/avassert.h"
#include "libavutil/dict.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mathematics.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavcodec/internal.h"
#include "avformat.h"
#include "avio.h"
#include "avio_internal.h"
#include "demux.h"
#include "id3v2.h"
#include "internal.h"
#include "metadata.h"
#include "pcm.h"
#include "riff.h"
#include "w64.h"
#include "spdif.h"
WAVDemuxContext;
#define OFFSET(x) …
#define DEC …
static const AVOption demux_options[] = …;
static void set_max_size(AVStream *st, WAVDemuxContext *wav)
{ … }
static void set_spdif(AVFormatContext *s, WAVDemuxContext *wav)
{ … }
#if CONFIG_WAV_DEMUXER
static int64_t next_tag(AVIOContext *pb, uint32_t *tag, int big_endian)
{ … }
static int64_t wav_seek_tag(WAVDemuxContext * wav, AVIOContext *s, int64_t offset, int whence)
{ … }
static int64_t find_tag(WAVDemuxContext * wav, AVIOContext *pb, uint32_t tag1)
{ … }
static int wav_probe(const AVProbeData *p)
{ … }
static void handle_stream_probing(AVStream *st)
{ … }
static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream *st)
{ … }
static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream *st)
{ … }
static inline int wav_parse_bext_string(AVFormatContext *s, const char *key,
int length)
{ … }
static int wav_parse_bext_tag(AVFormatContext *s, int64_t size)
{ … }
static const AVMetadataConv wav_metadata_conv[] = …;
static int wav_read_header(AVFormatContext *s)
{ … }
static int64_t find_guid(AVIOContext *pb, const uint8_t guid1[16])
{ … }
static int wav_read_packet(AVFormatContext *s, AVPacket *pkt)
{ … }
static int wav_read_seek(AVFormatContext *s,
int stream_index, int64_t timestamp, int flags)
{ … }
static const AVClass wav_demuxer_class = …;
const FFInputFormat ff_wav_demuxer = …;
#endif
#if CONFIG_W64_DEMUXER
static int w64_probe(const AVProbeData *p)
{
if (p->buf_size <= 40)
return 0;
if (!memcmp(p->buf, ff_w64_guid_riff, 16) &&
!memcmp(p->buf + 24, ff_w64_guid_wave, 16))
return AVPROBE_SCORE_MAX;
else
return 0;
}
static int w64_read_header(AVFormatContext *s)
{
int64_t size, data_ofs = 0;
AVIOContext *pb = s->pb;
WAVDemuxContext *wav = s->priv_data;
AVStream *st;
uint8_t guid[16];
int ret;
avio_read(pb, guid, 16);
if (memcmp(guid, ff_w64_guid_riff, 16))
return AVERROR_INVALIDDATA;
if (avio_rl64(pb) < 16 + 8 + 16 + 8 + 16 + 8)
return AVERROR_INVALIDDATA;
avio_read(pb, guid, 16);
if (memcmp(guid, ff_w64_guid_wave, 16)) {
av_log(s, AV_LOG_ERROR, "could not find wave guid\n");
return AVERROR_INVALIDDATA;
}
wav->w64 = 1;
st = avformat_new_stream(s, NULL);
if (!st)
return AVERROR(ENOMEM);
while (!avio_feof(pb)) {
if (avio_read(pb, guid, 16) != 16)
break;
size = avio_rl64(pb);
if (size <= 24 || INT64_MAX - size < avio_tell(pb)) {
if (data_ofs)
break;
return AVERROR_INVALIDDATA;
}
if (!memcmp(guid, ff_w64_guid_fmt, 16)) {
ret = ff_get_wav_header(s, pb, st->codecpar, size - 24, 0);
if (ret < 0)
return ret;
avio_skip(pb, FFALIGN(size, INT64_C(8)) - size);
if (st->codecpar->block_align &&
st->codecpar->ch_layout.nb_channels < FF_SANE_NB_CHANNELS &&
st->codecpar->bits_per_coded_sample < 128) {
int block_align = st->codecpar->block_align;
block_align = FFMAX(block_align,
((st->codecpar->bits_per_coded_sample + 7) / 8) *
st->codecpar->ch_layout.nb_channels);
if (block_align > st->codecpar->block_align) {
av_log(s, AV_LOG_WARNING, "invalid block_align: %d, broken file.\n",
st->codecpar->block_align);
st->codecpar->block_align = block_align;
}
}
avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
} else if (!memcmp(guid, ff_w64_guid_fact, 16)) {
int64_t samples;
samples = avio_rl64(pb);
if (samples > 0)
st->duration = samples;
avio_skip(pb, FFALIGN(size, INT64_C(8)) - 32);
} else if (!memcmp(guid, ff_w64_guid_data, 16)) {
wav->data_end = avio_tell(pb) + size - 24;
data_ofs = avio_tell(pb);
if (!(pb->seekable & AVIO_SEEKABLE_NORMAL))
break;
avio_skip(pb, size - 24);
} 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;
count = avio_rl32(pb);
for (i = 0; i < count; i++) {
char chunk_key[5], *value;
if (avio_feof(pb) || (cur = avio_tell(pb)) < 0 || cur > end - 8 )
break;
chunk_key[4] = 0;
avio_read(pb, chunk_key, 4);
chunk_size = avio_rl32(pb);
if (chunk_size == UINT32_MAX || (filesize >= 0 && chunk_size > filesize))
return AVERROR_INVALIDDATA;
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);
}
avio_skip(pb, end - avio_tell(pb));
} else {
av_log(s, AV_LOG_DEBUG, "unknown guid: "FF_PRI_GUID"\n", FF_ARG_GUID(guid));
avio_skip(pb, FFALIGN(size, INT64_C(8)) - 24);
}
}
if (!data_ofs)
return AVERROR_EOF;
ff_metadata_conv_ctx(s, NULL, wav_metadata_conv);
ff_metadata_conv_ctx(s, NULL, ff_riff_info_conv);
handle_stream_probing(st);
ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
avio_seek(pb, data_ofs, SEEK_SET);
set_spdif(s, wav);
set_max_size(st, wav);
return 0;
}
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 FFInputFormat ff_w64_demuxer = {
.p.name = "w64",
.p.long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"),
.p.flags = AVFMT_GENERIC_INDEX,
.p.codec_tag = ff_wav_codec_tags_list,
.p.priv_class = &w64_demuxer_class,
.priv_data_size = sizeof(WAVDemuxContext),
.read_probe = w64_probe,
.read_header = w64_read_header,
.read_packet = wav_read_packet,
.read_seek = wav_read_seek,
};
#endif