diff --git a/doc/ffmpeg-doc.texi b/doc/ffmpeg-doc.texi index 54af81db2d..8c351c6374 100644 --- a/doc/ffmpeg-doc.texi +++ b/doc/ffmpeg-doc.texi @@ -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 diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 175755eb90..a07879e962 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -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 diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index 8800c3a20c..cf282f1071 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -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 diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 09612b40d2..be41c147f5 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -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); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index b7ed16248d..421171a183 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -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); diff --git a/libavformat/thp.c b/libavformat/thp.c index d0d80428c4..1b7567e78e 100644 --- a/libavformat/thp.c +++ b/libavformat/thp.c @@ -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; }