alac: split element parsing into a separate function
authorJustin Ruggles <justin.ruggles@gmail.com>
Mon, 9 Jul 2012 20:05:53 +0000 (16:05 -0400)
committerJustin Ruggles <justin.ruggles@gmail.com>
Thu, 19 Jul 2012 17:26:48 +0000 (13:26 -0400)
This will make multi-channel implementation simpler.
Based partially on a patch by Andrew D'Addesio <modchipv12@gmail.com>.

libavcodec/alac.c

index b44b5c4..1e37290 100644 (file)
@@ -78,6 +78,18 @@ typedef struct {
     int nb_samples;                         /**< number of samples in the current frame */
 } ALACContext;
 
+enum RawDataBlockType {
+    /* At the moment, only SCE, CPE, LFE, and END are recognized. */
+    TYPE_SCE,
+    TYPE_CPE,
+    TYPE_CCE,
+    TYPE_LFE,
+    TYPE_DSE,
+    TYPE_PCE,
+    TYPE_FIL,
+    TYPE_END
+};
+
 static inline unsigned int decode_scalar(GetBitContext *gb, int k,
                                          int readsamplesize)
 {
@@ -268,27 +280,18 @@ static void append_extra_bits(int32_t *buffer[MAX_CHANNELS],
             buffer[ch][i] = (buffer[ch][i] << extra_bits) | extra_bits_buffer[ch][i];
 }
 
-static int alac_decode_frame(AVCodecContext *avctx, void *data,
-                             int *got_frame_ptr, AVPacket *avpkt)
+static int decode_element(AVCodecContext *avctx, void *data, int ch_index,
+                          int channels)
 {
     ALACContext *alac = avctx->priv_data;
-
-    int channels;
     int hassize;
     unsigned int readsamplesize;
     int is_compressed;
     uint8_t interlacing_shift;
     uint8_t interlacing_leftweight;
+    uint32_t output_samples;
     int i, ch, ret;
 
-    init_get_bits(&alac->gb, avpkt->data, avpkt->size * 8);
-
-    channels = get_bits(&alac->gb, 3) + 1;
-    if (channels != avctx->channels) {
-        av_log(avctx, AV_LOG_ERROR, "frame header channel count mismatch\n");
-        return AVERROR_INVALIDDATA;
-    }
-
     skip_bits(&alac->gb, 4);  /* element instance tag */
     skip_bits(&alac->gb, 12); /* unused header bits */
 
@@ -305,28 +308,32 @@ static int alac_decode_frame(AVCodecContext *avctx, void *data,
     /* whether the frame is compressed */
     is_compressed = !get_bits1(&alac->gb);
 
-    if (hassize) {
-        /* now read the number of samples as a 32bit integer */
-        uint32_t output_samples = get_bits_long(&alac->gb, 32);
-        if (!output_samples || output_samples > alac->max_samples_per_frame) {
-            av_log(avctx, AV_LOG_ERROR, "invalid samples per frame: %d\n",
-                   output_samples);
-            return AVERROR_INVALIDDATA;
-        }
-        alac->nb_samples = output_samples;
-    } else
-        alac->nb_samples = alac->max_samples_per_frame;
-
-    /* get output buffer */
-    alac->frame.nb_samples = alac->nb_samples;
-    if ((ret = avctx->get_buffer(avctx, &alac->frame)) < 0) {
-        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
-        return ret;
+    if (hassize)
+        output_samples = get_bits_long(&alac->gb, 32);
+    else
+        output_samples = alac->max_samples_per_frame;
+    if (!output_samples || output_samples > alac->max_samples_per_frame) {
+        av_log(avctx, AV_LOG_ERROR, "invalid samples per frame: %d\n",
+               output_samples);
+        return AVERROR_INVALIDDATA;
     }
-    if (alac->sample_size > 16) {
-        for (ch = 0; ch < alac->channels; ch++)
-            alac->output_samples_buffer[ch] = (int32_t *)alac->frame.data[ch];
+    if (!alac->nb_samples) {
+        /* get output buffer */
+        alac->frame.nb_samples = output_samples;
+        if ((ret = avctx->get_buffer(avctx, &alac->frame)) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+            return ret;
+        }
+        if (alac->sample_size > 16) {
+            for (ch = 0; ch < channels; ch++)
+                alac->output_samples_buffer[ch] = (int32_t *)alac->frame.data[ch_index + ch];
+        }
+    } else if (output_samples != alac->nb_samples) {
+        av_log(avctx, AV_LOG_ERROR, "sample count mismatch: %u != %d\n",
+               output_samples, alac->nb_samples);
+        return AVERROR_INVALIDDATA;
     }
+    alac->nb_samples = output_samples;
 
     if (is_compressed) {
         int16_t predictor_coef_table[MAX_CHANNELS][32];
@@ -391,16 +398,13 @@ static int alac_decode_frame(AVCodecContext *avctx, void *data,
         /* not compressed, easy case */
         for (i = 0; i < alac->nb_samples; i++) {
             for (ch = 0; ch < channels; ch++) {
-                alac->output_samples_buffer[ch][i] = get_sbits_long(&alac->gb,
-                                                                    alac->sample_size);
+                alac->output_samples_buffer[ch][i] = get_sbits_long(&alac->gb, alac->sample_size);
             }
         }
         alac->extra_bits = 0;
         interlacing_shift = 0;
         interlacing_leftweight = 0;
     }
-    if (get_bits(&alac->gb, 3) != 7)
-        av_log(avctx, AV_LOG_ERROR, "Error : Wrong End Of Frame\n");
 
     if (channels == 2 && interlacing_leftweight) {
         decorrelate_stereo(alac->output_samples_buffer, alac->nb_samples,
@@ -409,25 +413,62 @@ static int alac_decode_frame(AVCodecContext *avctx, void *data,
 
     if (alac->extra_bits) {
         append_extra_bits(alac->output_samples_buffer, alac->extra_bits_buffer,
-                          alac->extra_bits, alac->channels, alac->nb_samples);
+                          alac->extra_bits, channels, alac->nb_samples);
     }
 
     switch(alac->sample_size) {
     case 16: {
-        for (ch = 0; ch < alac->channels; ch++) {
-            int16_t *outbuffer = (int16_t *)alac->frame.data[ch];
+        for (ch = 0; ch < channels; ch++) {
+            int16_t *outbuffer = (int16_t *)alac->frame.data[ch_index + ch];
             for (i = 0; i < alac->nb_samples; i++)
                 *outbuffer++ = alac->output_samples_buffer[ch][i];
         }}
         break;
     case 24: {
-        for (ch = 0; ch < alac->channels; ch++) {
+        for (ch = 0; ch < channels; ch++) {
             for (i = 0; i < alac->nb_samples; i++)
                 alac->output_samples_buffer[ch][i] <<= 8;
         }}
         break;
     }
 
+    return 0;
+}
+
+static int alac_decode_frame(AVCodecContext *avctx, void *data,
+                             int *got_frame_ptr, AVPacket *avpkt)
+{
+    ALACContext *alac = avctx->priv_data;
+    enum RawDataBlockType element;
+    int channels;
+    int ch, ret;
+
+    init_get_bits(&alac->gb, avpkt->data, avpkt->size * 8);
+
+    alac->nb_samples = 0;
+    ch = 0;
+    while (get_bits_left(&alac->gb)) {
+        element = get_bits(&alac->gb, 3);
+        if (element == TYPE_END)
+            break;
+        if (element > TYPE_CPE && element != TYPE_LFE) {
+            av_log(avctx, AV_LOG_ERROR, "syntax element unsupported: %d", element);
+            return AVERROR_PATCHWELCOME;
+        }
+
+        channels = (element == TYPE_CPE) ? 2 : 1;
+        if (ch + channels > alac->channels) {
+            av_log(avctx, AV_LOG_ERROR, "invalid element channel count\n");
+            return AVERROR_INVALIDDATA;
+        }
+
+        ret = decode_element(avctx, data, ch, channels);
+        if (ret < 0)
+            return ret;
+
+        ch += channels;
+    }
+
     if (avpkt->size * 8 - get_bits_count(&alac->gb) > 8)
         av_log(avctx, AV_LOG_ERROR, "Error : %d bits left\n",
                avpkt->size * 8 - get_bits_count(&alac->gb));