gst_plugins_base/ext/cdparanoia/gstcdparanoiasrc.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     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)