~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to ext/sndfile/gstsfsink.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2007-05-03 19:52:54 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20070503195254-4dovmz251nut3yrt
Tags: 0.10.4+cvs20070502-1
* New CVS snapshot.
* debian/rules,
  debian/build-deps.in:
  + Update build dependencies.
* debian/gstreamer-plugins-bad-multiverse.install:
  + Add x264 plugin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GStreamer libsndfile plugin
 
2
 * Copyright (C) 2007 Andy Wingo <wingo at pobox dot com>
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Library General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
 
 
20
 
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
 
 
25
#include <gst/audio/audio.h>
 
26
 
 
27
#include <gst/gst-i18n-plugin.h>
 
28
 
 
29
#include "gstsfsink.h"
 
30
 
 
31
 
 
32
static const GstElementDetails sfsink_details =
 
33
GST_ELEMENT_DETAILS ("Sndfile sink",
 
34
    "Sink/Audio",
 
35
    "Write audio streams to disk using libsndfile",
 
36
    "Andy Wingo <wingo at pobox dot com>");
 
37
 
 
38
enum
 
39
{
 
40
  PROP_0,
 
41
  PROP_LOCATION,
 
42
  PROP_MAJOR_TYPE,
 
43
  PROP_MINOR_TYPE,
 
44
  PROP_BUFFER_FRAMES
 
45
};
 
46
 
 
47
#define DEFAULT_BUFFER_FRAMES (256)
 
48
 
 
49
static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
 
50
    GST_PAD_SINK,
 
51
    GST_PAD_ALWAYS,
 
52
    GST_STATIC_CAPS ("audio/x-raw-float, "
 
53
        "rate = (int) [ 1, MAX ], "
 
54
        "channels = (int) [ 1, MAX ], "
 
55
        "endianness = (int) BYTE_ORDER, "
 
56
        "width = (int) 32; "
 
57
        "audio/x-raw-int, "
 
58
        "rate = (int) [ 1, MAX ], "
 
59
        "channels = (int) [ 1, MAX ], "
 
60
        "endianness = (int) BYTE_ORDER, "
 
61
        "width = (int) {16, 32}, "
 
62
        "depth = (int) {16, 32}, " "signed = (boolean) true")
 
63
    );
 
64
 
 
65
GST_BOILERPLATE (GstSFSink, gst_sf_sink, GstBaseSink, GST_TYPE_BASE_SINK);
 
66
 
 
67
static void gst_sf_sink_set_property (GObject * object, guint prop_id,
 
68
    const GValue * value, GParamSpec * pspec);
 
69
static void gst_sf_sink_get_property (GObject * object, guint prop_id,
 
70
    GValue * value, GParamSpec * pspec);
 
71
 
 
72
static gboolean gst_sf_sink_start (GstBaseSink * bsink);
 
73
static gboolean gst_sf_sink_stop (GstBaseSink * bsink);
 
74
static void gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
 
75
static gboolean gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
 
76
static gboolean gst_sf_sink_activate_pull (GstBaseSink * bsink,
 
77
    gboolean active);
 
78
static GstFlowReturn gst_sf_sink_render (GstBaseSink * bsink,
 
79
    GstBuffer * buffer);
 
80
static gboolean gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event);
 
81
 
 
82
static gboolean gst_sf_sink_open_file (GstSFSink * this);
 
83
static void gst_sf_sink_close_file (GstSFSink * this);
 
84
 
 
85
GST_DEBUG_CATEGORY_STATIC (gst_sf_debug);
 
86
#define GST_CAT_DEFAULT gst_sf_debug
 
87
 
 
88
static void
 
89
gst_sf_sink_base_init (gpointer g_class)
 
90
{
 
91
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
92
 
 
93
  GST_DEBUG_CATEGORY_INIT (gst_sf_debug, "sfsink", 0, "sfsink element");
 
94
  gst_element_class_add_pad_template (element_class,
 
95
      gst_static_pad_template_get (&sf_sink_factory));
 
96
  gst_element_class_set_details (element_class, &sfsink_details);
 
97
}
 
98
 
 
99
static void
 
100
gst_sf_sink_class_init (GstSFSinkClass * klass)
 
101
{
 
102
  GObjectClass *gobject_class;
 
103
  GstBaseSinkClass *basesink_class;
 
104
  GParamSpec *pspec;
 
105
 
 
106
  gobject_class = (GObjectClass *) klass;
 
107
  basesink_class = (GstBaseSinkClass *) klass;
 
108
 
 
109
  gobject_class->set_property = gst_sf_sink_set_property;
 
110
  gobject_class->get_property = gst_sf_sink_get_property;
 
111
 
 
112
  g_object_class_install_property (gobject_class, PROP_LOCATION,
 
113
      g_param_spec_string ("location", "File Location",
 
114
          "Location of the file to write", NULL, G_PARAM_READWRITE));
 
115
  pspec = g_param_spec_enum
 
116
      ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
 
117
      SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
 
118
  g_object_class_install_property (gobject_class, PROP_MAJOR_TYPE, pspec);
 
119
  pspec = g_param_spec_enum
 
120
      ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
 
121
      SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
 
122
  g_object_class_install_property (gobject_class, PROP_MINOR_TYPE, pspec);
 
123
  pspec = g_param_spec_int
 
124
      ("buffer-frames", "Buffer frames",
 
125
      "Number of frames per buffer, in pull mode", 1, G_MAXINT,
 
126
      DEFAULT_BUFFER_FRAMES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
 
127
  g_object_class_install_property (gobject_class, PROP_BUFFER_FRAMES, pspec);
 
128
 
 
129
  basesink_class->get_times = NULL;
 
130
  basesink_class->start = GST_DEBUG_FUNCPTR (gst_sf_sink_start);
 
131
  basesink_class->stop = GST_DEBUG_FUNCPTR (gst_sf_sink_stop);
 
132
  basesink_class->fixate = GST_DEBUG_FUNCPTR (gst_sf_sink_fixate);
 
133
  basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_sf_sink_set_caps);
 
134
  basesink_class->activate_pull = GST_DEBUG_FUNCPTR (gst_sf_sink_activate_pull);
 
135
  basesink_class->render = GST_DEBUG_FUNCPTR (gst_sf_sink_render);
 
136
  basesink_class->event = GST_DEBUG_FUNCPTR (gst_sf_sink_event);
 
137
}
 
138
 
 
139
static void
 
140
gst_sf_sink_init (GstSFSink * this, GstSFSinkClass * klass)
 
141
{
 
142
  GST_BASE_SINK (this)->can_activate_pull = TRUE;
 
143
}
 
144
 
 
145
static void
 
146
gst_sf_sink_set_location (GstSFSink * this, const gchar * location)
 
147
{
 
148
  if (this->file)
 
149
    goto was_open;
 
150
 
 
151
  if (this->location)
 
152
    g_free (this->location);
 
153
 
 
154
  this->location = location ? g_strdup (location) : NULL;
 
155
 
 
156
  return;
 
157
 
 
158
was_open:
 
159
  {
 
160
    g_warning ("Changing the `location' property on sfsink when "
 
161
        "a file is open not supported.");
 
162
    return;
 
163
  }
 
164
}
 
165
 
 
166
 
 
167
static void
 
168
gst_sf_sink_set_property (GObject * object, guint prop_id, const GValue * value,
 
169
    GParamSpec * pspec)
 
170
{
 
171
  GstSFSink *this = GST_SF_SINK (object);
 
172
 
 
173
  switch (prop_id) {
 
174
    case PROP_LOCATION:
 
175
      gst_sf_sink_set_location (this, g_value_get_string (value));
 
176
      break;
 
177
 
 
178
    case PROP_MAJOR_TYPE:
 
179
      this->format_major = g_value_get_enum (value);
 
180
      break;
 
181
 
 
182
    case PROP_MINOR_TYPE:
 
183
      this->format_subtype = g_value_get_enum (value);
 
184
      break;
 
185
 
 
186
    case PROP_BUFFER_FRAMES:
 
187
      this->buffer_frames = g_value_get_int (value);
 
188
      break;
 
189
 
 
190
    default:
 
191
      break;
 
192
  }
 
193
}
 
194
 
 
195
static void
 
196
gst_sf_sink_get_property (GObject * object, guint prop_id, GValue * value,
 
197
    GParamSpec * pspec)
 
198
{
 
199
  GstSFSink *this = GST_SF_SINK (object);
 
200
 
 
201
  switch (prop_id) {
 
202
    case PROP_LOCATION:
 
203
      g_value_set_string (value, this->location);
 
204
      break;
 
205
 
 
206
    case PROP_MAJOR_TYPE:
 
207
      g_value_set_enum (value, this->format_major);
 
208
      break;
 
209
 
 
210
    case PROP_MINOR_TYPE:
 
211
      g_value_set_enum (value, this->format_subtype);
 
212
      break;
 
213
 
 
214
    case PROP_BUFFER_FRAMES:
 
215
      g_value_set_int (value, this->buffer_frames);
 
216
      break;
 
217
 
 
218
    default:
 
219
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
220
      break;
 
221
  }
 
222
}
 
223
 
 
224
static gboolean
 
225
gst_sf_sink_start (GstBaseSink * bsink)
 
226
{
 
227
  /* pass */
 
228
  return TRUE;
 
229
}
 
230
 
 
231
static gboolean
 
232
gst_sf_sink_stop (GstBaseSink * bsink)
 
233
{
 
234
  GstSFSink *this = GST_SF_SINK (bsink);
 
235
 
 
236
  if (this->file)
 
237
    gst_sf_sink_close_file (this);
 
238
 
 
239
  return TRUE;
 
240
}
 
241
 
 
242
static gboolean
 
243
gst_sf_sink_open_file (GstSFSink * this)
 
244
{
 
245
  int mode;
 
246
  SF_INFO info;
 
247
 
 
248
  g_return_val_if_fail (this->file == NULL, FALSE);
 
249
  g_return_val_if_fail (this->rate > 0, FALSE);
 
250
  g_return_val_if_fail (this->channels > 0, FALSE);
 
251
 
 
252
  if (!this->location)
 
253
    goto no_filename;
 
254
 
 
255
  mode = SFM_WRITE;
 
256
  this->format = this->format_major | this->format_subtype;
 
257
  info.samplerate = this->rate;
 
258
  info.channels = this->channels;
 
259
  info.format = this->format;
 
260
 
 
261
  GST_INFO_OBJECT (this, "Opening %s with rate %d, %d channels, format 0x%x",
 
262
      this->location, info.samplerate, info.channels, info.format);
 
263
 
 
264
  if (!sf_format_check (&info))
 
265
    goto bad_format;
 
266
 
 
267
  this->file = sf_open (this->location, mode, &info);
 
268
 
 
269
  if (!this->file)
 
270
    goto open_failed;
 
271
 
 
272
  return TRUE;
 
273
 
 
274
no_filename:
 
275
  {
 
276
    GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
 
277
        (_("No file name specified for writing.")), (NULL));
 
278
    return FALSE;
 
279
  }
 
280
bad_format:
 
281
  {
 
282
    GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
 
283
        ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
 
284
            info.samplerate, info.channels, info.format));
 
285
    return FALSE;
 
286
  }
 
287
open_failed:
 
288
  {
 
289
    GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
 
290
        (_("Could not open file \"%s\" for writing."), this->location),
 
291
        ("soundfile error: %s", sf_strerror (NULL)));
 
292
    return FALSE;
 
293
  }
 
294
}
 
295
 
 
296
static void
 
297
gst_sf_sink_close_file (GstSFSink * this)
 
298
{
 
299
  int err = 0;
 
300
 
 
301
  g_return_if_fail (this->file != NULL);
 
302
 
 
303
  GST_INFO_OBJECT (this, "Closing file %s", this->location);
 
304
 
 
305
  if ((err = sf_close (this->file)))
 
306
    goto close_failed;
 
307
 
 
308
  this->file = NULL;
 
309
 
 
310
  return;
 
311
 
 
312
close_failed:
 
313
  {
 
314
    GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
 
315
        ("Could not close file file \"%s\".", this->location),
 
316
        ("soundfile error: %s", sf_error_number (err)));
 
317
    return;
 
318
  }
 
319
}
 
320
 
 
321
static void
 
322
gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps)
 
323
{
 
324
  GstStructure *s;
 
325
  gint width, depth;
 
326
 
 
327
  s = gst_caps_get_structure (caps, 0);
 
328
 
 
329
  /* fields for all formats */
 
330
  gst_structure_fixate_field_nearest_int (s, "rate", 44100);
 
331
  gst_structure_fixate_field_nearest_int (s, "channels", 2);
 
332
  gst_structure_fixate_field_nearest_int (s, "width", 16);
 
333
 
 
334
  /* fields for int */
 
335
  if (gst_structure_has_field (s, "depth")) {
 
336
    gst_structure_get_int (s, "width", &width);
 
337
    /* round width to nearest multiple of 8 for the depth */
 
338
    depth = GST_ROUND_UP_8 (width);
 
339
    gst_structure_fixate_field_nearest_int (s, "depth", depth);
 
340
  }
 
341
  if (gst_structure_has_field (s, "signed"))
 
342
    gst_structure_fixate_field_boolean (s, "signed", TRUE);
 
343
  if (gst_structure_has_field (s, "endianness"))
 
344
    gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
 
345
}
 
346
 
 
347
static gboolean
 
348
gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
 
349
{
 
350
  GstSFSink *this = (GstSFSink *) bsink;
 
351
  GstStructure *structure;
 
352
  gint width, channels, rate;
 
353
 
 
354
  structure = gst_caps_get_structure (caps, 0);
 
355
 
 
356
  if (!gst_structure_get_int (structure, "width", &width)
 
357
      || !gst_structure_get_int (structure, "channels", &channels)
 
358
      || !gst_structure_get_int (structure, "rate", &rate))
 
359
    goto impossible;
 
360
 
 
361
  if (gst_structure_has_name (structure, "audio/x-raw-int")) {
 
362
    switch (width) {
 
363
      case 16:
 
364
        this->writer = (GstSFWriter) sf_writef_short;
 
365
        break;
 
366
      case 32:
 
367
        this->writer = (GstSFWriter) sf_writef_int;
 
368
        break;
 
369
      default:
 
370
        goto impossible;
 
371
    }
 
372
  } else {
 
373
    switch (width) {
 
374
      case 32:
 
375
        this->writer = (GstSFWriter) sf_writef_float;
 
376
        break;
 
377
      default:
 
378
        goto impossible;
 
379
    }
 
380
  }
 
381
 
 
382
  this->bytes_per_frame = width * channels / 8;
 
383
  this->rate = rate;
 
384
  this->channels = channels;
 
385
 
 
386
  return gst_sf_sink_open_file (this);
 
387
 
 
388
impossible:
 
389
  {
 
390
    g_warning ("something impossible happened");
 
391
    return FALSE;
 
392
  }
 
393
}
 
394
 
 
395
/* with STREAM_LOCK
 
396
 */
 
397
static void
 
398
gst_sf_sink_loop (GstPad * pad)
 
399
{
 
400
  GstSFSink *this;
 
401
  GstBaseSink *basesink;
 
402
  GstBuffer *buf = NULL;
 
403
  GstFlowReturn result;
 
404
 
 
405
  this = GST_SF_SINK (gst_pad_get_parent (pad));
 
406
  basesink = GST_BASE_SINK (this);
 
407
 
 
408
  result = gst_pad_pull_range (pad, basesink->offset,
 
409
      this->buffer_frames * this->bytes_per_frame, &buf);
 
410
  if (G_UNLIKELY (result != GST_FLOW_OK))
 
411
    goto paused;
 
412
 
 
413
  if (G_UNLIKELY (buf == NULL))
 
414
    goto no_buffer;
 
415
 
 
416
  basesink->offset += GST_BUFFER_SIZE (buf);
 
417
 
 
418
  GST_PAD_PREROLL_LOCK (pad);
 
419
  result = gst_sf_sink_render (basesink, buf);
 
420
  GST_PAD_PREROLL_UNLOCK (pad);
 
421
  if (G_UNLIKELY (result != GST_FLOW_OK))
 
422
    goto paused;
 
423
 
 
424
  gst_object_unref (this);
 
425
 
 
426
  return;
 
427
 
 
428
  /* ERRORS */
 
429
paused:
 
430
  {
 
431
    GST_INFO_OBJECT (basesink, "pausing task, reason %s",
 
432
        gst_flow_get_name (result));
 
433
    gst_pad_pause_task (pad);
 
434
    /* fatal errors and NOT_LINKED cause EOS */
 
435
    if (GST_FLOW_IS_FATAL (result) || result == GST_FLOW_NOT_LINKED) {
 
436
      gst_pad_send_event (pad, gst_event_new_eos ());
 
437
      /* EOS does not cause an ERROR message */
 
438
      if (result != GST_FLOW_UNEXPECTED) {
 
439
        GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
 
440
            (_("Internal data stream error.")),
 
441
            ("stream stopped, reason %s", gst_flow_get_name (result)));
 
442
      }
 
443
    }
 
444
    gst_object_unref (this);
 
445
    return;
 
446
  }
 
447
no_buffer:
 
448
  {
 
449
    GST_INFO_OBJECT (this, "no buffer, pausing");
 
450
    result = GST_FLOW_ERROR;
 
451
    goto paused;
 
452
  }
 
453
}
 
454
 
 
455
static gboolean
 
456
gst_sf_sink_activate_pull (GstBaseSink * basesink, gboolean active)
 
457
{
 
458
  gboolean result;
 
459
 
 
460
  if (active) {
 
461
    /* start task */
 
462
    result = gst_pad_start_task (basesink->sinkpad,
 
463
        (GstTaskFunction) gst_sf_sink_loop, basesink->sinkpad);
 
464
  } else {
 
465
    /* step 2, make sure streaming finishes */
 
466
    result = gst_pad_stop_task (basesink->sinkpad);
 
467
  }
 
468
 
 
469
  return result;
 
470
}
 
471
 
 
472
static GstFlowReturn
 
473
gst_sf_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
 
474
{
 
475
  GstSFSink *this;
 
476
  sf_count_t written, num_to_write;
 
477
 
 
478
  this = (GstSFSink *) bsink;
 
479
 
 
480
  if (GST_BUFFER_SIZE (buffer) % this->bytes_per_frame)
 
481
    goto bad_length;
 
482
 
 
483
  num_to_write = GST_BUFFER_SIZE (buffer) / this->bytes_per_frame;
 
484
 
 
485
  written = this->writer (this->file, GST_BUFFER_DATA (buffer), num_to_write);
 
486
  if (written != num_to_write)
 
487
    goto short_write;
 
488
 
 
489
  return GST_FLOW_OK;
 
490
 
 
491
bad_length:
 
492
  {
 
493
    GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
 
494
        (_("Could not write to file \"%s\"."), this->location),
 
495
        ("bad buffer size: %u %% %d != 0", GST_BUFFER_SIZE (buffer),
 
496
            this->bytes_per_frame));
 
497
    return GST_FLOW_ERROR;
 
498
  }
 
499
short_write:
 
500
  {
 
501
    GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
 
502
        (_("Could not write to file \"%s\"."), this->location),
 
503
        ("soundfile error: %s", sf_strerror (this->file)));
 
504
    return GST_FLOW_ERROR;
 
505
  }
 
506
}
 
507
 
 
508
static gboolean
 
509
gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event)
 
510
{
 
511
  GstSFSink *this;
 
512
  GstEventType type;
 
513
 
 
514
  this = (GstSFSink *) bsink;
 
515
 
 
516
  type = GST_EVENT_TYPE (event);
 
517
 
 
518
  switch (type) {
 
519
    case GST_EVENT_EOS:
 
520
      if (this->file)
 
521
        sf_write_sync (this->file);
 
522
      break;
 
523
    default:
 
524
      break;
 
525
  }
 
526
 
 
527
  return TRUE;
 
528
}