Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / ext / flac / gstflacenc.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /**
20  * SECTION:element-flacenc
21  * @see_also: #GstFlacDec
22  *
23  * flacenc encodes FLAC streams.
24  * <ulink url="http://flac.sourceforge.net/">FLAC</ulink>
25  * is a Free Lossless Audio Codec.
26  *
27  * <refsect2>
28  * <title>Example launch line</title>
29  * |[
30  * gst-launch audiotestsrc num-buffers=100 ! flacenc ! filesink location=beep.flac
31  * ]|
32  * </refsect2>
33  */
34
35 /* TODO: - We currently don't handle discontinuities in the stream in a useful
36  *         way and instead rely on the developer plugging in audiorate if
37  *         the stream contains discontinuities.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include <gstflacenc.h>
47 #include <gst/audio/audio.h>
48 #include <gst/tag/tag.h>
49 #include <gst/gsttagsetter.h>
50
51 /* Taken from http://flac.sourceforge.net/format.html#frame_header */
52 static const GstAudioChannelPosition channel_positions[8][8] = {
53   {GST_AUDIO_CHANNEL_POSITION_MONO},
54   {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
55       GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
56         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
57         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
58       GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
59         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
60         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
61         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
62       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
63         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
64         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
65         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
66         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
67       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
68         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
69         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
70         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
71         GST_AUDIO_CHANNEL_POSITION_LFE1,
72         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
73       GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
74   /* FIXME: 7/8 channel layouts are not defined in the FLAC specs */
75   {
76         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
77         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
78         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
79         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
80         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
81         GST_AUDIO_CHANNEL_POSITION_LFE1,
82       GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
83         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
84         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
85         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
86         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
87         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
88         GST_AUDIO_CHANNEL_POSITION_LFE1,
89         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
90       GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
91 };
92
93 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
94 #define FORMATS "{ S8LE, S16LE, S24LE, S24_32LE } "
95 #else
96 #define FORMATS "{ S8BE, S16BE, S24BE, S24_32BE } "
97 #endif
98
99 #define FLAC_SINK_CAPS                                    \
100     "audio/x-raw, "                                       \
101     "format = (string) " FORMATS ", "                     \
102     "layout = (string) interleaved, "                     \
103     "rate = (int) [ 1, 655350 ], "                        \
104     "channels = (int) [ 1, 8 ]"
105
106 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
107     GST_PAD_SRC,
108     GST_PAD_ALWAYS,
109     GST_STATIC_CAPS ("audio/x-flac")
110     );
111
112 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
113     GST_PAD_SINK,
114     GST_PAD_ALWAYS,
115     GST_STATIC_CAPS (FLAC_SINK_CAPS)
116     );
117
118 enum
119 {
120   PROP_0,
121   PROP_QUALITY,
122   PROP_STREAMABLE_SUBSET,
123   PROP_MID_SIDE_STEREO,
124   PROP_LOOSE_MID_SIDE_STEREO,
125   PROP_BLOCKSIZE,
126   PROP_MAX_LPC_ORDER,
127   PROP_QLP_COEFF_PRECISION,
128   PROP_QLP_COEFF_PREC_SEARCH,
129   PROP_ESCAPE_CODING,
130   PROP_EXHAUSTIVE_MODEL_SEARCH,
131   PROP_MIN_RESIDUAL_PARTITION_ORDER,
132   PROP_MAX_RESIDUAL_PARTITION_ORDER,
133   PROP_RICE_PARAMETER_SEARCH_DIST,
134   PROP_PADDING,
135   PROP_SEEKPOINTS
136 };
137
138 GST_DEBUG_CATEGORY_STATIC (flacenc_debug);
139 #define GST_CAT_DEFAULT flacenc_debug
140
141 #define gst_flac_enc_parent_class parent_class
142 G_DEFINE_TYPE_WITH_CODE (GstFlacEnc, gst_flac_enc, GST_TYPE_AUDIO_ENCODER,
143     G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
144
145 static gboolean gst_flac_enc_start (GstAudioEncoder * enc);
146 static gboolean gst_flac_enc_stop (GstAudioEncoder * enc);
147 static gboolean gst_flac_enc_set_format (GstAudioEncoder * enc,
148     GstAudioInfo * info);
149 static GstFlowReturn gst_flac_enc_handle_frame (GstAudioEncoder * enc,
150     GstBuffer * in_buf);
151 static GstCaps *gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter);
152 static gboolean gst_flac_enc_sink_event (GstAudioEncoder * enc,
153     GstEvent * event);
154
155 static void gst_flac_enc_finalize (GObject * object);
156
157 static gboolean gst_flac_enc_update_quality (GstFlacEnc * flacenc,
158     gint quality);
159 static void gst_flac_enc_set_property (GObject * object, guint prop_id,
160     const GValue * value, GParamSpec * pspec);
161 static void gst_flac_enc_get_property (GObject * object, guint prop_id,
162     GValue * value, GParamSpec * pspec);
163
164 static FLAC__StreamEncoderWriteStatus
165 gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder,
166     const FLAC__byte buffer[], size_t bytes,
167     unsigned samples, unsigned current_frame, void *client_data);
168 static FLAC__StreamEncoderSeekStatus
169 gst_flac_enc_seek_callback (const FLAC__StreamEncoder * encoder,
170     FLAC__uint64 absolute_byte_offset, void *client_data);
171 static FLAC__StreamEncoderTellStatus
172 gst_flac_enc_tell_callback (const FLAC__StreamEncoder * encoder,
173     FLAC__uint64 * absolute_byte_offset, void *client_data);
174
175 typedef struct
176 {
177   gboolean exhaustive_model_search;
178   gboolean escape_coding;
179   gboolean mid_side;
180   gboolean loose_mid_side;
181   guint qlp_coeff_precision;
182   gboolean qlp_coeff_prec_search;
183   guint min_residual_partition_order;
184   guint max_residual_partition_order;
185   guint rice_parameter_search_dist;
186   guint max_lpc_order;
187   guint blocksize;
188 }
189 GstFlacEncParams;
190
191 static const GstFlacEncParams flacenc_params[] = {
192   {FALSE, FALSE, FALSE, FALSE, 0, FALSE, 2, 2, 0, 0, 1152},
193   {FALSE, FALSE, TRUE, TRUE, 0, FALSE, 2, 2, 0, 0, 1152},
194   {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 0, 3, 0, 0, 1152},
195   {FALSE, FALSE, FALSE, FALSE, 0, FALSE, 3, 3, 0, 6, 4608},
196   {FALSE, FALSE, TRUE, TRUE, 0, FALSE, 3, 3, 0, 8, 4608},
197   {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 3, 3, 0, 8, 4608},
198   {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 0, 4, 0, 8, 4608},
199   {TRUE, FALSE, TRUE, FALSE, 0, FALSE, 0, 6, 0, 8, 4608},
200   {TRUE, FALSE, TRUE, FALSE, 0, FALSE, 0, 6, 0, 12, 4608},
201   {TRUE, TRUE, TRUE, FALSE, 0, FALSE, 0, 16, 0, 32, 4608},
202 };
203
204 #define DEFAULT_QUALITY 5
205 #define DEFAULT_PADDING 0
206 #define DEFAULT_SEEKPOINTS -10
207
208 #define GST_TYPE_FLAC_ENC_QUALITY (gst_flac_enc_quality_get_type ())
209 static GType
210 gst_flac_enc_quality_get_type (void)
211 {
212   static GType qtype = 0;
213
214   if (qtype == 0) {
215     static const GEnumValue values[] = {
216       {0, "0 - Fastest compression", "0"},
217       {1, "1", "1"},
218       {2, "2", "2"},
219       {3, "3", "3"},
220       {4, "4", "4"},
221       {5, "5 - Default", "5"},
222       {6, "6", "6"},
223       {7, "7", "7"},
224       {8, "8 - Highest compression", "8"},
225       {9, "9 - Insane", "9"},
226       {0, NULL, NULL}
227     };
228
229     qtype = g_enum_register_static ("GstFlacEncQuality", values);
230   }
231   return qtype;
232 }
233
234 static void
235 gst_flac_enc_class_init (GstFlacEncClass * klass)
236 {
237   GObjectClass *gobject_class;
238   GstElementClass *gstelement_class;
239   GstAudioEncoderClass *base_class;
240
241   gobject_class = (GObjectClass *) klass;
242   gstelement_class = (GstElementClass *) klass;
243   base_class = (GstAudioEncoderClass *) (klass);
244
245   GST_DEBUG_CATEGORY_INIT (flacenc_debug, "flacenc", 0,
246       "Flac encoding element");
247
248   gobject_class->set_property = gst_flac_enc_set_property;
249   gobject_class->get_property = gst_flac_enc_get_property;
250   gobject_class->finalize = gst_flac_enc_finalize;
251
252   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QUALITY,
253       g_param_spec_enum ("quality",
254           "Quality",
255           "Speed versus compression tradeoff",
256           GST_TYPE_FLAC_ENC_QUALITY, DEFAULT_QUALITY,
257           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
258   g_object_class_install_property (G_OBJECT_CLASS (klass),
259       PROP_STREAMABLE_SUBSET, g_param_spec_boolean ("streamable-subset",
260           "Streamable subset",
261           "true to limit encoder to generating a Subset stream, else false",
262           TRUE,
263           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
264   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MID_SIDE_STEREO,
265       g_param_spec_boolean ("mid-side-stereo", "Do mid side stereo",
266           "Do mid side stereo (only for stereo input)",
267           flacenc_params[DEFAULT_QUALITY].mid_side,
268           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
269   g_object_class_install_property (G_OBJECT_CLASS (klass),
270       PROP_LOOSE_MID_SIDE_STEREO, g_param_spec_boolean ("loose-mid-side-stereo",
271           "Loose mid side stereo", "Loose mid side stereo",
272           flacenc_params[DEFAULT_QUALITY].loose_mid_side,
273           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
274   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BLOCKSIZE,
275       g_param_spec_uint ("blocksize", "Blocksize", "Blocksize in samples",
276           FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE,
277           flacenc_params[DEFAULT_QUALITY].blocksize,
278           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
279   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_LPC_ORDER,
280       g_param_spec_uint ("max-lpc-order", "Max LPC order",
281           "Max LPC order; 0 => use only fixed predictors", 0,
282           FLAC__MAX_LPC_ORDER, flacenc_params[DEFAULT_QUALITY].max_lpc_order,
283           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
284   g_object_class_install_property (G_OBJECT_CLASS (klass),
285       PROP_QLP_COEFF_PRECISION, g_param_spec_uint ("qlp-coeff-precision",
286           "QLP coefficients precision",
287           "Precision in bits of quantized linear-predictor coefficients; 0 = automatic",
288           0, 32, flacenc_params[DEFAULT_QUALITY].qlp_coeff_precision,
289           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
290   g_object_class_install_property (G_OBJECT_CLASS (klass),
291       PROP_QLP_COEFF_PREC_SEARCH, g_param_spec_boolean ("qlp-coeff-prec-search",
292           "Do QLP coefficients precision search",
293           "false = use qlp_coeff_precision, "
294           "true = search around qlp_coeff_precision, take best",
295           flacenc_params[DEFAULT_QUALITY].qlp_coeff_prec_search,
296           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
297   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ESCAPE_CODING,
298       g_param_spec_boolean ("escape-coding", "Do Escape coding",
299           "search for escape codes in the entropy coding stage "
300           "for slightly better compression",
301           flacenc_params[DEFAULT_QUALITY].escape_coding,
302           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
303   g_object_class_install_property (G_OBJECT_CLASS (klass),
304       PROP_EXHAUSTIVE_MODEL_SEARCH,
305       g_param_spec_boolean ("exhaustive-model-search",
306           "Do exhaustive model search",
307           "do exhaustive search of LP coefficient quantization (expensive!)",
308           flacenc_params[DEFAULT_QUALITY].exhaustive_model_search,
309           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
310   g_object_class_install_property (G_OBJECT_CLASS (klass),
311       PROP_MIN_RESIDUAL_PARTITION_ORDER,
312       g_param_spec_uint ("min-residual-partition-order",
313           "Min residual partition order",
314           "Min residual partition order (above 4 doesn't usually help much)", 0,
315           16, flacenc_params[DEFAULT_QUALITY].min_residual_partition_order,
316           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
317   g_object_class_install_property (G_OBJECT_CLASS (klass),
318       PROP_MAX_RESIDUAL_PARTITION_ORDER,
319       g_param_spec_uint ("max-residual-partition-order",
320           "Max residual partition order",
321           "Max residual partition order (above 4 doesn't usually help much)", 0,
322           16, flacenc_params[DEFAULT_QUALITY].max_residual_partition_order,
323           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
324   g_object_class_install_property (G_OBJECT_CLASS (klass),
325       PROP_RICE_PARAMETER_SEARCH_DIST,
326       g_param_spec_uint ("rice-parameter-search-dist",
327           "rice_parameter_search_dist",
328           "0 = try only calc'd parameter k; else try all [k-dist..k+dist] "
329           "parameters, use best", 0, FLAC__MAX_RICE_PARTITION_ORDER,
330           flacenc_params[DEFAULT_QUALITY].rice_parameter_search_dist,
331           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
332
333   /**
334    * GstFlacEnc:padding
335    *
336    * Write a PADDING block with this length in bytes
337    *
338    * Since: 0.10.16
339    **/
340   g_object_class_install_property (G_OBJECT_CLASS (klass),
341       PROP_PADDING,
342       g_param_spec_uint ("padding",
343           "Padding",
344           "Write a PADDING block with this length in bytes", 0, G_MAXUINT,
345           DEFAULT_PADDING,
346           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
347
348   /**
349    * GstFlacEnc:seekpoints
350    *
351    * Write a SEEKTABLE block with a specific number of seekpoints
352    * or with a specific interval spacing.
353    *
354    * Since: 0.10.18
355    **/
356   g_object_class_install_property (G_OBJECT_CLASS (klass),
357       PROP_SEEKPOINTS,
358       g_param_spec_int ("seekpoints",
359           "Seekpoints",
360           "Add SEEKTABLE metadata (if > 0, number of entries, if < 0, interval in sec)",
361           -G_MAXINT, G_MAXINT,
362           DEFAULT_SEEKPOINTS,
363           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
364
365   gst_element_class_add_pad_template (gstelement_class,
366       gst_static_pad_template_get (&src_factory));
367   gst_element_class_add_pad_template (gstelement_class,
368       gst_static_pad_template_get (&sink_factory));
369
370   gst_element_class_set_details_simple (gstelement_class, "FLAC audio encoder",
371       "Codec/Encoder/Audio",
372       "Encodes audio with the FLAC lossless audio encoder",
373       "Wim Taymans <wim.taymans@chello.be>");
374
375   base_class->start = GST_DEBUG_FUNCPTR (gst_flac_enc_start);
376   base_class->stop = GST_DEBUG_FUNCPTR (gst_flac_enc_stop);
377   base_class->set_format = GST_DEBUG_FUNCPTR (gst_flac_enc_set_format);
378   base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_flac_enc_handle_frame);
379   base_class->getcaps = GST_DEBUG_FUNCPTR (gst_flac_enc_getcaps);
380   base_class->event = GST_DEBUG_FUNCPTR (gst_flac_enc_sink_event);
381 }
382
383 static void
384 gst_flac_enc_init (GstFlacEnc * flacenc)
385 {
386   GstAudioEncoder *enc = GST_AUDIO_ENCODER (flacenc);
387
388   flacenc->encoder = FLAC__stream_encoder_new ();
389   gst_flac_enc_update_quality (flacenc, DEFAULT_QUALITY);
390
391   /* arrange granulepos marking (and required perfect ts) */
392   gst_audio_encoder_set_mark_granule (enc, TRUE);
393   gst_audio_encoder_set_perfect_timestamp (enc, TRUE);
394 }
395
396 static void
397 gst_flac_enc_finalize (GObject * object)
398 {
399   GstFlacEnc *flacenc = GST_FLAC_ENC (object);
400
401   FLAC__stream_encoder_delete (flacenc->encoder);
402
403   G_OBJECT_CLASS (parent_class)->finalize (object);
404 }
405
406 static gboolean
407 gst_flac_enc_start (GstAudioEncoder * enc)
408 {
409   GstFlacEnc *flacenc = GST_FLAC_ENC (enc);
410
411   GST_DEBUG_OBJECT (enc, "start");
412   flacenc->stopped = TRUE;
413   flacenc->got_headers = FALSE;
414   flacenc->last_flow = GST_FLOW_OK;
415   flacenc->offset = 0;
416   flacenc->eos = FALSE;
417   flacenc->tags = gst_tag_list_new_empty ();
418
419   return TRUE;
420 }
421
422 static gboolean
423 gst_flac_enc_stop (GstAudioEncoder * enc)
424 {
425   GstFlacEnc *flacenc = GST_FLAC_ENC (enc);
426
427   GST_DEBUG_OBJECT (enc, "stop");
428   gst_tag_list_free (flacenc->tags);
429   flacenc->tags = NULL;
430   if (FLAC__stream_encoder_get_state (flacenc->encoder) !=
431       FLAC__STREAM_ENCODER_UNINITIALIZED) {
432     flacenc->stopped = TRUE;
433     FLAC__stream_encoder_finish (flacenc->encoder);
434   }
435   if (flacenc->meta) {
436     FLAC__metadata_object_delete (flacenc->meta[0]);
437
438     if (flacenc->meta[1])
439       FLAC__metadata_object_delete (flacenc->meta[1]);
440
441     if (flacenc->meta[2])
442       FLAC__metadata_object_delete (flacenc->meta[2]);
443
444     g_free (flacenc->meta);
445     flacenc->meta = NULL;
446   }
447   g_list_foreach (flacenc->headers, (GFunc) gst_mini_object_unref, NULL);
448   g_list_free (flacenc->headers);
449   flacenc->headers = NULL;
450
451   gst_tag_setter_reset_tags (GST_TAG_SETTER (enc));
452
453   return TRUE;
454 }
455
456 static void
457 add_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
458 {
459   GList *comments;
460   GList *it;
461   GstFlacEnc *flacenc = GST_FLAC_ENC (user_data);
462
463   /* IMAGE and PREVIEW_IMAGE tags are already written
464    * differently, no need to store them inside the
465    * vorbiscomments too */
466   if (strcmp (tag, GST_TAG_IMAGE) == 0
467       || strcmp (tag, GST_TAG_PREVIEW_IMAGE) == 0)
468     return;
469
470   comments = gst_tag_to_vorbis_comments (list, tag);
471   for (it = comments; it != NULL; it = it->next) {
472     FLAC__StreamMetadata_VorbisComment_Entry commment_entry;
473
474     commment_entry.length = strlen (it->data);
475     commment_entry.entry = it->data;
476     FLAC__metadata_object_vorbiscomment_insert_comment (flacenc->meta[0],
477         flacenc->meta[0]->data.vorbis_comment.num_comments,
478         commment_entry, TRUE);
479     g_free (it->data);
480   }
481   g_list_free (comments);
482 }
483
484 static void
485 gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
486 {
487   const GstTagList *user_tags;
488   GstTagList *copy;
489   gint entries = 1;
490   gint n_images, n_preview_images;
491   GstAudioInfo *info =
492       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
493
494   g_return_if_fail (flacenc != NULL);
495   user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc));
496   if ((flacenc->tags == NULL) && (user_tags == NULL)) {
497     return;
498   }
499   copy = gst_tag_list_merge (user_tags, flacenc->tags,
500       gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (flacenc)));
501   n_images = gst_tag_list_get_tag_size (copy, GST_TAG_IMAGE);
502   n_preview_images = gst_tag_list_get_tag_size (copy, GST_TAG_PREVIEW_IMAGE);
503
504   flacenc->meta =
505       g_new0 (FLAC__StreamMetadata *, 3 + n_images + n_preview_images);
506
507   flacenc->meta[0] =
508       FLAC__metadata_object_new (FLAC__METADATA_TYPE_VORBIS_COMMENT);
509   gst_tag_list_foreach (copy, add_one_tag, flacenc);
510
511   if (n_images + n_preview_images > 0) {
512     GstBuffer *buffer;
513 #if 0
514     GstCaps *caps;
515     GstStructure *structure;
516     GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE;
517 #endif
518     gint i;
519     GstMapInfo map;
520
521     for (i = 0; i < n_images + n_preview_images; i++) {
522       if (i < n_images) {
523         if (!gst_tag_list_get_buffer_index (copy, GST_TAG_IMAGE, i, &buffer))
524           continue;
525       } else {
526         if (!gst_tag_list_get_buffer_index (copy, GST_TAG_PREVIEW_IMAGE,
527                 i - n_images, &buffer))
528           continue;
529       }
530
531       flacenc->meta[entries] =
532           FLAC__metadata_object_new (FLAC__METADATA_TYPE_PICTURE);
533
534 #if 0
535       caps = gst_buffer_get_caps (buffer);
536       structure = gst_caps_get_structure (caps, 0);
537
538       gst_structure_get (structure, "image-type", GST_TYPE_TAG_IMAGE_TYPE,
539           &image_type, NULL);
540       /* Convert to ID3v2 APIC image type */
541       if (image_type == GST_TAG_IMAGE_TYPE_NONE)
542         image_type = (i < n_images) ? 0x00 : 0x01;
543       else
544         image_type = image_type + 2;
545 #endif
546
547       gst_buffer_map (buffer, &map, GST_MAP_READ);
548       FLAC__metadata_object_picture_set_data (flacenc->meta[entries],
549           map.data, map.size, TRUE);
550       gst_buffer_unmap (buffer, &map);
551
552 #if 0
553       /* FIXME: There's no way to set the picture type in libFLAC */
554       flacenc->meta[entries]->data.picture.type = image_type;
555       FLAC__metadata_object_picture_set_mime_type (flacenc->meta[entries],
556           (char *) gst_structure_get_name (structure), TRUE);
557       gst_caps_unref (caps);
558 #endif
559
560       gst_buffer_unref (buffer);
561       entries++;
562     }
563   }
564
565   if (flacenc->seekpoints && total_samples != GST_CLOCK_TIME_NONE) {
566     gboolean res;
567     guint samples;
568
569     flacenc->meta[entries] =
570         FLAC__metadata_object_new (FLAC__METADATA_TYPE_SEEKTABLE);
571     if (flacenc->seekpoints > 0) {
572       res =
573           FLAC__metadata_object_seektable_template_append_spaced_points
574           (flacenc->meta[entries], flacenc->seekpoints, total_samples);
575     } else {
576       samples = -flacenc->seekpoints * GST_AUDIO_INFO_RATE (info);
577       res =
578           FLAC__metadata_object_seektable_template_append_spaced_points_by_samples
579           (flacenc->meta[entries], samples, total_samples);
580     }
581     if (!res) {
582       GST_DEBUG_OBJECT (flacenc, "adding seekpoint template %d failed",
583           flacenc->seekpoints);
584       FLAC__metadata_object_delete (flacenc->meta[1]);
585       flacenc->meta[entries] = NULL;
586     } else {
587       entries++;
588     }
589   } else if (flacenc->seekpoints && total_samples == GST_CLOCK_TIME_NONE) {
590     GST_WARNING_OBJECT (flacenc, "total time unknown; can not add seekpoints");
591   }
592
593   if (flacenc->padding > 0) {
594     flacenc->meta[entries] =
595         FLAC__metadata_object_new (FLAC__METADATA_TYPE_PADDING);
596     flacenc->meta[entries]->length = flacenc->padding;
597     entries++;
598   }
599
600   if (FLAC__stream_encoder_set_metadata (flacenc->encoder,
601           flacenc->meta, entries) != true)
602     g_warning ("Dude, i'm already initialized!");
603
604   gst_tag_list_free (copy);
605 }
606
607 static GstCaps *
608 gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter)
609 {
610   GstCaps *ret = NULL, *caps = NULL;
611   GstPad *pad;
612
613   pad = GST_AUDIO_ENCODER_SINK_PAD (enc);
614
615   if (gst_pad_has_current_caps (pad)) {
616     ret = gst_pad_get_current_caps (pad);
617   } else {
618     gint i;
619     GValue v_list = { 0, };
620     GValue v = { 0, };
621     GstStructure *s, *s2;
622
623     g_value_init (&v_list, GST_TYPE_LIST);
624     g_value_init (&v, G_TYPE_STRING);
625
626     g_value_set_static_string (&v, GST_AUDIO_NE (S8));
627     gst_value_list_append_value (&v_list, &v);
628     g_value_set_static_string (&v, GST_AUDIO_NE (S16));
629     gst_value_list_append_value (&v_list, &v);
630     g_value_set_static_string (&v, GST_AUDIO_NE (S24));
631     gst_value_list_append_value (&v_list, &v);
632     g_value_set_static_string (&v, GST_AUDIO_NE (S24_32));
633     gst_value_list_append_value (&v_list, &v);
634     g_value_unset (&v);
635
636     s = gst_structure_new_empty ("audio/x-raw");
637     gst_structure_take_value (s, "format", &v_list);
638
639     gst_structure_set (s, "layout", G_TYPE_STRING, "interleaved",
640         "rate", GST_TYPE_INT_RANGE, 1, 655350, NULL);
641
642     ret = gst_caps_new_empty ();
643     for (i = 1; i <= 8; i++) {
644       s2 = gst_structure_copy (s);
645
646       if (i == 1) {
647         gst_structure_set (s2, "channels", G_TYPE_INT, 1, NULL);
648       } else {
649         guint64 channel_mask;
650
651         gst_audio_channel_positions_to_mask (channel_positions[i - 1], i,
652             &channel_mask);
653         gst_structure_set (s2, "channels", G_TYPE_INT, i, "channel-mask",
654             GST_TYPE_BITMASK, channel_mask, NULL);
655       }
656
657       gst_caps_append_structure (ret, s2);
658     }
659     gst_structure_free (s);
660   }
661
662   GST_DEBUG_OBJECT (pad, "Return caps %" GST_PTR_FORMAT, ret);
663
664   caps = gst_audio_encoder_proxy_getcaps (enc, ret);
665   gst_caps_unref (ret);
666
667   return caps;
668 }
669
670 static guint64
671 gst_flac_enc_peer_query_total_samples (GstFlacEnc * flacenc, GstPad * pad)
672 {
673   gint64 duration;
674   GstAudioInfo *info =
675       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
676
677   GST_DEBUG_OBJECT (flacenc, "querying peer for DEFAULT format duration");
678   if (gst_pad_peer_query_duration (pad, GST_FORMAT_DEFAULT, &duration)
679       && duration != GST_CLOCK_TIME_NONE)
680     goto done;
681
682   GST_DEBUG_OBJECT (flacenc, "querying peer for TIME format duration");
683
684   if (gst_pad_peer_query_duration (pad, GST_FORMAT_TIME, &duration)
685       && duration != GST_CLOCK_TIME_NONE) {
686     GST_DEBUG_OBJECT (flacenc, "peer reported duration %" GST_TIME_FORMAT,
687         GST_TIME_ARGS (duration));
688     duration = GST_CLOCK_TIME_TO_FRAMES (duration, GST_AUDIO_INFO_RATE (info));
689
690     goto done;
691   }
692
693   GST_DEBUG_OBJECT (flacenc, "Upstream reported no total samples");
694   return GST_CLOCK_TIME_NONE;
695
696 done:
697   GST_DEBUG_OBJECT (flacenc,
698       "Upstream reported %" G_GUINT64_FORMAT " total samples", duration);
699
700   return duration;
701 }
702
703 static gboolean
704 gst_flac_enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
705 {
706   GstFlacEnc *flacenc;
707   guint64 total_samples = GST_CLOCK_TIME_NONE;
708   FLAC__StreamEncoderInitStatus init_status;
709   GstCaps *caps;
710
711   flacenc = GST_FLAC_ENC (enc);
712
713   /* if configured again, means something changed, can't handle that */
714   if (FLAC__stream_encoder_get_state (flacenc->encoder) !=
715       FLAC__STREAM_ENCODER_UNINITIALIZED)
716     goto encoder_already_initialized;
717
718   caps = gst_caps_new_simple ("audio/x-flac",
719       "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
720       "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
721
722   if (!gst_audio_encoder_set_output_format (enc, caps))
723     goto setting_src_caps_failed;
724
725   gst_caps_unref (caps);
726
727   gst_audio_get_channel_reorder_map (GST_AUDIO_INFO_CHANNELS (info),
728       channel_positions[GST_AUDIO_INFO_CHANNELS (info) - 1], info->position,
729       flacenc->channel_reorder_map);
730
731   total_samples = gst_flac_enc_peer_query_total_samples (flacenc,
732       GST_AUDIO_ENCODER_SINK_PAD (enc));
733
734   FLAC__stream_encoder_set_bits_per_sample (flacenc->encoder,
735       GST_AUDIO_INFO_WIDTH (info));
736   FLAC__stream_encoder_set_sample_rate (flacenc->encoder,
737       GST_AUDIO_INFO_RATE (info));
738   FLAC__stream_encoder_set_channels (flacenc->encoder,
739       GST_AUDIO_INFO_CHANNELS (info));
740
741   if (total_samples != GST_CLOCK_TIME_NONE)
742     FLAC__stream_encoder_set_total_samples_estimate (flacenc->encoder,
743         MIN (total_samples, G_GUINT64_CONSTANT (0x0FFFFFFFFF)));
744
745   gst_flac_enc_set_metadata (flacenc, total_samples);
746
747   /* callbacks clear to go now;
748    * write callbacks receives headers during init */
749   flacenc->stopped = FALSE;
750
751   init_status = FLAC__stream_encoder_init_stream (flacenc->encoder,
752       gst_flac_enc_write_callback, gst_flac_enc_seek_callback,
753       gst_flac_enc_tell_callback, NULL, flacenc);
754   if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
755     goto failed_to_initialize;
756
757   /* no special feedback to base class; should provide all available samples */
758
759   return TRUE;
760
761 encoder_already_initialized:
762   {
763     g_warning ("flac already initialized -- fixme allow this");
764     return FALSE;
765   }
766 setting_src_caps_failed:
767   {
768     GST_DEBUG_OBJECT (flacenc,
769         "Couldn't set caps on source pad: %" GST_PTR_FORMAT, caps);
770     gst_caps_unref (caps);
771     return FALSE;
772   }
773 failed_to_initialize:
774   {
775     GST_ELEMENT_ERROR (flacenc, LIBRARY, INIT, (NULL),
776         ("could not initialize encoder (wrong parameters?) %d", init_status));
777     return FALSE;
778   }
779 }
780
781 static gboolean
782 gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality)
783 {
784   GstAudioInfo *info =
785       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
786
787   flacenc->quality = quality;
788
789 #define DO_UPDATE(name, val, str)                                               \
790   G_STMT_START {                                                                \
791     if (FLAC__stream_encoder_get_##name (flacenc->encoder) !=                   \
792         flacenc_params[quality].val) {                                          \
793       FLAC__stream_encoder_set_##name (flacenc->encoder,                        \
794           flacenc_params[quality].val);                                         \
795       g_object_notify (G_OBJECT (flacenc), str);                                \
796     }                                                                           \
797   } G_STMT_END
798
799   g_object_freeze_notify (G_OBJECT (flacenc));
800
801   if (GST_AUDIO_INFO_CHANNELS (info) == 2
802       || GST_AUDIO_INFO_CHANNELS (info) == 0) {
803     DO_UPDATE (do_mid_side_stereo, mid_side, "mid_side_stereo");
804     DO_UPDATE (loose_mid_side_stereo, loose_mid_side, "loose_mid_side");
805   }
806
807   DO_UPDATE (blocksize, blocksize, "blocksize");
808   DO_UPDATE (max_lpc_order, max_lpc_order, "max_lpc_order");
809   DO_UPDATE (qlp_coeff_precision, qlp_coeff_precision, "qlp_coeff_precision");
810   DO_UPDATE (do_qlp_coeff_prec_search, qlp_coeff_prec_search,
811       "qlp_coeff_prec_search");
812   DO_UPDATE (do_escape_coding, escape_coding, "escape_coding");
813   DO_UPDATE (do_exhaustive_model_search, exhaustive_model_search,
814       "exhaustive_model_search");
815   DO_UPDATE (min_residual_partition_order, min_residual_partition_order,
816       "min_residual_partition_order");
817   DO_UPDATE (max_residual_partition_order, max_residual_partition_order,
818       "max_residual_partition_order");
819   DO_UPDATE (rice_parameter_search_dist, rice_parameter_search_dist,
820       "rice_parameter_search_dist");
821
822 #undef DO_UPDATE
823
824   g_object_thaw_notify (G_OBJECT (flacenc));
825
826   return TRUE;
827 }
828
829 static FLAC__StreamEncoderSeekStatus
830 gst_flac_enc_seek_callback (const FLAC__StreamEncoder * encoder,
831     FLAC__uint64 absolute_byte_offset, void *client_data)
832 {
833   GstFlacEnc *flacenc;
834   GstPad *peerpad;
835   GstSegment seg;
836
837   flacenc = GST_FLAC_ENC (client_data);
838
839   if (flacenc->stopped)
840     return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
841
842   if ((peerpad = gst_pad_get_peer (GST_AUDIO_ENCODER_SRC_PAD (flacenc)))) {
843     GstEvent *event;
844     gboolean ret;
845
846     gst_segment_init (&seg, GST_FORMAT_BYTES);
847     seg.start = absolute_byte_offset;
848     seg.stop = GST_BUFFER_OFFSET_NONE;
849     seg.time = 0;
850     event = gst_event_new_segment (&seg);
851
852     ret = gst_pad_send_event (peerpad, event);
853     gst_object_unref (peerpad);
854
855     if (ret) {
856       GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s",
857           (guint64) absolute_byte_offset, "succeeded");
858     } else {
859       GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s",
860           (guint64) absolute_byte_offset, "failed");
861       return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
862     }
863   } else {
864     GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " failed (no peer pad)",
865         (guint64) absolute_byte_offset);
866   }
867
868   flacenc->offset = absolute_byte_offset;
869   return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
870 }
871
872 static void
873 notgst_value_array_append_buffer (GValue * array_val, GstBuffer * buf)
874 {
875   GValue value = { 0, };
876
877   g_value_init (&value, GST_TYPE_BUFFER);
878   /* copy buffer to avoid problems with circular refcounts */
879   buf = gst_buffer_copy (buf);
880   /* again, for good measure */
881   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
882   gst_value_set_buffer (&value, buf);
883   gst_buffer_unref (buf);
884   gst_value_array_append_value (array_val, &value);
885   g_value_unset (&value);
886 }
887
888 #define HDR_TYPE_STREAMINFO     0
889 #define HDR_TYPE_VORBISCOMMENT  4
890
891 static GstFlowReturn
892 gst_flac_enc_process_stream_headers (GstFlacEnc * enc)
893 {
894   GstBuffer *vorbiscomment = NULL;
895   GstBuffer *streaminfo = NULL;
896   GstBuffer *marker = NULL;
897   GValue array = { 0, };
898   GstCaps *caps;
899   GList *l;
900   GstFlowReturn ret = GST_FLOW_OK;
901   GstAudioInfo *info =
902       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (enc));
903
904   caps = gst_caps_new_simple ("audio/x-flac",
905       "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
906       "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
907
908   for (l = enc->headers; l != NULL; l = l->next) {
909     GstBuffer *buf;
910     GstMapInfo map;
911     guint8 *data;
912     gsize size;
913
914     /* mark buffers so oggmux will ignore them if it already muxed the
915      * header buffers from the streamheaders field in the caps */
916     l->data = gst_buffer_make_writable (GST_BUFFER_CAST (l->data));
917
918     buf = GST_BUFFER_CAST (l->data);
919     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
920
921     gst_buffer_map (buf, &map, GST_MAP_READ);
922     data = map.data;
923     size = map.size;
924
925     /* find initial 4-byte marker which we need to skip later on */
926     if (size == 4 && memcmp (data, "fLaC", 4) == 0) {
927       marker = buf;
928     } else if (size > 1 && (data[0] & 0x7f) == HDR_TYPE_STREAMINFO) {
929       streaminfo = buf;
930     } else if (size > 1 && (data[0] & 0x7f) == HDR_TYPE_VORBISCOMMENT) {
931       vorbiscomment = buf;
932     }
933
934     gst_buffer_unmap (buf, &map);
935   }
936
937   if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) {
938     GST_WARNING_OBJECT (enc, "missing header %p %p %p, muxing into container "
939         "formats may be broken", marker, streaminfo, vorbiscomment);
940     goto push_headers;
941   }
942
943   g_value_init (&array, GST_TYPE_ARRAY);
944
945   /* add marker including STREAMINFO header */
946   {
947     GstBuffer *buf;
948     guint16 num;
949     GstMapInfo map;
950     guint8 *bdata;
951     gsize slen;
952
953     /* minus one for the marker that is merged with streaminfo here */
954     num = g_list_length (enc->headers) - 1;
955
956     slen = gst_buffer_get_size (streaminfo);
957     buf = gst_buffer_new_and_alloc (13 + slen);
958
959     gst_buffer_map (buf, &map, GST_MAP_WRITE);
960     bdata = map.data;
961     bdata[0] = 0x7f;
962     memcpy (bdata + 1, "FLAC", 4);
963     bdata[5] = 0x01;            /* mapping version major */
964     bdata[6] = 0x00;            /* mapping version minor */
965     bdata[7] = (num & 0xFF00) >> 8;
966     bdata[8] = (num & 0x00FF) >> 0;
967     memcpy (bdata + 9, "fLaC", 4);
968     gst_buffer_extract (streaminfo, 0, bdata + 13, slen);
969     gst_buffer_unmap (buf, &map);
970
971     notgst_value_array_append_buffer (&array, buf);
972     gst_buffer_unref (buf);
973   }
974
975   /* add VORBISCOMMENT header */
976   notgst_value_array_append_buffer (&array, vorbiscomment);
977
978   /* add other headers, if there are any */
979   for (l = enc->headers; l != NULL; l = l->next) {
980     GstBuffer *buf = GST_BUFFER_CAST (l->data);
981
982     if (buf != marker && buf != streaminfo && buf != vorbiscomment) {
983       notgst_value_array_append_buffer (&array, buf);
984     }
985   }
986
987   gst_structure_set_value (gst_caps_get_structure (caps, 0),
988       "streamheader", &array);
989   g_value_unset (&array);
990
991 push_headers:
992   gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (enc), caps);
993
994   /* push header buffers; update caps, so when we push the first buffer the
995    * negotiated caps will change to caps that include the streamheader field */
996   for (l = enc->headers; l != NULL; l = l->next) {
997     GstBuffer *buf;
998
999     buf = GST_BUFFER (l->data);
1000     GST_LOG_OBJECT (enc,
1001         "Pushing header buffer, size %" G_GSIZE_FORMAT " bytes",
1002         gst_buffer_get_size (buf));
1003 #if 0
1004     GST_MEMDUMP_OBJECT (enc, "header buffer", GST_BUFFER_DATA (buf),
1005         GST_BUFFER_SIZE (buf));
1006 #endif
1007     ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), buf);
1008     l->data = NULL;
1009   }
1010   g_list_free (enc->headers);
1011   enc->headers = NULL;
1012
1013   gst_caps_unref (caps);
1014
1015   return ret;
1016 }
1017
1018 static FLAC__StreamEncoderWriteStatus
1019 gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder,
1020     const FLAC__byte buffer[], size_t bytes,
1021     unsigned samples, unsigned current_frame, void *client_data)
1022 {
1023   GstFlowReturn ret = GST_FLOW_OK;
1024   GstFlacEnc *flacenc;
1025   GstBuffer *outbuf;
1026
1027   flacenc = GST_FLAC_ENC (client_data);
1028
1029   if (flacenc->stopped)
1030     return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
1031
1032   outbuf = gst_buffer_new_and_alloc (bytes);
1033   gst_buffer_fill (outbuf, 0, buffer, bytes);
1034
1035   /* we assume libflac passes us stuff neatly framed */
1036   if (!flacenc->got_headers) {
1037     if (samples == 0) {
1038       GST_DEBUG_OBJECT (flacenc, "Got header, queueing (%u bytes)",
1039           (guint) bytes);
1040       flacenc->headers = g_list_append (flacenc->headers, outbuf);
1041       /* note: it's important that we increase our byte offset */
1042       goto out;
1043     } else {
1044       GST_INFO_OBJECT (flacenc, "Non-header packet, we have all headers now");
1045       ret = gst_flac_enc_process_stream_headers (flacenc);
1046       flacenc->got_headers = TRUE;
1047     }
1048   }
1049
1050   if (flacenc->got_headers && samples == 0) {
1051     /* header fixup, push downstream directly */
1052     GST_DEBUG_OBJECT (flacenc, "Fixing up headers at pos=%" G_GUINT64_FORMAT
1053         ", size=%u", flacenc->offset, (guint) bytes);
1054 #if 0
1055     GST_MEMDUMP_OBJECT (flacenc, "Presumed header fragment",
1056         GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
1057 #endif
1058     ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (flacenc), outbuf);
1059   } else {
1060     /* regular frame data, pass to base class */
1061     GST_LOG ("Pushing buffer: ts=%" GST_TIME_FORMAT ", samples=%u, size=%u, "
1062         "pos=%" G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1063         samples, (guint) bytes, flacenc->offset);
1064     ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (flacenc),
1065         outbuf, samples);
1066   }
1067
1068   if (ret != GST_FLOW_OK)
1069     GST_DEBUG_OBJECT (flacenc, "flow: %s", gst_flow_get_name (ret));
1070
1071   flacenc->last_flow = ret;
1072
1073 out:
1074   flacenc->offset += bytes;
1075
1076   if (ret != GST_FLOW_OK)
1077     return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
1078
1079   return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
1080 }
1081
1082 static FLAC__StreamEncoderTellStatus
1083 gst_flac_enc_tell_callback (const FLAC__StreamEncoder * encoder,
1084     FLAC__uint64 * absolute_byte_offset, void *client_data)
1085 {
1086   GstFlacEnc *flacenc = GST_FLAC_ENC (client_data);
1087
1088   *absolute_byte_offset = flacenc->offset;
1089
1090   return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
1091 }
1092
1093 static gboolean
1094 gst_flac_enc_sink_event (GstAudioEncoder * enc, GstEvent * event)
1095 {
1096   GstFlacEnc *flacenc;
1097   GstTagList *taglist;
1098   gboolean ret = FALSE;
1099
1100   flacenc = GST_FLAC_ENC (enc);
1101
1102   GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event));
1103
1104   switch (GST_EVENT_TYPE (event)) {
1105     case GST_EVENT_EOS:
1106       flacenc->eos = TRUE;
1107       ret = GST_AUDIO_ENCODER_CLASS (parent_class)->event (enc, event);
1108       break;
1109     case GST_EVENT_TAG:
1110       if (flacenc->tags) {
1111         gst_event_parse_tag (event, &taglist);
1112         gst_tag_list_insert (flacenc->tags, taglist,
1113             gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (flacenc)));
1114       } else {
1115         g_assert_not_reached ();
1116       }
1117       ret = GST_AUDIO_ENCODER_CLASS (parent_class)->event (enc, event);
1118       break;
1119     default:
1120       ret = GST_AUDIO_ENCODER_CLASS (parent_class)->event (enc, event);
1121       break;
1122   }
1123
1124   return ret;
1125 }
1126
1127 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1128 #define READ_INT24 GST_READ_UINT24_LE
1129 #else
1130 #define READ_INT24 GST_READ_UINT24_BE
1131 #endif
1132
1133 static GstFlowReturn
1134 gst_flac_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer)
1135 {
1136   GstFlacEnc *flacenc;
1137   FLAC__int32 *data;
1138   gint samples, width, channels;
1139   gulong i;
1140   gint j;
1141   FLAC__bool res;
1142   GstMapInfo map;
1143   GstAudioInfo *info =
1144       gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (enc));
1145   gint *reorder_map;
1146
1147   flacenc = GST_FLAC_ENC (enc);
1148
1149   /* base class ensures configuration */
1150   g_return_val_if_fail (GST_AUDIO_INFO_WIDTH (info) != 0,
1151       GST_FLOW_NOT_NEGOTIATED);
1152
1153   width = GST_AUDIO_INFO_WIDTH (info);
1154   channels = GST_AUDIO_INFO_CHANNELS (info);
1155   reorder_map = flacenc->channel_reorder_map;
1156
1157   if (G_UNLIKELY (!buffer)) {
1158     if (flacenc->eos) {
1159       FLAC__stream_encoder_finish (flacenc->encoder);
1160     } else {
1161       /* can't handle intermittent draining/resyncing */
1162       GST_ELEMENT_WARNING (flacenc, STREAM, FORMAT, (NULL),
1163           ("Stream discontinuity detected. "
1164               "The output may have wrong timestamps, "
1165               "consider using audiorate to handle discontinuities"));
1166     }
1167     return flacenc->last_flow;
1168   }
1169
1170   gst_buffer_map (buffer, &map, GST_MAP_READ);
1171   samples = map.size / (width >> 3);
1172
1173   data = g_malloc (samples * sizeof (FLAC__int32));
1174
1175   samples /= channels;
1176   if (width == 8) {
1177     gint8 *indata = (gint8 *) map.data;
1178
1179     for (i = 0; i < samples; i++)
1180       for (j = 0; j < channels; j++)
1181         data[i * channels + reorder_map[j]] =
1182             (FLAC__int32) indata[i * channels + j];
1183   } else if (width == 16) {
1184     gint16 *indata = (gint16 *) map.data;
1185
1186     for (i = 0; i < samples; i++)
1187       for (j = 0; j < channels; j++)
1188         data[i * channels + reorder_map[j]] =
1189             (FLAC__int32) indata[i * channels + j];
1190   } else if (width == 24) {
1191     guint8 *indata = (guint8 *) map.data;
1192     guint32 val;
1193
1194     for (i = 0; i < samples; i++)
1195       for (j = 0; j < channels; j++) {
1196         val = READ_INT24 (&indata[3 * (i * channels + j)]);
1197         if (val & 0x00800000)
1198           val |= 0xff000000;
1199         data[i * channels + reorder_map[j]] = (FLAC__int32) val;
1200       }
1201   } else if (width == 32) {
1202     gint32 *indata = (gint32 *) map.data;
1203
1204     for (i = 0; i < samples; i++)
1205       for (j = 0; j < channels; j++)
1206         data[i * channels + reorder_map[j]] =
1207             (FLAC__int32) indata[i * channels + j];
1208   } else {
1209     g_assert_not_reached ();
1210   }
1211   gst_buffer_unmap (buffer, &map);
1212
1213   res = FLAC__stream_encoder_process_interleaved (flacenc->encoder,
1214       (const FLAC__int32 *) data, samples / channels);
1215
1216   g_free (data);
1217
1218   if (!res) {
1219     if (flacenc->last_flow == GST_FLOW_OK)
1220       return GST_FLOW_ERROR;
1221     else
1222       return flacenc->last_flow;
1223   }
1224
1225   return GST_FLOW_OK;
1226 }
1227
1228 static void
1229 gst_flac_enc_set_property (GObject * object, guint prop_id,
1230     const GValue * value, GParamSpec * pspec)
1231 {
1232   GstFlacEnc *this = GST_FLAC_ENC (object);
1233
1234   GST_OBJECT_LOCK (this);
1235
1236   switch (prop_id) {
1237     case PROP_QUALITY:
1238       gst_flac_enc_update_quality (this, g_value_get_enum (value));
1239       break;
1240     case PROP_STREAMABLE_SUBSET:
1241       FLAC__stream_encoder_set_streamable_subset (this->encoder,
1242           g_value_get_boolean (value));
1243       break;
1244     case PROP_MID_SIDE_STEREO:
1245       FLAC__stream_encoder_set_do_mid_side_stereo (this->encoder,
1246           g_value_get_boolean (value));
1247       break;
1248     case PROP_LOOSE_MID_SIDE_STEREO:
1249       FLAC__stream_encoder_set_loose_mid_side_stereo (this->encoder,
1250           g_value_get_boolean (value));
1251       break;
1252     case PROP_BLOCKSIZE:
1253       FLAC__stream_encoder_set_blocksize (this->encoder,
1254           g_value_get_uint (value));
1255       break;
1256     case PROP_MAX_LPC_ORDER:
1257       FLAC__stream_encoder_set_max_lpc_order (this->encoder,
1258           g_value_get_uint (value));
1259       break;
1260     case PROP_QLP_COEFF_PRECISION:
1261       FLAC__stream_encoder_set_qlp_coeff_precision (this->encoder,
1262           g_value_get_uint (value));
1263       break;
1264     case PROP_QLP_COEFF_PREC_SEARCH:
1265       FLAC__stream_encoder_set_do_qlp_coeff_prec_search (this->encoder,
1266           g_value_get_boolean (value));
1267       break;
1268     case PROP_ESCAPE_CODING:
1269       FLAC__stream_encoder_set_do_escape_coding (this->encoder,
1270           g_value_get_boolean (value));
1271       break;
1272     case PROP_EXHAUSTIVE_MODEL_SEARCH:
1273       FLAC__stream_encoder_set_do_exhaustive_model_search (this->encoder,
1274           g_value_get_boolean (value));
1275       break;
1276     case PROP_MIN_RESIDUAL_PARTITION_ORDER:
1277       FLAC__stream_encoder_set_min_residual_partition_order (this->encoder,
1278           g_value_get_uint (value));
1279       break;
1280     case PROP_MAX_RESIDUAL_PARTITION_ORDER:
1281       FLAC__stream_encoder_set_max_residual_partition_order (this->encoder,
1282           g_value_get_uint (value));
1283       break;
1284     case PROP_RICE_PARAMETER_SEARCH_DIST:
1285       FLAC__stream_encoder_set_rice_parameter_search_dist (this->encoder,
1286           g_value_get_uint (value));
1287       break;
1288     case PROP_PADDING:
1289       this->padding = g_value_get_uint (value);
1290       break;
1291     case PROP_SEEKPOINTS:
1292       this->seekpoints = g_value_get_int (value);
1293       break;
1294     default:
1295       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1296       break;
1297   }
1298
1299   GST_OBJECT_UNLOCK (this);
1300 }
1301
1302 static void
1303 gst_flac_enc_get_property (GObject * object, guint prop_id,
1304     GValue * value, GParamSpec * pspec)
1305 {
1306   GstFlacEnc *this = GST_FLAC_ENC (object);
1307
1308   GST_OBJECT_LOCK (this);
1309
1310   switch (prop_id) {
1311     case PROP_QUALITY:
1312       g_value_set_enum (value, this->quality);
1313       break;
1314     case PROP_STREAMABLE_SUBSET:
1315       g_value_set_boolean (value,
1316           FLAC__stream_encoder_get_streamable_subset (this->encoder));
1317       break;
1318     case PROP_MID_SIDE_STEREO:
1319       g_value_set_boolean (value,
1320           FLAC__stream_encoder_get_do_mid_side_stereo (this->encoder));
1321       break;
1322     case PROP_LOOSE_MID_SIDE_STEREO:
1323       g_value_set_boolean (value,
1324           FLAC__stream_encoder_get_loose_mid_side_stereo (this->encoder));
1325       break;
1326     case PROP_BLOCKSIZE:
1327       g_value_set_uint (value,
1328           FLAC__stream_encoder_get_blocksize (this->encoder));
1329       break;
1330     case PROP_MAX_LPC_ORDER:
1331       g_value_set_uint (value,
1332           FLAC__stream_encoder_get_max_lpc_order (this->encoder));
1333       break;
1334     case PROP_QLP_COEFF_PRECISION:
1335       g_value_set_uint (value,
1336           FLAC__stream_encoder_get_qlp_coeff_precision (this->encoder));
1337       break;
1338     case PROP_QLP_COEFF_PREC_SEARCH:
1339       g_value_set_boolean (value,
1340           FLAC__stream_encoder_get_do_qlp_coeff_prec_search (this->encoder));
1341       break;
1342     case PROP_ESCAPE_CODING:
1343       g_value_set_boolean (value,
1344           FLAC__stream_encoder_get_do_escape_coding (this->encoder));
1345       break;
1346     case PROP_EXHAUSTIVE_MODEL_SEARCH:
1347       g_value_set_boolean (value,
1348           FLAC__stream_encoder_get_do_exhaustive_model_search (this->encoder));
1349       break;
1350     case PROP_MIN_RESIDUAL_PARTITION_ORDER:
1351       g_value_set_uint (value,
1352           FLAC__stream_encoder_get_min_residual_partition_order
1353           (this->encoder));
1354       break;
1355     case PROP_MAX_RESIDUAL_PARTITION_ORDER:
1356       g_value_set_uint (value,
1357           FLAC__stream_encoder_get_max_residual_partition_order
1358           (this->encoder));
1359       break;
1360     case PROP_RICE_PARAMETER_SEARCH_DIST:
1361       g_value_set_uint (value,
1362           FLAC__stream_encoder_get_rice_parameter_search_dist (this->encoder));
1363       break;
1364     case PROP_PADDING:
1365       g_value_set_uint (value, this->padding);
1366       break;
1367     case PROP_SEEKPOINTS:
1368       g_value_set_int (value, this->seekpoints);
1369       break;
1370     default:
1371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1372       break;
1373   }
1374
1375   GST_OBJECT_UNLOCK (this);
1376 }