ext/wavpack/: Add support for encoding, parsing and decoding multichannel files with...
[platform/upstream/gst-plugins-good.git] / ext / wavpack / gstwavpackenc.c
1 /* GStreamer Wavpack encoder plugin
2  * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.org>
3  *
4  * gstwavpackdec.c: Wavpack audio encoder
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-wavpackenc
24  *
25  * <refsect2>
26  * WavpackEnc encodes raw audio into a framed Wavpack stream.
27  * <ulink url="http://www.wavpack.com/">Wavpack</ulink> is an open-source
28  * audio codec that features both lossless and lossy encoding.
29  * <title>Example launch line</title>
30  * <para>
31  * <programlisting>
32  * gst-launch audiotestsrc num-buffers=500 ! audioconvert ! wavpackenc ! filesink location=sinewave.wv
33  * </programlisting>
34  * This pipeline encodes audio from audiotestsrc into a Wavpack file. The audioconvert element is needed
35  * as the Wavpack encoder only accepts input with 32 bit width (and every depth between 1 and 32 bits).
36  * </para>
37  * <para>
38  * <programlisting>
39  * gst-launch cdda://1 ! audioconvert ! wavpackenc ! filesink location=track1.wv
40  * </programlisting>
41  * This pipeline encodes audio from an audio CD into a Wavpack file using
42  * lossless encoding (the file output will be fairly large).
43  * </para>
44  * <para>
45  * <programlisting>
46  * gst-launch cdda://1 ! audioconvert ! wavpackenc bitrate=128000 ! filesink location=track1.wv
47  * </programlisting>
48  * This pipeline encodes audio from an audio CD into a Wavpack file using
49  * lossy encoding at a certain bitrate (the file will be fairly small).
50  * </para>
51  * </refsect2>
52  */
53
54 /*
55  * TODO: - add 32 bit float mode. CONFIG_FLOAT_DATA
56  */
57
58 #include <string.h>
59 #include <gst/gst.h>
60 #include <glib/gprintf.h>
61
62 #include <wavpack/wavpack.h>
63 #include "gstwavpackenc.h"
64 #include "gstwavpackcommon.h"
65 #include "md5.h"
66
67 static GstFlowReturn gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buffer);
68 static gboolean gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps);
69 static int gst_wavpack_enc_push_block (void *id, void *data, int32_t count);
70 static gboolean gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event);
71 static GstStateChangeReturn gst_wavpack_enc_change_state (GstElement * element,
72     GstStateChange transition);
73 static void gst_wavpack_enc_set_property (GObject * object, guint prop_id,
74     const GValue * value, GParamSpec * pspec);
75 static void gst_wavpack_enc_get_property (GObject * object, guint prop_id,
76     GValue * value, GParamSpec * pspec);
77
78 enum
79 {
80   ARG_0,
81   ARG_MODE,
82   ARG_BITRATE,
83   ARG_BITSPERSAMPLE,
84   ARG_CORRECTION_MODE,
85   ARG_MD5,
86   ARG_EXTRA_PROCESSING,
87   ARG_JOINT_STEREO_MODE
88 };
89
90 GST_DEBUG_CATEGORY_STATIC (gst_wavpack_enc_debug);
91 #define GST_CAT_DEFAULT gst_wavpack_enc_debug
92
93 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
94     GST_PAD_SINK,
95     GST_PAD_ALWAYS,
96     GST_STATIC_CAPS ("audio/x-raw-int, "
97         "width = (int) 32, "
98         "depth = (int) [ 1, 32], "
99         "endianness = (int) BYTE_ORDER, "
100         "channels = (int) [ 1, 8 ], "
101         "rate = (int) [ 6000, 192000 ]," "signed = (boolean) TRUE")
102     );
103
104 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
105     GST_PAD_SRC,
106     GST_PAD_ALWAYS,
107     GST_STATIC_CAPS ("audio/x-wavpack, "
108         "width = (int) [ 1, 32 ], "
109         "channels = (int) [ 1, 2 ], "
110         "rate = (int) [ 6000, 192000 ], " "framed = (boolean) TRUE")
111     );
112
113 static GstStaticPadTemplate wvcsrc_factory = GST_STATIC_PAD_TEMPLATE ("wvcsrc",
114     GST_PAD_SRC,
115     GST_PAD_SOMETIMES,
116     GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) TRUE")
117     );
118
119 enum
120 {
121   GST_WAVPACK_ENC_MODE_VERY_FAST = 0,
122   GST_WAVPACK_ENC_MODE_FAST,
123   GST_WAVPACK_ENC_MODE_DEFAULT,
124   GST_WAVPACK_ENC_MODE_HIGH,
125   GST_WAVPACK_ENC_MODE_VERY_HIGH
126 };
127
128 #define GST_TYPE_WAVPACK_ENC_MODE (gst_wavpack_enc_mode_get_type ())
129 static GType
130 gst_wavpack_enc_mode_get_type (void)
131 {
132   static GType qtype = 0;
133
134   if (qtype == 0) {
135     static const GEnumValue values[] = {
136 #if 0
137       /* Very Fast Compression is not supported yet, but will be supported
138        * in future wavpack versions */
139       {GST_WAVPACK_ENC_MODE_VERY_FAST, "Very Fast Compression", "veryfast"},
140 #endif
141       {GST_WAVPACK_ENC_MODE_FAST, "Fast Compression", "fast"},
142       {GST_WAVPACK_ENC_MODE_DEFAULT, "Normal Compression", "normal"},
143       {GST_WAVPACK_ENC_MODE_HIGH, "High Compression", "high"},
144 #ifndef WAVPACK_OLD_API
145       {GST_WAVPACK_ENC_MODE_VERY_HIGH, "Very High Compression", "veryhigh"},
146 #endif
147       {0, NULL, NULL}
148     };
149
150     qtype = g_enum_register_static ("GstWavpackEncMode", values);
151   }
152   return qtype;
153 }
154
155 enum
156 {
157   GST_WAVPACK_CORRECTION_MODE_OFF = 0,
158   GST_WAVPACK_CORRECTION_MODE_ON,
159   GST_WAVPACK_CORRECTION_MODE_OPTIMIZED
160 };
161
162 #define GST_TYPE_WAVPACK_ENC_CORRECTION_MODE (gst_wavpack_enc_correction_mode_get_type ())
163 static GType
164 gst_wavpack_enc_correction_mode_get_type (void)
165 {
166   static GType qtype = 0;
167
168   if (qtype == 0) {
169     static const GEnumValue values[] = {
170       {GST_WAVPACK_CORRECTION_MODE_OFF, "Create no correction file", "off"},
171       {GST_WAVPACK_CORRECTION_MODE_ON, "Create correction file", "on"},
172       {GST_WAVPACK_CORRECTION_MODE_OPTIMIZED,
173           "Create optimized correction file", "optimized"},
174       {0, NULL, NULL}
175     };
176
177     qtype = g_enum_register_static ("GstWavpackEncCorrectionMode", values);
178   }
179   return qtype;
180 }
181
182 enum
183 {
184   GST_WAVPACK_JS_MODE_AUTO = 0,
185   GST_WAVPACK_JS_MODE_LEFT_RIGHT,
186   GST_WAVPACK_JS_MODE_MID_SIDE
187 };
188
189 #define GST_TYPE_WAVPACK_ENC_JOINT_STEREO_MODE (gst_wavpack_enc_joint_stereo_mode_get_type ())
190 static GType
191 gst_wavpack_enc_joint_stereo_mode_get_type (void)
192 {
193   static GType qtype = 0;
194
195   if (qtype == 0) {
196     static const GEnumValue values[] = {
197       {GST_WAVPACK_JS_MODE_AUTO, "auto", "auto"},
198       {GST_WAVPACK_JS_MODE_LEFT_RIGHT, "left/right", "leftright"},
199       {GST_WAVPACK_JS_MODE_MID_SIDE, "mid/side", "midside"},
200       {0, NULL, NULL}
201     };
202
203     qtype = g_enum_register_static ("GstWavpackEncJSMode", values);
204   }
205   return qtype;
206 }
207
208 GST_BOILERPLATE (GstWavpackEnc, gst_wavpack_enc, GstElement, GST_TYPE_ELEMENT);
209
210 static void
211 gst_wavpack_enc_base_init (gpointer klass)
212 {
213   static const GstElementDetails element_details = {
214     "Wavpack audio encoder",
215     "Codec/Encoder/Audio",
216     "Encodes audio with the Wavpack lossless/lossy audio codec",
217     "Sebastian Dröge <slomo@circular-chaos.org>"
218   };
219   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
220
221   /* add pad templates */
222   gst_element_class_add_pad_template (element_class,
223       gst_static_pad_template_get (&sink_factory));
224   gst_element_class_add_pad_template (element_class,
225       gst_static_pad_template_get (&src_factory));
226   gst_element_class_add_pad_template (element_class,
227       gst_static_pad_template_get (&wvcsrc_factory));
228
229   /* set element details */
230   gst_element_class_set_details (element_class, &element_details);
231 }
232
233
234 static void
235 gst_wavpack_enc_class_init (GstWavpackEncClass * klass)
236 {
237   GObjectClass *gobject_class = (GObjectClass *) klass;
238   GstElementClass *gstelement_class = (GstElementClass *) klass;
239
240   parent_class = g_type_class_peek_parent (klass);
241
242   /* set state change handler */
243   gstelement_class->change_state =
244       GST_DEBUG_FUNCPTR (gst_wavpack_enc_change_state);
245
246   /* set property handlers */
247   gobject_class->set_property =
248       GST_DEBUG_FUNCPTR (gst_wavpack_enc_set_property);
249   gobject_class->get_property =
250       GST_DEBUG_FUNCPTR (gst_wavpack_enc_get_property);
251
252   /* install all properties */
253   g_object_class_install_property (gobject_class, ARG_MODE,
254       g_param_spec_enum ("mode", "Encoding mode",
255           "Speed versus compression tradeoff.",
256           GST_TYPE_WAVPACK_ENC_MODE, GST_WAVPACK_ENC_MODE_DEFAULT,
257           G_PARAM_READWRITE));
258   g_object_class_install_property (gobject_class, ARG_BITRATE,
259       g_param_spec_uint ("bitrate", "Bitrate",
260           "Try to encode with this average bitrate (bits/sec). "
261           "This enables lossy encoding, values smaller than 24000 disable it again.",
262           0, 9600000, 0, G_PARAM_READWRITE));
263   g_object_class_install_property (gobject_class, ARG_BITSPERSAMPLE,
264       g_param_spec_double ("bits-per-sample", "Bits per sample",
265           "Try to encode with this amount of bits per sample. "
266           "This enables lossy encoding, values smaller than 2.0 disable it again.",
267           0.0, 24.0, 0.0, G_PARAM_READWRITE));
268   g_object_class_install_property (gobject_class, ARG_CORRECTION_MODE,
269       g_param_spec_enum ("correction-mode", "Correction stream mode",
270           "Use this mode for the correction stream. Only works in lossy mode!",
271           GST_TYPE_WAVPACK_ENC_CORRECTION_MODE, GST_WAVPACK_CORRECTION_MODE_OFF,
272           G_PARAM_READWRITE));
273   g_object_class_install_property (gobject_class, ARG_MD5,
274       g_param_spec_boolean ("md5", "MD5",
275           "Store MD5 hash of raw samples within the file.", FALSE,
276           G_PARAM_READWRITE));
277   g_object_class_install_property (gobject_class, ARG_EXTRA_PROCESSING,
278       g_param_spec_uint ("extra-processing", "Extra processing",
279           "Use better but slower filters for better compression/quality.",
280           0, 6, 0, G_PARAM_READWRITE));
281   g_object_class_install_property (gobject_class, ARG_JOINT_STEREO_MODE,
282       g_param_spec_enum ("joint-stereo-mode", "Joint-Stereo mode",
283           "Use this joint-stereo mode.", GST_TYPE_WAVPACK_ENC_JOINT_STEREO_MODE,
284           GST_WAVPACK_JS_MODE_AUTO, G_PARAM_READWRITE));
285 }
286
287 static void
288 gst_wavpack_enc_reset (GstWavpackEnc * enc)
289 {
290   /* close and free everything stream related if we already did something */
291   if (enc->wp_context) {
292     WavpackCloseFile (enc->wp_context);
293     enc->wp_context = NULL;
294   }
295   if (enc->wp_config) {
296     g_free (enc->wp_config);
297     enc->wp_config = NULL;
298   }
299   if (enc->first_block) {
300     g_free (enc->first_block);
301     enc->first_block = NULL;
302   }
303   enc->first_block_size = 0;
304   if (enc->md5_context) {
305     g_free (enc->md5_context);
306     enc->md5_context = NULL;
307   }
308
309   if (enc->pending_buffer) {
310     gst_buffer_unref (enc->pending_buffer);
311     enc->pending_buffer = NULL;
312     enc->pending_offset = 0;
313   }
314
315   /* reset the last returns to GST_FLOW_OK. This is only set to something else
316    * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
317    * so not valid anymore */
318   enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
319
320   /* reset stream information */
321   enc->samplerate = 0;
322   enc->depth = 0;
323   enc->channels = 0;
324   enc->channel_mask = 0;
325   enc->need_channel_remap = FALSE;
326 }
327
328 static void
329 gst_wavpack_enc_init (GstWavpackEnc * enc, GstWavpackEncClass * gclass)
330 {
331   enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
332   gst_pad_set_setcaps_function (enc->sinkpad,
333       GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_set_caps));
334   gst_pad_set_chain_function (enc->sinkpad,
335       GST_DEBUG_FUNCPTR (gst_wavpack_enc_chain));
336   gst_pad_set_event_function (enc->sinkpad,
337       GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_event));
338   gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
339
340   /* setup src pad */
341   enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
342   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
343
344   /* initialize object attributes */
345   enc->wp_config = NULL;
346   enc->wp_context = NULL;
347   enc->first_block = NULL;
348   enc->md5_context = NULL;
349   gst_wavpack_enc_reset (enc);
350
351   enc->wv_id.correction = FALSE;
352   enc->wv_id.wavpack_enc = enc;
353   enc->wv_id.passthrough = FALSE;
354   enc->wvc_id.correction = TRUE;
355   enc->wvc_id.wavpack_enc = enc;
356   enc->wvc_id.passthrough = FALSE;
357
358   /* set default values of params */
359   enc->mode = GST_WAVPACK_ENC_MODE_DEFAULT;
360   enc->bitrate = 0;
361   enc->bps = 0.0;
362   enc->correction_mode = GST_WAVPACK_CORRECTION_MODE_OFF;
363   enc->md5 = FALSE;
364   enc->extra_processing = 0;
365   enc->joint_stereo_mode = GST_WAVPACK_JS_MODE_AUTO;
366 }
367
368 static gboolean
369 gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
370 {
371   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
372   GstStructure *structure = gst_caps_get_structure (caps, 0);
373   GstAudioChannelPosition *pos;
374
375   if (!gst_structure_get_int (structure, "channels", &enc->channels) ||
376       !gst_structure_get_int (structure, "rate", &enc->samplerate) ||
377       !gst_structure_get_int (structure, "depth", &enc->depth)) {
378     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
379         ("got invalid caps: %" GST_PTR_FORMAT, caps));
380     gst_object_unref (enc);
381     return FALSE;
382   }
383
384   pos = gst_audio_get_channel_positions (structure);
385   /* If one channel is NONE they'll be all undefined */
386   if (pos != NULL && pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) {
387     g_free (pos);
388     pos = NULL;
389   }
390
391   if (pos == NULL) {
392     GST_ELEMENT_ERROR (enc, STREAM, FORMAT, (NULL),
393         ("input has no valid channel layout"));
394
395     gst_object_unref (enc);
396     return FALSE;
397   }
398
399   enc->channel_mask =
400       gst_wavpack_get_channel_mask_from_positions (pos, enc->channels);
401   enc->need_channel_remap =
402       gst_wavpack_set_channel_mapping (pos, enc->channels,
403       enc->channel_mapping);
404   g_free (pos);
405
406   /* set fixed src pad caps now that we know what we will get */
407   caps = gst_caps_new_simple ("audio/x-wavpack",
408       "channels", G_TYPE_INT, enc->channels,
409       "rate", G_TYPE_INT, enc->samplerate,
410       "width", G_TYPE_INT, enc->depth, "framed", G_TYPE_BOOLEAN, TRUE, NULL);
411
412   if (!gst_wavpack_set_channel_layout (caps, enc->channel_mask))
413     GST_WARNING_OBJECT (enc, "setting channel layout failed");
414
415   if (!gst_pad_set_caps (enc->srcpad, caps)) {
416     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
417         ("setting caps failed: %" GST_PTR_FORMAT, caps));
418     gst_caps_unref (caps);
419     gst_object_unref (enc);
420     return FALSE;
421   }
422   gst_pad_use_fixed_caps (enc->srcpad);
423
424   gst_caps_unref (caps);
425   gst_object_unref (enc);
426   return TRUE;
427 }
428
429 static void
430 gst_wavpack_enc_set_wp_config (GstWavpackEnc * enc)
431 {
432   enc->wp_config = g_new0 (WavpackConfig, 1);
433   /* set general stream informations in the WavpackConfig */
434   enc->wp_config->bytes_per_sample = GST_ROUND_UP_8 (enc->depth) / 8;
435   enc->wp_config->bits_per_sample = enc->depth;
436   enc->wp_config->num_channels = enc->channels;
437   enc->wp_config->channel_mask = enc->channel_mask;
438   enc->wp_config->sample_rate = enc->samplerate;
439
440   /*
441    * Set parameters in WavpackConfig
442    */
443
444   /* Encoding mode */
445   switch (enc->mode) {
446 #if 0
447     case GST_WAVPACK_ENC_MODE_VERY_FAST:
448       enc->wp_config->flags |= CONFIG_VERY_FAST_FLAG;
449       enc->wp_config->flags |= CONFIG_FAST_FLAG;
450       break;
451 #endif
452     case GST_WAVPACK_ENC_MODE_FAST:
453       enc->wp_config->flags |= CONFIG_FAST_FLAG;
454       break;
455     case GST_WAVPACK_ENC_MODE_DEFAULT:
456       break;
457     case GST_WAVPACK_ENC_MODE_HIGH:
458       enc->wp_config->flags |= CONFIG_HIGH_FLAG;
459       break;
460 #ifndef WAVPACK_OLD_API
461     case GST_WAVPACK_ENC_MODE_VERY_HIGH:
462       enc->wp_config->flags |= CONFIG_HIGH_FLAG;
463       enc->wp_config->flags |= CONFIG_VERY_HIGH_FLAG;
464       break;
465 #endif
466   }
467
468   /* Bitrate, enables lossy mode */
469   if (enc->bitrate) {
470     enc->wp_config->flags |= CONFIG_HYBRID_FLAG;
471     enc->wp_config->flags |= CONFIG_BITRATE_KBPS;
472     enc->wp_config->bitrate = enc->bitrate / 1000.0;
473   } else if (enc->bps) {
474     enc->wp_config->flags |= CONFIG_HYBRID_FLAG;
475     enc->wp_config->bitrate = enc->bps;
476   }
477
478   /* Correction Mode, only in lossy mode */
479   if (enc->wp_config->flags & CONFIG_HYBRID_FLAG) {
480     if (enc->correction_mode > GST_WAVPACK_CORRECTION_MODE_OFF) {
481       enc->wvcsrcpad =
482           gst_pad_new_from_static_template (&wvcsrc_factory, "wvcsrc");
483
484       /* try to add correction src pad, don't set correction mode on failure */
485       GstCaps *caps = gst_caps_new_simple ("audio/x-wavpack-correction",
486           "framed", G_TYPE_BOOLEAN, TRUE, NULL);
487
488       GST_DEBUG_OBJECT (enc, "Adding correction pad with caps %"
489           GST_PTR_FORMAT, caps);
490       if (!gst_pad_set_caps (enc->wvcsrcpad, caps)) {
491         enc->correction_mode = 0;
492         GST_WARNING_OBJECT (enc, "setting correction caps failed");
493       } else {
494         gst_pad_use_fixed_caps (enc->wvcsrcpad);
495         gst_pad_set_active (enc->wvcsrcpad, TRUE);
496         gst_element_add_pad (GST_ELEMENT (enc), enc->wvcsrcpad);
497         enc->wp_config->flags |= CONFIG_CREATE_WVC;
498         if (enc->correction_mode == GST_WAVPACK_CORRECTION_MODE_OPTIMIZED) {
499           enc->wp_config->flags |= CONFIG_OPTIMIZE_WVC;
500         }
501       }
502       gst_caps_unref (caps);
503     }
504   } else {
505     if (enc->correction_mode > GST_WAVPACK_CORRECTION_MODE_OFF) {
506       enc->correction_mode = 0;
507       GST_WARNING_OBJECT (enc, "setting correction mode only has "
508           "any effect if a bitrate is provided.");
509     }
510   }
511   gst_element_no_more_pads (GST_ELEMENT (enc));
512
513   /* MD5, setup MD5 context */
514   if ((enc->md5) && !(enc->md5_context)) {
515     enc->wp_config->flags |= CONFIG_MD5_CHECKSUM;
516     enc->md5_context = g_new0 (MD5_CTX, 1);
517     MD5Init (enc->md5_context);
518   }
519
520   /* Extra encode processing */
521   if (enc->extra_processing) {
522     enc->wp_config->flags |= CONFIG_EXTRA_MODE;
523     enc->wp_config->xmode = enc->extra_processing;
524   }
525
526   /* Joint stereo mode */
527   switch (enc->joint_stereo_mode) {
528     case GST_WAVPACK_JS_MODE_AUTO:
529       break;
530     case GST_WAVPACK_JS_MODE_LEFT_RIGHT:
531       enc->wp_config->flags |= CONFIG_JOINT_OVERRIDE;
532       enc->wp_config->flags &= ~CONFIG_JOINT_STEREO;
533       break;
534     case GST_WAVPACK_JS_MODE_MID_SIDE:
535       enc->wp_config->flags |= (CONFIG_JOINT_OVERRIDE | CONFIG_JOINT_STEREO);
536       break;
537   }
538 }
539
540 static int
541 gst_wavpack_enc_push_block (void *id, void *data, int32_t count)
542 {
543   GstWavpackEncWriteID *wid = (GstWavpackEncWriteID *) id;
544   GstWavpackEnc *enc = GST_WAVPACK_ENC (wid->wavpack_enc);
545   GstFlowReturn *flow;
546   GstBuffer *buffer;
547   GstPad *pad;
548   guchar *block = (guchar *) data;
549
550   pad = (wid->correction) ? enc->wvcsrcpad : enc->srcpad;
551   flow =
552       (wid->correction) ? &enc->wvcsrcpad_last_return : &enc->
553       srcpad_last_return;
554
555   *flow = gst_pad_alloc_buffer_and_set_caps (pad, GST_BUFFER_OFFSET_NONE,
556       count, GST_PAD_CAPS (pad), &buffer);
557
558   if (*flow != GST_FLOW_OK) {
559     GST_WARNING_OBJECT (enc, "flow on %s:%s = %s",
560         GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow));
561     return FALSE;
562   }
563
564   g_memmove (GST_BUFFER_DATA (buffer), block, count);
565
566   if (count > sizeof (WavpackHeader) && memcmp (block, "wvpk", 4) == 0) {
567     /* if it's a Wavpack block set buffer timestamp and duration, etc */
568     WavpackHeader wph;
569
570     GST_LOG_OBJECT (enc, "got %d bytes of encoded wavpack %sdata",
571         count, (wid->correction) ? "correction " : "");
572
573     gst_wavpack_read_header (&wph, block);
574
575     /* Only set when pushing the first buffer again, in that case
576      * we don't want to delay the buffer or push newsegment events
577      */
578     if (!wid->passthrough) {
579       /* Only push complete blocks */
580       if (enc->pending_buffer == NULL) {
581         enc->pending_buffer = buffer;
582         enc->pending_offset = wph.block_index;
583       } else if (enc->pending_offset == wph.block_index) {
584         enc->pending_buffer = gst_buffer_join (enc->pending_buffer, buffer);
585       } else {
586         GST_ERROR ("Got incomplete block, dropping");
587         gst_buffer_unref (enc->pending_buffer);
588         enc->pending_buffer = buffer;
589         enc->pending_offset = wph.block_index;
590       }
591
592       if (!(wph.flags & FINAL_BLOCK))
593         return TRUE;
594
595       buffer = enc->pending_buffer;
596       enc->pending_buffer = NULL;
597       enc->pending_offset = 0;
598
599       /* if it's the first wavpack block, send a NEW_SEGMENT event */
600       if (wph.block_index == 0) {
601         gst_pad_push_event (pad,
602             gst_event_new_new_segment (FALSE,
603                 1.0, GST_FORMAT_TIME, 0, GST_BUFFER_OFFSET_NONE, 0));
604
605         /* save header for later reference, so we can re-send it later on
606          * EOS with fixed up values for total sample count etc. */
607         if (enc->first_block == NULL && !wid->correction) {
608           enc->first_block =
609               g_memdup (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
610           enc->first_block_size = GST_BUFFER_SIZE (buffer);
611         }
612       }
613     }
614
615     /* set buffer timestamp, duration, offset, offset_end from
616      * the wavpack header */
617     GST_BUFFER_TIMESTAMP (buffer) =
618         gst_util_uint64_scale_int (GST_SECOND, wph.block_index,
619         enc->samplerate);
620     GST_BUFFER_DURATION (buffer) =
621         gst_util_uint64_scale_int (GST_SECOND, wph.block_samples,
622         enc->samplerate);
623     GST_BUFFER_OFFSET (buffer) = wph.block_index;
624     GST_BUFFER_OFFSET_END (buffer) = wph.block_index + wph.block_samples;
625   } else {
626     /* if it's something else set no timestamp and duration on the buffer */
627     GST_DEBUG_OBJECT (enc, "got %d bytes of unknown data", count);
628
629     GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
630     GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
631   }
632
633   /* push the buffer and forward errors */
634   GST_DEBUG_OBJECT (enc, "pushing buffer with %d bytes",
635       GST_BUFFER_SIZE (buffer));
636   *flow = gst_pad_push (pad, buffer);
637
638   if (*flow != GST_FLOW_OK) {
639     GST_WARNING_OBJECT (enc, "flow on %s:%s = %s",
640         GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow));
641     return FALSE;
642   }
643
644   return TRUE;
645 }
646
647 static void
648 gst_wavpack_enc_fix_channel_order (GstWavpackEnc * enc, gint32 * data,
649     gint nsamples)
650 {
651   gint i, j;
652   gint32 tmp[8];
653
654   for (i = 0; i < nsamples / enc->channels; i++) {
655     for (j = 0; j < enc->channels; j++) {
656       tmp[enc->channel_mapping[j]] = data[j];
657     }
658     for (j = 0; j < enc->channels; j++) {
659       data[j] = tmp[j];
660     }
661     data += enc->channels;
662   }
663 }
664
665 static GstFlowReturn
666 gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf)
667 {
668   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
669   uint32_t sample_count = GST_BUFFER_SIZE (buf) / 4;
670   GstFlowReturn ret;
671
672   /* reset the last returns to GST_FLOW_OK. This is only set to something else
673    * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
674    * so not valid anymore */
675   enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
676
677   GST_DEBUG ("got %u raw samples", sample_count);
678
679   /* check if we already have a valid WavpackContext, otherwise make one */
680   if (!enc->wp_context) {
681     /* create raw context */
682     enc->wp_context =
683         WavpackOpenFileOutput (gst_wavpack_enc_push_block, &enc->wv_id,
684         (enc->correction_mode > 0) ? &enc->wvc_id : NULL);
685     if (!enc->wp_context) {
686       GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
687           ("error creating Wavpack context"));
688       gst_object_unref (enc);
689       gst_buffer_unref (buf);
690       return GST_FLOW_ERROR;
691     }
692
693     /* set the WavpackConfig according to our parameters */
694     gst_wavpack_enc_set_wp_config (enc);
695
696     /* set the configuration to the context now that we know everything
697      * and initialize the encoder */
698     if (!WavpackSetConfiguration (enc->wp_context,
699             enc->wp_config, (uint32_t) (-1))
700         || !WavpackPackInit (enc->wp_context)) {
701       GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL),
702           ("error setting up wavpack encoding context"));
703       WavpackCloseFile (enc->wp_context);
704       gst_object_unref (enc);
705       gst_buffer_unref (buf);
706       return GST_FLOW_ERROR;
707     }
708     GST_DEBUG ("setup of encoding context successfull");
709   }
710
711   if (enc->need_channel_remap) {
712     buf = gst_buffer_make_writable (buf);
713     gst_wavpack_enc_fix_channel_order (enc, (gint32 *) GST_BUFFER_DATA (buf),
714         sample_count);
715   }
716
717   /* if we want to append the MD5 sum to the stream update it here
718    * with the current raw samples */
719   if (enc->md5) {
720     MD5Update (enc->md5_context, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
721   }
722
723   /* encode and handle return values from encoding */
724   if (WavpackPackSamples (enc->wp_context, (int32_t *) GST_BUFFER_DATA (buf),
725           sample_count / enc->channels)) {
726     GST_DEBUG ("encoding samples successful");
727     ret = GST_FLOW_OK;
728   } else {
729     if ((enc->srcpad_last_return == GST_FLOW_RESEND) ||
730         (enc->wvcsrcpad_last_return == GST_FLOW_RESEND)) {
731       ret = GST_FLOW_RESEND;
732     } else if ((enc->srcpad_last_return == GST_FLOW_OK) ||
733         (enc->wvcsrcpad_last_return == GST_FLOW_OK)) {
734       ret = GST_FLOW_OK;
735     } else if ((enc->srcpad_last_return == GST_FLOW_NOT_LINKED) &&
736         (enc->wvcsrcpad_last_return == GST_FLOW_NOT_LINKED)) {
737       ret = GST_FLOW_NOT_LINKED;
738     } else if ((enc->srcpad_last_return == GST_FLOW_WRONG_STATE) &&
739         (enc->wvcsrcpad_last_return == GST_FLOW_WRONG_STATE)) {
740       ret = GST_FLOW_WRONG_STATE;
741     } else {
742       GST_ELEMENT_ERROR (enc, LIBRARY, ENCODE, (NULL),
743           ("encoding samples failed"));
744       ret = GST_FLOW_ERROR;
745     }
746   }
747
748   gst_buffer_unref (buf);
749   gst_object_unref (enc);
750   return ret;
751 }
752
753 static void
754 gst_wavpack_enc_rewrite_first_block (GstWavpackEnc * enc)
755 {
756   GstEvent *event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES,
757       0, GST_BUFFER_OFFSET_NONE, 0);
758   gboolean ret;
759
760   g_return_if_fail (enc);
761   g_return_if_fail (enc->first_block);
762
763   /* update the sample count in the first block */
764   WavpackUpdateNumSamples (enc->wp_context, enc->first_block);
765
766   /* try to seek to the beginning of the output */
767   ret = gst_pad_push_event (enc->srcpad, event);
768   if (ret) {
769     /* try to rewrite the first block */
770     GST_DEBUG_OBJECT (enc, "rewriting first block ...");
771     enc->wv_id.passthrough = TRUE;
772     ret = gst_wavpack_enc_push_block (&enc->wv_id,
773         enc->first_block, enc->first_block_size);
774     enc->wv_id.passthrough = FALSE;
775   } else {
776     GST_WARNING_OBJECT (enc, "rewriting of first block failed. "
777         "Seeking to first block failed!");
778   }
779 }
780
781 static gboolean
782 gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event)
783 {
784   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
785   gboolean ret = TRUE;
786
787   GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event));
788
789   switch (GST_EVENT_TYPE (event)) {
790     case GST_EVENT_EOS:
791       /* Encode all remaining samples and flush them to the src pads */
792       WavpackFlushSamples (enc->wp_context);
793
794       /* Drop all remaining data, this is no complete block otherwise
795        * it would've been pushed already */
796       if (enc->pending_buffer) {
797         gst_object_unref (enc->pending_buffer);
798         enc->pending_buffer = NULL;
799         enc->pending_offset = 0;
800       }
801
802       /* write the MD5 sum if we have to write one */
803       if ((enc->md5) && (enc->md5_context)) {
804         guchar md5_digest[16];
805
806         MD5Final (md5_digest, enc->md5_context);
807         WavpackStoreMD5Sum (enc->wp_context, md5_digest);
808       }
809
810       /* Try to rewrite the first frame with the correct sample number */
811       if (enc->first_block)
812         gst_wavpack_enc_rewrite_first_block (enc);
813
814       /* close the context if not already happened */
815       if (enc->wp_context) {
816         WavpackCloseFile (enc->wp_context);
817         enc->wp_context = NULL;
818       }
819
820       ret = gst_pad_event_default (pad, event);
821       break;
822     case GST_EVENT_NEWSEGMENT:
823       if (enc->wp_context) {
824         GST_WARNING_OBJECT (enc, "got NEWSEGMENT after encoding "
825             "already started");
826       }
827       /* drop NEWSEGMENT events, we create our own when pushing
828        * the first buffer to the pads */
829       gst_event_unref (event);
830       ret = TRUE;
831       break;
832     default:
833       ret = gst_pad_event_default (pad, event);
834       break;
835   }
836
837   gst_object_unref (enc);
838   return ret;
839 }
840
841 static GstStateChangeReturn
842 gst_wavpack_enc_change_state (GstElement * element, GstStateChange transition)
843 {
844   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
845   GstWavpackEnc *enc = GST_WAVPACK_ENC (element);
846
847   switch (transition) {
848     case GST_STATE_CHANGE_NULL_TO_READY:
849       /* set the last returned GstFlowReturns of the two pads to GST_FLOW_OK
850        * as they're only set to something else in WavpackPackSamples() or more
851        * specific gst_wavpack_enc_push_block() and nothing happened there yet */
852       enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
853       break;
854     case GST_STATE_CHANGE_READY_TO_PAUSED:
855       break;
856     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
857     default:
858       break;
859   }
860
861   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
862
863   switch (transition) {
864     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
865       break;
866     case GST_STATE_CHANGE_PAUSED_TO_READY:
867       gst_wavpack_enc_reset (enc);
868       break;
869     case GST_STATE_CHANGE_READY_TO_NULL:
870       break;
871     default:
872       break;
873   }
874
875   return ret;
876 }
877
878 static void
879 gst_wavpack_enc_set_property (GObject * object, guint prop_id,
880     const GValue * value, GParamSpec * pspec)
881 {
882   GstWavpackEnc *enc = GST_WAVPACK_ENC (object);
883
884   switch (prop_id) {
885     case ARG_MODE:
886       enc->mode = g_value_get_enum (value);
887       break;
888     case ARG_BITRATE:{
889       guint val = g_value_get_uint (value);
890
891       if ((val >= 24000) && (val <= 9600000)) {
892         enc->bitrate = val;
893         enc->bps = 0.0;
894       } else {
895         enc->bitrate = 0;
896         enc->bps = 0.0;
897       }
898       break;
899     }
900     case ARG_BITSPERSAMPLE:{
901       gdouble val = g_value_get_double (value);
902
903       if ((val >= 2.0) && (val <= 24.0)) {
904         enc->bps = val;
905         enc->bitrate = 0;
906       } else {
907         enc->bps = 0.0;
908         enc->bitrate = 0;
909       }
910       break;
911     }
912     case ARG_CORRECTION_MODE:
913       enc->correction_mode = g_value_get_enum (value);
914       break;
915     case ARG_MD5:
916       enc->md5 = g_value_get_boolean (value);
917       break;
918     case ARG_EXTRA_PROCESSING:
919       enc->extra_processing = g_value_get_uint (value);
920       break;
921     case ARG_JOINT_STEREO_MODE:
922       enc->joint_stereo_mode = g_value_get_enum (value);
923       break;
924     default:
925       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
926       break;
927   }
928 }
929
930 static void
931 gst_wavpack_enc_get_property (GObject * object, guint prop_id, GValue * value,
932     GParamSpec * pspec)
933 {
934   GstWavpackEnc *enc = GST_WAVPACK_ENC (object);
935
936   switch (prop_id) {
937     case ARG_MODE:
938       g_value_set_enum (value, enc->mode);
939       break;
940     case ARG_BITRATE:
941       if (enc->bps == 0.0) {
942         g_value_set_uint (value, enc->bitrate);
943       } else {
944         g_value_set_uint (value, 0);
945       }
946       break;
947     case ARG_BITSPERSAMPLE:
948       if (enc->bitrate == 0) {
949         g_value_set_double (value, enc->bps);
950       } else {
951         g_value_set_double (value, 0.0);
952       }
953       break;
954     case ARG_CORRECTION_MODE:
955       g_value_set_enum (value, enc->correction_mode);
956       break;
957     case ARG_MD5:
958       g_value_set_boolean (value, enc->md5);
959       break;
960     case ARG_EXTRA_PROCESSING:
961       g_value_set_uint (value, enc->extra_processing);
962       break;
963     case ARG_JOINT_STEREO_MODE:
964       g_value_set_enum (value, enc->joint_stereo_mode);
965       break;
966     default:
967       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
968       break;
969   }
970 }
971
972 gboolean
973 gst_wavpack_enc_plugin_init (GstPlugin * plugin)
974 {
975   if (!gst_element_register (plugin, "wavpackenc",
976           GST_RANK_NONE, GST_TYPE_WAVPACK_ENC))
977     return FALSE;
978
979   GST_DEBUG_CATEGORY_INIT (gst_wavpack_enc_debug, "wavpack_enc", 0,
980       "Wavpack encoder");
981
982   return TRUE;
983 }