From ded2cc6e39501f249ac16b88f18c1a92e136b95a Mon Sep 17 00:00:00 2001 From: Sebastian Dröge Date: Tue, 20 Nov 2007 13:08:45 +0000 Subject: ext/wavpack/: Add support for encoding, parsing and decoding multichannel files with up to 8 channels. This also impr... 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. --- ext/wavpack/gstwavpackcommon.c | 183 +++++++++++++++++++++++++++++++++++ ext/wavpack/gstwavpackcommon.h | 6 ++ ext/wavpack/gstwavpackdec.c | 48 ++++++++- ext/wavpack/gstwavpackdec.h | 1 + ext/wavpack/gstwavpackenc.c | 146 +++++++++++++++++++++------- ext/wavpack/gstwavpackenc.h | 7 ++ ext/wavpack/gstwavpackparse.c | 69 +++++++++++-- ext/wavpack/gstwavpackparse.h | 3 + ext/wavpack/gstwavpackstreamreader.c | 18 +++- 9 files changed, 432 insertions(+), 49 deletions(-) (limited to 'ext/wavpack') diff --git a/ext/wavpack/gstwavpackcommon.c b/ext/wavpack/gstwavpackcommon.c index ad161867..4fa3e121 100644 --- a/ext/wavpack/gstwavpackcommon.c +++ b/ext/wavpack/gstwavpackcommon.c @@ -28,6 +28,9 @@ #include "gstwavpackcommon.h" #include +#include +#include + 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; +} diff --git a/ext/wavpack/gstwavpackcommon.h b/ext/wavpack/gstwavpackcommon.h index 3cf6e56c..6a9e5160 100644 --- a/ext/wavpack/gstwavpackcommon.h +++ b/ext/wavpack/gstwavpackcommon.h @@ -24,6 +24,7 @@ #define __GST_WAVPACK_COMMON_H__ #include +#include #include 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 diff --git a/ext/wavpack/gstwavpackdec.c b/ext/wavpack/gstwavpackdec.c index c4aa1d04..0dde6c7d 100644 --- a/ext/wavpack/gstwavpackdec.c +++ b/ext/wavpack/gstwavpackdec.c @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -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; diff --git a/ext/wavpack/gstwavpackdec.h b/ext/wavpack/gstwavpackdec.h index 007e7c22..eb6e4c3b 100644 --- a/ext/wavpack/gstwavpackdec.h +++ b/ext/wavpack/gstwavpackdec.h @@ -62,6 +62,7 @@ struct _GstWavpackDec gint sample_rate; gint depth; gint channels; + gint channel_mask; gint error_count; }; diff --git a/ext/wavpack/gstwavpackenc.c b/ext/wavpack/gstwavpackenc.c index 5562ea76..1c8a2f51 100644 --- a/ext/wavpack/gstwavpackenc.c +++ b/ext/wavpack/gstwavpackenc.c @@ -52,21 +52,7 @@ */ /* - * 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 @@ -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]; diff --git a/ext/wavpack/gstwavpackenc.h b/ext/wavpack/gstwavpackenc.h index 243e8d7b..dbafcd19 100644 --- a/ext/wavpack/gstwavpackenc.h +++ b/ext/wavpack/gstwavpackenc.h @@ -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 diff --git a/ext/wavpack/gstwavpackparse.c b/ext/wavpack/gstwavpackparse.c index 3ab84b4c..71c4d83d 100644 --- a/ext/wavpack/gstwavpackparse.c +++ b/ext/wavpack/gstwavpackparse.c @@ -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) diff --git a/ext/wavpack/gstwavpackparse.h b/ext/wavpack/gstwavpackparse.h index d1c76365..67d093fa 100644 --- a/ext/wavpack/gstwavpackparse.h +++ b/ext/wavpack/gstwavpackparse.h @@ -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 */ diff --git a/ext/wavpack/gstwavpackstreamreader.c b/ext/wavpack/gstwavpackstreamreader.c index 9b6260b8..868f7bfa 100644 --- a/ext/wavpack/gstwavpackstreamreader.c +++ b/ext/wavpack/gstwavpackstreamreader.c @@ -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; } -- cgit