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