Skip to content
Snippets Groups Projects
Commit d1e0d21f authored by Marco Gerards's avatar Marco Gerards Committed by Diego Biurrun
Browse files

THP PCM decoder, used on the Nintendo GameCube.

patch by Marco Gerards, mgerards xs4all nl

Originally committed as revision 8646 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent efd2afc2
No related branches found
No related tags found
No related merge requests found
......@@ -902,7 +902,7 @@ library:
@tab This format is used in non-Windows version of Feeble Files game and
different game cutscenes repacked for use with ScummVM.
@item THP @tab @tab X
@tab Used on the Nintendo GameCube (video only)
@tab Used on the Nintendo GameCube.
@item C93 @tab @tab X
@tab Used in the game Cyberia from Interplay.
@end multitable
......
......@@ -250,6 +250,7 @@ OBJS-$(CONFIG_ADPCM_SBPRO_4_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_SBPRO_4_ENCODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_SWF_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_SWF_ENCODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_THP_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_XA_ENCODER) += adpcm.o
OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o
......
......@@ -29,6 +29,7 @@
* by Mike Melanson (melanson@pcisys.net)
* CD-ROM XA ADPCM codec by BERO
* EA ADPCM decoder by Robin Kay (komadori@myrealbox.com)
* THP ADPCM decoder by Marco Gerards (mgerards@xs4all.nl)
*
* Features and limitations:
*
......@@ -1308,6 +1309,72 @@ static int adpcm_decode_frame(AVCodecContext *avctx,
src++;
}
break;
case CODEC_ID_ADPCM_THP:
{
GetBitContext gb;
int table[16][2];
unsigned int samplecnt;
int prev1[2], prev2[2];
int ch;
if (buf_size < 80) {
av_log(avctx, AV_LOG_ERROR, "frame too small\n");
return -1;
}
init_get_bits(&gb, src, buf_size * 8);
src += buf_size;
get_bits_long(&gb, 32); /* Channel size */
samplecnt = get_bits_long(&gb, 32);
for (ch = 0; ch < 2; ch++)
for (i = 0; i < 16; i++)
table[i][ch] = get_sbits(&gb, 16);
/* Initialize the previous sample. */
for (ch = 0; ch < 2; ch++) {
prev1[ch] = get_sbits(&gb, 16);
prev2[ch] = get_sbits(&gb, 16);
}
if (samplecnt >= (samples_end - samples) / (st + 1)) {
av_log(avctx, AV_LOG_ERROR, "allocated output buffer is too small\n");
return -1;
}
for (ch = 0; ch <= st; ch++) {
samples = (unsigned short *) data + ch;
/* Read in every sample for this channel. */
for (i = 0; i < samplecnt / 14; i++) {
uint8_t index = get_bits (&gb, 4) & 7;
unsigned int exp = get_bits (&gb, 4);
int factor1 = table[index * 2][ch];
int factor2 = table[index * 2 + 1][ch];
/* Decode 14 samples. */
for (n = 0; n < 14; n++) {
int sampledat = get_sbits (&gb, 4);
*samples = ((prev1[ch]*factor1
+ prev2[ch]*factor2) >> 11) + (sampledat << exp);
prev2[ch] = prev1[ch];
prev1[ch] = *samples++;
/* In case of stereo, skip one sample, this sample
is for the other channel. */
samples += st;
}
}
}
/* In the previous loop, in case stereo is used, samples is
increased exactly one time too often. */
samples -= st;
break;
}
default:
return -1;
}
......@@ -1368,5 +1435,6 @@ ADPCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha);
ADPCM_CODEC(CODEC_ID_ADPCM_SBPRO_4, adpcm_sbpro_4);
ADPCM_CODEC(CODEC_ID_ADPCM_SBPRO_3, adpcm_sbpro_3);
ADPCM_CODEC(CODEC_ID_ADPCM_SBPRO_2, adpcm_sbpro_2);
ADPCM_CODEC(CODEC_ID_ADPCM_THP, adpcm_thp);
#undef ADPCM_CODEC
......@@ -244,6 +244,7 @@ void avcodec_register_all(void)
REGISTER_ENCDEC (ADPCM_SBPRO_3, adpcm_sbpro_3);
REGISTER_ENCDEC (ADPCM_SBPRO_4, adpcm_sbpro_4);
REGISTER_ENCDEC (ADPCM_SWF, adpcm_swf);
REGISTER_DECODER(ADPCM_THP, adpcm_thp);
REGISTER_ENCDEC (ADPCM_XA, adpcm_xa);
REGISTER_ENCDEC (ADPCM_YAMAHA, adpcm_yamaha);
......
......@@ -200,6 +200,7 @@ enum CodecID {
CODEC_ID_ADPCM_SBPRO_4,
CODEC_ID_ADPCM_SBPRO_3,
CODEC_ID_ADPCM_SBPRO_2,
CODEC_ID_ADPCM_THP,
/* AMR */
CODEC_ID_AMR_NB= 0x12000,
......@@ -2417,6 +2418,7 @@ PCM_CODEC(CODEC_ID_ADPCM_SBPRO_3, adpcm_sbpro_3);
PCM_CODEC(CODEC_ID_ADPCM_SBPRO_4, adpcm_sbpro_4);
PCM_CODEC(CODEC_ID_ADPCM_SMJPEG, adpcm_ima_smjpeg);
PCM_CODEC(CODEC_ID_ADPCM_SWF, adpcm_swf);
PCM_CODEC(CODEC_ID_ADPCM_THP, adpcm_thp);
PCM_CODEC(CODEC_ID_ADPCM_XA, adpcm_xa);
PCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha);
......
......@@ -35,10 +35,12 @@ typedef struct ThpDemuxContext {
int next_frame;
int next_framesz;
int video_stream_index;
int audio_stream_index;
int compcount;
unsigned char components[16];
AVStream* vst;
int has_audio;
int audiosize;
} ThpDemuxContext;
......@@ -116,7 +118,23 @@ static int thp_read_header(AVFormatContext *s,
get_be32(pb); /* Unknown. */
}
else if (thp->components[i] == 1) {
/* XXX: Required for audio playback. */
if (thp->has_audio != 0)
break;
/* Audio component. */
st = av_new_stream(s, 0);
if (!st)
return AVERROR_NOMEM;
st->codec->codec_type = CODEC_TYPE_AUDIO;
st->codec->codec_id = CODEC_ID_ADPCM_THP;
st->codec->codec_tag = 0; /* no fourcc */
st->codec->channels = get_be32(pb); /* numChannels. */
st->codec->sample_rate = get_be32(pb); /* Frequency. */
av_set_pts_info(st, 64, 1, st->codec->sample_rate);
thp->audio_stream_index = st->index;
thp->has_audio = 1;
}
}
......@@ -132,6 +150,8 @@ static int thp_read_packet(AVFormatContext *s,
int size;
int ret;
if (thp->audiosize == 0) {
/* Terminate when last frame is reached. */
if (thp->frame >= thp->framecnt)
return AVERROR_IO;
......@@ -145,8 +165,12 @@ static int thp_read_packet(AVFormatContext *s,
get_be32(pb); /* Previous total size. */
size = get_be32(pb); /* Total size of this frame. */
/* Store the audiosize so the next time this function is called,
the audio can be read. */
if (thp->has_audio)
get_be32(pb); /* Audio size. */
thp->audiosize = get_be32(pb); /* Audio size. */
else
thp->frame++;
ret = av_get_packet(pb, pkt, size);
if (ret != size) {
......@@ -155,7 +179,18 @@ static int thp_read_packet(AVFormatContext *s,
}
pkt->stream_index = thp->video_stream_index;
thp->frame++;
}
else {
ret = av_get_packet(pb, pkt, thp->audiosize);
if (ret != thp->audiosize) {
av_free_packet(pkt);
return AVERROR_IO;
}
pkt->stream_index = thp->audio_stream_index;
thp->audiosize = 0;
thp->frame++;
}
return 0;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment