* 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
#include <stdint.h>
+#include "config.h"
#include "libavutil/avassert.h"
#include "libavutil/dict.h"
#include "libavutil/intreadwrite.h"
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;
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) {
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 {
return size;
}
-static int wav_probe(AVProbeData *p)
+static int wav_probe(const AVProbeData *p)
{
/* check file header */
if (p->buf_size <= 32)
* 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;
}
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)
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;
}
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;
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,
{
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;
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));
return AVERROR_INVALIDDATA;
}
- if (rf64) {
+ if (rf64 || bw64) {
if (avio_rl32(pb) != MKTAG('d', 's', '6', '4'))
return AVERROR_INVALIDDATA;
size = avio_rl32(pb);
}
+ /* 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);
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");
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");
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;
/* 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'):
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;
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;
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 = NULL;
+ 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_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;
}
/* seek to next tag unless we know that we'll run into EOF */
} 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);
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;
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;
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) {
}
}
- st = s->streams[0];
-
left = wav->data_end - avio_tell(s->pb);
if (wav->ignore_length)
left = INT_MAX;
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;
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:
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,
.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;
} 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;
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);
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,
+};
+
AVInputFormat ff_w64_demuxer = {
.name = "w64",
.long_name = NULL_IF_CONFIG_SMALL("Sony Wave64"),
.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 */