mssdemux: improve flow return handling
[platform/upstream/gstreamer.git] / ext / sndfile / gstsfsink.c
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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 enum
32 {
33   PROP_0,
34   PROP_LOCATION,
35   PROP_MAJOR_TYPE,
36   PROP_MINOR_TYPE,
37   PROP_BUFFER_FRAMES
38 };
39
40 #define DEFAULT_BUFFER_FRAMES (256)
41
42 static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("audio/x-raw-float, "
46         "rate = (int) [ 1, MAX ], "
47         "channels = (int) [ 1, MAX ], "
48         "endianness = (int) BYTE_ORDER, "
49         "width = (int) 32; "
50         "audio/x-raw-int, "
51         "rate = (int) [ 1, MAX ], "
52         "channels = (int) [ 1, MAX ], "
53         "endianness = (int) BYTE_ORDER, "
54         "width = (int) {16, 32}, "
55         "depth = (int) {16, 32}, " "signed = (boolean) true")
56     );
57
58 GST_BOILERPLATE (GstSFSink, gst_sf_sink, GstBaseSink, GST_TYPE_BASE_SINK);
59
60 static void gst_sf_sink_set_property (GObject * object, guint prop_id,
61     const GValue * value, GParamSpec * pspec);
62 static void gst_sf_sink_get_property (GObject * object, guint prop_id,
63     GValue * value, GParamSpec * pspec);
64
65 static gboolean gst_sf_sink_start (GstBaseSink * bsink);
66 static gboolean gst_sf_sink_stop (GstBaseSink * bsink);
67 static void gst_sf_sink_fixate (GstBaseSink * bsink, GstCaps * caps);
68 static gboolean gst_sf_sink_set_caps (GstBaseSink * bsink, GstCaps * caps);
69 static gboolean gst_sf_sink_activate_pull (GstBaseSink * bsink,
70     gboolean active);
71 static GstFlowReturn gst_sf_sink_render (GstBaseSink * bsink,
72     GstBuffer * buffer);
73 static gboolean gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event);
74
75 static gboolean gst_sf_sink_open_file (GstSFSink * this);
76 static void gst_sf_sink_close_file (GstSFSink * this);
77
78 GST_DEBUG_CATEGORY_STATIC (gst_sf_debug);
79 #define GST_CAT_DEFAULT gst_sf_debug
80
81 static void
82 gst_sf_sink_base_init (gpointer g_class)
83 {
84   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
85
86   GST_DEBUG_CATEGORY_INIT (gst_sf_debug, "sfsink", 0, "sfsink element");
87   gst_element_class_add_pad_template (element_class,
88       gst_static_pad_template_get (&sf_sink_factory));
89   gst_element_class_set_static_metadata (element_class, "Sndfile sink",
90       "Sink/Audio",
91       "Write audio streams to disk using libsndfile",
92       "Andy Wingo <wingo at pobox dot com>");
93 }
94
95 static void
96 gst_sf_sink_class_init (GstSFSinkClass * klass)
97 {
98   GObjectClass *gobject_class;
99   GstBaseSinkClass *basesink_class;
100   GParamSpec *pspec;
101
102   gobject_class = (GObjectClass *) klass;
103   basesink_class = (GstBaseSinkClass *) klass;
104
105   gobject_class->set_property = gst_sf_sink_set_property;
106   gobject_class->get_property = gst_sf_sink_get_property;
107
108   g_object_class_install_property (gobject_class, PROP_LOCATION,
109       g_param_spec_string ("location", "File Location",
110           "Location of the file to write", NULL,
111           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
112   pspec = g_param_spec_enum
113       ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
114       SF_FORMAT_WAV,
115       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
116   g_object_class_install_property (gobject_class, PROP_MAJOR_TYPE, pspec);
117   pspec = g_param_spec_enum
118       ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
119       SF_FORMAT_FLOAT,
120       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
121   g_object_class_install_property (gobject_class, PROP_MINOR_TYPE, pspec);
122   pspec = g_param_spec_int
123       ("buffer-frames", "Buffer frames",
124       "Number of frames per buffer, in pull mode", 1, G_MAXINT,
125       DEFAULT_BUFFER_FRAMES,
126       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
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_BASE_SINK_PREROLL_LOCK (basesink);
419   result = gst_sf_sink_render (basesink, buf);
420   GST_BASE_SINK_PREROLL_UNLOCK (basesink);
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 (result == GST_FLOW_UNEXPECTED) {
436       gst_pad_send_event (pad, gst_event_new_eos ());
437     } else if (result < GST_FLOW_UNEXPECTED || result == GST_FLOW_NOT_LINKED) {
438       GST_ELEMENT_ERROR (basesink, STREAM, FAILED,
439           (_("Internal data stream error.")),
440           ("stream stopped, reason %s", gst_flow_get_name (result)));
441       gst_pad_send_event (pad, gst_event_new_eos ());
442     }
443     gst_object_unref (this);
444     return;
445   }
446 no_buffer:
447   {
448     GST_INFO_OBJECT (this, "no buffer, pausing");
449     result = GST_FLOW_ERROR;
450     goto paused;
451   }
452 }
453
454 static gboolean
455 gst_sf_sink_activate_pull (GstBaseSink * basesink, gboolean active)
456 {
457   gboolean result;
458
459   if (active) {
460     /* start task */
461     result = gst_pad_start_task (basesink->sinkpad,
462         (GstTaskFunction) gst_sf_sink_loop, basesink->sinkpad, NULL);
463   } else {
464     /* step 2, make sure streaming finishes */
465     result = gst_pad_stop_task (basesink->sinkpad);
466   }
467
468   return result;
469 }
470
471 static GstFlowReturn
472 gst_sf_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
473 {
474   GstSFSink *this;
475   sf_count_t written, num_to_write;
476
477   this = (GstSFSink *) bsink;
478
479   if (GST_BUFFER_SIZE (buffer) % this->bytes_per_frame)
480     goto bad_length;
481
482   num_to_write = GST_BUFFER_SIZE (buffer) / this->bytes_per_frame;
483
484   written = this->writer (this->file, GST_BUFFER_DATA (buffer), num_to_write);
485   if (written != num_to_write)
486     goto short_write;
487
488   return GST_FLOW_OK;
489
490 bad_length:
491   {
492     GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
493         (_("Could not write to file \"%s\"."), this->location),
494         ("bad buffer size: %u %% %d != 0", GST_BUFFER_SIZE (buffer),
495             this->bytes_per_frame));
496     return GST_FLOW_ERROR;
497   }
498 short_write:
499   {
500     GST_ELEMENT_ERROR (this, RESOURCE, WRITE,
501         (_("Could not write to file \"%s\"."), this->location),
502         ("soundfile error: %s", sf_strerror (this->file)));
503     return GST_FLOW_ERROR;
504   }
505 }
506
507 static gboolean
508 gst_sf_sink_event (GstBaseSink * bsink, GstEvent * event)
509 {
510   GstSFSink *this;
511   GstEventType type;
512
513   this = (GstSFSink *) bsink;
514
515   type = GST_EVENT_TYPE (event);
516
517   switch (type) {
518     case GST_EVENT_EOS:
519       if (this->file)
520         sf_write_sync (this->file);
521       break;
522     default:
523       break;
524   }
525
526   return TRUE;
527 }