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