572fb89c739e20693abf91064795753d5ac98b46
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / ext / cdparanoia / gstcdparanoiasrc.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* GStreamer
3  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4  *               <2005> Wim Taymans <wim@fluendo.com>
5  *               <2005> Tim-Philipp Müller <tim centricular net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <string.h>
28 #include <errno.h>
29 #include <stdio.h>
30
31 #include "gstcdparanoiasrc.h"
32 #include "gst/gst-i18n-plugin.h"
33
34 enum
35 {
36   TRANSPORT_ERROR,
37   UNCORRECTED_ERROR,
38   NUM_SIGNALS
39 };
40
41 enum
42 {
43   PROP_0,
44   PROP_READ_SPEED,
45   PROP_PARANOIA_MODE,
46   PROP_SEARCH_OVERLAP,
47   PROP_GENERIC_DEVICE,
48   PROP_CACHE_SIZE
49 };
50
51 #define DEFAULT_READ_SPEED              -1
52 #define DEFAULT_SEARCH_OVERLAP          -1
53 #define DEFAULT_PARANOIA_MODE            PARANOIA_MODE_FRAGMENT
54 #define DEFAULT_GENERIC_DEVICE           NULL
55 #define DEFAULT_CACHE_SIZE              -1
56
57 GST_DEBUG_CATEGORY_STATIC (gst_cd_paranoia_src_debug);
58 #define GST_CAT_DEFAULT gst_cd_paranoia_src_debug
59
60 #define gst_cd_paranoia_src_parent_class parent_class
61 G_DEFINE_TYPE (GstCdParanoiaSrc, gst_cd_paranoia_src, GST_TYPE_AUDIO_CD_SRC);
62 GST_ELEMENT_REGISTER_DEFINE (cdparanoiasrc, "cdparanoiasrc", GST_RANK_SECONDARY,
63     GST_TYPE_CD_PARANOIA_SRC);
64
65 static void gst_cd_paranoia_src_finalize (GObject * obj);
66 static void gst_cd_paranoia_src_get_property (GObject * object, guint prop_id,
67     GValue * value, GParamSpec * pspec);
68 static void gst_cd_paranoia_src_set_property (GObject * object, guint prop_id,
69     const GValue * value, GParamSpec * pspec);
70 static GstBuffer *gst_cd_paranoia_src_read_sector (GstAudioCdSrc * src,
71     gint sector);
72 static gboolean gst_cd_paranoia_src_open (GstAudioCdSrc * src,
73     const gchar * device);
74 static void gst_cd_paranoia_src_close (GstAudioCdSrc * src);
75
76 /* We use these to serialize calls to paranoia_read() among several
77  * cdparanoiasrc instances. We do this because it's the only reasonably
78  * easy way to find out the calling object from within the paranoia
79  * callback, and we need the object instance in there to emit our signals */
80 static GstCdParanoiaSrc *cur_cb_source;
81 static GMutex cur_cb_mutex;
82
83 static gint cdpsrc_signals[NUM_SIGNALS];        /* all 0 */
84
85 #define GST_TYPE_CD_PARANOIA_MODE (gst_cd_paranoia_mode_get_type())
86 static GType
87 gst_cd_paranoia_mode_get_type (void)
88 {
89   static const GFlagsValue paranoia_modes[] = {
90     {PARANOIA_MODE_DISABLE, "PARANOIA_MODE_DISABLE", "disable"},
91     {PARANOIA_MODE_FRAGMENT, "PARANOIA_MODE_FRAGMENT", "fragment"},
92     {PARANOIA_MODE_OVERLAP, "PARANOIA_MODE_OVERLAP", "overlap"},
93     {PARANOIA_MODE_SCRATCH, "PARANOIA_MODE_SCRATCH", "scratch"},
94     {PARANOIA_MODE_REPAIR, "PARANOIA_MODE_REPAIR", "repair"},
95     {PARANOIA_MODE_FULL, "PARANOIA_MODE_FULL", "full"},
96     {0, NULL, NULL},
97   };
98
99   static GType type;            /* 0 */
100
101   if (!type) {
102     type = g_flags_register_static ("GstCdParanoiaMode", paranoia_modes);
103   }
104
105   return type;
106 }
107
108 static void
109 gst_cd_paranoia_src_init (GstCdParanoiaSrc * src)
110 {
111   src->d = NULL;
112   src->p = NULL;
113   src->next_sector = -1;
114
115   src->search_overlap = DEFAULT_SEARCH_OVERLAP;
116   src->paranoia_mode = DEFAULT_PARANOIA_MODE;
117   src->read_speed = DEFAULT_READ_SPEED;
118   src->generic_device = g_strdup (DEFAULT_GENERIC_DEVICE);
119   src->cache_size = DEFAULT_CACHE_SIZE;
120 }
121
122 static void
123 gst_cd_paranoia_src_class_init (GstCdParanoiaSrcClass * klass)
124 {
125   GstAudioCdSrcClass *audiocdsrc_class = GST_AUDIO_CD_SRC_CLASS (klass);
126   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
127   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
128
129   gobject_class->set_property = gst_cd_paranoia_src_set_property;
130   gobject_class->get_property = gst_cd_paranoia_src_get_property;
131   gobject_class->finalize = gst_cd_paranoia_src_finalize;
132
133   gst_element_class_set_static_metadata (element_class,
134       "CD Audio (cdda) Source, Paranoia IV", "Source/File",
135       "Read audio from CD in paranoid mode",
136       "Erik Walthinsen <omega@cse.ogi.edu>, Wim Taymans <wim@fluendo.com>");
137
138   audiocdsrc_class->open = gst_cd_paranoia_src_open;
139   audiocdsrc_class->close = gst_cd_paranoia_src_close;
140   audiocdsrc_class->read_sector = gst_cd_paranoia_src_read_sector;
141
142   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_GENERIC_DEVICE,
143       g_param_spec_string ("generic-device", "Generic device",
144           "Use specified generic scsi device", DEFAULT_GENERIC_DEVICE,
145           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_READ_SPEED,
147       g_param_spec_int ("read-speed", "Read speed",
148           "Read from device at specified speed (-1 and 0 = full speed)",
149           -1, G_MAXINT, DEFAULT_READ_SPEED,
150           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PARANOIA_MODE,
152       g_param_spec_flags ("paranoia-mode", "Paranoia mode",
153           "Type of checking to perform", GST_TYPE_CD_PARANOIA_MODE,
154           DEFAULT_PARANOIA_MODE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SEARCH_OVERLAP,
156       g_param_spec_int ("search-overlap", "Search overlap",
157           "Force minimum overlap search during verification to n sectors", -1,
158           75, DEFAULT_SEARCH_OVERLAP,
159           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
160   /**
161    * GstCdParanoiaSrc:cache-size:
162    *
163    * Set CD cache size to n sectors (-1 = auto)
164    */
165   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CACHE_SIZE,
166       g_param_spec_int ("cache-size", "Cache size",
167           "Set CD cache size to n sectors (-1 = auto)", -1,
168           G_MAXINT, DEFAULT_CACHE_SIZE,
169           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170
171   /* FIXME: we don't really want signals for this, but messages on the bus,
172    * but then we can't check any longer whether anyone is interested in them */
173   /**
174    * GstCdParanoiaSrc::transport-error:
175    * @cdparanoia: The CdParanoia instance
176    * @sector: The sector number at which the error was encountered.
177    *
178    * This signal is emitted whenever an error occurs while reading.
179    * CdParanoia will attempt to recover the data.
180    */
181   cdpsrc_signals[TRANSPORT_ERROR] =
182       g_signal_new ("transport-error", G_TYPE_FROM_CLASS (klass),
183       G_SIGNAL_RUN_LAST,
184       G_STRUCT_OFFSET (GstCdParanoiaSrcClass, transport_error),
185       NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
186   /**
187    * GstCdParanoiaSrc::uncorrected-error:
188    * @cdparanoia: The CdParanoia instance
189    * @sector: The sector number at which the error was encountered.
190    *
191    * This signal is emitted whenever an uncorrectable error occurs while
192    * reading. The data could not be read.
193    */
194   cdpsrc_signals[UNCORRECTED_ERROR] =
195       g_signal_new ("uncorrected-error", G_TYPE_FROM_CLASS (klass),
196       G_SIGNAL_RUN_LAST,
197       G_STRUCT_OFFSET (GstCdParanoiaSrcClass, uncorrected_error),
198       NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
199
200   gst_type_mark_as_plugin_api (GST_TYPE_CD_PARANOIA_MODE, 0);
201 }
202
203 static gboolean
204 gst_cd_paranoia_src_open (GstAudioCdSrc * audiocdsrc, const gchar * device)
205 {
206   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
207   gint i, cache_size;
208
209   GST_DEBUG_OBJECT (src, "trying to open device %s (generic-device=%s) ...",
210       device, GST_STR_NULL (src->generic_device));
211
212   /* find the device */
213   if (src->generic_device != NULL) {
214     src->d = cdda_identify_scsi (src->generic_device, device, FALSE, NULL);
215   } else {
216     if (device != NULL) {
217       src->d = cdda_identify (device, FALSE, NULL);
218     } else {
219       src->d = cdda_identify ("/dev/cdrom", FALSE, NULL);
220     }
221   }
222
223   /* fail if the device couldn't be found */
224   if (src->d == NULL)
225     goto no_device;
226
227   /* set verbosity mode */
228   cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
229
230   /* open the disc */
231   if (cdda_open (src->d))
232     goto open_failed;
233
234   GST_INFO_OBJECT (src, "set read speed to %d", src->read_speed);
235   cdda_speed_set (src->d, src->read_speed);
236
237   for (i = 1; i < src->d->tracks + 1; i++) {
238     GstAudioCdSrcTrack track = { 0, };
239
240     track.num = i;
241     track.is_audio = IS_AUDIO (src->d, i - 1);
242     track.start = cdda_track_firstsector (src->d, i);
243     track.end = cdda_track_lastsector (src->d, i);
244     track.tags = NULL;
245
246     gst_audio_cd_src_add_track (GST_AUDIO_CD_SRC (src), &track);
247   }
248
249   /* create the paranoia struct and set it up */
250   src->p = paranoia_init (src->d);
251   if (src->p == NULL)
252     goto init_failed;
253
254   paranoia_modeset (src->p, src->paranoia_mode);
255   GST_INFO_OBJECT (src, "set paranoia mode to 0x%02x", src->paranoia_mode);
256
257   if (src->search_overlap != -1) {
258     paranoia_overlapset (src->p, src->search_overlap);
259     GST_INFO_OBJECT (src, "search overlap set to %u", src->search_overlap);
260   }
261
262   cache_size = src->cache_size;
263   if (cache_size == -1) {
264     /* if paranoia mode is low (the default), assume we're doing playback */
265     if (src->paranoia_mode <= PARANOIA_MODE_FRAGMENT)
266       cache_size = 150;
267     else
268       cache_size = paranoia_cachemodel_size (src->p, -1);
269   }
270   paranoia_cachemodel_size (src->p, cache_size);
271   GST_INFO_OBJECT (src, "set cachemodel size to %u", cache_size);
272
273   src->next_sector = -1;
274
275   return TRUE;
276
277   /* ERRORS */
278 no_device:
279   {
280     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
281         (_("Could not open CD device for reading.")), ("cdda_identify failed"));
282     return FALSE;
283   }
284 open_failed:
285   {
286     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
287         (_("Could not open CD device for reading.")), ("cdda_open failed"));
288     cdda_close (src->d);
289     src->d = NULL;
290     return FALSE;
291   }
292 init_failed:
293   {
294     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
295         ("failed to initialize paranoia"), ("failed to initialize paranoia"));
296     return FALSE;
297   }
298 }
299
300 static void
301 gst_cd_paranoia_src_close (GstAudioCdSrc * audiocdsrc)
302 {
303   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
304
305   if (src->p) {
306     paranoia_free (src->p);
307     src->p = NULL;
308   }
309
310   if (src->d) {
311     cdda_close (src->d);
312     src->d = NULL;
313   }
314
315   src->next_sector = -1;
316 }
317
318 static void
319 gst_cd_paranoia_dummy_callback (long inpos, int function)
320 {
321   /* Used by instanced where no one is interested what's happening here */
322 }
323
324 static void
325 gst_cd_paranoia_paranoia_callback (long inpos, int function)
326 {
327   GstCdParanoiaSrc *src = cur_cb_source;
328   gint sector = (gint) (inpos / CD_FRAMEWORDS);
329
330   switch (function) {
331     case PARANOIA_CB_SKIP:
332       GST_INFO_OBJECT (src, "Skip at sector %d", sector);
333       g_signal_emit (src, cdpsrc_signals[UNCORRECTED_ERROR], 0, sector);
334       break;
335     case PARANOIA_CB_READERR:
336       GST_INFO_OBJECT (src, "Transport error at sector %d", sector);
337       g_signal_emit (src, cdpsrc_signals[TRANSPORT_ERROR], 0, sector);
338       break;
339     default:
340       break;
341   }
342 }
343
344 static gboolean
345 gst_cd_paranoia_src_signal_is_being_watched (GstCdParanoiaSrc * src, gint sig)
346 {
347   return g_signal_has_handler_pending (src, cdpsrc_signals[sig], 0, FALSE);
348 }
349
350 static GstBuffer *
351 gst_cd_paranoia_src_read_sector (GstAudioCdSrc * audiocdsrc, gint sector)
352 {
353   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
354   GstBuffer *buf;
355   gboolean do_serialize;
356   gint16 *cdda_buf;
357
358 #if 0
359   /* Do we really need to output this? (tpm) */
360   /* Due to possible autocorrections of start sectors of audio tracks on 
361    * multisession cds, we can maybe not compute the correct discid.
362    * So issue a warning.
363    * See cdparanoia/interface/common-interface.c:FixupTOC */
364   if (src->d && src->d->cd_extra) {
365     g_message
366         ("DiscID on multisession discs might be broken. Use at own risk.");
367   }
368 #endif
369
370   if (src->next_sector == -1 || src->next_sector != sector) {
371     if (paranoia_seek (src->p, sector, SEEK_SET) == -1)
372       goto seek_failed;
373
374     GST_DEBUG_OBJECT (src, "successfully seeked to sector %d", sector);
375     src->next_sector = sector;
376   }
377
378   do_serialize =
379       gst_cd_paranoia_src_signal_is_being_watched (src, TRANSPORT_ERROR) ||
380       gst_cd_paranoia_src_signal_is_being_watched (src, UNCORRECTED_ERROR);
381
382   if (do_serialize) {
383     GST_LOG_OBJECT (src, "Signal handlers connected, serialising access");
384     g_mutex_lock (&cur_cb_mutex);
385     GST_LOG_OBJECT (src, "Got lock");
386     cur_cb_source = src;
387
388     cdda_buf = paranoia_read (src->p, gst_cd_paranoia_paranoia_callback);
389
390     cur_cb_source = NULL;
391     GST_LOG_OBJECT (src, "Releasing lock");
392     g_mutex_unlock (&cur_cb_mutex);
393   } else {
394     cdda_buf = paranoia_read (src->p, gst_cd_paranoia_dummy_callback);
395   }
396
397   if (cdda_buf == NULL)
398     goto read_failed;
399
400   buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW);
401   gst_buffer_fill (buf, 0, cdda_buf, CD_FRAMESIZE_RAW);
402
403   /* cdda base class will take care of timestamping etc. */
404   ++src->next_sector;
405
406   return buf;
407
408   /* ERRORS */
409 seek_failed:
410   {
411     GST_WARNING_OBJECT (src, "seek to sector %d failed!", sector);
412     GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
413         (_("Could not seek CD.")),
414         ("paranoia_seek to %d failed: %s", sector, g_strerror (errno)));
415     return NULL;
416   }
417 read_failed:
418   {
419     GST_WARNING_OBJECT (src, "read at sector %d failed!", sector);
420     GST_ELEMENT_ERROR (src, RESOURCE, READ,
421         (_("Could not read CD.")),
422         ("paranoia_read at %d failed: %s", sector, g_strerror (errno)));
423     return NULL;
424   }
425 }
426
427 static void
428 gst_cd_paranoia_src_finalize (GObject * obj)
429 {
430   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (obj);
431
432   g_free (src->generic_device);
433
434   G_OBJECT_CLASS (parent_class)->finalize (obj);
435 }
436
437 static void
438 gst_cd_paranoia_src_set_property (GObject * object, guint prop_id,
439     const GValue * value, GParamSpec * pspec)
440 {
441   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object);
442
443   GST_OBJECT_LOCK (src);
444
445   switch (prop_id) {
446     case PROP_GENERIC_DEVICE:{
447       g_free (src->generic_device);
448       src->generic_device = g_value_dup_string (value);
449       if (src->generic_device && src->generic_device[0] == '\0') {
450         g_free (src->generic_device);
451         src->generic_device = NULL;
452       }
453       break;
454     }
455     case PROP_READ_SPEED:{
456       src->read_speed = g_value_get_int (value);
457       if (src->read_speed == 0)
458         src->read_speed = -1;
459       break;
460     }
461     case PROP_PARANOIA_MODE:{
462       src->paranoia_mode = g_value_get_flags (value) & PARANOIA_MODE_FULL;
463       break;
464     }
465     case PROP_SEARCH_OVERLAP:{
466       src->search_overlap = g_value_get_int (value);
467       break;
468     }
469     case PROP_CACHE_SIZE:{
470       src->cache_size = g_value_get_int (value);
471       break;
472     }
473     default:
474       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
475       break;
476   }
477
478   GST_OBJECT_UNLOCK (src);
479 }
480
481 static void
482 gst_cd_paranoia_src_get_property (GObject * object, guint prop_id,
483     GValue * value, GParamSpec * pspec)
484 {
485   GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object);
486
487   GST_OBJECT_LOCK (src);
488
489   switch (prop_id) {
490     case PROP_READ_SPEED:
491       g_value_set_int (value, src->read_speed);
492       break;
493     case PROP_PARANOIA_MODE:
494       g_value_set_flags (value, src->paranoia_mode);
495       break;
496     case PROP_GENERIC_DEVICE:
497       g_value_set_string (value, src->generic_device);
498       break;
499     case PROP_SEARCH_OVERLAP:
500       g_value_set_int (value, src->search_overlap);
501       break;
502     case PROP_CACHE_SIZE:
503       g_value_set_int (value, src->cache_size);
504       break;
505     default:
506       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
507       break;
508   }
509
510   GST_OBJECT_UNLOCK (src);
511 }
512
513 static gboolean
514 plugin_init (GstPlugin * plugin)
515 {
516   gboolean ret = FALSE;
517
518   GST_DEBUG_CATEGORY_INIT (gst_cd_paranoia_src_debug, "cdparanoiasrc", 0,
519       "CD Paranoia Source");
520
521   ret |= GST_ELEMENT_REGISTER (cdparanoiasrc, plugin);
522
523 #ifdef ENABLE_NLS
524   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
525       LOCALEDIR);
526   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
527   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
528 #endif
529
530   return ret;
531 }
532
533 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
534     GST_VERSION_MINOR,
535     cdparanoia,
536     "Read audio from CD in paranoid mode",
537     plugin_init, GST_PLUGINS_BASE_VERSION, "LGPL", GST_PACKAGE_NAME,
538     GST_PACKAGE_ORIGIN)