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