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