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