gstflacenc.c 42.3 KB
Newer Older
Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
1
/* GStreamer
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20
21
22
23
/* FIXME:
 *  - we assume timestamps start from 0 and that we get a perfect stream; we
 *    don't handle non-zero starts and mid-stream discontinuities, esp. not if
 *    we're muxing into ogg
24
25
26
 *  - need to support wider caps, flac can do 1-8 channels and 4-32 bit pcm
 *    http://flac.sourceforge.net/faq.html#general__channels
 *    it also support sampling rate from 1Hz - 655350Hz
27
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
28

29
30
31
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
32
33
34
35
#include <stdlib.h>
#include <string.h>

#include <gstflacenc.h>
36
#include <gst/audio/audio.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37
#include <gst/tag/tag.h>
38
#include <gst/gsttagsetter.h>
39

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40

41
42
static const GstElementDetails flacenc_details =
GST_ELEMENT_DETAILS ("FLAC audio encoder",
Wim Taymans's avatar
Wim Taymans committed
43
44
45
    "Codec/Encoder/Audio",
    "Encodes audio with the FLAC lossless audio encoder",
    "Wim Taymans <wim.taymans@chello.be>");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
46

47
48
49
50
51
52
#define FLAC_SINK_CAPS \
  "audio/x-raw-int, "               \
  "endianness = (int) BYTE_ORDER, " \
  "signed = (boolean) TRUE, "       \
  "width = (int) 16, "              \
  "depth = (int) 16, "              \
53
  "rate = (int) [ 8000, 96000 ], " \
54
  "channels = (int) [ 1, 2 ]"
55
56
57
58
59
60
61
62
63
64

static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("audio/x-flac")
    );

static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
65
    GST_STATIC_CAPS (FLAC_SINK_CAPS)
66
67
    );

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68
69
enum
{
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  PROP_0,
  PROP_QUALITY,
  PROP_STREAMABLE_SUBSET,
  PROP_MID_SIDE_STEREO,
  PROP_LOOSE_MID_SIDE_STEREO,
  PROP_BLOCKSIZE,
  PROP_MAX_LPC_ORDER,
  PROP_QLP_COEFF_PRECISION,
  PROP_QLP_COEFF_PREC_SEARCH,
  PROP_ESCAPE_CODING,
  PROP_EXHAUSTIVE_MODEL_SEARCH,
  PROP_MIN_RESIDUAL_PARTITION_ORDER,
  PROP_MAX_RESIDUAL_PARTITION_ORDER,
  PROP_RICE_PARAMETER_SEARCH_DIST
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84
85
};

86
87
88
GST_DEBUG_CATEGORY_STATIC (flacenc_debug);
#define GST_CAT_DEFAULT flacenc_debug

89
90
91
92
93
94
95
96
97
98
99
100

#define _do_init(type)                                                          \
  G_STMT_START{                                                                 \
    static const GInterfaceInfo tag_setter_info = {                             \
      NULL,                                                                     \
      NULL,                                                                     \
      NULL                                                                      \
    };                                                                          \
    g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,                     \
                                 &tag_setter_info);                             \
  }G_STMT_END

101
GST_BOILERPLATE_FULL (GstFlacEnc, gst_flac_enc, GstElement, GST_TYPE_ELEMENT,
102
103
    _do_init);

104
static void gst_flac_enc_finalize (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
105

106
107
108
static gboolean gst_flac_enc_sink_setcaps (GstPad * pad, GstCaps * caps);
static gboolean gst_flac_enc_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109

110
111
112
static gboolean gst_flac_enc_update_quality (GstFlacEnc * flacenc,
    gint quality);
static void gst_flac_enc_set_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
113
    const GValue * value, GParamSpec * pspec);
114
static void gst_flac_enc_get_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115
    GValue * value, GParamSpec * pspec);
116
static GstStateChangeReturn gst_flac_enc_change_state (GstElement * element,
117
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118

119
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
120
static FLAC__StreamEncoderWriteStatus
121
gst_flac_enc_write_callback (const FLAC__SeekableStreamEncoder * encoder,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122
123
    const FLAC__byte buffer[], unsigned bytes,
    unsigned samples, unsigned current_frame, void *client_data);
124
static FLAC__SeekableStreamEncoderSeekStatus
125
gst_flac_enc_seek_callback (const FLAC__SeekableStreamEncoder * encoder,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
126
    FLAC__uint64 absolute_byte_offset, void *client_data);
127
static FLAC__SeekableStreamEncoderTellStatus
128
gst_flac_enc_tell_callback (const FLAC__SeekableStreamEncoder * encoder,
129
    FLAC__uint64 * absolute_byte_offset, void *client_data);
130
131
132
133
134
135
136
137
138
139
140
141
#else
static FLAC__StreamEncoderWriteStatus
gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder,
    const FLAC__byte buffer[], size_t bytes,
    unsigned samples, unsigned current_frame, void *client_data);
static FLAC__StreamEncoderSeekStatus
gst_flac_enc_seek_callback (const FLAC__StreamEncoder * encoder,
    FLAC__uint64 absolute_byte_offset, void *client_data);
static FLAC__StreamEncoderTellStatus
gst_flac_enc_tell_callback (const FLAC__StreamEncoder * encoder,
    FLAC__uint64 * absolute_byte_offset, void *client_data);
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143
144
145
146
147
148
149
150
151
152
153
154
155
typedef struct
{
  gboolean exhaustive_model_search;
  gboolean escape_coding;
  gboolean mid_side;
  gboolean loose_mid_side;
  guint qlp_coeff_precision;
  gboolean qlp_coeff_prec_search;
  guint min_residual_partition_order;
  guint max_residual_partition_order;
  guint rice_parameter_search_dist;
  guint max_lpc_order;
  guint blocksize;
156
}
157
GstFlacEncParams;
Wim Taymans's avatar
Wim Taymans committed
158

159
static const GstFlacEncParams flacenc_params[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
160
161
162
163
164
165
166
167
168
169
  {FALSE, FALSE, FALSE, FALSE, 0, FALSE, 2, 2, 0, 0, 1152},
  {FALSE, FALSE, TRUE, TRUE, 0, FALSE, 2, 2, 0, 0, 1152},
  {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 0, 3, 0, 0, 1152},
  {FALSE, FALSE, FALSE, FALSE, 0, FALSE, 3, 3, 0, 6, 4608},
  {FALSE, FALSE, TRUE, TRUE, 0, FALSE, 3, 3, 0, 8, 4608},
  {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 3, 3, 0, 8, 4608},
  {FALSE, FALSE, TRUE, FALSE, 0, FALSE, 0, 4, 0, 8, 4608},
  {TRUE, FALSE, TRUE, FALSE, 0, FALSE, 0, 6, 0, 8, 4608},
  {TRUE, FALSE, TRUE, FALSE, 0, FALSE, 0, 6, 0, 12, 4608},
  {TRUE, TRUE, TRUE, FALSE, 0, FALSE, 0, 16, 0, 32, 4608},
Wim Taymans's avatar
Wim Taymans committed
170
171
172
173
};

#define DEFAULT_QUALITY 5

174
#define GST_TYPE_FLAC_ENC_QUALITY (gst_flac_enc_quality_get_type ())
Wim Taymans's avatar
Wim Taymans committed
175
GType
176
gst_flac_enc_quality_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
177
178
{
  static GType qtype = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179

Wim Taymans's avatar
Wim Taymans committed
180
181
  if (qtype == 0) {
    static const GEnumValue values[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182
      {0, "0 - Fastest compression", "0"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
183
184
185
186
      {1, "1", "1"},
      {2, "2", "2"},
      {3, "3", "3"},
      {4, "4", "4"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
187
      {5, "5 - Default", "5"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
189
      {6, "6", "6"},
      {7, "7", "7"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
190
191
      {8, "8 - Highest compression", "8"},
      {9, "9 - Insane", "9"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
192
      {0, NULL, NULL}
Wim Taymans's avatar
Wim Taymans committed
193
    };
194

195
    qtype = g_enum_register_static ("GstFlacEncQuality", values);
Wim Taymans's avatar
Wim Taymans committed
196
197
198
199
  }
  return qtype;
}

200
static void
201
gst_flac_enc_base_init (gpointer g_class)
202
203
204
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

205
206
207
208
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&src_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&sink_factory));
209

210
  gst_element_class_set_details (element_class, &flacenc_details);
211
212
213

  GST_DEBUG_CATEGORY_INIT (flacenc_debug, "flacenc", 0,
      "Flac encoding element");
214
215
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216
static void
217
gst_flac_enc_class_init (GstFlacEncClass * klass)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
218
219
220
221
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
222
223
224
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

225
226
227
  gobject_class->set_property = gst_flac_enc_set_property;
  gobject_class->get_property = gst_flac_enc_get_property;
  gobject_class->finalize = gst_flac_enc_finalize;
Wim Taymans's avatar
Wim Taymans committed
228

229
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QUALITY,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
230
      g_param_spec_enum ("quality",
231
232
          "Quality",
          "Speed versus compression tradeoff",
233
          GST_TYPE_FLAC_ENC_QUALITY, DEFAULT_QUALITY, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
234
  g_object_class_install_property (G_OBJECT_CLASS (klass),
235
      PROP_STREAMABLE_SUBSET, g_param_spec_boolean ("streamable_subset",
236
237
238
          "Streamable subset",
          "true to limit encoder to generating a Subset stream, else false",
          TRUE, G_PARAM_READWRITE));
239
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MID_SIDE_STEREO,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
240
      g_param_spec_boolean ("mid_side_stereo", "Do mid side stereo",
241
242
          "Do mid side stereo (only for stereo input)",
          flacenc_params[DEFAULT_QUALITY].mid_side, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
243
  g_object_class_install_property (G_OBJECT_CLASS (klass),
244
      PROP_LOOSE_MID_SIDE_STEREO, g_param_spec_boolean ("loose_mid_side_stereo",
245
246
          "Loose mid side stereo", "Loose mid side stereo",
          flacenc_params[DEFAULT_QUALITY].loose_mid_side, G_PARAM_READWRITE));
247
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BLOCKSIZE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
      g_param_spec_uint ("blocksize", "Blocksize", "Blocksize in samples",
249
250
          FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE,
          flacenc_params[DEFAULT_QUALITY].blocksize, G_PARAM_READWRITE));
251
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_LPC_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252
      g_param_spec_uint ("max_lpc_order", "Max LPC order",
253
254
255
          "Max LPC order; 0 => use only fixed predictors", 0,
          FLAC__MAX_LPC_ORDER, flacenc_params[DEFAULT_QUALITY].max_lpc_order,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
256
  g_object_class_install_property (G_OBJECT_CLASS (klass),
257
      PROP_QLP_COEFF_PRECISION, g_param_spec_uint ("qlp_coeff_precision",
258
259
260
261
          "QLP coefficients precision",
          "Precision in bits of quantized linear-predictor coefficients; 0 = automatic",
          0, 32, flacenc_params[DEFAULT_QUALITY].qlp_coeff_precision,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
262
  g_object_class_install_property (G_OBJECT_CLASS (klass),
263
      PROP_QLP_COEFF_PREC_SEARCH, g_param_spec_boolean ("qlp_coeff_prec_search",
264
265
266
267
268
          "Do QLP coefficients precision search",
          "false = use qlp_coeff_precision, "
          "true = search around qlp_coeff_precision, take best",
          flacenc_params[DEFAULT_QUALITY].qlp_coeff_prec_search,
          G_PARAM_READWRITE));
269
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ESCAPE_CODING,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
270
      g_param_spec_boolean ("escape_coding", "Do Escape coding",
271
272
273
          "search for escape codes in the entropy coding stage "
          "for slightly better compression",
          flacenc_params[DEFAULT_QUALITY].escape_coding, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
274
  g_object_class_install_property (G_OBJECT_CLASS (klass),
275
      PROP_EXHAUSTIVE_MODEL_SEARCH,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
276
      g_param_spec_boolean ("exhaustive_model_search",
277
278
279
280
          "Do exhaustive model search",
          "do exhaustive search of LP coefficient quantization (expensive!)",
          flacenc_params[DEFAULT_QUALITY].exhaustive_model_search,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
  g_object_class_install_property (G_OBJECT_CLASS (klass),
282
      PROP_MIN_RESIDUAL_PARTITION_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
283
      g_param_spec_uint ("min_residual_partition_order",
284
285
286
287
          "Min residual partition order",
          "Min residual partition order (above 4 doesn't usually help much)", 0,
          16, flacenc_params[DEFAULT_QUALITY].min_residual_partition_order,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
288
  g_object_class_install_property (G_OBJECT_CLASS (klass),
289
      PROP_MAX_RESIDUAL_PARTITION_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
290
      g_param_spec_uint ("max_residual_partition_order",
291
292
293
294
          "Max residual partition order",
          "Max residual partition order (above 4 doesn't usually help much)", 0,
          16, flacenc_params[DEFAULT_QUALITY].max_residual_partition_order,
          G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295
  g_object_class_install_property (G_OBJECT_CLASS (klass),
296
      PROP_RICE_PARAMETER_SEARCH_DIST,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
297
      g_param_spec_uint ("rice_parameter_search_dist",
298
299
300
301
302
          "rice_parameter_search_dist",
          "0 = try only calc'd parameter k; else try all [k-dist..k+dist] "
          "parameters, use best", 0, FLAC__MAX_RICE_PARTITION_ORDER,
          flacenc_params[DEFAULT_QUALITY].rice_parameter_search_dist,
          G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
303

304
  gstelement_class->change_state = gst_flac_enc_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
305
306
}

Wim Taymans's avatar
Wim Taymans committed
307
static void
308
gst_flac_enc_init (GstFlacEnc * flacenc, GstFlacEncClass * klass)
Wim Taymans's avatar
Wim Taymans committed
309
{
310
311
312
313
314
  GstElementClass *eclass = GST_ELEMENT_CLASS (klass);

  flacenc->sinkpad =
      gst_pad_new_from_template (gst_element_class_get_pad_template (eclass,
          "sink"), "sink");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315
  gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->sinkpad);
316
317
318
  gst_pad_set_chain_function (flacenc->sinkpad, gst_flac_enc_chain);
  gst_pad_set_event_function (flacenc->sinkpad, gst_flac_enc_sink_event);
  gst_pad_set_setcaps_function (flacenc->sinkpad, gst_flac_enc_sink_setcaps);
Wim Taymans's avatar
Wim Taymans committed
319

320
321
322
323
  flacenc->srcpad =
      gst_pad_new_from_template (gst_element_class_get_pad_template (eclass,
          "src"), "src");
  gst_pad_use_fixed_caps (flacenc->srcpad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
324
  gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->srcpad);
Wim Taymans's avatar
Wim Taymans committed
325

326
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
327
  flacenc->encoder = FLAC__seekable_stream_encoder_new ();
328
329
330
#else
  flacenc->encoder = FLAC__stream_encoder_new ();
#endif
Wim Taymans's avatar
Wim Taymans committed
331

332
  flacenc->offset = 0;
333
  flacenc->samples_written = 0;
334
  gst_flac_enc_update_quality (flacenc, DEFAULT_QUALITY);
335
  flacenc->tags = gst_tag_list_new ();
336
337
  flacenc->got_headers = FALSE;
  flacenc->headers = NULL;
Wim Taymans's avatar
Wim Taymans committed
338
339
340
}

static void
341
gst_flac_enc_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
342
{
343
  GstFlacEnc *flacenc = GST_FLAC_ENC (object);
Wim Taymans's avatar
Wim Taymans committed
344

345
  gst_tag_list_free (flacenc->tags);
346
#ifdef LEGACY_FLAC
347
  FLAC__seekable_stream_encoder_delete (flacenc->encoder);
348
349
350
#else
  FLAC__stream_encoder_delete (flacenc->encoder);
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351

352
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
353
354
}

355
356
357
358
359
static void
add_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
{
  GList *comments;
  GList *it;
360
  GstFlacEnc *flacenc = GST_FLAC_ENC (user_data);
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

  comments = gst_tag_to_vorbis_comments (list, tag);
  for (it = comments; it != NULL; it = it->next) {
    FLAC__StreamMetadata_VorbisComment_Entry commment_entry;

    commment_entry.length = strlen (it->data);
    commment_entry.entry = it->data;
    FLAC__metadata_object_vorbiscomment_insert_comment (flacenc->meta[0],
        flacenc->meta[0]->data.vorbis_comment.num_comments,
        commment_entry, TRUE);
    g_free (it->data);
  }
  g_list_free (comments);
}

static void
377
gst_flac_enc_set_metadata (GstFlacEnc * flacenc)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
378
{
379
380
381
382
  const GstTagList *user_tags;
  GstTagList *copy;

  g_return_if_fail (flacenc != NULL);
383
  user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc));
384
385
386
387
  if ((flacenc->tags == NULL) && (user_tags == NULL)) {
    return;
  }
  copy = gst_tag_list_merge (user_tags, flacenc->tags,
388
      gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (flacenc)));
389
390
391
392
393
394
  flacenc->meta = g_malloc (sizeof (FLAC__StreamMetadata **));

  flacenc->meta[0] =
      FLAC__metadata_object_new (FLAC__METADATA_TYPE_VORBIS_COMMENT);
  gst_tag_list_foreach (copy, add_one_tag, flacenc);

395
#ifdef LEGACY_FLAC
396
397
  if (FLAC__seekable_stream_encoder_set_metadata (flacenc->encoder,
          flacenc->meta, 1) != true)
398
399
400
401
#else
  if (FLAC__stream_encoder_set_metadata (flacenc->encoder,
          flacenc->meta, 1) != true)
#endif
402
403
404
405
406
    g_warning ("Dude, i'm already initialized!");
  gst_tag_list_free (copy);
}

static gboolean
407
gst_flac_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
408
409
{
  GstFlacEnc *flacenc;
David Schleef's avatar
David Schleef committed
410
  GstStructure *structure;
411
412

#ifdef LEGACY_FLAC
413
  FLAC__SeekableStreamEncoderState state;
414
415
416
#else
  FLAC__StreamEncoderInitStatus init_status;
#endif
417
  gint depth, chans, rate, width;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
418

419
  flacenc = GST_FLAC_ENC (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420

421
#ifdef LEGACY_FLAC
422
423
  if (FLAC__seekable_stream_encoder_get_state (flacenc->encoder) !=
      FLAC__SEEKABLE_STREAM_ENCODER_UNINITIALIZED)
424
425
426
427
428
#else
  if (FLAC__stream_encoder_get_state (flacenc->encoder) !=
      FLAC__STREAM_ENCODER_UNINITIALIZED)
#endif

429
430
    goto encoder_already_initialized;

David Schleef's avatar
David Schleef committed
431
  structure = gst_caps_get_structure (caps, 0);
432

433
434
435
436
437
438
439
440
441
442
443
  if (!gst_structure_get_int (structure, "channels", &chans) ||
      !gst_structure_get_int (structure, "width", &width) ||
      !gst_structure_get_int (structure, "depth", &depth) ||
      !gst_structure_get_int (structure, "rate", &rate)) {
    GST_DEBUG_OBJECT (flacenc, "incomplete caps: %" GST_PTR_FORMAT, caps);
    return FALSE;
  }

  flacenc->channels = chans;
  flacenc->depth = depth;
  flacenc->sample_rate = rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444

David Schleef's avatar
David Schleef committed
445
446
447
  caps = gst_caps_new_simple ("audio/x-flac",
      "channels", G_TYPE_INT, flacenc->channels,
      "rate", G_TYPE_INT, flacenc->sample_rate, NULL);
448

449
450
451
452
  if (!gst_pad_set_caps (flacenc->srcpad, caps))
    goto setting_src_caps_failed;

  gst_caps_unref (caps);
453

454
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455
456
457
458
459
460
  FLAC__seekable_stream_encoder_set_bits_per_sample (flacenc->encoder,
      flacenc->depth);
  FLAC__seekable_stream_encoder_set_sample_rate (flacenc->encoder,
      flacenc->sample_rate);
  FLAC__seekable_stream_encoder_set_channels (flacenc->encoder,
      flacenc->channels);
Wim Taymans's avatar
Wim Taymans committed
461

462
  FLAC__seekable_stream_encoder_set_write_callback (flacenc->encoder,
463
      gst_flac_enc_write_callback);
464
  FLAC__seekable_stream_encoder_set_seek_callback (flacenc->encoder,
465
      gst_flac_enc_seek_callback);
466
  FLAC__seekable_stream_encoder_set_tell_callback (flacenc->encoder,
467
      gst_flac_enc_tell_callback);
468

469
  FLAC__seekable_stream_encoder_set_client_data (flacenc->encoder, flacenc);
470
471
472
473
474
#else
  FLAC__stream_encoder_set_bits_per_sample (flacenc->encoder, flacenc->depth);
  FLAC__stream_encoder_set_sample_rate (flacenc->encoder, flacenc->sample_rate);
  FLAC__stream_encoder_set_channels (flacenc->encoder, flacenc->channels);
#endif
475

476
  gst_flac_enc_set_metadata (flacenc);
477

478
#ifdef LEGACY_FLAC
479
480
481
  state = FLAC__seekable_stream_encoder_init (flacenc->encoder);
  if (state != FLAC__STREAM_ENCODER_OK)
    goto failed_to_initialize;
482
483
484
485
486
487
488
#else
  init_status = FLAC__stream_encoder_init_stream (flacenc->encoder,
      gst_flac_enc_write_callback, gst_flac_enc_seek_callback,
      gst_flac_enc_tell_callback, NULL, flacenc);
  if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
    goto failed_to_initialize;
#endif
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514

  gst_object_unref (flacenc);

  return TRUE;

encoder_already_initialized:
  {
    g_warning ("flac already initialized -- fixme allow this");
    gst_object_unref (flacenc);
    return FALSE;
  }
setting_src_caps_failed:
  {
    GST_DEBUG_OBJECT (flacenc,
        "Couldn't set caps on source pad: %" GST_PTR_FORMAT, caps);
    gst_caps_unref (caps);
    gst_object_unref (flacenc);
    return FALSE;
  }
failed_to_initialize:
  {
    GST_ELEMENT_ERROR (flacenc, LIBRARY, INIT, (NULL),
        ("could not initialize encoder (wrong parameters?)"));
    gst_object_unref (flacenc);
    return FALSE;
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
515
516
}

Wim Taymans's avatar
Wim Taymans committed
517
static gboolean
518
gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
519
{
Wim Taymans's avatar
Wim Taymans committed
520
521
  flacenc->quality = quality;

522
#ifdef LEGACY_FLAC
523
524
525
526
527
528
529
530
#define DO_UPDATE(name, val, str)                                               \
  G_STMT_START {                                                                \
    if (FLAC__seekable_stream_encoder_get_##name (flacenc->encoder) !=          \
        flacenc_params[quality].val) {                                          \
      FLAC__seekable_stream_encoder_set_##name (flacenc->encoder,               \
          flacenc_params[quality].val);                                         \
      g_object_notify (G_OBJECT (flacenc), str);                                \
    }                                                                           \
531
  } G_STMT_END
Wim Taymans's avatar
Wim Taymans committed
532

533
534
535
536
537
538
539
540
541
542
543
544
545
#else
#define DO_UPDATE(name, val, str)                                               \
  G_STMT_START {                                                                \
    if (FLAC__stream_encoder_get_##name (flacenc->encoder) !=                   \
        flacenc_params[quality].val) {                                          \
      FLAC__stream_encoder_set_##name (flacenc->encoder,                        \
          flacenc_params[quality].val);                                         \
      g_object_notify (G_OBJECT (flacenc), str);                                \
    }                                                                           \
  } G_STMT_END

#endif

Wim Taymans's avatar
Wim Taymans committed
546
547
  g_object_freeze_notify (G_OBJECT (flacenc));

548
549
550
551
552
  if (flacenc->channels == 2) {
    DO_UPDATE (do_mid_side_stereo, mid_side, "mid_side_stereo");
    DO_UPDATE (loose_mid_side_stereo, loose_mid_side, "loose_mid_side");
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  DO_UPDATE (blocksize, blocksize, "blocksize");
  DO_UPDATE (max_lpc_order, max_lpc_order, "max_lpc_order");
  DO_UPDATE (qlp_coeff_precision, qlp_coeff_precision, "qlp_coeff_precision");
  DO_UPDATE (do_qlp_coeff_prec_search, qlp_coeff_prec_search,
      "qlp_coeff_prec_search");
  DO_UPDATE (do_escape_coding, escape_coding, "escape_coding");
  DO_UPDATE (do_exhaustive_model_search, exhaustive_model_search,
      "exhaustive_model_search");
  DO_UPDATE (min_residual_partition_order, min_residual_partition_order,
      "min_residual_partition_order");
  DO_UPDATE (max_residual_partition_order, max_residual_partition_order,
      "max_residual_partition_order");
  DO_UPDATE (rice_parameter_search_dist, rice_parameter_search_dist,
      "rice_parameter_search_dist");
Wim Taymans's avatar
Wim Taymans committed
567
568
569
570
571
572

#undef DO_UPDATE

  g_object_thaw_notify (G_OBJECT (flacenc));

  return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
573
574
}

575
#ifdef LEGACY_FLAC
576
static FLAC__SeekableStreamEncoderSeekStatus
577
gst_flac_enc_seek_callback (const FLAC__SeekableStreamEncoder * encoder,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
578
    FLAC__uint64 absolute_byte_offset, void *client_data)
579
580
581
582
583
#else
static FLAC__StreamEncoderSeekStatus
gst_flac_enc_seek_callback (const FLAC__StreamEncoder * encoder,
    FLAC__uint64 absolute_byte_offset, void *client_data)
#endif
584
{
585
  GstFlacEnc *flacenc;
586
587
  GstEvent *event;
  GstPad *peerpad;
588

589
  flacenc = GST_FLAC_ENC (client_data);
590

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
591
  if (flacenc->stopped)
592
#ifdef LEGACY_FLAC
593
    return FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK;
594
595
596
#else
    return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
#endif
597
  event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES,
598
      absolute_byte_offset, GST_BUFFER_OFFSET_NONE, 0);
599

600
601
  if ((peerpad = gst_pad_get_peer (flacenc->srcpad))) {
    gboolean ret = gst_pad_send_event (peerpad, event);
602

603
    gst_object_unref (peerpad);
604

605
606
607
608
609
610
611
    if (ret) {
      GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s", absolute_byte_offset,
          "succeeded");
    } else {
      GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s", absolute_byte_offset,
          "failed");
    }
612
613
614
  } else {
    GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " failed (no peer pad)",
        absolute_byte_offset);
615
  }
616
617

  flacenc->offset = absolute_byte_offset;
618
#ifdef LEGACY_FLAC
619
  return FLAC__SEEKABLE_STREAM_ENCODER_SEEK_STATUS_OK;
620
621
622
#else
  return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
#endif
623
624
}

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
static void
notgst_value_array_append_buffer (GValue * array_val, GstBuffer * buf)
{
  GValue value = { 0, };

  g_value_init (&value, GST_TYPE_BUFFER);
  /* copy buffer to avoid problems with circular refcounts */
  buf = gst_buffer_copy (buf);
  /* again, for good measure */
  GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
  gst_value_set_buffer (&value, buf);
  gst_buffer_unref (buf);
  gst_value_array_append_value (array_val, &value);
  g_value_unset (&value);
}

#define HDR_TYPE_STREAMINFO     0
#define HDR_TYPE_VORBISCOMMENT  4

static void
gst_flac_enc_process_stream_headers (GstFlacEnc * enc)
{
  GstBuffer *vorbiscomment = NULL;
  GstBuffer *streaminfo = NULL;
  GstBuffer *marker = NULL;
  GValue array = { 0, };
  GstCaps *caps;
  GList *l;

  caps = gst_caps_new_simple ("audio/x-flac",
      "channels", G_TYPE_INT, enc->channels,
      "rate", G_TYPE_INT, enc->sample_rate, NULL);

  for (l = enc->headers; l != NULL; l = l->next) {
    const guint8 *data;
    guint size;

    /* mark buffers so oggmux will ignore them if it already muxed the
     * header buffers from the streamheaders field in the caps */
    l->data = gst_buffer_make_metadata_writable (GST_BUFFER (l->data));
    GST_BUFFER_FLAG_SET (GST_BUFFER (l->data), GST_BUFFER_FLAG_IN_CAPS);

    data = GST_BUFFER_DATA (GST_BUFFER_CAST (l->data));
    size = GST_BUFFER_SIZE (GST_BUFFER_CAST (l->data));

    /* find initial 4-byte marker which we need to skip later on */
    if (size == 4 && memcmp (data, "fLaC", 4) == 0) {
      marker = GST_BUFFER_CAST (l->data);
    } else if (size > 1 && (data[0] & 0x7f) == HDR_TYPE_STREAMINFO) {
      streaminfo = GST_BUFFER_CAST (l->data);
    } else if (size > 1 && (data[0] & 0x7f) == HDR_TYPE_VORBISCOMMENT) {
      vorbiscomment = GST_BUFFER_CAST (l->data);
    }
  }

  if (marker == NULL || streaminfo == NULL || vorbiscomment == NULL) {
    GST_WARNING_OBJECT (enc, "missing header %p %p %p, muxing into container "
        "formats may be broken", marker, streaminfo, vorbiscomment);
    goto push_headers;
  }

  g_value_init (&array, GST_TYPE_ARRAY);

  /* add marker including STREAMINFO header */
  {
    GstBuffer *buf;
    guint16 num;

    /* minus one for the marker that is merged with streaminfo here */
    num = g_list_length (enc->headers) - 1;

    buf = gst_buffer_new_and_alloc (13 + GST_BUFFER_SIZE (streaminfo));
    GST_BUFFER_DATA (buf)[0] = 0x7f;
    memcpy (GST_BUFFER_DATA (buf) + 1, "FLAC", 4);
    GST_BUFFER_DATA (buf)[5] = 0x01;    /* mapping version major */
    GST_BUFFER_DATA (buf)[6] = 0x00;    /* mapping version minor */
    GST_BUFFER_DATA (buf)[7] = (num & 0xFF00) >> 8;
    GST_BUFFER_DATA (buf)[8] = (num & 0x00FF) >> 0;
    memcpy (GST_BUFFER_DATA (buf) + 9, "fLaC", 4);
    memcpy (GST_BUFFER_DATA (buf) + 13, GST_BUFFER_DATA (streaminfo),
        GST_BUFFER_SIZE (streaminfo));
    notgst_value_array_append_buffer (&array, buf);
    gst_buffer_unref (buf);
  }

  /* add VORBISCOMMENT header */
  notgst_value_array_append_buffer (&array, vorbiscomment);

  /* add other headers, if there are any */
  for (l = enc->headers; l != NULL; l = l->next) {
    if (GST_BUFFER_CAST (l->data) != marker &&
        GST_BUFFER_CAST (l->data) != streaminfo &&
        GST_BUFFER_CAST (l->data) != vorbiscomment) {
      notgst_value_array_append_buffer (&array, GST_BUFFER_CAST (l->data));
    }
  }

  gst_structure_set_value (gst_caps_get_structure (caps, 0),
      "streamheader", &array);
  g_value_unset (&array);

push_headers:

  gst_pad_set_caps (enc->srcpad, caps);

  /* push header buffers; update caps, so when we push the first buffer the
   * negotiated caps will change to caps that include the streamheader field */
  for (l = enc->headers; l != NULL; l = l->next) {
    GstBuffer *buf;

    buf = GST_BUFFER (l->data);
    gst_buffer_set_caps (buf, caps);
    GST_LOG ("Pushing header buffer, size %u bytes", GST_BUFFER_SIZE (buf));
    (void) gst_pad_push (enc->srcpad, buf);
    l->data = NULL;
  }
  g_list_free (enc->headers);
  enc->headers = NULL;

  gst_caps_unref (caps);
}

747
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
748
static FLAC__StreamEncoderWriteStatus
749
gst_flac_enc_write_callback (const FLAC__SeekableStreamEncoder * encoder,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
750
751
    const FLAC__byte buffer[], unsigned bytes,
    unsigned samples, unsigned current_frame, void *client_data)
752
753
754
755
756
757
#else
static FLAC__StreamEncoderWriteStatus
gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder,
    const FLAC__byte buffer[], size_t bytes,
    unsigned samples, unsigned current_frame, void *client_data)
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
758
{
759
  GstFlowReturn ret = GST_FLOW_OK;
760
  GstFlacEnc *flacenc;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
761
  GstBuffer *outbuf;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
762

763
  flacenc = GST_FLAC_ENC (client_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
764

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
765
  if (flacenc->stopped)
766
    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
Wim Taymans's avatar
Wim Taymans committed
767

768
  outbuf = gst_buffer_new_and_alloc (bytes);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
769
770
  memcpy (GST_BUFFER_DATA (outbuf), buffer, bytes);

771
  if (samples > 0 && flacenc->samples_written != (guint64) - 1) {
772
773
    guint64 granulepos;

774
775
776
777
778
779
    GST_BUFFER_TIMESTAMP (outbuf) =
        GST_FRAMES_TO_CLOCK_TIME (flacenc->samples_written,
        flacenc->sample_rate);
    GST_BUFFER_DURATION (outbuf) =
        GST_FRAMES_TO_CLOCK_TIME (samples, flacenc->sample_rate);
    /* offset_end = granulepos for ogg muxer */
780
781
782
783
784
785
    granulepos = flacenc->samples_written + samples;
    GST_BUFFER_OFFSET_END (outbuf) = granulepos;
    /* offset = timestamp corresponding to granulepos for ogg muxer
     * (see vorbisenc for a much more elaborate version of this) */
    GST_BUFFER_OFFSET (outbuf) =
        GST_FRAMES_TO_CLOCK_TIME (granulepos, flacenc->sample_rate);
786
787
788
789
790
  } else {
    GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
    GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
  }

791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  /* we assume libflac passes us stuff neatly framed */
  if (!flacenc->got_headers) {
    if (samples == 0) {
      GST_DEBUG_OBJECT (flacenc, "Got header, queueing (%u bytes)", bytes);
      flacenc->headers = g_list_append (flacenc->headers, outbuf);
      /* note: it's important that we increase our byte offset */
      goto out;
    } else {
      GST_INFO_OBJECT (flacenc, "Non-header packet, we have all headers now");
      gst_flac_enc_process_stream_headers (flacenc);
      flacenc->got_headers = TRUE;
    }
  }

  GST_LOG ("Pushing buffer: ts=%" GST_TIME_FORMAT ", samples=%u, size=%u, "
806
807
808
      "pos=%" G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
      samples, bytes, flacenc->offset);

809
  gst_buffer_set_caps (outbuf, GST_PAD_CAPS (flacenc->srcpad));
810
811
  ret = gst_pad_push (flacenc->srcpad, outbuf);

812
813
out:

814
  flacenc->offset += bytes;
815
  flacenc->samples_written += samples;
816

817
  if (GST_FLOW_IS_FATAL (ret))
818
819
820
    return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;

  return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
821
822
}

823
#ifdef LEGACY_FLAC
824
static FLAC__SeekableStreamEncoderTellStatus
825
gst_flac_enc_tell_callback (const FLAC__SeekableStreamEncoder * encoder,
826
    FLAC__uint64 * absolute_byte_offset, void *client_data)
827
828
829
830
831
#else
static FLAC__StreamEncoderTellStatus
gst_flac_enc_tell_callback (const FLAC__StreamEncoder * encoder,
    FLAC__uint64 * absolute_byte_offset, void *client_data)
#endif
832
{
833
  GstFlacEnc *flacenc = GST_FLAC_ENC (client_data);
834
835

  *absolute_byte_offset = flacenc->offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836

837
#ifdef LEGACY_FLAC
838
  return FLAC__SEEKABLE_STREAM_ENCODER_TELL_STATUS_OK;
839
840
841
#else
  return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
842
843
}

844
static gboolean
845
gst_flac_enc_sink_event (GstPad * pad, GstEvent * event)
846
{
847
848
  GstFlacEnc *flacenc;
  GstTagList *taglist;
849
  gboolean ret = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
850

851
  flacenc = GST_FLAC_ENC (gst_pad_get_parent (pad));
852

853
854
  GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event));

855
  switch (GST_EVENT_TYPE (event)) {
856
857
858
859
860
    case GST_EVENT_NEWSEGMENT:{
      GstFormat format;
      gint64 start, stream_time;

      if (flacenc->offset == 0) {
861
        gst_event_parse_new_segment (event, NULL, NULL, &format, &start, NULL,
862
863
864
865
866
867
868
869
870
871
            &stream_time);
      } else {
        start = -1;
      }
      if (start != 0) {
        if (flacenc->offset > 0)
          GST_DEBUG ("Not handling mid-stream newsegment event");
        else
          GST_DEBUG ("Not handling newsegment event with non-zero start");
      } else {
872
        GstEvent *e = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
873
874
875
876
877
878
879
880
881
882
883
            0, -1, 0);

        ret = gst_pad_push_event (flacenc->srcpad, e);
      }
      if (stream_time != 0) {
        GST_DEBUG ("Not handling non-zero stream time");
      }
      gst_event_unref (event);
      /* don't push it downstream, we'll generate our own via seek to 0 */
      break;
    }
884
    case GST_EVENT_EOS:
885
#ifdef LEGACY_FLAC
886
      FLAC__seekable_stream_encoder_finish (flacenc->encoder);
887
888
889
#else
      FLAC__stream_encoder_finish (flacenc->encoder);
#endif
890
891
892
893
894
895
896
897
898
899
900
901
902
903
      ret = gst_pad_event_default (pad, event);
      break;
    case GST_EVENT_TAG:
      if (flacenc->tags) {
        gst_event_parse_tag (event, &taglist);
        gst_tag_list_insert (flacenc->tags, taglist, GST_TAG_MERGE_REPLACE);
      } else {
        g_assert_not_reached ();
      }
      ret = gst_pad_event_default (pad, event);
      break;
    default:
      ret = gst_pad_event_default (pad, event);
      break;
904
  }
905

906
  gst_object_unref (flacenc);
907

908
  return ret;
909
}
910

911
static GstFlowReturn
912
gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
913
{
914
  GstFlacEnc *flacenc;
Wim Taymans's avatar
Wim Taymans committed
915
  FLAC__int32 *data;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
916
  gulong insize;
Wim Taymans's avatar
Wim Taymans committed
917
918
919
  gint samples, depth;
  gulong i;
  FLAC__bool res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
920

921
922
  flacenc = GST_FLAC_ENC (GST_PAD_PARENT (pad));

923
  /* make sure setcaps has been called and the encoder is set up */
924
925
  if (G_UNLIKELY (flacenc->depth == 0))
    return GST_FLOW_NOT_NEGOTIATED;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
926
927
928

  depth = flacenc->depth;

929
  insize = GST_BUFFER_SIZE (buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
930
  samples = insize / ((depth + 7) >> 3);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
931

932
  data = g_malloc (samples * sizeof (FLAC__int32));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
933

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
934
  if (depth == 8) {
935
    gint8 *indata = (gint8 *) GST_BUFFER_DATA (buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
936

937
938
    for (i = 0; i < samples; i++)
      data[i] = (FLAC__int32) indata[i];
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
939
  } else if (depth == 16) {
940
    gint16 *indata = (gint16 *) GST_BUFFER_DATA (buffer);
Wim Taymans's avatar
Wim Taymans committed
941

942
943
944
945
    for (i = 0; i < samples; i++)
      data[i] = (FLAC__int32) indata[i];
  } else {
    g_assert_not_reached ();
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
946
947
  }

948
  gst_buffer_unref (buffer);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
949

950
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
951
952
  res = FLAC__seekable_stream_encoder_process_interleaved (flacenc->encoder,
      (const FLAC__int32 *) data, samples / flacenc->channels);
953
954
955
956
#else
  res = FLAC__stream_encoder_process_interleaved (flacenc->encoder,
      (const FLAC__int32 *) data, samples / flacenc->channels);
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
957

958
  g_free (data);
Wim Taymans's avatar
Wim Taymans committed
959

960
961
962
963
  if (res)
    return GST_FLOW_OK;
  else
    return GST_FLOW_ERROR;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
964
965
966
}

static void
967
gst_flac_enc_set_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
968
    const GValue * value, GParamSpec * pspec)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
969
{
970
  GstFlacEnc *this = GST_FLAC_ENC (object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
971

972
  GST_OBJECT_LOCK (this);
973

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
974
  switch (prop_id) {
975
    case PROP_QUALITY:
976
      gst_flac_enc_update_quality (this, g_value_get_enum (value));
Wim Taymans's avatar
Wim Taymans committed
977
      break;
978
    case PROP_STREAMABLE_SUBSET:
979
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
980
      FLAC__seekable_stream_encoder_set_streamable_subset (this->encoder,
981
          g_value_get_boolean (value));
982
983
984
985
#else
      FLAC__stream_encoder_set_streamable_subset (this->encoder,
          g_value_get_boolean (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
986
      break;
987
    case PROP_MID_SIDE_STEREO:
988
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
989
      FLAC__seekable_stream_encoder_set_do_mid_side_stereo (this->encoder,
990
          g_value_get_boolean (value));
991
992
993
994
#else
      FLAC__stream_encoder_set_do_mid_side_stereo (this->encoder,
          g_value_get_boolean (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
995
      break;
996
    case PROP_LOOSE_MID_SIDE_STEREO:
997
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
998
      FLAC__seekable_stream_encoder_set_loose_mid_side_stereo (this->encoder,
999
          g_value_get_boolean (value));
1000
1001
1002
1003
#else
      FLAC__stream_encoder_set_loose_mid_side_stereo (this->encoder,
          g_value_get_boolean (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
1004
      break;
1005
    case PROP_BLOCKSIZE:
1006
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1007
      FLAC__seekable_stream_encoder_set_blocksize (this->encoder,
1008
          g_value_get_uint (value));
1009
1010
1011
1012
#else
      FLAC__stream_encoder_set_blocksize (this->encoder,
          g_value_get_uint (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
1013
      break;
1014
    case PROP_MAX_LPC_ORDER:
1015
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1016
      FLAC__seekable_stream_encoder_set_max_lpc_order (this->encoder,
1017
          g_value_get_uint (value));
1018
1019
1020
1021
#else
      FLAC__stream_encoder_set_max_lpc_order (this->encoder,
          g_value_get_uint (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
1022
      break;
1023
    case PROP_QLP_COEFF_PRECISION:
1024
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1025
      FLAC__seekable_stream_encoder_set_qlp_coeff_precision (this->encoder,
1026
          g_value_get_uint (value));
1027
1028
1029
1030
#else
      FLAC__stream_encoder_set_qlp_coeff_precision (this->encoder,
          g_value_get_uint (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
1031
      break;
1032
    case PROP_QLP_COEFF_PREC_SEARCH:
1033
#ifdef LEGACY_FLAC
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1034
      FLAC__seekable_stream_encoder_set_do_qlp_coeff_prec_search (this->encoder,
1035
          g_value_get_boolean (value));
1036
1037
1038
1039
#else
      FLAC__stream_encoder_set_do_qlp_coeff_prec_search (this->encoder,
          g_value_get_boolean (value));
#endif
Wim Taymans's avatar
Wim Taymans committed
1040
      break;
1041
    case PROP_ESCAPE_CODING:
10