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