ext/wavpack/: Add support for encoding, parsing and decoding multichannel files with...
authorSebastian Dröge <slomo@circular-chaos.org>
Tue, 20 Nov 2007 13:08:45 +0000 (13:08 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Tue, 20 Nov 2007 13:08:45 +0000 (13:08 +0000)
Original commit message from CVS:
* ext/wavpack/gstwavpackcommon.c:
(gst_wavpack_get_default_channel_mask),
(gst_wavpack_set_channel_layout),
(gst_wavpack_get_default_channel_positions),
(gst_wavpack_get_channel_mask_from_positions),
(gst_wavpack_set_channel_mapping):
* ext/wavpack/gstwavpackcommon.h:
* ext/wavpack/gstwavpackdec.c: (gst_wavpack_dec_reset),
(gst_wavpack_dec_sink_set_caps), (gst_wavpack_dec_chain):
* ext/wavpack/gstwavpackdec.h:
* ext/wavpack/gstwavpackenc.c: (gst_wavpack_enc_reset),
(gst_wavpack_enc_init), (gst_wavpack_enc_sink_set_caps),
(gst_wavpack_enc_set_wp_config), (gst_wavpack_enc_push_block),
(gst_wavpack_enc_fix_channel_order), (gst_wavpack_enc_chain),
(gst_wavpack_enc_rewrite_first_block),
(gst_wavpack_enc_sink_event):
* ext/wavpack/gstwavpackenc.h:
* ext/wavpack/gstwavpackparse.c:
(gst_wavpack_parse_index_append_entry), (gst_wavpack_parse_reset),
(gst_wavpack_parse_scan_to_find_sample),
(gst_wavpack_parse_sink_event), (gst_wavpack_parse_create_src_pad),
(gst_wavpack_parse_push_buffer), (gst_wavpack_parse_loop):
* ext/wavpack/gstwavpackparse.h:
Add support for encoding, parsing and decoding multichannel
files with up to 8 channels. This also improves the robustness
of parsing quite a bit.
* ext/wavpack/gstwavpackstreamreader.c:
(gst_wavpack_stream_reader_read_bytes),
(gst_wavpack_stream_reader_get_pos),
(gst_wavpack_stream_reader_set_pos_abs),
(gst_wavpack_stream_reader_set_pos_rel),
(gst_wavpack_stream_reader_push_back_byte),
(gst_wavpack_stream_reader_get_length),
(gst_wavpack_stream_reader_can_seek),
(gst_wavpack_stream_reader_write_bytes):
Improve debugging.

ChangeLog
ext/wavpack/gstwavpackcommon.c
ext/wavpack/gstwavpackcommon.h
ext/wavpack/gstwavpackdec.c
ext/wavpack/gstwavpackdec.h
ext/wavpack/gstwavpackenc.c
ext/wavpack/gstwavpackenc.h
ext/wavpack/gstwavpackparse.c
ext/wavpack/gstwavpackparse.h
ext/wavpack/gstwavpackstreamreader.c

index ec9656e..692508f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2007-11-20  Sebastian Dröge  <slomo@circular-chaos.org>
+
+       * ext/wavpack/gstwavpackcommon.c:
+       (gst_wavpack_get_default_channel_mask),
+       (gst_wavpack_set_channel_layout),
+       (gst_wavpack_get_default_channel_positions),
+       (gst_wavpack_get_channel_mask_from_positions),
+       (gst_wavpack_set_channel_mapping):
+       * ext/wavpack/gstwavpackcommon.h:
+       * ext/wavpack/gstwavpackdec.c: (gst_wavpack_dec_reset),
+       (gst_wavpack_dec_sink_set_caps), (gst_wavpack_dec_chain):
+       * ext/wavpack/gstwavpackdec.h:
+       * ext/wavpack/gstwavpackenc.c: (gst_wavpack_enc_reset),
+       (gst_wavpack_enc_init), (gst_wavpack_enc_sink_set_caps),
+       (gst_wavpack_enc_set_wp_config), (gst_wavpack_enc_push_block),
+       (gst_wavpack_enc_fix_channel_order), (gst_wavpack_enc_chain),
+       (gst_wavpack_enc_rewrite_first_block),
+       (gst_wavpack_enc_sink_event):
+       * ext/wavpack/gstwavpackenc.h:
+       * ext/wavpack/gstwavpackparse.c:
+       (gst_wavpack_parse_index_append_entry), (gst_wavpack_parse_reset),
+       (gst_wavpack_parse_scan_to_find_sample),
+       (gst_wavpack_parse_sink_event), (gst_wavpack_parse_create_src_pad),
+       (gst_wavpack_parse_push_buffer), (gst_wavpack_parse_loop):
+       * ext/wavpack/gstwavpackparse.h:
+       Add support for encoding, parsing and decoding multichannel
+       files with up to 8 channels. This also improves the robustness
+       of parsing quite a bit.
+
+       * ext/wavpack/gstwavpackstreamreader.c:
+       (gst_wavpack_stream_reader_read_bytes),
+       (gst_wavpack_stream_reader_get_pos),
+       (gst_wavpack_stream_reader_set_pos_abs),
+       (gst_wavpack_stream_reader_set_pos_rel),
+       (gst_wavpack_stream_reader_push_back_byte),
+       (gst_wavpack_stream_reader_get_length),
+       (gst_wavpack_stream_reader_can_seek),
+       (gst_wavpack_stream_reader_write_bytes):
+       Improve debugging.
+
 2007-11-20  Stefan Kost  <ensonic@users.sf.net>
 
        * ext/libpng/gstpngdec.c:
index ad16186..4fa3e12 100644 (file)
@@ -28,6 +28,9 @@
 #include "gstwavpackcommon.h"
 #include <string.h>
 
+#include <gst/gst.h>
+#include <gst/audio/multichannel.h>
+
 GST_DEBUG_CATEGORY_EXTERN (wavpack_debug);
 #define GST_CAT_DEFAULT wavpack_debug
 
@@ -96,3 +99,183 @@ gst_wavpack_read_metadata (GstWavpackMetadata * wpmd, guint8 * header_data,
 
   return TRUE;
 }
+
+gint
+gst_wavpack_get_default_channel_mask (gint nchannels)
+{
+  gint channel_mask = 0;
+
+  /* Set the default channel mask for the given number of channels.
+   * It's the same as for WAVE_FORMAT_EXTENDED:
+   * http://www.microsoft.com/whdc/device/audio/multichaud.mspx
+   */
+  switch (nchannels) {
+    case 11:
+      channel_mask |= 0x00400;
+      channel_mask |= 0x00200;
+    case 9:
+      channel_mask |= 0x00100;
+    case 8:
+      channel_mask |= 0x00080;
+      channel_mask |= 0x00040;
+    case 6:
+      channel_mask |= 0x00020;
+      channel_mask |= 0x00010;
+    case 4:
+      channel_mask |= 0x00008;
+    case 3:
+      channel_mask |= 0x00004;
+    case 2:
+      channel_mask |= 0x00002;
+      channel_mask |= 0x00001;
+      break;
+    case 1:
+      /* For mono use front center */
+      channel_mask |= 0x00004;
+      break;
+  }
+
+  return channel_mask;
+}
+
+static const struct
+{
+  const guint32 ms_mask;
+  const GstAudioChannelPosition gst_pos;
+} layout_mapping[] = {
+  {
+  0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
+  0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
+  0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
+  0x00008, GST_AUDIO_CHANNEL_POSITION_LFE}, {
+  0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
+  0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
+  0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
+  0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
+  0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
+  0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
+  0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
+  0x00800, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_CENTER       */
+  {
+  0x01000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_LEFT   */
+  {
+  0x02000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_CENTER */
+  {
+  0x04000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_RIGHT  */
+  {
+  0x08000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_BACK_LEFT    */
+  {
+  0x10000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_BACK_CENTER  */
+  {
+  0x20000, GST_AUDIO_CHANNEL_POSITION_INVALID}  /* TOP_BACK_RIGHT   */
+};
+
+#define MAX_CHANNEL_POSITIONS G_N_ELEMENTS (layout_mapping)
+
+gboolean
+gst_wavpack_set_channel_layout (GstCaps * caps, gint layout)
+{
+  GstAudioChannelPosition pos[MAX_CHANNEL_POSITIONS];
+  GstStructure *s;
+  gint num_channels, i, p;
+
+  s = gst_caps_get_structure (caps, 0);
+  if (!gst_structure_get_int (s, "channels", &num_channels))
+    g_return_val_if_reached (FALSE);
+
+  if (num_channels == 1 && layout == 0x00004) {
+    pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
+    return TRUE;
+  }
+
+  p = 0;
+  for (i = 0; i < MAX_CHANNEL_POSITIONS; ++i) {
+    if ((layout & layout_mapping[i].ms_mask) != 0) {
+      if (p >= num_channels) {
+        GST_WARNING ("More bits set in the channel layout map than there "
+            "are channels! Broken file");
+        return FALSE;
+      }
+      if (layout_mapping[i].gst_pos == GST_AUDIO_CHANNEL_POSITION_INVALID) {
+        GST_WARNING ("Unsupported channel position (mask 0x%08x) in channel "
+            "layout map - ignoring those channels", layout_mapping[i].ms_mask);
+        /* what to do? just ignore it and let downstream deal with a channel
+         * layout that has INVALID positions in it for now ... */
+      }
+      pos[p] = layout_mapping[i].gst_pos;
+      ++p;
+    }
+  }
+
+  if (p != num_channels) {
+    GST_WARNING ("Only %d bits set in the channel layout map, but there are "
+        "supposed to be %d channels! Broken file", p, num_channels);
+    return FALSE;
+  }
+
+  gst_audio_set_channel_positions (s, pos);
+  return TRUE;
+}
+
+GstAudioChannelPosition *
+gst_wavpack_get_default_channel_positions (gint nchannels)
+{
+  GstAudioChannelPosition *pos = g_new (GstAudioChannelPosition, nchannels);
+  gint i;
+
+  if (nchannels == 1) {
+    pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+    return pos;
+  }
+
+  for (i = 0; i < nchannels; i++)
+    pos[i] = layout_mapping[i].gst_pos;
+
+  return pos;
+}
+
+gint
+gst_wavpack_get_channel_mask_from_positions (GstAudioChannelPosition * pos,
+    gint nchannels)
+{
+  gint channel_mask = 0;
+  gint i, j;
+
+  if (nchannels == 1 && pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) {
+    channel_mask = 0x00000004;
+    return channel_mask;
+  }
+
+  /* FIXME: not exactly efficient but otherwise we need an inverse
+   * mapping table too */
+  for (i = 0; i < nchannels; i++) {
+    for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) {
+      if (pos[i] == layout_mapping[j].gst_pos) {
+        channel_mask |= layout_mapping[j].ms_mask;
+        break;
+      }
+    }
+  }
+
+  return channel_mask;
+}
+
+gboolean
+gst_wavpack_set_channel_mapping (GstAudioChannelPosition * pos, gint nchannels,
+    gint8 * channel_mapping)
+{
+  gint i, j;
+  gboolean ret = TRUE;
+
+  for (i = 0; i < nchannels; i++) {
+    for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) {
+      if (pos[i] == layout_mapping[j].gst_pos) {
+        channel_mapping[i] = j;
+        ret &= (i == j);
+        break;
+      }
+    }
+  }
+
+  return !ret;
+}
index 3cf6e56..6a9e516 100644 (file)
@@ -24,6 +24,7 @@
 #define __GST_WAVPACK_COMMON_H__
 
 #include <gst/gst.h>
+#include <gst/audio/multichannel.h>
 #include <wavpack/wavpack.h>
 
 typedef struct
@@ -65,5 +66,10 @@ typedef struct
 gboolean gst_wavpack_read_header (WavpackHeader * header, guint8 * buf);
 gboolean gst_wavpack_read_metadata (GstWavpackMetadata * meta,
     guint8 * header_data, guint8 ** p_data);
+gint gst_wavpack_get_default_channel_mask (gint nchannels);
+gboolean gst_wavpack_set_channel_layout (GstCaps * caps, gint layout);
+GstAudioChannelPosition *gst_wavpack_get_default_channel_positions (gint nchannels);
+gint gst_wavpack_get_channel_mask_from_positions (GstAudioChannelPosition *pos, gint nchannels);
+gboolean gst_wavpack_set_channel_mapping (GstAudioChannelPosition *pos, gint nchannels, gint8 *channel_mapping);
 
 #endif
index c4aa1d0..0dde6c7 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <gst/gst.h>
 #include <gst/audio/audio.h>
+#include <gst/audio/multichannel.h>
 
 #include <math.h>
 #include <string.h>
@@ -62,7 +63,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("audio/x-wavpack, "
         "width = (int) [ 1, 32 ], "
-        "channels = (int) [ 1, 2 ], "
+        "channels = (int) [ 1, 8 ], "
         "rate = (int) [ 6000, 192000 ], " "framed = (boolean) true")
     );
 
@@ -72,7 +73,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_STATIC_CAPS ("audio/x-raw-int, "
         "width = (int) 32, "
         "depth = (int) [ 1, 32 ], "
-        "channels = (int) [ 1, 2 ], "
+        "channels = (int) [ 1, 8 ], "
         "rate = (int) [ 6000, 192000 ], "
         "endianness = (int) BYTE_ORDER, " "signed = (boolean) true")
     );
@@ -126,6 +127,7 @@ gst_wavpack_dec_reset (GstWavpackDec * dec)
   dec->error_count = 0;
 
   dec->channels = 0;
+  dec->channel_mask = 0;
   dec->sample_rate = 0;
   dec->depth = 0;
 
@@ -177,6 +179,7 @@ gst_wavpack_dec_sink_set_caps (GstPad * pad, GstCaps * caps)
       gst_structure_get_int (structure, "rate", &dec->sample_rate) &&
       gst_structure_get_int (structure, "width", &dec->depth)) {
     GstCaps *caps;
+    GstAudioChannelPosition *pos;
 
     caps = gst_caps_new_simple ("audio/x-raw-int",
         "rate", G_TYPE_INT, dec->sample_rate,
@@ -186,6 +189,22 @@ gst_wavpack_dec_sink_set_caps (GstPad * pad, GstCaps * caps)
         "endianness", G_TYPE_INT, G_BYTE_ORDER,
         "signed", G_TYPE_BOOLEAN, TRUE, NULL);
 
+    /* If we already have the channel layout set from upstream
+     * take this */
+    if (gst_structure_has_field (structure, "channel-positions")) {
+      pos = gst_audio_get_channel_positions (structure);
+      if (pos != NULL && dec->channels > 2) {
+        GstStructure *new_str = gst_caps_get_structure (caps, 0);
+
+        gst_audio_set_channel_positions (new_str, pos);
+        dec->channel_mask =
+            gst_wavpack_get_channel_mask_from_positions (pos, dec->channels);
+      }
+
+      if (pos != NULL)
+        g_free (pos);
+    }
+
     GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps);
 
     /* should always succeed */
@@ -248,7 +267,10 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf)
   if (!gst_wavpack_read_header (&wph, GST_BUFFER_DATA (buf)))
     goto invalid_header;
 
-  if (GST_BUFFER_SIZE (buf) != wph.ckSize + 4 * 1 + 4)
+  if (GST_BUFFER_SIZE (buf) < wph.ckSize + 4 * 1 + 4)
+    goto input_not_framed;
+
+  if (!(wph.flags & INITIAL_BLOCK))
     goto input_not_framed;
 
   dec->wv_id.buffer = GST_BUFFER_DATA (buf);
@@ -282,10 +304,12 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf)
   format_changed =
       (dec->sample_rate != WavpackGetSampleRate (dec->context)) ||
       (dec->channels != WavpackGetNumChannels (dec->context)) ||
-      (dec->depth != WavpackGetBitsPerSample (dec->context));
+      (dec->depth != WavpackGetBitsPerSample (dec->context)) ||
+      (dec->channel_mask != WavpackGetChannelMask (dec->context));
 
   if (!GST_PAD_CAPS (dec->srcpad) || format_changed) {
     GstCaps *caps;
+    gint channel_mask;
 
     dec->sample_rate = WavpackGetSampleRate (dec->context);
     dec->channels = WavpackGetNumChannels (dec->context);
@@ -299,6 +323,18 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf)
         "endianness", G_TYPE_INT, G_BYTE_ORDER,
         "signed", G_TYPE_BOOLEAN, TRUE, NULL);
 
+    channel_mask = WavpackGetChannelMask (dec->context);
+    if (channel_mask == 0)
+      channel_mask = gst_wavpack_get_default_channel_mask (dec->channels);
+
+    dec->channel_mask = channel_mask;
+
+    /* Only set the channel layout for more than two channels
+     * otherwise things break unfortunately */
+    if (channel_mask != 0 && dec->channels > 2)
+      if (!gst_wavpack_set_channel_layout (caps, channel_mask))
+        GST_WARNING_OBJECT (dec, "Failed to set channel layout");
+
     GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps);
 
     /* should always succeed */
@@ -367,7 +403,9 @@ invalid_header:
 decode_error:
   {
     GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
-        ("Failed to decode wavpack stream"));
+        ("Failed to decode wavpack stream: %s",
+            (dec->context) ? WavpackGetErrorMessage (dec->
+                context) : "couldn't create decoder context"));
     gst_buffer_unref (outbuf);
     gst_buffer_unref (buf);
     return GST_FLOW_ERROR;
index 007e7c2..eb6e4c3 100644 (file)
@@ -62,6 +62,7 @@ struct _GstWavpackDec
   gint sample_rate;
   gint depth;
   gint channels;
+  gint channel_mask;
 
   gint error_count;
 };
index 5562ea7..1c8a2f5 100644 (file)
  */
 
 /*
- * TODO: - add multichannel handling. channel_mask is:
- *                  front left
- *                  front right
- *                  center
- *                  LFE
- *                  back left
- *                  back right
- *                  front left center
- *                  front right center
- *                  back left
- *                  back center
- *                  side left
- *                  side right
- *                  ...
- *        - add 32 bit float mode. CONFIG_FLOAT_DATA
+ * TODO: - add 32 bit float mode. CONFIG_FLOAT_DATA
  */
 
 #include <string.h>
@@ -111,7 +97,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
         "width = (int) 32, "
         "depth = (int) [ 1, 32], "
         "endianness = (int) BYTE_ORDER, "
-        "channels = (int) [ 1, 2 ], "
+        "channels = (int) [ 1, 8 ], "
         "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE")
     );
 
@@ -320,6 +306,12 @@ gst_wavpack_enc_reset (GstWavpackEnc * enc)
     enc->md5_context = NULL;
   }
 
+  if (enc->pending_buffer) {
+    gst_buffer_unref (enc->pending_buffer);
+    enc->pending_buffer = NULL;
+    enc->pending_offset = 0;
+  }
+
   /* reset the last returns to GST_FLOW_OK. This is only set to something else
    * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
    * so not valid anymore */
@@ -329,6 +321,8 @@ gst_wavpack_enc_reset (GstWavpackEnc * enc)
   enc->samplerate = 0;
   enc->depth = 0;
   enc->channels = 0;
+  enc->channel_mask = 0;
+  enc->need_channel_remap = FALSE;
 }
 
 static void
@@ -356,8 +350,10 @@ gst_wavpack_enc_init (GstWavpackEnc * enc, GstWavpackEncClass * gclass)
 
   enc->wv_id.correction = FALSE;
   enc->wv_id.wavpack_enc = enc;
+  enc->wv_id.passthrough = FALSE;
   enc->wvc_id.correction = TRUE;
   enc->wvc_id.wavpack_enc = enc;
+  enc->wvc_id.passthrough = FALSE;
 
   /* set default values of params */
   enc->mode = GST_WAVPACK_ENC_MODE_DEFAULT;
@@ -374,6 +370,7 @@ gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
 {
   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
   GstStructure *structure = gst_caps_get_structure (caps, 0);
+  GstAudioChannelPosition *pos;
 
   if (!gst_structure_get_int (structure, "channels", &enc->channels) ||
       !gst_structure_get_int (structure, "rate", &enc->samplerate) ||
@@ -384,12 +381,37 @@ gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
     return FALSE;
   }
 
+  pos = gst_audio_get_channel_positions (structure);
+  /* If one channel is NONE they'll be all undefined */
+  if (pos != NULL && pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) {
+    g_free (pos);
+    pos = NULL;
+  }
+
+  if (pos == NULL) {
+    GST_ELEMENT_ERROR (enc, STREAM, FORMAT, (NULL),
+        ("input has no valid channel layout"));
+
+    gst_object_unref (enc);
+    return FALSE;
+  }
+
+  enc->channel_mask =
+      gst_wavpack_get_channel_mask_from_positions (pos, enc->channels);
+  enc->need_channel_remap =
+      gst_wavpack_set_channel_mapping (pos, enc->channels,
+      enc->channel_mapping);
+  g_free (pos);
+
   /* set fixed src pad caps now that we know what we will get */
   caps = gst_caps_new_simple ("audio/x-wavpack",
       "channels", G_TYPE_INT, enc->channels,
       "rate", G_TYPE_INT, enc->samplerate,
       "width", G_TYPE_INT, enc->depth, "framed", G_TYPE_BOOLEAN, TRUE, NULL);
 
+  if (!gst_wavpack_set_channel_layout (caps, enc->channel_mask))
+    GST_WARNING_OBJECT (enc, "setting channel layout failed");
+
   if (!gst_pad_set_caps (enc->srcpad, caps)) {
     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
         ("setting caps failed: %" GST_PTR_FORMAT, caps));
@@ -412,13 +434,7 @@ gst_wavpack_enc_set_wp_config (GstWavpackEnc * enc)
   enc->wp_config->bytes_per_sample = GST_ROUND_UP_8 (enc->depth) / 8;
   enc->wp_config->bits_per_sample = enc->depth;
   enc->wp_config->num_channels = enc->channels;
-
-  /* TODO: handle more than 2 channels correctly! */
-  if (enc->channels == 1) {
-    enc->wp_config->channel_mask = 0x4;
-  } else if (enc->channels == 2) {
-    enc->wp_config->channel_mask = 0x2 | 0x1;
-  }
+  enc->wp_config->channel_mask = enc->channel_mask;
   enc->wp_config->sample_rate = enc->samplerate;
 
   /*
@@ -556,17 +572,43 @@ gst_wavpack_enc_push_block (void *id, void *data, int32_t count)
 
     gst_wavpack_read_header (&wph, block);
 
-    /* if it's the first wavpack block, send a NEW_SEGMENT event */
-    if (wph.block_index == 0) {
-      gst_pad_push_event (pad,
-          gst_event_new_new_segment (FALSE,
-              1.0, GST_FORMAT_TIME, 0, GST_BUFFER_OFFSET_NONE, 0));
-
-      /* save header for later reference, so we can re-send it later on
-       * EOS with fixed up values for total sample count etc. */
-      if (enc->first_block == NULL && !wid->correction) {
-        enc->first_block = g_memdup (block, count);
-        enc->first_block_size = count;
+    /* Only set when pushing the first buffer again, in that case
+     * we don't want to delay the buffer or push newsegment events
+     */
+    if (!wid->passthrough) {
+      /* Only push complete blocks */
+      if (enc->pending_buffer == NULL) {
+        enc->pending_buffer = buffer;
+        enc->pending_offset = wph.block_index;
+      } else if (enc->pending_offset == wph.block_index) {
+        enc->pending_buffer = gst_buffer_join (enc->pending_buffer, buffer);
+      } else {
+        GST_ERROR ("Got incomplete block, dropping");
+        gst_buffer_unref (enc->pending_buffer);
+        enc->pending_buffer = buffer;
+        enc->pending_offset = wph.block_index;
+      }
+
+      if (!(wph.flags & FINAL_BLOCK))
+        return TRUE;
+
+      buffer = enc->pending_buffer;
+      enc->pending_buffer = NULL;
+      enc->pending_offset = 0;
+
+      /* if it's the first wavpack block, send a NEW_SEGMENT event */
+      if (wph.block_index == 0) {
+        gst_pad_push_event (pad,
+            gst_event_new_new_segment (FALSE,
+                1.0, GST_FORMAT_TIME, 0, GST_BUFFER_OFFSET_NONE, 0));
+
+        /* save header for later reference, so we can re-send it later on
+         * EOS with fixed up values for total sample count etc. */
+        if (enc->first_block == NULL && !wid->correction) {
+          enc->first_block =
+              g_memdup (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
+          enc->first_block_size = GST_BUFFER_SIZE (buffer);
+        }
       }
     }
 
@@ -589,6 +631,8 @@ gst_wavpack_enc_push_block (void *id, void *data, int32_t count)
   }
 
   /* push the buffer and forward errors */
+  GST_DEBUG_OBJECT (enc, "pushing buffer with %d bytes",
+      GST_BUFFER_SIZE (buffer));
   *flow = gst_pad_push (pad, buffer);
 
   if (*flow != GST_FLOW_OK) {
@@ -600,6 +644,24 @@ gst_wavpack_enc_push_block (void *id, void *data, int32_t count)
   return TRUE;
 }
 
+static void
+gst_wavpack_enc_fix_channel_order (GstWavpackEnc * enc, gint32 * data,
+    gint nsamples)
+{
+  gint i, j;
+  gint32 tmp[8];
+
+  for (i = 0; i < nsamples / enc->channels; i++) {
+    for (j = 0; j < enc->channels; j++) {
+      tmp[enc->channel_mapping[j]] = data[j];
+    }
+    for (j = 0; j < enc->channels; j++) {
+      data[j] = tmp[j];
+    }
+    data += enc->channels;
+  }
+}
+
 static GstFlowReturn
 gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf)
 {
@@ -646,6 +708,12 @@ gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf)
     GST_DEBUG ("setup of encoding context successfull");
   }
 
+  if (enc->need_channel_remap) {
+    buf = gst_buffer_make_writable (buf);
+    gst_wavpack_enc_fix_channel_order (enc, (gint32 *) GST_BUFFER_DATA (buf),
+        sample_count);
+  }
+
   /* if we want to append the MD5 sum to the stream update it here
    * with the current raw samples */
   if (enc->md5) {
@@ -700,8 +768,10 @@ gst_wavpack_enc_rewrite_first_block (GstWavpackEnc * enc)
   if (ret) {
     /* try to rewrite the first block */
     GST_DEBUG_OBJECT (enc, "rewriting first block ...");
+    enc->wv_id.passthrough = TRUE;
     ret = gst_wavpack_enc_push_block (&enc->wv_id,
         enc->first_block, enc->first_block_size);
+    enc->wv_id.passthrough = FALSE;
   } else {
     GST_WARNING_OBJECT (enc, "rewriting of first block failed. "
         "Seeking to first block failed!");
@@ -721,6 +791,14 @@ gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event)
       /* Encode all remaining samples and flush them to the src pads */
       WavpackFlushSamples (enc->wp_context);
 
+      /* Drop all remaining data, this is no complete block otherwise
+       * it would've been pushed already */
+      if (enc->pending_buffer) {
+        gst_object_unref (enc->pending_buffer);
+        enc->pending_buffer = NULL;
+        enc->pending_offset = 0;
+      }
+
       /* write the MD5 sum if we have to write one */
       if ((enc->md5) && (enc->md5_context)) {
         guchar md5_digest[16];
index 243e8d7..dbafcd1 100644 (file)
@@ -45,6 +45,7 @@ typedef struct
 {
   gboolean correction;
   GstWavpackEnc *wavpack_enc;
+  gboolean passthrough;
 } GstWavpackEncWriteID;
 
 
@@ -64,6 +65,9 @@ struct _GstWavpackEnc
 
   gint samplerate;
   gint channels;
+  gint channel_mask;
+  gint8 channel_mapping[8];
+  gboolean need_channel_remap;
   gint depth;
 
   GstWavpackEncWriteID wv_id;
@@ -80,6 +84,9 @@ struct _GstWavpackEnc
 
   void *first_block;
   int32_t first_block_size;
+
+  GstBuffer *pending_buffer;
+  gint32 pending_offset;
 };
 
 struct _GstWavpackEncClass
index 3ab84b4..71c4d83 100644 (file)
@@ -70,7 +70,7 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SOMETIMES,
     GST_STATIC_CAPS ("audio/x-wavpack, "
         "width = (int) [ 1, 32 ], "
-        "channels = (int) [ 1, 2 ], "
+        "channels = (int) [ 1, 8 ], "
         "rate = (int) [ 6000, 192000 ], " "framed = (boolean) true")
     );
 
@@ -189,7 +189,8 @@ gst_wavpack_parse_index_append_entry (GstWavpackParse * wvparse,
   /* do we have this one already? */
   if (wvparse->entries) {
     entry = gst_wavpack_parse_index_get_last_entry (wvparse);
-    if (entry->byte_offset >= byte_offset)
+    if (entry->byte_offset >= byte_offset
+        || entry->sample_offset >= sample_offset)
       return;
   }
 
@@ -245,6 +246,11 @@ gst_wavpack_parse_reset (GstWavpackParse * parse)
   g_list_foreach (parse->queued_events, (GFunc) gst_mini_object_unref, NULL);
   g_list_free (parse->queued_events);
   parse->queued_events = NULL;
+
+  if (parse->pending_buffer)
+    gst_buffer_unref (parse->pending_buffer);
+
+  parse->pending_buffer = NULL;
 }
 
 static const GstQueryType *
@@ -422,8 +428,11 @@ gst_wavpack_parse_scan_to_find_sample (GstWavpackParse * parse,
     gst_wavpack_read_header (&header, GST_BUFFER_DATA (buf));
     gst_buffer_unref (buf);
 
-    gst_wavpack_parse_index_append_entry (parse, off, header.block_index,
-        header.block_samples);
+    if (header.flags & INITIAL_BLOCK)
+      gst_wavpack_parse_index_append_entry (parse, off, header.block_index,
+          header.block_samples);
+    else
+      continue;
 
     if (header.block_index <= sample &&
         sample < (header.block_index + header.block_samples)) {
@@ -631,6 +640,11 @@ gst_wavpack_parse_sink_event (GstPad * pad, GstEvent * event)
       if (parse->adapter) {
         gst_adapter_clear (parse->adapter);
       }
+      if (parse->pending_buffer) {
+        gst_buffer_unref (parse->pending_buffer);
+        parse->pending_buffer = NULL;
+        parse->pending_offset = 0;
+      }
       ret = gst_pad_push_event (parse->srcpad, event);
       break;
     }
@@ -646,6 +660,11 @@ gst_wavpack_parse_sink_event (GstPad * pad, GstEvent * event)
          * be a complete Wavpack block and we can't do anything with them */
         gst_adapter_clear (parse->adapter);
       }
+      if (parse->pending_buffer) {
+        gst_buffer_unref (parse->pending_buffer);
+        parse->pending_buffer = NULL;
+        parse->pending_offset = 0;
+      }
       ret = gst_pad_push_event (parse->srcpad, event);
       break;
     }
@@ -794,6 +813,7 @@ gst_wavpack_parse_create_src_pad (GstWavpackParse * wvparse, GstBuffer * buf,
         WavpackContext *wpc;
         gchar error_msg[80];
         read_id rid;
+        gint channel_mask;
 
         rid.buffer = GST_BUFFER_DATA (buf);
         rid.length = GST_BUFFER_SIZE (buf);
@@ -816,6 +836,23 @@ gst_wavpack_parse_create_src_pad (GstWavpackParse * wvparse, GstBuffer * buf,
             "channels", G_TYPE_INT, wvparse->channels,
             "rate", G_TYPE_INT, wvparse->samplerate,
             "framed", G_TYPE_BOOLEAN, TRUE, NULL);
+
+        channel_mask = WavpackGetChannelMask (wpc);
+        if (channel_mask == 0)
+          channel_mask =
+              gst_wavpack_get_default_channel_mask (wvparse->channels);
+
+        if (channel_mask != 0) {
+          if (!gst_wavpack_set_channel_layout (caps, channel_mask)) {
+            GST_WARNING_OBJECT (wvparse, "Failed to set channel layout");
+            gst_caps_unref (caps);
+            caps = NULL;
+            WavpackCloseFile (wpc);
+            g_free (stream_reader);
+            break;
+          }
+        }
+
         wvparse->srcpad =
             gst_pad_new_from_template (gst_element_class_get_pad_template
             (GST_ELEMENT_GET_CLASS (wvparse), "src"), "src");
@@ -880,6 +917,24 @@ gst_wavpack_parse_push_buffer (GstWavpackParse * wvparse, GstBuffer * buf,
     wvparse->queued_events = NULL;
   }
 
+  if (wvparse->pending_buffer == NULL) {
+    wvparse->pending_buffer = buf;
+    wvparse->pending_offset = header->block_index;
+  } else if (wvparse->pending_offset == header->block_index) {
+    wvparse->pending_buffer = gst_buffer_join (wvparse->pending_buffer, buf);
+  } else {
+    GST_ERROR ("Got incomplete block, dropping");
+    gst_buffer_unref (wvparse->pending_buffer);
+    wvparse->pending_buffer = buf;
+    wvparse->pending_offset = header->block_index;
+  }
+
+  if (!(header->flags & FINAL_BLOCK))
+    return GST_FLOW_OK;
+
+  buf = wvparse->pending_buffer;
+  wvparse->pending_buffer = NULL;
+
   GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (header->block_index,
       GST_SECOND, wvparse->samplerate);
   GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (header->block_samples,
@@ -1014,9 +1069,9 @@ gst_wavpack_parse_loop (GstElement * element)
       goto pause;
     }
   }
-
-  gst_wavpack_parse_index_append_entry (parse, parse->current_offset,
-      header.block_index, header.block_samples);
+  if (header.flags & INITIAL_BLOCK)
+    gst_wavpack_parse_index_append_entry (parse, parse->current_offset,
+        header.block_index, header.block_samples);
 
   flow_ret = gst_wavpack_parse_push_buffer (parse, buf, &header);
   if (flow_ret != GST_FLOW_OK)
index d1c7636..67d093f 100644 (file)
@@ -67,6 +67,9 @@ struct _GstWavpackParse
 
   GstSegment segment;           /* the currently configured segment, in
                                  * samples/audio frames (DEFAULT format) */
+
+  GstBuffer *pending_buffer;
+  gint32 pending_offset;
   guint32 next_block_index;
 
   GstAdapter *adapter;          /* when operating chain-based, otherwise NULL */
index 9b6260b..868f7bf 100644 (file)
@@ -35,11 +35,15 @@ gst_wavpack_stream_reader_read_bytes (void *id, void *data, int32_t bcount)
   uint32_t left = rid->length - rid->position;
   uint32_t to_read = MIN (left, bcount);
 
+  GST_DEBUG ("Trying to read %d of %d bytes from position %d", bcount,
+      rid->length, rid->position);
+
   if (to_read > 0) {
     g_memmove (data, rid->buffer + rid->position, to_read);
     rid->position += to_read;
     return to_read;
   } else {
+    GST_WARNING ("Couldn't read %d bytes");
     return 0;
   }
 }
@@ -47,20 +51,23 @@ gst_wavpack_stream_reader_read_bytes (void *id, void *data, int32_t bcount)
 static uint32_t
 gst_wavpack_stream_reader_get_pos (void *id)
 {
+  GST_DEBUG ("Returning position %d", ((read_id *) id)->position);
   return ((read_id *) id)->position;
 }
 
 static int
 gst_wavpack_stream_reader_set_pos_abs (void *id, uint32_t pos)
 {
-  GST_DEBUG ("should not be called");
+  GST_WARNING ("Should not be called: tried to set absolute position to %d",
+      pos);
   return -1;
 }
 
 static int
 gst_wavpack_stream_reader_set_pos_rel (void *id, int32_t delta, int mode)
 {
-  GST_DEBUG ("should not be called");
+  GST_WARNING ("Should not be called: tried to set relative position to %d"
+      " with mode %d", delta, mode);
   return -1;
 }
 
@@ -69,6 +76,8 @@ gst_wavpack_stream_reader_push_back_byte (void *id, int c)
 {
   read_id *rid = (read_id *) id;
 
+  GST_DEBUG ("Pushing back one byte: 0x%x", c);
+
   rid->position -= 1;
   if (rid->position < 0)
     rid->position = 0;
@@ -78,19 +87,22 @@ gst_wavpack_stream_reader_push_back_byte (void *id, int c)
 static uint32_t
 gst_wavpack_stream_reader_get_length (void *id)
 {
+  GST_DEBUG ("Returning length %d", ((read_id *) id)->length);
+
   return ((read_id *) id)->length;
 }
 
 static int
 gst_wavpack_stream_reader_can_seek (void *id)
 {
+  GST_DEBUG ("Can't seek");
   return FALSE;
 }
 
 static int32_t
 gst_wavpack_stream_reader_write_bytes (void *id, void *data, int32_t bcount)
 {
-  GST_DEBUG ("should not be called");
+  GST_WARNING ("Should not be called, tried to write %d bytes", bcount);
   return 0;
 }