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 #ifdef PARANOIA_CB_CACHEERR
259 cache_size = src->cache_size;
260 if (cache_size == -1) {
261 /* if paranoia mode is low (the default), assume we're doing playback */
262 if (src->paranoia_mode <= PARANOIA_MODE_FRAGMENT)
263 cache_size = 150;
264 else
265 cache_size = paranoia_cachemodel_size (src->p, -1);
266 }
267 paranoia_cachemodel_size (src->p, cache_size);
268 GST_INFO_OBJECT (src, "set cachemodel size to %u", cache_size);
269 #endif
270
271 src->next_sector = -1;
272
273 return TRUE;
274
275 /* ERRORS */
276 no_device:
277 {
278 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
279 (_("Could not open CD device for reading.")), ("cdda_identify failed"));
280 return FALSE;
281 }
282 open_failed:
283 {
284 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
285 (_("Could not open CD device for reading.")), ("cdda_open failed"));
286 cdda_close (src->d);
287 src->d = NULL;
288 return FALSE;
289 }
290 init_failed:
291 {
292 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
293 ("failed to initialize paranoia"), ("failed to initialize paranoia"));
294 return FALSE;
295 }
296 }
297
298 static void
gst_cd_paranoia_src_close(GstAudioCdSrc * audiocdsrc)299 gst_cd_paranoia_src_close (GstAudioCdSrc * audiocdsrc)
300 {
301 GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
302
303 if (src->p) {
304 paranoia_free (src->p);
305 src->p = NULL;
306 }
307
308 if (src->d) {
309 cdda_close (src->d);
310 src->d = NULL;
311 }
312
313 src->next_sector = -1;
314 }
315
316 static void
gst_cd_paranoia_dummy_callback(long inpos,int function)317 gst_cd_paranoia_dummy_callback (long inpos, int function)
318 {
319 /* Used by instanced where no one is interested what's happening here */
320 }
321
322 static void
gst_cd_paranoia_paranoia_callback(long inpos,int function)323 gst_cd_paranoia_paranoia_callback (long inpos, int function)
324 {
325 GstCdParanoiaSrc *src = cur_cb_source;
326 gint sector = (gint) (inpos / CD_FRAMEWORDS);
327
328 switch (function) {
329 case PARANOIA_CB_SKIP:
330 GST_INFO_OBJECT (src, "Skip at sector %d", sector);
331 g_signal_emit (src, cdpsrc_signals[UNCORRECTED_ERROR], 0, sector);
332 break;
333 case PARANOIA_CB_READERR:
334 GST_INFO_OBJECT (src, "Transport error at sector %d", sector);
335 g_signal_emit (src, cdpsrc_signals[TRANSPORT_ERROR], 0, sector);
336 break;
337 default:
338 break;
339 }
340 }
341
342 static gboolean
gst_cd_paranoia_src_signal_is_being_watched(GstCdParanoiaSrc * src,gint sig)343 gst_cd_paranoia_src_signal_is_being_watched (GstCdParanoiaSrc * src, gint sig)
344 {
345 return g_signal_has_handler_pending (src, cdpsrc_signals[sig], 0, FALSE);
346 }
347
348 static GstBuffer *
gst_cd_paranoia_src_read_sector(GstAudioCdSrc * audiocdsrc,gint sector)349 gst_cd_paranoia_src_read_sector (GstAudioCdSrc * audiocdsrc, gint sector)
350 {
351 GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (audiocdsrc);
352 GstBuffer *buf;
353 gboolean do_serialize;
354 gint16 *cdda_buf;
355
356 #if 0
357 /* Do we really need to output this? (tpm) */
358 /* Due to possible autocorrections of start sectors of audio tracks on
359 * multisession cds, we can maybe not compute the correct discid.
360 * So issue a warning.
361 * See cdparanoia/interface/common-interface.c:FixupTOC */
362 if (src->d && src->d->cd_extra) {
363 g_message
364 ("DiscID on multisession discs might be broken. Use at own risk.");
365 }
366 #endif
367
368 if (src->next_sector == -1 || src->next_sector != sector) {
369 if (paranoia_seek (src->p, sector, SEEK_SET) == -1)
370 goto seek_failed;
371
372 GST_DEBUG_OBJECT (src, "successfully seeked to sector %d", sector);
373 src->next_sector = sector;
374 }
375
376 do_serialize =
377 gst_cd_paranoia_src_signal_is_being_watched (src, TRANSPORT_ERROR) ||
378 gst_cd_paranoia_src_signal_is_being_watched (src, UNCORRECTED_ERROR);
379
380 if (do_serialize) {
381 GST_LOG_OBJECT (src, "Signal handlers connected, serialising access");
382 g_mutex_lock (&cur_cb_mutex);
383 GST_LOG_OBJECT (src, "Got lock");
384 cur_cb_source = src;
385
386 cdda_buf = paranoia_read (src->p, gst_cd_paranoia_paranoia_callback);
387
388 cur_cb_source = NULL;
389 GST_LOG_OBJECT (src, "Releasing lock");
390 g_mutex_unlock (&cur_cb_mutex);
391 } else {
392 cdda_buf = paranoia_read (src->p, gst_cd_paranoia_dummy_callback);
393 }
394
395 if (cdda_buf == NULL)
396 goto read_failed;
397
398 buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW);
399 gst_buffer_fill (buf, 0, cdda_buf, CD_FRAMESIZE_RAW);
400
401 /* cdda base class will take care of timestamping etc. */
402 ++src->next_sector;
403
404 return buf;
405
406 /* ERRORS */
407 seek_failed:
408 {
409 GST_WARNING_OBJECT (src, "seek to sector %d failed!", sector);
410 GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
411 (_("Could not seek CD.")),
412 ("paranoia_seek to %d failed: %s", sector, g_strerror (errno)));
413 return NULL;
414 }
415 read_failed:
416 {
417 GST_WARNING_OBJECT (src, "read at sector %d failed!", sector);
418 GST_ELEMENT_ERROR (src, RESOURCE, READ,
419 (_("Could not read CD.")),
420 ("paranoia_read at %d failed: %s", sector, g_strerror (errno)));
421 return NULL;
422 }
423 }
424
425 static void
gst_cd_paranoia_src_finalize(GObject * obj)426 gst_cd_paranoia_src_finalize (GObject * obj)
427 {
428 GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (obj);
429
430 g_free (src->generic_device);
431
432 G_OBJECT_CLASS (parent_class)->finalize (obj);
433 }
434
435 static void
gst_cd_paranoia_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)436 gst_cd_paranoia_src_set_property (GObject * object, guint prop_id,
437 const GValue * value, GParamSpec * pspec)
438 {
439 GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object);
440
441 GST_OBJECT_LOCK (src);
442
443 switch (prop_id) {
444 case PROP_GENERIC_DEVICE:{
445 g_free (src->generic_device);
446 src->generic_device = g_value_dup_string (value);
447 if (src->generic_device && src->generic_device[0] == '\0') {
448 g_free (src->generic_device);
449 src->generic_device = NULL;
450 }
451 break;
452 }
453 case PROP_READ_SPEED:{
454 src->read_speed = g_value_get_int (value);
455 if (src->read_speed == 0)
456 src->read_speed = -1;
457 break;
458 }
459 case PROP_PARANOIA_MODE:{
460 src->paranoia_mode = g_value_get_flags (value) & PARANOIA_MODE_FULL;
461 break;
462 }
463 case PROP_SEARCH_OVERLAP:{
464 src->search_overlap = g_value_get_int (value);
465 break;
466 }
467 case PROP_CACHE_SIZE:{
468 src->cache_size = g_value_get_int (value);
469 break;
470 }
471 default:
472 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
473 break;
474 }
475
476 GST_OBJECT_UNLOCK (src);
477 }
478
479 static void
gst_cd_paranoia_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)480 gst_cd_paranoia_src_get_property (GObject * object, guint prop_id,
481 GValue * value, GParamSpec * pspec)
482 {
483 GstCdParanoiaSrc *src = GST_CD_PARANOIA_SRC (object);
484
485 GST_OBJECT_LOCK (src);
486
487 switch (prop_id) {
488 case PROP_READ_SPEED:
489 g_value_set_int (value, src->read_speed);
490 break;
491 case PROP_PARANOIA_MODE:
492 g_value_set_flags (value, src->paranoia_mode);
493 break;
494 case PROP_GENERIC_DEVICE:
495 g_value_set_string (value, src->generic_device);
496 break;
497 case PROP_SEARCH_OVERLAP:
498 g_value_set_int (value, src->search_overlap);
499 break;
500 case PROP_CACHE_SIZE:
501 g_value_set_int (value, src->cache_size);
502 break;
503 default:
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
505 break;
506 }
507
508 GST_OBJECT_UNLOCK (src);
509 }
510
511 static gboolean
plugin_init(GstPlugin * plugin)512 plugin_init (GstPlugin * plugin)
513 {
514 GST_DEBUG_CATEGORY_INIT (gst_cd_paranoia_src_debug, "cdparanoiasrc", 0,
515 "CD Paranoia Source");
516
517 if (!gst_element_register (plugin, "cdparanoiasrc", GST_RANK_SECONDARY,
518 GST_TYPE_CD_PARANOIA_SRC))
519 return FALSE;
520
521 #ifdef ENABLE_NLS
522 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
523 LOCALEDIR);
524 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
525 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
526 #endif
527
528 return TRUE;
529 }
530
531 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
532 GST_VERSION_MINOR,
533 cdparanoia,
534 "Read audio from CD in paranoid mode",
535 plugin_init, GST_PLUGINS_BASE_VERSION, "LGPL", GST_PACKAGE_NAME,
536 GST_PACKAGE_ORIGIN)
537