mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
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
This commit is contained in:
parent
efd2afc2ae
commit
d1e0d21f94
@ -902,7 +902,7 @@ library:
|
|||||||
@tab This format is used in non-Windows version of Feeble Files game and
|
@tab This format is used in non-Windows version of Feeble Files game and
|
||||||
different game cutscenes repacked for use with ScummVM.
|
different game cutscenes repacked for use with ScummVM.
|
||||||
@item THP @tab @tab X
|
@item THP @tab @tab X
|
||||||
@tab Used on the Nintendo GameCube (video only)
|
@tab Used on the Nintendo GameCube.
|
||||||
@item C93 @tab @tab X
|
@item C93 @tab @tab X
|
||||||
@tab Used in the game Cyberia from Interplay.
|
@tab Used in the game Cyberia from Interplay.
|
||||||
@end multitable
|
@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_SBPRO_4_ENCODER) += adpcm.o
|
||||||
OBJS-$(CONFIG_ADPCM_SWF_DECODER) += adpcm.o
|
OBJS-$(CONFIG_ADPCM_SWF_DECODER) += adpcm.o
|
||||||
OBJS-$(CONFIG_ADPCM_SWF_ENCODER) += 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_DECODER) += adpcm.o
|
||||||
OBJS-$(CONFIG_ADPCM_XA_ENCODER) += adpcm.o
|
OBJS-$(CONFIG_ADPCM_XA_ENCODER) += adpcm.o
|
||||||
OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o
|
OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
* by Mike Melanson (melanson@pcisys.net)
|
* by Mike Melanson (melanson@pcisys.net)
|
||||||
* CD-ROM XA ADPCM codec by BERO
|
* CD-ROM XA ADPCM codec by BERO
|
||||||
* EA ADPCM decoder by Robin Kay (komadori@myrealbox.com)
|
* EA ADPCM decoder by Robin Kay (komadori@myrealbox.com)
|
||||||
|
* THP ADPCM decoder by Marco Gerards (mgerards@xs4all.nl)
|
||||||
*
|
*
|
||||||
* Features and limitations:
|
* Features and limitations:
|
||||||
*
|
*
|
||||||
@ -1308,6 +1309,72 @@ static int adpcm_decode_frame(AVCodecContext *avctx,
|
|||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
return -1;
|
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_4, adpcm_sbpro_4);
|
||||||
ADPCM_CODEC(CODEC_ID_ADPCM_SBPRO_3, adpcm_sbpro_3);
|
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_SBPRO_2, adpcm_sbpro_2);
|
||||||
|
ADPCM_CODEC(CODEC_ID_ADPCM_THP, adpcm_thp);
|
||||||
|
|
||||||
#undef ADPCM_CODEC
|
#undef ADPCM_CODEC
|
||||||
|
@ -244,6 +244,7 @@ void avcodec_register_all(void)
|
|||||||
REGISTER_ENCDEC (ADPCM_SBPRO_3, adpcm_sbpro_3);
|
REGISTER_ENCDEC (ADPCM_SBPRO_3, adpcm_sbpro_3);
|
||||||
REGISTER_ENCDEC (ADPCM_SBPRO_4, adpcm_sbpro_4);
|
REGISTER_ENCDEC (ADPCM_SBPRO_4, adpcm_sbpro_4);
|
||||||
REGISTER_ENCDEC (ADPCM_SWF, adpcm_swf);
|
REGISTER_ENCDEC (ADPCM_SWF, adpcm_swf);
|
||||||
|
REGISTER_DECODER(ADPCM_THP, adpcm_thp);
|
||||||
REGISTER_ENCDEC (ADPCM_XA, adpcm_xa);
|
REGISTER_ENCDEC (ADPCM_XA, adpcm_xa);
|
||||||
REGISTER_ENCDEC (ADPCM_YAMAHA, adpcm_yamaha);
|
REGISTER_ENCDEC (ADPCM_YAMAHA, adpcm_yamaha);
|
||||||
|
|
||||||
|
@ -200,6 +200,7 @@ enum CodecID {
|
|||||||
CODEC_ID_ADPCM_SBPRO_4,
|
CODEC_ID_ADPCM_SBPRO_4,
|
||||||
CODEC_ID_ADPCM_SBPRO_3,
|
CODEC_ID_ADPCM_SBPRO_3,
|
||||||
CODEC_ID_ADPCM_SBPRO_2,
|
CODEC_ID_ADPCM_SBPRO_2,
|
||||||
|
CODEC_ID_ADPCM_THP,
|
||||||
|
|
||||||
/* AMR */
|
/* AMR */
|
||||||
CODEC_ID_AMR_NB= 0x12000,
|
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_SBPRO_4, adpcm_sbpro_4);
|
||||||
PCM_CODEC(CODEC_ID_ADPCM_SMJPEG, adpcm_ima_smjpeg);
|
PCM_CODEC(CODEC_ID_ADPCM_SMJPEG, adpcm_ima_smjpeg);
|
||||||
PCM_CODEC(CODEC_ID_ADPCM_SWF, adpcm_swf);
|
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_XA, adpcm_xa);
|
||||||
PCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha);
|
PCM_CODEC(CODEC_ID_ADPCM_YAMAHA, adpcm_yamaha);
|
||||||
|
|
||||||
|
@ -35,10 +35,12 @@ typedef struct ThpDemuxContext {
|
|||||||
int next_frame;
|
int next_frame;
|
||||||
int next_framesz;
|
int next_framesz;
|
||||||
int video_stream_index;
|
int video_stream_index;
|
||||||
|
int audio_stream_index;
|
||||||
int compcount;
|
int compcount;
|
||||||
unsigned char components[16];
|
unsigned char components[16];
|
||||||
AVStream* vst;
|
AVStream* vst;
|
||||||
int has_audio;
|
int has_audio;
|
||||||
|
int audiosize;
|
||||||
} ThpDemuxContext;
|
} ThpDemuxContext;
|
||||||
|
|
||||||
|
|
||||||
@ -116,7 +118,23 @@ static int thp_read_header(AVFormatContext *s,
|
|||||||
get_be32(pb); /* Unknown. */
|
get_be32(pb); /* Unknown. */
|
||||||
}
|
}
|
||||||
else if (thp->components[i] == 1) {
|
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;
|
thp->has_audio = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +150,8 @@ static int thp_read_packet(AVFormatContext *s,
|
|||||||
int size;
|
int size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (thp->audiosize == 0) {
|
||||||
|
|
||||||
/* Terminate when last frame is reached. */
|
/* Terminate when last frame is reached. */
|
||||||
if (thp->frame >= thp->framecnt)
|
if (thp->frame >= thp->framecnt)
|
||||||
return AVERROR_IO;
|
return AVERROR_IO;
|
||||||
@ -145,8 +165,12 @@ static int thp_read_packet(AVFormatContext *s,
|
|||||||
get_be32(pb); /* Previous total size. */
|
get_be32(pb); /* Previous total size. */
|
||||||
size = get_be32(pb); /* Total size of this frame. */
|
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)
|
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);
|
ret = av_get_packet(pb, pkt, size);
|
||||||
if (ret != size) {
|
if (ret != size) {
|
||||||
@ -155,7 +179,18 @@ static int thp_read_packet(AVFormatContext *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pkt->stream_index = thp->video_stream_index;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user