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