ext/wavpack/: Use GSlice for allocating index entries and use gst_element_class_set_d...
[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   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
214
215   /* add pad templates */
216   gst_element_class_add_pad_template (element_class,
217       gst_static_pad_template_get (&sink_factory));
218   gst_element_class_add_pad_template (element_class,
219       gst_static_pad_template_get (&src_factory));
220   gst_element_class_add_pad_template (element_class,
221       gst_static_pad_template_get (&wvcsrc_factory));
222
223   /* set element details */
224   gst_element_class_set_details_simple (element_class, "Wavpack audio encoder",
225       "Codec/Encoder/Audio",
226       "Encodes audio with the Wavpack lossless/lossy audio codec",
227       "Sebastian Dröge <slomo@circular-chaos.org>");
228 }
229
230
231 static void
232 gst_wavpack_enc_class_init (GstWavpackEncClass * klass)
233 {
234   GObjectClass *gobject_class = (GObjectClass *) klass;
235   GstElementClass *gstelement_class = (GstElementClass *) klass;
236
237   parent_class = g_type_class_peek_parent (klass);
238
239   /* set state change handler */
240   gstelement_class->change_state =
241       GST_DEBUG_FUNCPTR (gst_wavpack_enc_change_state);
242
243   /* set property handlers */
244   gobject_class->set_property =
245       GST_DEBUG_FUNCPTR (gst_wavpack_enc_set_property);
246   gobject_class->get_property =
247       GST_DEBUG_FUNCPTR (gst_wavpack_enc_get_property);
248
249   /* install all properties */
250   g_object_class_install_property (gobject_class, ARG_MODE,
251       g_param_spec_enum ("mode", "Encoding mode",
252           "Speed versus compression tradeoff.",
253           GST_TYPE_WAVPACK_ENC_MODE, GST_WAVPACK_ENC_MODE_DEFAULT,
254           G_PARAM_READWRITE));
255   g_object_class_install_property (gobject_class, ARG_BITRATE,
256       g_param_spec_uint ("bitrate", "Bitrate",
257           "Try to encode with this average bitrate (bits/sec). "
258           "This enables lossy encoding, values smaller than 24000 disable it again.",
259           0, 9600000, 0, G_PARAM_READWRITE));
260   g_object_class_install_property (gobject_class, ARG_BITSPERSAMPLE,
261       g_param_spec_double ("bits-per-sample", "Bits per sample",
262           "Try to encode with this amount of bits per sample. "
263           "This enables lossy encoding, values smaller than 2.0 disable it again.",
264           0.0, 24.0, 0.0, G_PARAM_READWRITE));
265   g_object_class_install_property (gobject_class, ARG_CORRECTION_MODE,
266       g_param_spec_enum ("correction-mode", "Correction stream mode",
267           "Use this mode for the correction stream. Only works in lossy mode!",
268           GST_TYPE_WAVPACK_ENC_CORRECTION_MODE, GST_WAVPACK_CORRECTION_MODE_OFF,
269           G_PARAM_READWRITE));
270   g_object_class_install_property (gobject_class, ARG_MD5,
271       g_param_spec_boolean ("md5", "MD5",
272           "Store MD5 hash of raw samples within the file.", FALSE,
273           G_PARAM_READWRITE));
274   g_object_class_install_property (gobject_class, ARG_EXTRA_PROCESSING,
275       g_param_spec_uint ("extra-processing", "Extra processing",
276           "Use better but slower filters for better compression/quality.",
277           0, 6, 0, G_PARAM_READWRITE));
278   g_object_class_install_property (gobject_class, ARG_JOINT_STEREO_MODE,
279       g_param_spec_enum ("joint-stereo-mode", "Joint-Stereo mode",
280           "Use this joint-stereo mode.", GST_TYPE_WAVPACK_ENC_JOINT_STEREO_MODE,
281           GST_WAVPACK_JS_MODE_AUTO, G_PARAM_READWRITE));
282 }
283
284 static void
285 gst_wavpack_enc_reset (GstWavpackEnc * enc)
286 {
287   /* close and free everything stream related if we already did something */
288   if (enc->wp_context) {
289     WavpackCloseFile (enc->wp_context);
290     enc->wp_context = NULL;
291   }
292   if (enc->wp_config) {
293     g_free (enc->wp_config);
294     enc->wp_config = NULL;
295   }
296   if (enc->first_block) {
297     g_free (enc->first_block);
298     enc->first_block = NULL;
299   }
300   enc->first_block_size = 0;
301   if (enc->md5_context) {
302     g_free (enc->md5_context);
303     enc->md5_context = NULL;
304   }
305
306   if (enc->pending_buffer) {
307     gst_buffer_unref (enc->pending_buffer);
308     enc->pending_buffer = NULL;
309     enc->pending_offset = 0;
310   }
311
312   /* reset the last returns to GST_FLOW_OK. This is only set to something else
313    * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
314    * so not valid anymore */
315   enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
316
317   /* reset stream information */
318   enc->samplerate = 0;
319   enc->depth = 0;
320   enc->channels = 0;
321   enc->channel_mask = 0;
322   enc->need_channel_remap = FALSE;
323 }
324
325 static void
326 gst_wavpack_enc_init (GstWavpackEnc * enc, GstWavpackEncClass * gclass)
327 {
328   enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
329   gst_pad_set_setcaps_function (enc->sinkpad,
330       GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_set_caps));
331   gst_pad_set_chain_function (enc->sinkpad,
332       GST_DEBUG_FUNCPTR (gst_wavpack_enc_chain));
333   gst_pad_set_event_function (enc->sinkpad,
334       GST_DEBUG_FUNCPTR (gst_wavpack_enc_sink_event));
335   gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
336
337   /* setup src pad */
338   enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
339   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
340
341   /* initialize object attributes */
342   enc->wp_config = NULL;
343   enc->wp_context = NULL;
344   enc->first_block = NULL;
345   enc->md5_context = NULL;
346   gst_wavpack_enc_reset (enc);
347
348   enc->wv_id.correction = FALSE;
349   enc->wv_id.wavpack_enc = enc;
350   enc->wv_id.passthrough = FALSE;
351   enc->wvc_id.correction = TRUE;
352   enc->wvc_id.wavpack_enc = enc;
353   enc->wvc_id.passthrough = FALSE;
354
355   /* set default values of params */
356   enc->mode = GST_WAVPACK_ENC_MODE_DEFAULT;
357   enc->bitrate = 0;
358   enc->bps = 0.0;
359   enc->correction_mode = GST_WAVPACK_CORRECTION_MODE_OFF;
360   enc->md5 = FALSE;
361   enc->extra_processing = 0;
362   enc->joint_stereo_mode = GST_WAVPACK_JS_MODE_AUTO;
363 }
364
365 static gboolean
366 gst_wavpack_enc_sink_set_caps (GstPad * pad, GstCaps * caps)
367 {
368   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
369   GstStructure *structure = gst_caps_get_structure (caps, 0);
370   GstAudioChannelPosition *pos;
371
372   if (!gst_structure_get_int (structure, "channels", &enc->channels) ||
373       !gst_structure_get_int (structure, "rate", &enc->samplerate) ||
374       !gst_structure_get_int (structure, "depth", &enc->depth)) {
375     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
376         ("got invalid caps: %" GST_PTR_FORMAT, caps));
377     gst_object_unref (enc);
378     return FALSE;
379   }
380
381   pos = gst_audio_get_channel_positions (structure);
382   /* If one channel is NONE they'll be all undefined */
383   if (pos != NULL && pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) {
384     g_free (pos);
385     pos = NULL;
386   }
387
388   if (pos == NULL) {
389     GST_ELEMENT_ERROR (enc, STREAM, FORMAT, (NULL),
390         ("input has no valid channel layout"));
391
392     gst_object_unref (enc);
393     return FALSE;
394   }
395
396   enc->channel_mask =
397       gst_wavpack_get_channel_mask_from_positions (pos, enc->channels);
398   enc->need_channel_remap =
399       gst_wavpack_set_channel_mapping (pos, enc->channels,
400       enc->channel_mapping);
401   g_free (pos);
402
403   /* set fixed src pad caps now that we know what we will get */
404   caps = gst_caps_new_simple ("audio/x-wavpack",
405       "channels", G_TYPE_INT, enc->channels,
406       "rate", G_TYPE_INT, enc->samplerate,
407       "width", G_TYPE_INT, enc->depth, "framed", G_TYPE_BOOLEAN, TRUE, NULL);
408
409   if (!gst_wavpack_set_channel_layout (caps, enc->channel_mask))
410     GST_WARNING_OBJECT (enc, "setting channel layout failed");
411
412   if (!gst_pad_set_caps (enc->srcpad, caps)) {
413     GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
414         ("setting caps failed: %" GST_PTR_FORMAT, caps));
415     gst_caps_unref (caps);
416     gst_object_unref (enc);
417     return FALSE;
418   }
419   gst_pad_use_fixed_caps (enc->srcpad);
420
421   gst_caps_unref (caps);
422   gst_object_unref (enc);
423   return TRUE;
424 }
425
426 static void
427 gst_wavpack_enc_set_wp_config (GstWavpackEnc * enc)
428 {
429   enc->wp_config = g_new0 (WavpackConfig, 1);
430   /* set general stream informations in the WavpackConfig */
431   enc->wp_config->bytes_per_sample = GST_ROUND_UP_8 (enc->depth) / 8;
432   enc->wp_config->bits_per_sample = enc->depth;
433   enc->wp_config->num_channels = enc->channels;
434   enc->wp_config->channel_mask = enc->channel_mask;
435   enc->wp_config->sample_rate = enc->samplerate;
436
437   /*
438    * Set parameters in WavpackConfig
439    */
440
441   /* Encoding mode */
442   switch (enc->mode) {
443 #if 0
444     case GST_WAVPACK_ENC_MODE_VERY_FAST:
445       enc->wp_config->flags |= CONFIG_VERY_FAST_FLAG;
446       enc->wp_config->flags |= CONFIG_FAST_FLAG;
447       break;
448 #endif
449     case GST_WAVPACK_ENC_MODE_FAST:
450       enc->wp_config->flags |= CONFIG_FAST_FLAG;
451       break;
452     case GST_WAVPACK_ENC_MODE_DEFAULT:
453       break;
454     case GST_WAVPACK_ENC_MODE_HIGH:
455       enc->wp_config->flags |= CONFIG_HIGH_FLAG;
456       break;
457 #ifndef WAVPACK_OLD_API
458     case GST_WAVPACK_ENC_MODE_VERY_HIGH:
459       enc->wp_config->flags |= CONFIG_HIGH_FLAG;
460       enc->wp_config->flags |= CONFIG_VERY_HIGH_FLAG;
461       break;
462 #endif
463   }
464
465   /* Bitrate, enables lossy mode */
466   if (enc->bitrate) {
467     enc->wp_config->flags |= CONFIG_HYBRID_FLAG;
468     enc->wp_config->flags |= CONFIG_BITRATE_KBPS;
469     enc->wp_config->bitrate = enc->bitrate / 1000.0;
470   } else if (enc->bps) {
471     enc->wp_config->flags |= CONFIG_HYBRID_FLAG;
472     enc->wp_config->bitrate = enc->bps;
473   }
474
475   /* Correction Mode, only in lossy mode */
476   if (enc->wp_config->flags & CONFIG_HYBRID_FLAG) {
477     if (enc->correction_mode > GST_WAVPACK_CORRECTION_MODE_OFF) {
478       enc->wvcsrcpad =
479           gst_pad_new_from_static_template (&wvcsrc_factory, "wvcsrc");
480
481       /* try to add correction src pad, don't set correction mode on failure */
482       GstCaps *caps = gst_caps_new_simple ("audio/x-wavpack-correction",
483           "framed", G_TYPE_BOOLEAN, TRUE, NULL);
484
485       GST_DEBUG_OBJECT (enc, "Adding correction pad with caps %"
486           GST_PTR_FORMAT, caps);
487       if (!gst_pad_set_caps (enc->wvcsrcpad, caps)) {
488         enc->correction_mode = 0;
489         GST_WARNING_OBJECT (enc, "setting correction caps failed");
490       } else {
491         gst_pad_use_fixed_caps (enc->wvcsrcpad);
492         gst_pad_set_active (enc->wvcsrcpad, TRUE);
493         gst_element_add_pad (GST_ELEMENT (enc), enc->wvcsrcpad);
494         enc->wp_config->flags |= CONFIG_CREATE_WVC;
495         if (enc->correction_mode == GST_WAVPACK_CORRECTION_MODE_OPTIMIZED) {
496           enc->wp_config->flags |= CONFIG_OPTIMIZE_WVC;
497         }
498       }
499       gst_caps_unref (caps);
500     }
501   } else {
502     if (enc->correction_mode > GST_WAVPACK_CORRECTION_MODE_OFF) {
503       enc->correction_mode = 0;
504       GST_WARNING_OBJECT (enc, "setting correction mode only has "
505           "any effect if a bitrate is provided.");
506     }
507   }
508   gst_element_no_more_pads (GST_ELEMENT (enc));
509
510   /* MD5, setup MD5 context */
511   if ((enc->md5) && !(enc->md5_context)) {
512     enc->wp_config->flags |= CONFIG_MD5_CHECKSUM;
513     enc->md5_context = g_new0 (MD5_CTX, 1);
514     MD5Init (enc->md5_context);
515   }
516
517   /* Extra encode processing */
518   if (enc->extra_processing) {
519     enc->wp_config->flags |= CONFIG_EXTRA_MODE;
520     enc->wp_config->xmode = enc->extra_processing;
521   }
522
523   /* Joint stereo mode */
524   switch (enc->joint_stereo_mode) {
525     case GST_WAVPACK_JS_MODE_AUTO:
526       break;
527     case GST_WAVPACK_JS_MODE_LEFT_RIGHT:
528       enc->wp_config->flags |= CONFIG_JOINT_OVERRIDE;
529       enc->wp_config->flags &= ~CONFIG_JOINT_STEREO;
530       break;
531     case GST_WAVPACK_JS_MODE_MID_SIDE:
532       enc->wp_config->flags |= (CONFIG_JOINT_OVERRIDE | CONFIG_JOINT_STEREO);
533       break;
534   }
535 }
536
537 static int
538 gst_wavpack_enc_push_block (void *id, void *data, int32_t count)
539 {
540   GstWavpackEncWriteID *wid = (GstWavpackEncWriteID *) id;
541   GstWavpackEnc *enc = GST_WAVPACK_ENC (wid->wavpack_enc);
542   GstFlowReturn *flow;
543   GstBuffer *buffer;
544   GstPad *pad;
545   guchar *block = (guchar *) data;
546
547   pad = (wid->correction) ? enc->wvcsrcpad : enc->srcpad;
548   flow =
549       (wid->correction) ? &enc->wvcsrcpad_last_return : &enc->
550       srcpad_last_return;
551
552   *flow = gst_pad_alloc_buffer_and_set_caps (pad, GST_BUFFER_OFFSET_NONE,
553       count, GST_PAD_CAPS (pad), &buffer);
554
555   if (*flow != GST_FLOW_OK) {
556     GST_WARNING_OBJECT (enc, "flow on %s:%s = %s",
557         GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow));
558     return FALSE;
559   }
560
561   g_memmove (GST_BUFFER_DATA (buffer), block, count);
562
563   if (count > sizeof (WavpackHeader) && memcmp (block, "wvpk", 4) == 0) {
564     /* if it's a Wavpack block set buffer timestamp and duration, etc */
565     WavpackHeader wph;
566
567     GST_LOG_OBJECT (enc, "got %d bytes of encoded wavpack %sdata",
568         count, (wid->correction) ? "correction " : "");
569
570     gst_wavpack_read_header (&wph, block);
571
572     /* Only set when pushing the first buffer again, in that case
573      * we don't want to delay the buffer or push newsegment events
574      */
575     if (!wid->passthrough) {
576       /* Only push complete blocks */
577       if (enc->pending_buffer == NULL) {
578         enc->pending_buffer = buffer;
579         enc->pending_offset = wph.block_index;
580       } else if (enc->pending_offset == wph.block_index) {
581         enc->pending_buffer = gst_buffer_join (enc->pending_buffer, buffer);
582       } else {
583         GST_ERROR ("Got incomplete block, dropping");
584         gst_buffer_unref (enc->pending_buffer);
585         enc->pending_buffer = buffer;
586         enc->pending_offset = wph.block_index;
587       }
588
589       if (!(wph.flags & FINAL_BLOCK))
590         return TRUE;
591
592       buffer = enc->pending_buffer;
593       enc->pending_buffer = NULL;
594       enc->pending_offset = 0;
595
596       /* if it's the first wavpack block, send a NEW_SEGMENT event */
597       if (wph.block_index == 0) {
598         gst_pad_push_event (pad,
599             gst_event_new_new_segment (FALSE,
600                 1.0, GST_FORMAT_TIME, 0, GST_BUFFER_OFFSET_NONE, 0));
601
602         /* save header for later reference, so we can re-send it later on
603          * EOS with fixed up values for total sample count etc. */
604         if (enc->first_block == NULL && !wid->correction) {
605           enc->first_block =
606               g_memdup (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
607           enc->first_block_size = GST_BUFFER_SIZE (buffer);
608         }
609       }
610     }
611
612     /* set buffer timestamp, duration, offset, offset_end from
613      * the wavpack header */
614     GST_BUFFER_TIMESTAMP (buffer) =
615         gst_util_uint64_scale_int (GST_SECOND, wph.block_index,
616         enc->samplerate);
617     GST_BUFFER_DURATION (buffer) =
618         gst_util_uint64_scale_int (GST_SECOND, wph.block_samples,
619         enc->samplerate);
620     GST_BUFFER_OFFSET (buffer) = wph.block_index;
621     GST_BUFFER_OFFSET_END (buffer) = wph.block_index + wph.block_samples;
622   } else {
623     /* if it's something else set no timestamp and duration on the buffer */
624     GST_DEBUG_OBJECT (enc, "got %d bytes of unknown data", count);
625
626     GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
627     GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
628   }
629
630   /* push the buffer and forward errors */
631   GST_DEBUG_OBJECT (enc, "pushing buffer with %d bytes",
632       GST_BUFFER_SIZE (buffer));
633   *flow = gst_pad_push (pad, buffer);
634
635   if (*flow != GST_FLOW_OK) {
636     GST_WARNING_OBJECT (enc, "flow on %s:%s = %s",
637         GST_DEBUG_PAD_NAME (pad), gst_flow_get_name (*flow));
638     return FALSE;
639   }
640
641   return TRUE;
642 }
643
644 static void
645 gst_wavpack_enc_fix_channel_order (GstWavpackEnc * enc, gint32 * data,
646     gint nsamples)
647 {
648   gint i, j;
649   gint32 tmp[8];
650
651   for (i = 0; i < nsamples / enc->channels; i++) {
652     for (j = 0; j < enc->channels; j++) {
653       tmp[enc->channel_mapping[j]] = data[j];
654     }
655     for (j = 0; j < enc->channels; j++) {
656       data[j] = tmp[j];
657     }
658     data += enc->channels;
659   }
660 }
661
662 static GstFlowReturn
663 gst_wavpack_enc_chain (GstPad * pad, GstBuffer * buf)
664 {
665   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
666   uint32_t sample_count = GST_BUFFER_SIZE (buf) / 4;
667   GstFlowReturn ret;
668
669   /* reset the last returns to GST_FLOW_OK. This is only set to something else
670    * while WavpackPackSamples() or more specific gst_wavpack_enc_push_block()
671    * so not valid anymore */
672   enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
673
674   GST_DEBUG ("got %u raw samples", sample_count);
675
676   /* check if we already have a valid WavpackContext, otherwise make one */
677   if (!enc->wp_context) {
678     /* create raw context */
679     enc->wp_context =
680         WavpackOpenFileOutput (gst_wavpack_enc_push_block, &enc->wv_id,
681         (enc->correction_mode > 0) ? &enc->wvc_id : NULL);
682     if (!enc->wp_context) {
683       GST_ELEMENT_ERROR (enc, LIBRARY, INIT, (NULL),
684           ("error creating Wavpack context"));
685       gst_object_unref (enc);
686       gst_buffer_unref (buf);
687       return GST_FLOW_ERROR;
688     }
689
690     /* set the WavpackConfig according to our parameters */
691     gst_wavpack_enc_set_wp_config (enc);
692
693     /* set the configuration to the context now that we know everything
694      * and initialize the encoder */
695     if (!WavpackSetConfiguration (enc->wp_context,
696             enc->wp_config, (uint32_t) (-1))
697         || !WavpackPackInit (enc->wp_context)) {
698       GST_ELEMENT_ERROR (enc, LIBRARY, SETTINGS, (NULL),
699           ("error setting up wavpack encoding context"));
700       WavpackCloseFile (enc->wp_context);
701       gst_object_unref (enc);
702       gst_buffer_unref (buf);
703       return GST_FLOW_ERROR;
704     }
705     GST_DEBUG ("setup of encoding context successfull");
706   }
707
708   if (enc->need_channel_remap) {
709     buf = gst_buffer_make_writable (buf);
710     gst_wavpack_enc_fix_channel_order (enc, (gint32 *) GST_BUFFER_DATA (buf),
711         sample_count);
712   }
713
714   /* if we want to append the MD5 sum to the stream update it here
715    * with the current raw samples */
716   if (enc->md5) {
717     MD5Update (enc->md5_context, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
718   }
719
720   /* encode and handle return values from encoding */
721   if (WavpackPackSamples (enc->wp_context, (int32_t *) GST_BUFFER_DATA (buf),
722           sample_count / enc->channels)) {
723     GST_DEBUG ("encoding samples successful");
724     ret = GST_FLOW_OK;
725   } else {
726     if ((enc->srcpad_last_return == GST_FLOW_RESEND) ||
727         (enc->wvcsrcpad_last_return == GST_FLOW_RESEND)) {
728       ret = GST_FLOW_RESEND;
729     } else if ((enc->srcpad_last_return == GST_FLOW_OK) ||
730         (enc->wvcsrcpad_last_return == GST_FLOW_OK)) {
731       ret = GST_FLOW_OK;
732     } else if ((enc->srcpad_last_return == GST_FLOW_NOT_LINKED) &&
733         (enc->wvcsrcpad_last_return == GST_FLOW_NOT_LINKED)) {
734       ret = GST_FLOW_NOT_LINKED;
735     } else if ((enc->srcpad_last_return == GST_FLOW_WRONG_STATE) &&
736         (enc->wvcsrcpad_last_return == GST_FLOW_WRONG_STATE)) {
737       ret = GST_FLOW_WRONG_STATE;
738     } else {
739       GST_ELEMENT_ERROR (enc, LIBRARY, ENCODE, (NULL),
740           ("encoding samples failed"));
741       ret = GST_FLOW_ERROR;
742     }
743   }
744
745   gst_buffer_unref (buf);
746   gst_object_unref (enc);
747   return ret;
748 }
749
750 static void
751 gst_wavpack_enc_rewrite_first_block (GstWavpackEnc * enc)
752 {
753   GstEvent *event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES,
754       0, GST_BUFFER_OFFSET_NONE, 0);
755   gboolean ret;
756
757   g_return_if_fail (enc);
758   g_return_if_fail (enc->first_block);
759
760   /* update the sample count in the first block */
761   WavpackUpdateNumSamples (enc->wp_context, enc->first_block);
762
763   /* try to seek to the beginning of the output */
764   ret = gst_pad_push_event (enc->srcpad, event);
765   if (ret) {
766     /* try to rewrite the first block */
767     GST_DEBUG_OBJECT (enc, "rewriting first block ...");
768     enc->wv_id.passthrough = TRUE;
769     ret = gst_wavpack_enc_push_block (&enc->wv_id,
770         enc->first_block, enc->first_block_size);
771     enc->wv_id.passthrough = FALSE;
772   } else {
773     GST_WARNING_OBJECT (enc, "rewriting of first block failed. "
774         "Seeking to first block failed!");
775   }
776 }
777
778 static gboolean
779 gst_wavpack_enc_sink_event (GstPad * pad, GstEvent * event)
780 {
781   GstWavpackEnc *enc = GST_WAVPACK_ENC (gst_pad_get_parent (pad));
782   gboolean ret = TRUE;
783
784   GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event));
785
786   switch (GST_EVENT_TYPE (event)) {
787     case GST_EVENT_EOS:
788       /* Encode all remaining samples and flush them to the src pads */
789       WavpackFlushSamples (enc->wp_context);
790
791       /* Drop all remaining data, this is no complete block otherwise
792        * it would've been pushed already */
793       if (enc->pending_buffer) {
794         gst_object_unref (enc->pending_buffer);
795         enc->pending_buffer = NULL;
796         enc->pending_offset = 0;
797       }
798
799       /* write the MD5 sum if we have to write one */
800       if ((enc->md5) && (enc->md5_context)) {
801         guchar md5_digest[16];
802
803         MD5Final (md5_digest, enc->md5_context);
804         WavpackStoreMD5Sum (enc->wp_context, md5_digest);
805       }
806
807       /* Try to rewrite the first frame with the correct sample number */
808       if (enc->first_block)
809         gst_wavpack_enc_rewrite_first_block (enc);
810
811       /* close the context if not already happened */
812       if (enc->wp_context) {
813         WavpackCloseFile (enc->wp_context);
814         enc->wp_context = NULL;
815       }
816
817       ret = gst_pad_event_default (pad, event);
818       break;
819     case GST_EVENT_NEWSEGMENT:
820       if (enc->wp_context) {
821         GST_WARNING_OBJECT (enc, "got NEWSEGMENT after encoding "
822             "already started");
823       }
824       /* drop NEWSEGMENT events, we create our own when pushing
825        * the first buffer to the pads */
826       gst_event_unref (event);
827       ret = TRUE;
828       break;
829     default:
830       ret = gst_pad_event_default (pad, event);
831       break;
832   }
833
834   gst_object_unref (enc);
835   return ret;
836 }
837
838 static GstStateChangeReturn
839 gst_wavpack_enc_change_state (GstElement * element, GstStateChange transition)
840 {
841   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
842   GstWavpackEnc *enc = GST_WAVPACK_ENC (element);
843
844   switch (transition) {
845     case GST_STATE_CHANGE_NULL_TO_READY:
846       /* set the last returned GstFlowReturns of the two pads to GST_FLOW_OK
847        * as they're only set to something else in WavpackPackSamples() or more
848        * specific gst_wavpack_enc_push_block() and nothing happened there yet */
849       enc->srcpad_last_return = enc->wvcsrcpad_last_return = GST_FLOW_OK;
850       break;
851     case GST_STATE_CHANGE_READY_TO_PAUSED:
852       break;
853     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
854     default:
855       break;
856   }
857
858   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
859
860   switch (transition) {
861     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
862       break;
863     case GST_STATE_CHANGE_PAUSED_TO_READY:
864       gst_wavpack_enc_reset (enc);
865       break;
866     case GST_STATE_CHANGE_READY_TO_NULL:
867       break;
868     default:
869       break;
870   }
871
872   return ret;
873 }
874
875 static void
876 gst_wavpack_enc_set_property (GObject * object, guint prop_id,
877     const GValue * value, GParamSpec * pspec)
878 {
879   GstWavpackEnc *enc = GST_WAVPACK_ENC (object);
880
881   switch (prop_id) {
882     case ARG_MODE:
883       enc->mode = g_value_get_enum (value);
884       break;
885     case ARG_BITRATE:{
886       guint val = g_value_get_uint (value);
887
888       if ((val >= 24000) && (val <= 9600000)) {
889         enc->bitrate = val;
890         enc->bps = 0.0;
891       } else {
892         enc->bitrate = 0;
893         enc->bps = 0.0;
894       }
895       break;
896     }
897     case ARG_BITSPERSAMPLE:{
898       gdouble val = g_value_get_double (value);
899
900       if ((val >= 2.0) && (val <= 24.0)) {
901         enc->bps = val;
902         enc->bitrate = 0;
903       } else {
904         enc->bps = 0.0;
905         enc->bitrate = 0;
906       }
907       break;
908     }
909     case ARG_CORRECTION_MODE:
910       enc->correction_mode = g_value_get_enum (value);
911       break;
912     case ARG_MD5:
913       enc->md5 = g_value_get_boolean (value);
914       break;
915     case ARG_EXTRA_PROCESSING:
916       enc->extra_processing = g_value_get_uint (value);
917       break;
918     case ARG_JOINT_STEREO_MODE:
919       enc->joint_stereo_mode = g_value_get_enum (value);
920       break;
921     default:
922       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
923       break;
924   }
925 }
926
927 static void
928 gst_wavpack_enc_get_property (GObject * object, guint prop_id, GValue * value,
929     GParamSpec * pspec)
930 {
931   GstWavpackEnc *enc = GST_WAVPACK_ENC (object);
932
933   switch (prop_id) {
934     case ARG_MODE:
935       g_value_set_enum (value, enc->mode);
936       break;
937     case ARG_BITRATE:
938       if (enc->bps == 0.0) {
939         g_value_set_uint (value, enc->bitrate);
940       } else {
941         g_value_set_uint (value, 0);
942       }
943       break;
944     case ARG_BITSPERSAMPLE:
945       if (enc->bitrate == 0) {
946         g_value_set_double (value, enc->bps);
947       } else {
948         g_value_set_double (value, 0.0);
949       }
950       break;
951     case ARG_CORRECTION_MODE:
952       g_value_set_enum (value, enc->correction_mode);
953       break;
954     case ARG_MD5:
955       g_value_set_boolean (value, enc->md5);
956       break;
957     case ARG_EXTRA_PROCESSING:
958       g_value_set_uint (value, enc->extra_processing);
959       break;
960     case ARG_JOINT_STEREO_MODE:
961       g_value_set_enum (value, enc->joint_stereo_mode);
962       break;
963     default:
964       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
965       break;
966   }
967 }
968
969 gboolean
970 gst_wavpack_enc_plugin_init (GstPlugin * plugin)
971 {
972   if (!gst_element_register (plugin, "wavpackenc",
973           GST_RANK_NONE, GST_TYPE_WAVPACK_ENC))
974     return FALSE;
975
976   GST_DEBUG_CATEGORY_INIT (gst_wavpack_enc_debug, "wavpack_enc", 0,
977       "Wavpack encoder");
978
979   return TRUE;
980 }