[Ffmpeg-devel] [PATCH] THP PCM decoder (GSoC Qualification)
Marco Gerards
mgerards
Wed Apr 4 02:44:18 CEST 2007
Michael Niedermayer <michaelni at gmx.at> writes:
Hi,
> Hi
>
> On Tue, Apr 03, 2007 at 08:59:31PM +0200, Marco Gerards wrote:
>> Michael Niedermayer <michaelni at gmx.at> writes:
>>
>> > Hi
>> >
>> > On Tue, Apr 03, 2007 at 03:21:56PM +0200, Marco Gerards wrote:
>> > [...]
>> >> In this new patch there are still two issues. For some reason the
>> >> quality of the sound was not that good. It wasn't as bad as someone
>> >> reported, for example he said that there were issues with mono sound.
>> >> I can not reproduce this.
>> >>
>> >> The problem is in stereo sound. I have disabled stereo in this patch
>> >> and the sound is just fine now. To be honest, I am not sure what the
>> >> problem is. I have tried all kinds of things without much success.
>> >> Perhaps it is better to commit this first. After that I can fix this
>> >> with another patch.
>> >
>> > hmm did you properly interleave right and left channel samples? that is
>> > does it work if you decode just the other channel?
>> > could it be that the encoding is R+L,R-L ? instead of R,L
>>
>> I tried about anything. This is the first time I am working with
>> audio, so I perhaps made a silly mistake.
>>
>> What I tried was interleaving the data send to the output buffer. The
>> PCM data in the THP files is stored in packets. First x samples for
>> the first channel are stored, after that x samples for the second
>> channel.
>>
>> When I decode the other channel it seems to work as well. So I assume
>> the problem is in how I fill the output buffer and how I tell ffmpeg
>> the format (stereo, interleaved samples) of this data. How does one
>> in general deal with stereo from a decoder?
>
> stereo in ffmpeg is always
> channel0-sample0, channel1-sample0, channel0-sample1, channel1-sample1, channel0-sample2, channel1-sample2, ...
Well, I have this now and it works... kinda. When I am playing either
one of the two channels as mono sound, it works. When I am using both
channels, there is some kind of noise/cracks. The noise is not there
all the time and it is not very loud, but I am mentioning this because
there must still be something that I do wrong...
>>
>> [...]
>>
>> >> Index: libavcodec/allcodecs.c
>> >> ===================================================================
>> >> --- libavcodec/allcodecs.c (revision 8605)
>> >> +++ libavcodec/allcodecs.c (working copy)
>> >> @@ -242,6 +242,7 @@
>> >> REGISTER_ENCDEC (ADPCM_SBPRO_3, adpcm_sbpro_3);
>> >> REGISTER_ENCDEC (ADPCM_SBPRO_4, adpcm_sbpro_4);
>> >> REGISTER_ENCDEC (ADPCM_SWF, adpcm_swf);
>> >> + REGISTER_ENCDEC (ADPCM_THP, adpcm_thp);
>> >
>> > ENCDEC ?
>>
>>
>> Baptiste said "use CODEC and dummy return -1 to stay in conformance
>> with others (see IMA_QT)". I assumed it also applied to allcodecs.c.
>> Should I change this to REGISTER_DECODER?
>
> yes and flame baptiste ;)
Sorry, I didn't mean to blame anyone for my mistakes. Neither do I
want to insult anyone. I just misunderstood him, which is totally my
fault.
>>
>> >> REGISTER_ENCDEC (ADPCM_XA, adpcm_xa);
>> >> REGISTER_ENCDEC (ADPCM_YAMAHA, adpcm_yamaha);
>> >>
>> >> Index: libavcodec/Makefile
>> >> ===================================================================
>> >> --- libavcodec/Makefile (revision 8605)
>> >> +++ libavcodec/Makefile (working copy)
>> >> @@ -246,6 +246,8 @@
>> >> 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_THP_ENCODER) += adpcm.o
>> >> OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o
>> >
>> > unless you write an ADPCM_THP encoder there shouldnt be
>> > a CONFIG_ADPCM_THP_ENCODER ...
>>
>> Same as above. Should I remove this?
>
> yes
Done.
Hopefully I got everything right this time, except stereo... I hope
you guys aren't losing your patience with me... ;-)
--
Marco
Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile (revision 8622)
+++ libavcodec/Makefile (working copy)
@@ -247,6 +247,7 @@
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
Index: libavcodec/allcodecs.c
===================================================================
--- libavcodec/allcodecs.c (revision 8622)
+++ libavcodec/allcodecs.c (working copy)
@@ -242,6 +242,7 @@
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);
Index: libavcodec/avcodec.h
===================================================================
--- libavcodec/avcodec.h (revision 8622)
+++ libavcodec/avcodec.h (working copy)
@@ -198,6 +198,7 @@
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,
@@ -2409,6 +2410,7 @@
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);
Index: libavcodec/adpcm.c
===================================================================
--- libavcodec/adpcm.c (revision 8622)
+++ libavcodec/adpcm.c (working copy)
@@ -1308,6 +1308,66 @@
src++;
}
break;
+ case CODEC_ID_ADPCM_THP:
+ {
+ GetBitContext gb;
+ long table[16][2];
+ 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(&gb, 32); /* Channel size */
+ samplecnt = get_bits(&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 (samples + samplecnt >= samples_end) {
+ 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 = 1 << get_bits (&gb, 4);
+ signed long factor1 = table[index * 2][ch];
+ signed long 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++;
+ samples += st;
+ }
+ }
+ }
+
+ break;
+ }
+
default:
return -1;
}
@@ -1368,5 +1428,6 @@
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
Index: doc/ffmpeg-doc.texi
===================================================================
--- doc/ffmpeg-doc.texi (revision 8622)
+++ doc/ffmpeg-doc.texi (working copy)
@@ -902,7 +902,7 @@
@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
- at tab Used on the Nintendo GameCube (video only)
+ at tab Used on the Nintendo GameCube
@end multitable
@code{X} means that encoding (resp. decoding) is supported.
Index: libavformat/thp.c
===================================================================
--- libavformat/thp.c (revision 8622)
+++ libavformat/thp.c (working copy)
@@ -35,10 +35,12 @@
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 @@
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 @@
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 @@
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,8 +179,19 @@
}
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;
}
More information about the ffmpeg-devel
mailing list