flacdec: allow mid-stream channel layout change
authorJustin Ruggles <justin.ruggles@gmail.com>
Sun, 21 Oct 2012 21:02:28 +0000 (17:02 -0400)
committerJustin Ruggles <justin.ruggles@gmail.com>
Thu, 1 Nov 2012 15:29:16 +0000 (11:29 -0400)
Although the libFLAC decoder cannot handle such a change, it is allowed by the
spec and could potentially occur with live streams.

libavcodec/flac.c
libavcodec/flac.h
libavcodec/flac_parser.c
libavcodec/flacdec.c

index d624beb..07da702 100644 (file)
@@ -19,6 +19,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/audioconvert.h"
 #include "libavutil/crc.h"
 #include "libavutil/log.h"
 #include "bytestream.h"
 
 static const int8_t sample_size_table[] = { 0, 8, 12, 0, 16, 20, 24, 0 };
 
+static const int64_t flac_channel_layouts[6] = {
+    AV_CH_LAYOUT_MONO,
+    AV_CH_LAYOUT_STEREO,
+    AV_CH_LAYOUT_SURROUND,
+    AV_CH_LAYOUT_QUAD,
+    AV_CH_LAYOUT_5POINT0,
+    AV_CH_LAYOUT_5POINT1
+};
+
 static int64_t get_utf8(GetBitContext *gb)
 {
     int64_t val;
@@ -181,6 +191,14 @@ int avpriv_flac_is_extradata_valid(AVCodecContext *avctx,
     return 1;
 }
 
+void ff_flac_set_channel_layout(AVCodecContext *avctx)
+{
+    if (avctx->channels <= FF_ARRAY_ELEMS(flac_channel_layouts))
+        avctx->channel_layout = flac_channel_layouts[avctx->channels - 1];
+    else
+        avctx->channel_layout = 0;
+}
+
 void avpriv_flac_parse_streaminfo(AVCodecContext *avctx, struct FLACStreaminfo *s,
                               const uint8_t *buffer)
 {
@@ -205,6 +223,7 @@ void avpriv_flac_parse_streaminfo(AVCodecContext *avctx, struct FLACStreaminfo *
     avctx->channels = s->channels;
     avctx->sample_rate = s->samplerate;
     avctx->bits_per_raw_sample = s->bps;
+    ff_flac_set_channel_layout(avctx);
 
     s->samples  = get_bits_long(&gb, 32) << 4;
     s->samples |= get_bits(&gb, 4);
index 55bacea..63f41c2 100644 (file)
@@ -137,4 +137,7 @@ int ff_flac_get_max_frame_size(int blocksize, int ch, int bps);
  */
 int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb,
                                 FLACFrameInfo *fi, int log_level_offset);
+
+void ff_flac_set_channel_layout(AVCodecContext *avctx);
+
 #endif /* AVCODEC_FLAC_H */
index 6c8c046..f0a37f3 100644 (file)
@@ -459,6 +459,7 @@ static int get_best_header(FLACParseContext* fpc, const uint8_t **poutbuf,
 
     fpc->avctx->sample_rate = header->fi.samplerate;
     fpc->avctx->channels    = header->fi.channels;
+    ff_flac_set_channel_layout(fpc->avctx);
     fpc->pc->duration       = header->fi.blocksize;
     *poutbuf = flac_fifo_read_wrap(fpc, header->offset, *poutbuf_size,
                                         &fpc->wrap_buf,
index 4ecf5f2..20af820 100644 (file)
@@ -66,15 +66,6 @@ typedef struct FLACContext {
     FLACDSPContext dsp;
 } FLACContext;
 
-static const int64_t flac_channel_layouts[6] = {
-    AV_CH_LAYOUT_MONO,
-    AV_CH_LAYOUT_STEREO,
-    AV_CH_LAYOUT_SURROUND,
-    AV_CH_LAYOUT_QUAD,
-    AV_CH_LAYOUT_5POINT0,
-    AV_CH_LAYOUT_5POINT1
-};
-
 static int allocate_buffers(FLACContext *s);
 
 static void flac_set_bps(FLACContext *s)
@@ -127,9 +118,6 @@ static av_cold int flac_decode_init(AVCodecContext *avctx)
     avcodec_get_frame_defaults(&s->frame);
     avctx->coded_frame = &s->frame;
 
-    if (avctx->channels <= FF_ARRAY_ELEMS(flac_channel_layouts))
-        avctx->channel_layout = flac_channel_layouts[avctx->channels - 1];
-
     return 0;
 }
 
@@ -421,7 +409,7 @@ static inline int decode_subframe(FLACContext *s, int channel)
 
 static int decode_frame(FLACContext *s)
 {
-    int i;
+    int i, ret;
     GetBitContext *gb = &s->gb;
     FLACFrameInfo fi;
 
@@ -430,12 +418,15 @@ static int decode_frame(FLACContext *s)
         return -1;
     }
 
-    if (s->channels && fi.channels != s->channels) {
-        av_log(s->avctx, AV_LOG_ERROR, "switching channel layout mid-stream "
-                                       "is not supported\n");
-        return -1;
+    if (s->channels && fi.channels != s->channels && s->got_streaminfo) {
+        s->channels = s->avctx->channels = fi.channels;
+        ff_flac_set_channel_layout(s->avctx);
+        ret = allocate_buffers(s);
+        if (ret < 0)
+            return ret;
     }
     s->channels = s->avctx->channels = fi.channels;
+    ff_flac_set_channel_layout(s->avctx);
     s->ch_mode = fi.ch_mode;
 
     if (!s->bps && !fi.bps) {
@@ -478,7 +469,7 @@ static int decode_frame(FLACContext *s)
     s->samplerate = s->avctx->sample_rate = fi.samplerate;
 
     if (!s->got_streaminfo) {
-        int ret = allocate_buffers(s);
+        ret = allocate_buffers(s);
         if (ret < 0)
             return ret;
         ff_flacdsp_init(&s->dsp, s->avctx->sample_fmt, s->bps);