[Ffmpeg-devel] [PATCH] THP PCM decoder (GSoC Qualification)

Marco Gerards mgerards
Fri Apr 6 18:24:49 CEST 2007


Michael Niedermayer <michaelni at gmx.at> writes:

Hi,

> Hi
>
> On Fri, Apr 06, 2007 at 02:00:30PM +0200, Marco Gerards wrote:
>> Michael Niedermayer <michaelni at gmx.at> writes:
>> 
>> > Hi
>> >
>> > On Fri, Apr 06, 2007 at 11:30:07AM +0200, Marco Gerards wrote:
>> >> Michael Niedermayer <michaelni at gmx.at> writes:
>> >> 
>> >> Hi,
>> >> 
>> >> >> Or what are the bugs you mean?
>> >> >
>> >> > buffer overflow / segfault / exploit / ...
>> >> 
>> >> Oh, I multiplied with st, but I should have multiplied with (st + 1).
>> >
>> > yes
>> >
>> >
>> >> I have included a new patch.  If there are still bugs on this single
>> >> line, I either don't understand what you mean or I just don't see it
>> >> because I am misunderstanding something.
>> >> 
>> >> What I currently have is:
>> >> +        if (samples + samplecnt * (st + 1) >= samples_end) {
>> >
>> > that contains one bug and a fairly serious one, it still doenst
>> > catch all buffer overflow cases
>> >
>> >
>> >> 
>> >> I read this as: if (address_of_last_sample >= last_address_of_buffer_plus_one) {
>> >
>> > this line is equivalent to the one above so it also contains the bug
>> 
>> In that case I simply don't see it.  If this check evaluates to false,
>> the last sample is stored before the end of the output buffer.
>> Perhaps even in the last 16 bits.  But in that case there is no buffer
>> overflow.
>> 
>> Buffer address: addr
>> Buffer size: 0x0800
>> 
>> For example: address_of_last_sample = addr+0x7fe
>>     last_address_of_buffer_plus_one = addr+0x800
>> 
>> In this case, nothing wrong happened, addr+0x7fe and addr+0x7ff are the last
>> bytes in this buffer and are perfectly valid.
>> 
>> Just when address_of_last_sample >= addr+0x800, there is a buffer
>> overflow.
>> 
>> But obviously this is wrong, otherwise you wouldn't tell me there is a
>> bug in this line.  Could you please tell me what the problem is, so I
>> can properly fix this and avoid mistakes like this in future patches?
>
> heres a tip, i claim the line is buggy that is it will segfault when 
> the samples are written you dont see how that can be, so why dont
> you just test the code in a loop with:
>
> samplecnt= random() betweeen 0..0xFFFFFFFF
>
> if it doesnt segfault iam wrong, if it does you should be able to figure
> out why 

As usual, you were right :-)

Changing samplecnt to an unsigned int solved the problem.  I am
sorry it took me this much time to notice this problem.

--
Marco


Index: libavcodec/Makefile
===================================================================
--- libavcodec/Makefile	(revision 8634)
+++ 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 8634)
+++ 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 8634)
+++ 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 8634)
+++ libavcodec/adpcm.c	(working copy)
@@ -29,6 +29,7 @@
  *   by Mike Melanson (melanson at pcisys.net)
  * CD-ROM XA ADPCM codec by BERO
  * EA ADPCM decoder by Robin Kay (komadori at myrealbox.com)
+ * PCM THP Decoder by Marco Gerards (mgerards at xs4all.nl)
  *
  * Features and limitations:
  *
@@ -1308,6 +1309,72 @@
             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 (samples + samplecnt * (st + 1) >= 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 = 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_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 8634)
+++ 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 8634)
+++ 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