5d244b29 |
/*
* Creative Voice File demuxer.
* Copyright (c) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software |
e5a389a1 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
5d244b29 |
*/
|
6a5d31ac |
#include "libavutil/intreadwrite.h" |
5d244b29 |
#include "voc.h" |
80b39e1c |
#include "internal.h" |
5d244b29 |
static int voc_probe(AVProbeData *p)
{
int version, check;
|
090a4179 |
if (memcmp(p->buf, ff_voc_magic, sizeof(ff_voc_magic) - 1)) |
5d244b29 |
return 0; |
6bca498d |
version = AV_RL16(p->buf + 22);
check = AV_RL16(p->buf + 24); |
5d244b29 |
if (~version + 0x1234 != check)
return 10;
return AVPROBE_SCORE_MAX;
}
|
6e9651d1 |
static int voc_read_header(AVFormatContext *s) |
5d244b29 |
{ |
e998ba4f |
VocDecContext *voc = s->priv_data; |
ae628ec1 |
AVIOContext *pb = s->pb; |
5d244b29 |
int header_size;
AVStream *st;
|
45a8a02a |
avio_skip(pb, 20); |
b7effd4e |
header_size = avio_rl16(pb) - 22; |
5d244b29 |
if (header_size != 4) { |
cbc09a7d |
av_log(s, AV_LOG_ERROR, "unknown header size: %d\n", header_size); |
85565db0 |
return AVERROR(ENOSYS); |
5d244b29 |
} |
45a8a02a |
avio_skip(pb, header_size); |
3b3bbdd3 |
st = avformat_new_stream(s, NULL); |
5d244b29 |
if (!st) |
769e10f0 |
return AVERROR(ENOMEM); |
72415b2a |
st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
5d244b29 |
voc->remaining_size = 0;
return 0;
}
int |
167f3b8d |
ff_voc_get_packet(AVFormatContext *s, AVPacket *pkt, AVStream *st, int max_size) |
5d244b29 |
{ |
e998ba4f |
VocDecContext *voc = s->priv_data; |
5d244b29 |
AVCodecContext *dec = st->codec; |
ae628ec1 |
AVIOContext *pb = s->pb; |
e998ba4f |
VocType type; |
f61cbc22 |
int size, tmp_codec=-1; |
5d244b29 |
int sample_rate = 0;
int channels = 1;
while (!voc->remaining_size) { |
b7effd4e |
type = avio_r8(pb); |
5d244b29 |
if (type == VOC_TYPE_EOF) |
fa7e9f94 |
return AVERROR_EOF; |
b7effd4e |
voc->remaining_size = avio_rl24(pb); |
c351524c |
if (!voc->remaining_size) { |
8978feda |
if (!s->pb->seekable) |
c351524c |
return AVERROR(EIO); |
76aa876e |
voc->remaining_size = avio_size(pb) - avio_tell(pb); |
c351524c |
} |
5d244b29 |
max_size -= 4;
switch (type) {
case VOC_TYPE_VOICE_DATA: |
0883109b |
if (!dec->sample_rate) {
dec->sample_rate = 1000000 / (256 - avio_r8(pb));
if (sample_rate)
dec->sample_rate = sample_rate;
avpriv_set_pts_info(st, 64, 1, dec->sample_rate); |
5bbfe193 |
dec->channels = channels;
dec->bits_per_coded_sample = av_get_bits_per_sample(dec->codec_id); |
0883109b |
} else
avio_skip(pb, 1); |
b7effd4e |
tmp_codec = avio_r8(pb); |
5d244b29 |
voc->remaining_size -= 2;
max_size -= 2;
channels = 1;
break;
case VOC_TYPE_VOICE_DATA_CONT:
break;
case VOC_TYPE_EXTENDED: |
b7effd4e |
sample_rate = avio_rl16(pb);
avio_r8(pb);
channels = avio_r8(pb) + 1; |
5d244b29 |
sample_rate = 256000000 / (channels * (65536 - sample_rate));
voc->remaining_size = 0;
max_size -= 4;
break;
case VOC_TYPE_NEW_VOICE_DATA: |
0883109b |
if (!dec->sample_rate) {
dec->sample_rate = avio_rl32(pb);
avpriv_set_pts_info(st, 64, 1, dec->sample_rate); |
5bbfe193 |
dec->bits_per_coded_sample = avio_r8(pb);
dec->channels = avio_r8(pb); |
0883109b |
} else |
5bbfe193 |
avio_skip(pb, 6); |
b7effd4e |
tmp_codec = avio_rl16(pb); |
45a8a02a |
avio_skip(pb, 4); |
5d244b29 |
voc->remaining_size -= 12;
max_size -= 12;
break;
default: |
45a8a02a |
avio_skip(pb, voc->remaining_size); |
5d244b29 |
max_size -= voc->remaining_size;
voc->remaining_size = 0;
break;
} |
f61cbc22 |
}
if (tmp_codec >= 0) {
tmp_codec = ff_codec_get_id(ff_voc_codec_tags, tmp_codec); |
36ef5369 |
if (dec->codec_id == AV_CODEC_ID_NONE) |
f61cbc22 |
dec->codec_id = tmp_codec;
else if (dec->codec_id != tmp_codec)
av_log(s, AV_LOG_WARNING, "Ignoring mid-stream change in audio codec\n"); |
36ef5369 |
if (dec->codec_id == AV_CODEC_ID_NONE) {
if (s->audio_codec_id == AV_CODEC_ID_NONE) { |
f61cbc22 |
av_log(s, AV_LOG_ERROR, "unknown codec tag\n");
return AVERROR(EINVAL);
}
av_log(s, AV_LOG_WARNING, "unknown codec tag\n"); |
e048a9ca |
} |
5d244b29 |
}
|
056c13fd |
dec->bit_rate = dec->sample_rate * dec->channels * dec->bits_per_coded_sample; |
5d244b29 |
if (max_size <= 0) |
fd9a71ac |
max_size = 2048; |
5d244b29 |
size = FFMIN(voc->remaining_size, max_size);
voc->remaining_size -= size;
return av_get_packet(pb, pkt, size);
}
static int voc_read_packet(AVFormatContext *s, AVPacket *pkt)
{ |
167f3b8d |
return ff_voc_get_packet(s, pkt, s->streams[0], 0); |
5d244b29 |
}
|
c6610a21 |
AVInputFormat ff_voc_demuxer = { |
dfc2c4d9 |
.name = "voc", |
6774247a |
.long_name = NULL_IF_CONFIG_SMALL("Creative Voice"), |
dfc2c4d9 |
.priv_data_size = sizeof(VocDecContext),
.read_probe = voc_probe,
.read_header = voc_read_header,
.read_packet = voc_read_packet, |
20234a4b |
.codec_tag = (const AVCodecTag* const []){ ff_voc_codec_tags, 0 }, |
5d244b29 |
}; |