gstflacenc.c 48 KB
Newer Older
Andy Wingo's avatar
Andy 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
 * 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.
 */
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * SECTION:element-flacenc
 * @see_also: #GstFlacDec
 *
 * flacenc encodes FLAC streams.
 * <ulink url="http://flac.sourceforge.net/">FLAC</ulink>
 * is a Free Lossless Audio Codec.
 *
 * <refsect2>
 * <title>Example launch line</title>
 * |[
 * gst-launch audiotestsrc num-buffers=100 ! flacenc ! filesink location=beep.flac
 * ]|
 * </refsect2>
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
34

35
36
37
/* TODO: - We currently don't handle discontinuities in the stream in a useful
 *         way and instead rely on the developer plugging in audiorate if
 *         the stream contains discontinuities.
38
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
39

40
41
42
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
43
44
45
46
#include <stdlib.h>
#include <string.h>

#include <gstflacenc.h>
47
#include <gst/audio/audio.h>
48
#include <gst/audio/multichannel.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
49
#include <gst/tag/tag.h>
50
#include <gst/gsttagsetter.h>
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* Taken from http://flac.sourceforge.net/format.html#frame_header */
static const GstAudioChannelPosition channel_positions[8][8] = {
  {GST_AUDIO_CHANNEL_POSITION_FRONT_MONO},
  {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
      GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
      GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_LFE,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
      GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
  /* FIXME: 7/8 channel layouts are not defined in the FLAC specs */
  {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
        GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_LFE,
      GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
        GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
        GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
        GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
        GST_AUDIO_CHANNEL_POSITION_LFE,
        GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
      GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
};
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93

94
#define FLAC_SINK_CAPS \
95
96
97
98
99
100
101
  "audio/x-raw-int, "               \
  "endianness = (int) BYTE_ORDER, " \
  "signed = (boolean) TRUE, "       \
  "width = (int) 8, "               \
  "depth = (int) 8, "               \
  "rate = (int) [ 1, 655350 ], "    \
  "channels = (int) [ 1, 8 ]; "     \
102
103
104
105
  "audio/x-raw-int, "               \
  "endianness = (int) BYTE_ORDER, " \
  "signed = (boolean) TRUE, "       \
  "width = (int) 16, "              \
106
107
108
109
110
111
112
113
114
  "depth = (int) { 12, 16 }, "      \
  "rate = (int) [ 1, 655350 ], "    \
  "channels = (int) [ 1, 8 ]; "     \
  "audio/x-raw-int, "               \
  "endianness = (int) BYTE_ORDER, " \
  "signed = (boolean) TRUE, "       \
  "width = (int) 32, "              \
  "depth = (int) { 20, 24 }, "      \
  "rate = (int) [ 1, 655350 ], "    \
115
  "channels = (int) [ 1, 8 ]"
116
117
118
119
120
121
122
123
124
125

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,
126
    GST_STATIC_CAPS (FLAC_SINK_CAPS)
127
128
    );

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
130
enum
{
131
132
133
134
135
136
137
138
139
140
141
142
143
  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,
144
  PROP_RICE_PARAMETER_SEARCH_DIST,
145
146
  PROP_PADDING,
  PROP_SEEKPOINTS
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
147
148
};

149
150
151
GST_DEBUG_CATEGORY_STATIC (flacenc_debug);
#define GST_CAT_DEFAULT flacenc_debug

152
153
154
155
156
157
158
159

#define _do_init(type)                                                          \
  G_STMT_START{                                                                 \
    static const GInterfaceInfo tag_setter_info = {                             \
      NULL,                                                                     \
      NULL,                                                                     \
      NULL                                                                      \
    };                                                                          \
160
161
162
163
164
    static const GInterfaceInfo preset_info = {                                 \
      NULL,                                                                     \
      NULL,                                                                     \
      NULL                                                                      \
    };                                                                          \
165
166
    g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,                     \
                                 &tag_setter_info);                             \
167
168
    g_type_add_interface_static (type, GST_TYPE_PRESET,                         \
                                 &preset_info);                                 \
169
170
  }G_STMT_END

171
GST_BOILERPLATE_FULL (GstFlacEnc, gst_flac_enc, GstElement, GST_TYPE_ELEMENT,
172
173
    _do_init);

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

176
static gboolean gst_flac_enc_sink_setcaps (GstPad * pad, GstCaps * caps);
177
static GstCaps *gst_flac_enc_sink_getcaps (GstPad * pad);
178
179
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
180

181
182
183
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
184
    const GValue * value, GParamSpec * pspec);
185
static void gst_flac_enc_get_property (GObject * object, guint prop_id,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
186
    GValue * value, GParamSpec * pspec);
187
static GstStateChangeReturn gst_flac_enc_change_state (GstElement * element,
188
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
189

190
191
192
193
194
195
196
197
198
199
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);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
200

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
201
202
203
204
205
206
207
208
209
210
211
212
213
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;
214
}
215
GstFlacEncParams;
Wim Taymans's avatar
Wim Taymans committed
216

217
static const GstFlacEncParams flacenc_params[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
218
219
220
221
222
223
224
225
226
227
  {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
228
229
230
};

#define DEFAULT_QUALITY 5
231
#define DEFAULT_PADDING 0
232
#define DEFAULT_SEEKPOINTS 0
Wim Taymans's avatar
Wim Taymans committed
233

234
#define GST_TYPE_FLAC_ENC_QUALITY (gst_flac_enc_quality_get_type ())
235
static GType
236
gst_flac_enc_quality_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
237
238
{
  static GType qtype = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
239

Wim Taymans's avatar
Wim Taymans committed
240
241
  if (qtype == 0) {
    static const GEnumValue values[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
242
      {0, "0 - Fastest compression", "0"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
243
244
245
246
      {1, "1", "1"},
      {2, "2", "2"},
      {3, "3", "3"},
      {4, "4", "4"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
247
      {5, "5 - Default", "5"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
249
      {6, "6", "6"},
      {7, "7", "7"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
250
251
      {8, "8 - Highest compression", "8"},
      {9, "9 - Insane", "9"},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252
      {0, NULL, NULL}
Wim Taymans's avatar
Wim Taymans committed
253
    };
254

255
    qtype = g_enum_register_static ("GstFlacEncQuality", values);
Wim Taymans's avatar
Wim Taymans committed
256
257
258
259
  }
  return qtype;
}

260
static void
261
gst_flac_enc_base_init (gpointer g_class)
262
263
264
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

265
266
267
268
  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));
269

270
271
272
273
  gst_element_class_set_details_simple (element_class, "FLAC audio encoder",
      "Codec/Encoder/Audio",
      "Encodes audio with the FLAC lossless audio encoder",
      "Wim Taymans <wim.taymans@chello.be>");
274
275
276

  GST_DEBUG_CATEGORY_INIT (flacenc_debug, "flacenc", 0,
      "Flac encoding element");
277
278
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279
static void
280
gst_flac_enc_class_init (GstFlacEncClass * klass)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
282
283
284
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
285
286
287
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;

288
289
290
  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
291

292
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QUALITY,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
293
      g_param_spec_enum ("quality",
294
295
          "Quality",
          "Speed versus compression tradeoff",
296
297
          GST_TYPE_FLAC_ENC_QUALITY, DEFAULT_QUALITY,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
298
  g_object_class_install_property (G_OBJECT_CLASS (klass),
299
      PROP_STREAMABLE_SUBSET, g_param_spec_boolean ("streamable_subset",
300
301
          "Streamable subset",
          "true to limit encoder to generating a Subset stream, else false",
302
          TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
303
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MID_SIDE_STEREO,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
304
      g_param_spec_boolean ("mid_side_stereo", "Do mid side stereo",
305
          "Do mid side stereo (only for stereo input)",
306
307
          flacenc_params[DEFAULT_QUALITY].mid_side,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
308
  g_object_class_install_property (G_OBJECT_CLASS (klass),
309
      PROP_LOOSE_MID_SIDE_STEREO, g_param_spec_boolean ("loose_mid_side_stereo",
310
          "Loose mid side stereo", "Loose mid side stereo",
311
312
          flacenc_params[DEFAULT_QUALITY].loose_mid_side,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
313
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BLOCKSIZE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
      g_param_spec_uint ("blocksize", "Blocksize", "Blocksize in samples",
315
          FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE,
316
317
          flacenc_params[DEFAULT_QUALITY].blocksize,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
318
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_LPC_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
319
      g_param_spec_uint ("max_lpc_order", "Max LPC order",
320
321
          "Max LPC order; 0 => use only fixed predictors", 0,
          FLAC__MAX_LPC_ORDER, flacenc_params[DEFAULT_QUALITY].max_lpc_order,
322
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
323
  g_object_class_install_property (G_OBJECT_CLASS (klass),
324
      PROP_QLP_COEFF_PRECISION, g_param_spec_uint ("qlp_coeff_precision",
325
326
327
          "QLP coefficients precision",
          "Precision in bits of quantized linear-predictor coefficients; 0 = automatic",
          0, 32, flacenc_params[DEFAULT_QUALITY].qlp_coeff_precision,
328
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
329
  g_object_class_install_property (G_OBJECT_CLASS (klass),
330
      PROP_QLP_COEFF_PREC_SEARCH, g_param_spec_boolean ("qlp_coeff_prec_search",
331
332
333
334
          "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,
335
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
336
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ESCAPE_CODING,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
337
      g_param_spec_boolean ("escape_coding", "Do Escape coding",
338
339
          "search for escape codes in the entropy coding stage "
          "for slightly better compression",
340
341
          flacenc_params[DEFAULT_QUALITY].escape_coding,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
342
  g_object_class_install_property (G_OBJECT_CLASS (klass),
343
      PROP_EXHAUSTIVE_MODEL_SEARCH,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
344
      g_param_spec_boolean ("exhaustive_model_search",
345
346
347
          "Do exhaustive model search",
          "do exhaustive search of LP coefficient quantization (expensive!)",
          flacenc_params[DEFAULT_QUALITY].exhaustive_model_search,
348
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
349
  g_object_class_install_property (G_OBJECT_CLASS (klass),
350
      PROP_MIN_RESIDUAL_PARTITION_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351
      g_param_spec_uint ("min_residual_partition_order",
352
353
354
          "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,
355
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
356
  g_object_class_install_property (G_OBJECT_CLASS (klass),
357
      PROP_MAX_RESIDUAL_PARTITION_ORDER,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
358
      g_param_spec_uint ("max_residual_partition_order",
359
360
361
          "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,
362
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
363
  g_object_class_install_property (G_OBJECT_CLASS (klass),
364
      PROP_RICE_PARAMETER_SEARCH_DIST,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
365
      g_param_spec_uint ("rice_parameter_search_dist",
366
367
368
369
          "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,
370
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
Wim Taymans's avatar
Wim Taymans committed
371

372
373
374
375
376
377
378
379
380
381
382
383
384
385
  /**
   * GstFlacEnc:padding
   *
   * Write a PADDING block with this length in bytes
   *
   * Since: 0.10.16
   **/
  g_object_class_install_property (G_OBJECT_CLASS (klass),
      PROP_PADDING,
      g_param_spec_uint ("padding",
          "Padding",
          "Write a PADDING block with this length in bytes", 0, G_MAXUINT,
          DEFAULT_PADDING, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

386
  /**
387
   * GstFlacEnc:seekpoints
388
   *
389
390
   * Write a SEEKTABLE block with a specific number of seekpoints
   * or with a specific interval spacing.
391
392
393
394
395
396
397
398
399
400
401
   *
   * Since: 0.10.18
   **/
  g_object_class_install_property (G_OBJECT_CLASS (klass),
      PROP_SEEKPOINTS,
      g_param_spec_int ("seekpoints",
          "Seekpoints",
          "Add SEEKTABLE metadata (if > 0, number of entries, if < 0, interval in sec)",
          -G_MAXINT, G_MAXINT,
          DEFAULT_SEEKPOINTS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));

402
  gstelement_class->change_state = gst_flac_enc_change_state;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403
404
}

Wim Taymans's avatar
Wim Taymans committed
405
static void
406
gst_flac_enc_init (GstFlacEnc * flacenc, GstFlacEncClass * klass)
Wim Taymans's avatar
Wim Taymans committed
407
{
408
409
410
411
412
  flacenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
  gst_pad_set_chain_function (flacenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_flac_enc_chain));
  gst_pad_set_event_function (flacenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_flac_enc_sink_event));
413
414
  gst_pad_set_getcaps_function (flacenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_flac_enc_sink_getcaps));
415
416
  gst_pad_set_setcaps_function (flacenc->sinkpad,
      GST_DEBUG_FUNCPTR (gst_flac_enc_sink_setcaps));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417
  gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->sinkpad);
Wim Taymans's avatar
Wim Taymans committed
418

419
  flacenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
420
  gst_pad_use_fixed_caps (flacenc->srcpad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421
  gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->srcpad);
Wim Taymans's avatar
Wim Taymans committed
422

423
  flacenc->encoder = FLAC__stream_encoder_new ();
Wim Taymans's avatar
Wim Taymans committed
424

425
  flacenc->offset = 0;
426
  flacenc->samples_written = 0;
427
  flacenc->channels = 0;
428
  gst_flac_enc_update_quality (flacenc, DEFAULT_QUALITY);
429
  flacenc->tags = gst_tag_list_new ();
430
431
  flacenc->got_headers = FALSE;
  flacenc->headers = NULL;
432
  flacenc->last_flow = GST_FLOW_OK;
Wim Taymans's avatar
Wim Taymans committed
433
434
435
}

static void
436
gst_flac_enc_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
437
{
438
  GstFlacEnc *flacenc = GST_FLAC_ENC (object);
Wim Taymans's avatar
Wim Taymans committed
439

440
  gst_tag_list_free (flacenc->tags);
441
  FLAC__stream_encoder_delete (flacenc->encoder);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
442

443
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
444
445
}

446
447
448
449
450
static void
add_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
{
  GList *comments;
  GList *it;
451
  GstFlacEnc *flacenc = GST_FLAC_ENC (user_data);
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467

  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
468
gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
469
{
470
471
  const GstTagList *user_tags;
  GstTagList *copy;
472
  gint entries = 1;
473
474

  g_return_if_fail (flacenc != NULL);
475
  user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc));
476
477
478
479
  if ((flacenc->tags == NULL) && (user_tags == NULL)) {
    return;
  }
  copy = gst_tag_list_merge (user_tags, flacenc->tags,
480
      gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (flacenc)));
481
  flacenc->meta = g_new0 (FLAC__StreamMetadata *, 3);
482
483
484
485
486

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

487
488
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
  if (flacenc->seekpoints && total_samples != GST_CLOCK_TIME_NONE) {
    gboolean res;
    guint samples;

    flacenc->meta[1] =
        FLAC__metadata_object_new (FLAC__METADATA_TYPE_SEEKTABLE);
    if (flacenc->seekpoints > 0) {
      res =
          FLAC__metadata_object_seektable_template_append_spaced_points
          (flacenc->meta[1], flacenc->seekpoints, total_samples);
    } else {
      samples = -flacenc->seekpoints * flacenc->sample_rate;
      res =
          FLAC__metadata_object_seektable_template_append_spaced_points_by_samples
          (flacenc->meta[1], samples, total_samples);
    }
    if (!res) {
      GST_DEBUG_OBJECT (flacenc, "adding seekpoint template %d failed",
          flacenc->seekpoints);
      FLAC__metadata_object_delete (flacenc->meta[1]);
      flacenc->meta[1] = NULL;
    } else {
      entries++;
    }
  } else if (flacenc->seekpoints && total_samples == GST_CLOCK_TIME_NONE) {
    GST_WARNING_OBJECT (flacenc, "total time unknown; can not add seekpoints");
  }

515
  if (flacenc->padding > 0) {
516
517
518
519
    flacenc->meta[entries] =
        FLAC__metadata_object_new (FLAC__METADATA_TYPE_PADDING);
    flacenc->meta[entries]->length = flacenc->padding;
    entries++;
520
521
  }

522
  if (FLAC__stream_encoder_set_metadata (flacenc->encoder,
523
          flacenc->meta, entries) != true)
524
    g_warning ("Dude, i'm already initialized!");
525

526
527
528
  gst_tag_list_free (copy);
}

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
static void
gst_flac_enc_caps_append_structure_with_widths (GstCaps * caps,
    GstStructure * s)
{
  GstStructure *tmp;
  GValue list = { 0, };
  GValue depth = { 0, };


  tmp = gst_structure_copy (s);
  gst_structure_set (tmp, "width", G_TYPE_INT, 8, "depth", G_TYPE_INT, 8, NULL);
  gst_caps_append_structure (caps, tmp);

  tmp = gst_structure_copy (s);

  g_value_init (&depth, G_TYPE_INT);
  g_value_init (&list, GST_TYPE_LIST);
  g_value_set_int (&depth, 12);
  gst_value_list_append_value (&list, &depth);
  g_value_set_int (&depth, 16);
  gst_value_list_append_value (&list, &depth);

  gst_structure_set (tmp, "width", G_TYPE_INT, 16, NULL);
  gst_structure_set_value (tmp, "depth", &list);
  gst_caps_append_structure (caps, tmp);

  g_value_reset (&list);

  tmp = s;

  g_value_set_int (&depth, 20);
  gst_value_list_append_value (&list, &depth);
  g_value_set_int (&depth, 24);
  gst_value_list_append_value (&list, &depth);

  gst_structure_set (tmp, "width", G_TYPE_INT, 32, NULL);
  gst_structure_set_value (tmp, "depth", &list);
  gst_caps_append_structure (caps, tmp);

  g_value_unset (&list);
  g_value_unset (&depth);
}

572
573
574
575
576
577
578
579
580
581
582
583
584
585
static GstCaps *
gst_flac_enc_sink_getcaps (GstPad * pad)
{
  GstCaps *ret = NULL;

  GST_OBJECT_LOCK (pad);

  if (GST_PAD_CAPS (pad)) {
    ret = gst_caps_ref (GST_PAD_CAPS (pad));
  } else {
    gint i, c;

    ret = gst_caps_new_empty ();

586
587
    gst_flac_enc_caps_append_structure_with_widths (ret,
        gst_structure_new ("audio/x-raw-int",
588
589
            "endianness", G_TYPE_INT, G_BYTE_ORDER,
            "signed", G_TYPE_BOOLEAN, TRUE,
590
            "rate", GST_TYPE_INT_RANGE, 1, 655350,
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
            "channels", GST_TYPE_INT_RANGE, 1, 2, NULL));

    for (i = 3; i <= 8; i++) {
      GValue positions = { 0, };
      GValue pos = { 0, };
      GstStructure *s;

      g_value_init (&positions, GST_TYPE_ARRAY);
      g_value_init (&pos, GST_TYPE_AUDIO_CHANNEL_POSITION);

      for (c = 0; c < i; c++) {
        g_value_set_enum (&pos, channel_positions[i - 1][c]);
        gst_value_array_append_value (&positions, &pos);
      }
      g_value_unset (&pos);

      s = gst_structure_new ("audio/x-raw-int",
          "endianness", G_TYPE_INT, G_BYTE_ORDER,
          "signed", G_TYPE_BOOLEAN, TRUE,
610
          "rate", GST_TYPE_INT_RANGE, 1, 655350,
611
612
613
614
          "channels", G_TYPE_INT, i, NULL);
      gst_structure_set_value (s, "channel-positions", &positions);
      g_value_unset (&positions);

615
      gst_flac_enc_caps_append_structure_with_widths (ret, s);
616
617
618
619
620
621
622
623
624
625
    }
  }

  GST_OBJECT_UNLOCK (pad);

  GST_DEBUG_OBJECT (pad, "Return caps %" GST_PTR_FORMAT, ret);

  return ret;
}

626
627
628
629
630
631
static guint64
gst_flac_enc_query_peer_total_samples (GstFlacEnc * flacenc, GstPad * pad)
{
  GstFormat fmt = GST_FORMAT_DEFAULT;
  gint64 duration;

632
  GST_DEBUG_OBJECT (flacenc, "querying peer for DEFAULT format duration");
633
634
635
636
637
  if (gst_pad_query_peer_duration (pad, &fmt, &duration)
      && fmt == GST_FORMAT_DEFAULT && duration != GST_CLOCK_TIME_NONE)
    goto done;

  fmt = GST_FORMAT_TIME;
638
  GST_DEBUG_OBJECT (flacenc, "querying peer for TIME format duration");
639
640
641

  if (gst_pad_query_peer_duration (pad, &fmt, &duration) &&
      fmt == GST_FORMAT_TIME && duration != GST_CLOCK_TIME_NONE) {
642
643
644
    GST_DEBUG_OBJECT (flacenc, "peer reported duration %" GST_TIME_FORMAT,
        GST_TIME_ARGS (duration));
    duration = GST_CLOCK_TIME_TO_FRAMES (duration, flacenc->sample_rate);
645
646
647
648
649
650
651
652
653
654
655
656
657
658

    goto done;
  }

  GST_DEBUG_OBJECT (flacenc, "Upstream reported no total samples");
  return GST_CLOCK_TIME_NONE;

done:
  GST_DEBUG_OBJECT (flacenc,
      "Upstream reported %" G_GUINT64_FORMAT " total samples", duration);

  return duration;
}

659
static gboolean
660
gst_flac_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
661
662
{
  GstFlacEnc *flacenc;
David Schleef's avatar
David Schleef committed
663
  GstStructure *structure;
664
  guint64 total_samples = GST_CLOCK_TIME_NONE;
665
  FLAC__StreamEncoderInitStatus init_status;
666
  gint depth, chans, rate, width;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
667

668
  flacenc = GST_FLAC_ENC (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
669

670
671
  if (FLAC__stream_encoder_get_state (flacenc->encoder) !=
      FLAC__STREAM_ENCODER_UNINITIALIZED)
672
673
    goto encoder_already_initialized;

David Schleef's avatar
David Schleef committed
674
  structure = gst_caps_get_structure (caps, 0);
675

676
677
678
679
680
681
682
683
684
  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;
685
  flacenc->width = width;
686
687
  flacenc->depth = depth;
  flacenc->sample_rate = rate;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
688

David Schleef's avatar
David Schleef committed
689
690
691
  caps = gst_caps_new_simple ("audio/x-flac",
      "channels", G_TYPE_INT, flacenc->channels,
      "rate", G_TYPE_INT, flacenc->sample_rate, NULL);
692

693
694
695
696
  if (!gst_pad_set_caps (flacenc->srcpad, caps))
    goto setting_src_caps_failed;

  gst_caps_unref (caps);
697

698
699
  total_samples = gst_flac_enc_query_peer_total_samples (flacenc, pad);

700
701
702
  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);
703
704
705

  if (total_samples != GST_CLOCK_TIME_NONE)
    FLAC__stream_encoder_set_total_samples_estimate (flacenc->encoder,
706
        MIN (total_samples, G_GUINT64_CONSTANT (0x0FFFFFFFFF)));
707

708
  gst_flac_enc_set_metadata (flacenc, total_samples);
709

710
711
712
713
714
  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;
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

  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
741
742
}

Wim Taymans's avatar
Wim Taymans committed
743
static gboolean
744
gst_flac_enc_update_quality (GstFlacEnc * flacenc, gint quality)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
745
{
Wim Taymans's avatar
Wim Taymans committed
746
747
  flacenc->quality = quality;

748
749
750
751
752
753
754
755
756
757
#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

Wim Taymans's avatar
Wim Taymans committed
758
759
  g_object_freeze_notify (G_OBJECT (flacenc));

760
  if (flacenc->channels == 2 || flacenc->channels == 0) {
761
762
763
764
    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
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  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
779
780
781
782
783
784

#undef DO_UPDATE

  g_object_thaw_notify (G_OBJECT (flacenc));

  return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
785
786
}

787
788
789
static FLAC__StreamEncoderSeekStatus
gst_flac_enc_seek_callback (const FLAC__StreamEncoder * encoder,
    FLAC__uint64 absolute_byte_offset, void *client_data)
790
{
791
  GstFlacEnc *flacenc;
792
793
  GstEvent *event;
  GstPad *peerpad;
794

795
  flacenc = GST_FLAC_ENC (client_data);
796

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
797
  if (flacenc->stopped)
798
    return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
799

800
  event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES,
801
      absolute_byte_offset, GST_BUFFER_OFFSET_NONE, 0);
802

803
804
  if ((peerpad = gst_pad_get_peer (flacenc->srcpad))) {
    gboolean ret = gst_pad_send_event (peerpad, event);
805

806
    gst_object_unref (peerpad);
807

808
    if (ret) {
Josep Torra's avatar
Josep Torra committed
809
810
      GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s",
          (guint64) absolute_byte_offset, "succeeded");
811
    } else {
Josep Torra's avatar
Josep Torra committed
812
813
      GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " %s",
          (guint64) absolute_byte_offset, "failed");
814
      return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
815
    }
816
817
  } else {
    GST_DEBUG ("Seek to %" G_GUINT64_FORMAT " failed (no peer pad)",
Josep Torra's avatar
Josep Torra committed
818
        (guint64) absolute_byte_offset);
819
  }
820
821

  flacenc->offset = absolute_byte_offset;
822
  return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
823
824
}

825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
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);
937
938
939
940
    GST_LOG_OBJECT (enc, "Pushing header buffer, size %u bytes",
        GST_BUFFER_SIZE (buf));
    GST_MEMDUMP_OBJECT (enc, "header buffer", GST_BUFFER_DATA (buf),
        GST_BUFFER_SIZE (buf));
941
942
943
944
945
946
947
948
949
    (void) gst_pad_push (enc->srcpad, buf);
    l->data = NULL;
  }
  g_list_free (enc->headers);
  enc->headers = NULL;

  gst_caps_unref (caps);
}

950
951
952
953
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)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
954
{
955
  GstFlowReturn ret = GST_FLOW_OK;
956
  GstFlacEnc *flacenc;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
957
  GstBuffer *outbuf;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
958

959
  flacenc = GST_FLAC_ENC (client_data);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
960

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
961
  if (flacenc->stopped)
962
    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
Wim Taymans's avatar
Wim Taymans committed
963

964
  outbuf = gst_buffer_new_and_alloc (bytes);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
965
966
  memcpy (GST_BUFFER_DATA (outbuf), buffer, bytes);

967
  if (samples > 0 && flacenc->samples_written != (guint64) - 1) {
968
969
    guint64 granulepos;

970
    GST_BUFFER_TIMESTAMP (outbuf) = flacenc->start_ts +
971
972
973
974
975
        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 */
976
977
    granulepos =
        flacenc->granulepos_offset + flacenc->samples_written + samples;
978
979
980
981
982
    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);
983
984
985
  } else {
    GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
    GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
986
987
988
989
    GST_BUFFER_OFFSET (outbuf) =
        flacenc->samples_written * flacenc->width * flacenc->channels;
    GST_BUFFER_OFFSET_END (outbuf) = 0;
    GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_IN_CAPS);
990
991
  }

992
993
994
  /* we assume libflac passes us stuff neatly framed */
  if (!flacenc->got_headers) {
    if (samples == 0) {
995
996
      GST_DEBUG_OBJECT (flacenc, "Got header, queueing (%u bytes)",
          (guint) bytes);
997
998
999
1000
      flacenc->headers = g_list_append (flacenc->headers, outbuf);
      /* note: it's important that we increase our byte offset */
      goto out;
    } else {
For faster browsing, not all history is shown. View entire blame