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