Branch data Line data Source code
1 : : /* GStreamer libsndfile plugin
2 : : * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 : : * 2000,2005 Wim Taymans <wim@fluendo.com>
4 : : * 2003,2007 Andy Wingo <wingo@pobox.com>
5 : : *
6 : : * gstsfsrc.c:
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Library General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Library General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Library General Public
19 : : * License along with this library; if not, write to the
20 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 : : * Boston, MA 02111-1307, USA.
22 : : */
23 : :
24 : : #ifdef HAVE_CONFIG_H
25 : : # include "config.h"
26 : : #endif
27 : :
28 : : #include <gst/gst-i18n-plugin.h>
29 : :
30 : : #include "gstsfsrc.h"
31 : :
32 : : enum
33 : : {
34 : : PROP_0,
35 : : PROP_LOCATION
36 : : };
37 : :
38 : : static GstStaticPadTemplate sf_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
39 : : GST_PAD_SRC,
40 : : GST_PAD_ALWAYS,
41 : : GST_STATIC_CAPS ("audio/x-raw-float, "
42 : : "rate = (int) [ 1, MAX ], "
43 : : "channels = (int) [ 1, MAX ], "
44 : : "endianness = (int) BYTE_ORDER, "
45 : : "width = (int) 32; "
46 : : "audio/x-raw-int, "
47 : : "rate = (int) [ 1, MAX ], "
48 : : "channels = (int) [ 1, MAX ], "
49 : : "endianness = (int) BYTE_ORDER, "
50 : : "width = (int) {16, 32}, "
51 : : "depth = (int) {16, 32}, " "signed = (boolean) true")
52 : : );
53 : :
54 : :
55 : : GST_DEBUG_CATEGORY_STATIC (gst_sf_src_debug);
56 : : #define GST_CAT_DEFAULT gst_sf_src_debug
57 : :
58 : :
59 : : #define DEFAULT_BUFFER_FRAMES (256)
60 : :
61 : :
62 : : static void gst_sf_src_finalize (GObject * object);
63 : : static void gst_sf_src_set_property (GObject * object, guint prop_id,
64 : : const GValue * value, GParamSpec * pspec);
65 : : static void gst_sf_src_get_property (GObject * object, guint prop_id,
66 : : GValue * value, GParamSpec * pspec);
67 : :
68 : : static gboolean gst_sf_src_start (GstBaseSrc * bsrc);
69 : : static gboolean gst_sf_src_stop (GstBaseSrc * bsrc);
70 : : static gboolean gst_sf_src_is_seekable (GstBaseSrc * bsrc);
71 : : static gboolean gst_sf_src_get_size (GstBaseSrc * bsrc, guint64 * size);
72 : : static GstFlowReturn gst_sf_src_create (GstBaseSrc * bsrc, guint64 offset,
73 : : guint length, GstBuffer ** buffer);
74 : : static GstCaps *gst_sf_src_get_caps (GstBaseSrc * bsrc);
75 : : static gboolean gst_sf_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
76 : : static gboolean gst_sf_src_check_get_range (GstBaseSrc * bsrc);
77 : : static void gst_sf_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
78 : :
79 [ + + ]: 36 : GST_BOILERPLATE (GstSFSrc, gst_sf_src, GstBaseSrc, GST_TYPE_BASE_SRC);
80 : :
81 : : static void
82 : 6 : gst_sf_src_base_init (gpointer g_class)
83 : : {
84 : 6 : GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
85 : :
86 : 6 : gst_element_class_add_pad_template (gstelement_class,
87 : : gst_static_pad_template_get (&sf_src_factory));
88 : :
89 : 6 : gst_element_class_set_details_simple (gstelement_class, "Sndfile source",
90 : : "Source/Audio",
91 : : "Read audio streams from disk using libsndfile",
92 : : "Andy Wingo <wingo at pobox dot com>");
93 [ + - ]: 6 : GST_DEBUG_CATEGORY_INIT (gst_sf_src_debug, "sfsrc", 0, "sfsrc element");
94 : 6 : }
95 : :
96 : : static void
97 : 6 : gst_sf_src_class_init (GstSFSrcClass * klass)
98 : : {
99 : : GObjectClass *gobject_class;
100 : : GstBaseSrcClass *gstbasesrc_class;
101 : :
102 : 6 : gobject_class = G_OBJECT_CLASS (klass);
103 : 6 : gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
104 : :
105 : 6 : gobject_class->set_property = gst_sf_src_set_property;
106 : 6 : gobject_class->get_property = gst_sf_src_get_property;
107 : :
108 : 6 : g_object_class_install_property (gobject_class, PROP_LOCATION,
109 : : g_param_spec_string ("location", "File Location",
110 : : "Location of the file to read", NULL, G_PARAM_READWRITE));
111 : :
112 : 6 : gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_sf_src_finalize);
113 : :
114 : 6 : gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_sf_src_start);
115 : 6 : gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_sf_src_stop);
116 : 6 : gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_sf_src_is_seekable);
117 : 6 : gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_sf_src_get_size);
118 : 6 : gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_sf_src_create);
119 : 6 : gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_sf_src_get_caps);
120 : 6 : gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_sf_src_set_caps);
121 : 6 : gstbasesrc_class->check_get_range =
122 : 6 : GST_DEBUG_FUNCPTR (gst_sf_src_check_get_range);
123 : 6 : gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_sf_src_fixate);
124 : 6 : }
125 : :
126 : : static void
127 : 4 : gst_sf_src_init (GstSFSrc * src, GstSFSrcClass * g_class)
128 : : {
129 : 4 : }
130 : :
131 : : static void
132 : 4 : gst_sf_src_finalize (GObject * object)
133 : : {
134 : : GstSFSrc *src;
135 : :
136 : 4 : src = GST_SF_SRC (object);
137 : :
138 : 4 : g_free (src->location);
139 : :
140 : 4 : G_OBJECT_CLASS (parent_class)->finalize (object);
141 : 4 : }
142 : :
143 : : static void
144 : 0 : gst_sf_src_set_location (GstSFSrc * this, const gchar * location)
145 : : {
146 [ # # ]: 0 : if (this->file)
147 : 0 : goto was_open;
148 : :
149 [ # # ]: 0 : if (this->location)
150 : 0 : g_free (this->location);
151 : :
152 [ # # ]: 0 : this->location = location ? g_strdup (location) : NULL;
153 : :
154 : 0 : return;
155 : :
156 : : was_open:
157 : : {
158 : 0 : g_warning ("Changing the `location' property on sfsrc when "
159 : : "a file is open not supported.");
160 : 0 : return;
161 : : }
162 : : }
163 : :
164 : : static void
165 : 0 : gst_sf_src_set_property (GObject * object, guint prop_id, const GValue * value,
166 : : GParamSpec * pspec)
167 : : {
168 : 0 : GstSFSrc *this = GST_SF_SRC (object);
169 : :
170 [ # # ]: 0 : switch (prop_id) {
171 : : case PROP_LOCATION:
172 : 0 : gst_sf_src_set_location (this, g_value_get_string (value));
173 : 0 : break;
174 : :
175 : : default:
176 : 0 : break;
177 : : }
178 : 0 : }
179 : :
180 : : static void
181 : 1 : gst_sf_src_get_property (GObject * object, guint prop_id, GValue * value,
182 : : GParamSpec * pspec)
183 : : {
184 : 1 : GstSFSrc *this = GST_SF_SRC (object);
185 : :
186 [ + - ]: 1 : switch (prop_id) {
187 : : case PROP_LOCATION:
188 : 1 : g_value_set_string (value, this->location);
189 : 1 : break;
190 : :
191 : : default:
192 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
193 : 0 : break;
194 : : }
195 : 1 : }
196 : :
197 : : static GstFlowReturn
198 : 0 : gst_sf_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
199 : : GstBuffer ** buffer)
200 : : {
201 : : GstSFSrc *this;
202 : : GstBuffer *buf;
203 : 0 : gboolean discont = FALSE;
204 : : sf_count_t bytes_read;
205 : :
206 : 0 : this = GST_SF_SRC (bsrc);
207 : :
208 [ # # ]: 0 : if (G_UNLIKELY (offset % this->bytes_per_frame))
209 : 0 : goto bad_offset;
210 [ # # ]: 0 : if (G_UNLIKELY (length % this->bytes_per_frame))
211 : 0 : goto bad_length;
212 : :
213 : 0 : offset /= this->bytes_per_frame;
214 : :
215 [ # # ]: 0 : if (G_UNLIKELY (this->offset != offset)) {
216 : : sf_count_t pos;
217 : :
218 : 0 : pos = sf_seek (this->file, offset, SEEK_SET);
219 : :
220 [ # # ][ # # ]: 0 : if (G_UNLIKELY (pos < 0 || pos != offset))
221 : : goto seek_failed;
222 : :
223 : 0 : this->offset = offset;
224 : 0 : discont = TRUE;
225 : : }
226 : :
227 : 0 : buf = gst_buffer_new_and_alloc (length);
228 : :
229 : : /* now make length in frames */
230 : 0 : length /= this->bytes_per_frame;
231 : :
232 : 0 : bytes_read = this->reader (this->file, GST_BUFFER_DATA (buf), length);
233 [ # # ]: 0 : if (G_UNLIKELY (bytes_read < 0))
234 : 0 : goto could_not_read;
235 : :
236 [ # # ][ # # ]: 0 : if (G_UNLIKELY (bytes_read == 0 && length > 0))
237 : 0 : goto eos;
238 : :
239 : 0 : GST_BUFFER_SIZE (buf) = bytes_read * this->bytes_per_frame;
240 : :
241 : 0 : GST_BUFFER_OFFSET (buf) = offset;
242 : 0 : GST_BUFFER_OFFSET_END (buf) = offset + length;
243 : 0 : GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (offset,
244 : : GST_SECOND, this->rate);
245 : 0 : GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (offset + length,
246 : 0 : GST_SECOND, this->rate) - GST_BUFFER_TIMESTAMP (buf);
247 : :
248 : 0 : gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (bsrc)));
249 : :
250 : 0 : *buffer = buf;
251 : :
252 : 0 : this->offset += length;
253 : :
254 : 0 : return GST_FLOW_OK;
255 : :
256 : : /* ERROR */
257 : : bad_offset:
258 : : {
259 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, SEEK,
[ # # ][ # # ]
260 : : (NULL), ("offset %" G_GUINT64_FORMAT " not divisible by %d bytes per "
261 : : "frame", offset, this->bytes_per_frame));
262 : 0 : return GST_FLOW_ERROR;
263 : : }
264 : : bad_length:
265 : : {
266 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, SEEK, (NULL),
[ # # ][ # # ]
267 : : ("length %u not divisible by %d bytes per frame", length,
268 : : this->bytes_per_frame));
269 : 0 : return GST_FLOW_ERROR;
270 : : }
271 : : seek_failed:
272 : : {
273 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
[ # # ][ # # ]
274 : 0 : return GST_FLOW_ERROR;
275 : : }
276 : : could_not_read:
277 : : {
278 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
[ # # ][ # # ]
279 : 0 : gst_buffer_unref (buf);
280 : 0 : return GST_FLOW_ERROR;
281 : : }
282 : : eos:
283 : : {
284 [ # # ]: 0 : GST_DEBUG ("EOS, baby");
285 : 0 : gst_buffer_unref (buf);
286 : 0 : return GST_FLOW_UNEXPECTED;
287 : : }
288 : : }
289 : :
290 : : static gboolean
291 : 0 : gst_sf_src_is_seekable (GstBaseSrc * basesrc)
292 : : {
293 : 0 : return TRUE;
294 : : }
295 : :
296 : : static gboolean
297 : 0 : gst_sf_src_get_size (GstBaseSrc * basesrc, guint64 * size)
298 : : {
299 : : GstSFSrc *this;
300 : : sf_count_t end;
301 : :
302 : 0 : this = GST_SF_SRC (basesrc);
303 : :
304 : 0 : end = sf_seek (this->file, 0, SEEK_END);
305 : :
306 : 0 : sf_seek (this->file, this->offset, SEEK_SET);
307 : :
308 : 0 : *size = end * this->bytes_per_frame;
309 : :
310 : 0 : return TRUE;
311 : : }
312 : :
313 : : static gboolean
314 : 18 : gst_sf_src_open_file (GstSFSrc * this)
315 : : {
316 : : int mode;
317 : : SF_INFO info;
318 : :
319 [ - + ]: 18 : g_return_val_if_fail (this->file == NULL, FALSE);
320 : :
321 [ + - ]: 18 : if (!this->location)
322 : 18 : goto no_filename;
323 : :
324 : 0 : mode = SFM_READ;
325 : 0 : info.format = 0;
326 : :
327 : 0 : this->file = sf_open (this->location, mode, &info);
328 : :
329 [ # # ]: 0 : if (!this->file)
330 : 0 : goto open_failed;
331 : :
332 : 0 : this->channels = info.channels;
333 : 0 : this->rate = info.samplerate;
334 : : /* do something with info.seekable? */
335 : :
336 : 0 : return TRUE;
337 : :
338 : : no_filename:
339 : : {
340 [ + - ][ - + ]: 18 : GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
[ - + ][ # # ]
341 : : (_("No file name specified for writing.")), (NULL));
342 : 18 : return FALSE;
343 : : }
344 : : open_failed:
345 : : {
346 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
[ # # ][ # # ]
347 : : (_("Could not open file \"%s\" for writing."), this->location),
348 : : ("soundfile error: %s", sf_strerror (NULL)));
349 : 18 : return FALSE;
350 : : }
351 : : }
352 : :
353 : : static void
354 : 0 : gst_sf_src_close_file (GstSFSrc * this)
355 : : {
356 : 0 : int err = 0;
357 : :
358 [ # # ]: 0 : g_return_if_fail (this->file != NULL);
359 : :
360 [ # # ]: 0 : GST_INFO_OBJECT (this, "Closing file %s", this->location);
361 : :
362 [ # # ]: 0 : if ((err = sf_close (this->file)))
363 : 0 : goto close_failed;
364 : :
365 : 0 : this->file = NULL;
366 : 0 : this->offset = 0;
367 : 0 : this->channels = 0;
368 : 0 : this->rate = 0;
369 : :
370 : 0 : return;
371 : :
372 : : close_failed:
373 : : {
374 [ # # ][ # # ]: 0 : GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
[ # # ][ # # ]
375 : : ("Could not close file file \"%s\".", this->location),
376 : : ("soundfile error: %s", sf_error_number (err)));
377 : 0 : return;
378 : : }
379 : : }
380 : :
381 : : static gboolean
382 : 18 : gst_sf_src_start (GstBaseSrc * basesrc)
383 : : {
384 : 18 : GstSFSrc *this = GST_SF_SRC (basesrc);
385 : :
386 : 18 : return gst_sf_src_open_file (this);
387 : : }
388 : :
389 : : /* unmap and close the file */
390 : : static gboolean
391 : 0 : gst_sf_src_stop (GstBaseSrc * basesrc)
392 : : {
393 : 0 : GstSFSrc *this = GST_SF_SRC (basesrc);
394 : :
395 : 0 : gst_sf_src_close_file (this);
396 : :
397 : 0 : return TRUE;
398 : : }
399 : :
400 : : static GstCaps *
401 : 0 : gst_sf_src_get_caps (GstBaseSrc * bsrc)
402 : : {
403 : : GstSFSrc *this;
404 : : GstCaps *ret;
405 : :
406 : 0 : this = GST_SF_SRC (bsrc);
407 : :
408 : 0 : ret = gst_caps_copy (gst_pad_get_pad_template_caps (bsrc->srcpad));
409 : :
410 [ # # ]: 0 : if (this->file) {
411 : : GstStructure *s;
412 : : gint i;
413 : :
414 [ # # ]: 0 : for (i = 0; i < gst_caps_get_size (ret); i++) {
415 : 0 : s = gst_caps_get_structure (ret, i);
416 : 0 : gst_structure_set (s, "channels", G_TYPE_INT, this->channels,
417 : : "rate", G_TYPE_INT, this->rate, NULL);
418 : : }
419 : : }
420 : :
421 : 0 : return ret;
422 : : }
423 : :
424 : : static gboolean
425 : 0 : gst_sf_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
426 : : {
427 : 0 : GstSFSrc *this = (GstSFSrc *) bsrc;
428 : : GstStructure *structure;
429 : : gint width;
430 : :
431 : 0 : structure = gst_caps_get_structure (caps, 0);
432 : :
433 [ # # ]: 0 : if (!this->file)
434 : 0 : goto file_not_open;
435 : :
436 [ # # ]: 0 : if (!gst_structure_get_int (structure, "width", &width))
437 : 0 : goto impossible;
438 : :
439 [ # # ]: 0 : if (gst_structure_has_name (structure, "audio/x-raw-int")) {
440 [ # # # ]: 0 : switch (width) {
441 : : case 16:
442 : 0 : this->reader = (GstSFReader) sf_readf_short;
443 : 0 : break;
444 : : case 32:
445 : 0 : this->reader = (GstSFReader) sf_readf_int;
446 : 0 : break;
447 : : default:
448 : 0 : goto impossible;
449 : : }
450 : : } else {
451 [ # # ]: 0 : switch (width) {
452 : : case 32:
453 : 0 : this->reader = (GstSFReader) sf_readf_float;
454 : 0 : break;
455 : : default:
456 : 0 : goto impossible;
457 : : }
458 : : }
459 : :
460 : 0 : this->bytes_per_frame = width * this->channels / 8;
461 : :
462 : 0 : return TRUE;
463 : :
464 : : impossible:
465 : : {
466 : 0 : g_warning ("something impossible happened");
467 : 0 : return FALSE;
468 : : }
469 : : file_not_open:
470 : : {
471 [ # # ]: 0 : GST_WARNING_OBJECT (this, "file has to be open in order to set caps");
472 : 0 : return FALSE;
473 : : }
474 : : }
475 : :
476 : : static gboolean
477 : 0 : gst_sf_src_check_get_range (GstBaseSrc * bsrc)
478 : : {
479 : 0 : return TRUE;
480 : : }
481 : :
482 : : static void
483 : 0 : gst_sf_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
484 : : {
485 : : GstStructure *s;
486 : : gint width, depth;
487 : :
488 : 0 : s = gst_caps_get_structure (caps, 0);
489 : :
490 : 0 : gst_structure_fixate_field_nearest_int (s, "width", 16);
491 : :
492 : : /* fields for int */
493 [ # # ]: 0 : if (gst_structure_has_field (s, "depth")) {
494 : 0 : gst_structure_get_int (s, "width", &width);
495 : : /* round width to nearest multiple of 8 for the depth */
496 : 0 : depth = GST_ROUND_UP_8 (width);
497 : 0 : gst_structure_fixate_field_nearest_int (s, "depth", depth);
498 : : }
499 [ # # ]: 0 : if (gst_structure_has_field (s, "signed"))
500 : 0 : gst_structure_fixate_field_boolean (s, "signed", TRUE);
501 [ # # ]: 0 : if (gst_structure_has_field (s, "endianness"))
502 : 0 : gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
503 : 0 : }
|