Merging gst-plugins-ugly
[platform/upstream/gstreamer.git] / ext / cdio / gstcdiocddasrc.c
1 /* GStreamer
2  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-cdiocddasrc
22  * @title: cdiocddasrc
23  * @see_also: GstCdParanoiaSrc, GstAudioCdSrc
24  *
25  * cdiocddasrc reads and extracts raw audio from Audio CDs. It can operate
26  * in one of two modes:
27  *
28  *  * treat each track as a separate stream, counting time from the start
29  *    of the track to the end of the track and posting EOS at the end of
30  *    a track, or
31  *  * treat the entire disc as one stream, counting time from the start of
32  *    the first track to the end of the last track, posting EOS only at
33  *    the end of the last track.
34  *
35  * With a recent-enough version of libcdio, the element will extract
36  * CD-TEXT if this is supported by the CD-drive and CD-TEXT information
37  * is available on the CD. The information will be posted on the bus in
38  * form of a tag message.
39  *
40  * When opened, the element will also calculate a CDDB disc ID and a
41  * MusicBrainz disc ID, which applications can use to query online
42  * databases for artist/title information. These disc IDs will also be
43  * posted on the bus as part of the tag messages.
44  *
45  * cdiocddasrc supports the GstUriHandler interface, so applications can use
46  * playbin with cdda://&lt;track-number&gt; URIs for playback (they will have
47  * to connect to playbin's notify::source signal and set the device on the
48  * cd source in the notify callback if they want to set the device property).
49  * Applications should use seeks in "track" format to switch between different
50  * tracks of the same CD (passing a new cdda:// URI to playbin involves opening
51  * and closing the CD device, which is much slower).
52  *
53  * ## Example launch line
54  *
55  * |[
56  * gst-launch-1.0 cdiocddasrc track=5 device=/dev/cdrom ! audioconvert ! vorbisenc ! oggmux ! filesink location=track5.ogg
57  * ]|
58  * This pipeline extracts track 5 of the audio CD and encodes it into an
59  * Ogg/Vorbis file.
60  *
61  */
62
63 #ifdef HAVE_CONFIG_H
64 #include "config.h"
65 #endif
66
67 #include "gstcdio.h"
68 #include "gstcdiocddasrc.h"
69
70 #include <gst/gst.h>
71 #include "gst/gst-i18n-plugin.h"
72
73 #include <sys/types.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <errno.h>
77
78 #define SAMPLES_PER_SECTOR (CDIO_CD_FRAMESIZE_RAW / sizeof (gint16))
79
80 #define DEFAULT_READ_SPEED   -1
81
82 enum
83 {
84   PROP_0 = 0,
85   PROP_READ_SPEED
86 };
87
88 GST_DEBUG_CATEGORY (gst_cdio_debug);
89
90
91 G_DEFINE_TYPE (GstCdioCddaSrc, gst_cdio_cdda_src, GST_TYPE_AUDIO_CD_SRC);
92 #define _do_init \
93     GST_DEBUG_CATEGORY_INIT (gst_cdio_debug, "cdio", 0, "libcdio elements"); \
94     cdio_log_set_handler (gst_cdio_log_handler);
95 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (cdiocddasrc, "cdiocddasrc",
96     GST_RANK_SECONDARY - 1, GST_TYPE_CDIO_CDDA_SRC, _do_init);
97
98 static void gst_cdio_cdda_src_finalize (GObject * obj);
99 static void gst_cdio_cdda_src_set_property (GObject * object, guint prop_id,
100     const GValue * value, GParamSpec * pspec);
101 static void gst_cdio_cdda_src_get_property (GObject * object, guint prop_id,
102     GValue * value, GParamSpec * pspec);
103 static GstBuffer *gst_cdio_cdda_src_read_sector (GstAudioCdSrc * src,
104     gint sector);
105 static gboolean gst_cdio_cdda_src_open (GstAudioCdSrc * src,
106     const gchar * device);
107 static void gst_cdio_cdda_src_close (GstAudioCdSrc * src);
108
109 #if 0
110 static gchar *
111 gst_cdio_cdda_src_get_default_device (GstAudioCdSrc * audiocdsrc)
112 {
113   GstCdioCddaSrc *src;
114   gchar *default_device, *ret;
115
116   src = GST_CDIO_CDDA_SRC (audiocdsrc);
117
118   /* src->cdio may be NULL here */
119   default_device = cdio_get_default_device (src->cdio);
120
121   ret = g_strdup (default_device);
122   free (default_device);
123
124   GST_LOG_OBJECT (src, "returning default device: %s", GST_STR_NULL (ret));
125
126   return ret;
127 }
128
129 static gchar **
130 gst_cdio_cdda_src_probe_devices (GstAudioCdSrc * audiocdsrc)
131 {
132   char **devices, **ret, **d;
133
134   /* FIXME: might return the same hardware device twice, e.g.
135    * as /dev/cdrom and /dev/dvd - gotta do something more sophisticated */
136   devices = cdio_get_devices (DRIVER_DEVICE);
137
138   if (devices == NULL)
139     goto no_devices;
140
141   if (*devices == NULL)
142     goto empty_devices;
143
144   ret = g_strdupv (devices);
145   for (d = devices; *d != NULL; ++d) {
146     GST_DEBUG_OBJECT (audiocdsrc, "device: %s", GST_STR_NULL (*d));
147     free (*d);
148   }
149   free (devices);
150
151   return ret;
152
153   /* ERRORS */
154 no_devices:
155   {
156     GST_DEBUG_OBJECT (audiocdsrc, "no devices found");
157     return NULL;
158   }
159 empty_devices:
160   {
161     GST_DEBUG_OBJECT (audiocdsrc, "empty device list found");
162     free (devices);
163     return NULL;
164   }
165 }
166 #endif
167
168 static GstBuffer *
169 gst_cdio_cdda_src_read_sector (GstAudioCdSrc * audiocdsrc, gint sector)
170 {
171   GstCdioCddaSrc *src;
172   guint8 *data;
173
174   src = GST_CDIO_CDDA_SRC (audiocdsrc);
175
176   data = g_malloc (CDIO_CD_FRAMESIZE_RAW);
177
178   /* can't use pad_alloc because we can't return the GstFlowReturn (FIXME 0.11) */
179   if (cdio_read_audio_sector (src->cdio, data, sector) != 0)
180     goto read_failed;
181
182   if (src->swap_le_be) {
183     gint16 *pcm_data = (gint16 *) data;
184     gint i;
185
186     for (i = 0; i < SAMPLES_PER_SECTOR; ++i)
187       pcm_data[i] = GUINT16_SWAP_LE_BE (pcm_data[i]);
188   }
189
190   return gst_buffer_new_wrapped (data, CDIO_CD_FRAMESIZE_RAW);
191
192   /* ERRORS */
193 read_failed:
194   {
195     GST_WARNING_OBJECT (src, "read at sector %d failed!", sector);
196     GST_ELEMENT_ERROR (src, RESOURCE, READ,
197         (_("Could not read from CD.")),
198         ("cdio_read_audio_sector at %d failed: %s", sector,
199             g_strerror (errno)));
200     g_free (data);
201     return NULL;
202   }
203 }
204
205 static gboolean
206 gst_cdio_cdda_src_do_detect_drive_endianness (GstCdioCddaSrc * src, gint from,
207     gint to)
208 {
209   gint16 pcm_data[SAMPLES_PER_SECTOR], last_pcm_ne, last_pcm_oe;
210   gdouble ne_sumd0, ne_sumd1, ne_factor;
211   gdouble oe_sumd0, oe_sumd1, oe_factor;
212   gdouble diff;
213   gint sector;
214   gint i;
215
216   ne_sumd0 = ne_sumd1 = 0.0;
217   oe_sumd0 = oe_sumd1 = 0.0;
218   last_pcm_ne = 0;
219   last_pcm_oe = 0;
220
221   GST_LOG_OBJECT (src, "checking sector %d to %d", from, to);
222
223   for (sector = from; sector < to; ++sector) {
224     if (cdio_read_audio_sector (src->cdio, pcm_data, sector) != 0)
225       goto read_failed;
226
227     /* only evaluate samples for left channel */
228     for (i = 0; i < SAMPLES_PER_SECTOR; i += 2) {
229       gint16 pcm;
230
231       /* Native endianness first */
232       pcm = pcm_data[i];
233       ne_sumd0 += abs (pcm);
234       ne_sumd1 += abs (pcm - last_pcm_ne);
235       last_pcm_ne = pcm;
236
237       /* other endianness next */
238       pcm = GUINT16_SWAP_LE_BE (pcm);
239       oe_sumd0 += abs (pcm);
240       oe_sumd1 += abs (pcm - last_pcm_oe);
241       last_pcm_oe = pcm;
242     }
243
244   }
245
246   ne_factor = (ne_sumd1 / ne_sumd0);
247   oe_factor = (oe_sumd1 / oe_sumd0);
248   diff = ne_factor - oe_factor;
249
250   GST_DEBUG_OBJECT (src, "Native: %.2f, Other: %.2f, diff: %.2f",
251       ne_factor, oe_factor, diff);
252
253   if (diff > 0.5) {
254     GST_INFO_OBJECT (src, "Drive produces samples in other endianness");
255     src->swap_le_be = TRUE;
256     return TRUE;
257   } else if (diff < -0.5) {
258     GST_INFO_OBJECT (src, "Drive produces samples in host endianness");
259     src->swap_le_be = FALSE;
260     return TRUE;
261   } else {
262     GST_INFO_OBJECT (src, "Inconclusive, assuming host endianness");
263     src->swap_le_be = FALSE;
264     return FALSE;
265   }
266
267 /* ERRORS */
268 read_failed:
269   {
270     GST_WARNING_OBJECT (src, "could not read sector %d", sector);
271     src->swap_le_be = FALSE;
272     return FALSE;
273   }
274 }
275
276 static void
277 gst_cdio_cdda_src_detect_drive_endianness (GstCdioCddaSrc * src, gint first,
278     gint last)
279 {
280   gint from, to;
281
282   GST_INFO ("Detecting drive endianness");
283
284   /* try middle of disc first */
285   from = (first + last) / 2;
286   to = MIN (from + 10, last);
287   if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
288     return;
289
290   /* if that was inconclusive, try other places */
291   from = (first + last) / 4;
292   to = MIN (from + 10, last);
293   if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
294     return;
295
296   from = (first + last) * 3 / 4;
297   to = MIN (from + 10, last);
298   if (gst_cdio_cdda_src_do_detect_drive_endianness (src, from, to))
299     return;
300
301   /* if that's still inconclusive, we give up and assume host endianness */
302   return;
303 }
304
305 static gboolean
306 notcdio_track_is_audio_track (const CdIo * p_cdio, track_t i_track)
307 {
308   return (cdio_get_track_format (p_cdio, i_track) == TRACK_FORMAT_AUDIO);
309 }
310
311 static gboolean
312 gst_cdio_cdda_src_open (GstAudioCdSrc * audiocdsrc, const gchar * device)
313 {
314   GstCdioCddaSrc *src;
315   discmode_t discmode;
316   gint first_track, num_tracks, i;
317   gint first_audio_sector = 0, last_audio_sector = 0;
318 #if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
319   cdtext_t *cdtext;
320 #endif
321
322   src = GST_CDIO_CDDA_SRC (audiocdsrc);
323
324   g_assert (device != NULL);
325   g_assert (src->cdio == NULL);
326
327   GST_LOG_OBJECT (src, "trying to open device %s", device);
328
329   if (!(src->cdio = cdio_open (device, DRIVER_UNKNOWN)))
330     goto open_failed;
331
332   discmode = cdio_get_discmode (src->cdio);
333   GST_LOG_OBJECT (src, "discmode: %d", (gint) discmode);
334
335   if (discmode != CDIO_DISC_MODE_CD_DA && discmode != CDIO_DISC_MODE_CD_MIXED)
336     goto not_audio;
337
338   first_track = cdio_get_first_track_num (src->cdio);
339   num_tracks = cdio_get_num_tracks (src->cdio);
340
341   if (num_tracks <= 0 || first_track < 0)
342     return TRUE;                /* base class will generate 'has no tracks' error */
343
344   if (src->read_speed != -1)
345     cdio_set_speed (src->cdio, src->read_speed);
346
347 #if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
348   cdtext = cdio_get_cdtext (src->cdio);
349
350   if (NULL == cdtext)
351     GST_DEBUG_OBJECT (src, "no CD-TEXT on disc");
352   else
353     gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), cdtext,
354         audiocdsrc->tags);
355 #else
356   gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), src->cdio,
357       audiocdsrc->tags);
358 #endif
359
360   GST_LOG_OBJECT (src, "%u tracks, first track: %d", num_tracks, first_track);
361
362   for (i = 0; i < num_tracks; ++i) {
363     GstAudioCdSrcTrack track = { 0, };
364     gint len_sectors;
365
366     len_sectors = cdio_get_track_sec_count (src->cdio, i + first_track);
367
368     track.num = i + first_track;
369     track.is_audio = notcdio_track_is_audio_track (src->cdio, i + first_track);
370
371     /* Note: LSN/LBA confusion all around us; in any case, this does
372      * the right thing here (for cddb id calculations etc. as well) */
373     track.start = cdio_get_track_lsn (src->cdio, i + first_track);
374     track.end = track.start + len_sectors - 1;  /* -1? */
375
376     if (track.is_audio) {
377       first_audio_sector = MIN (first_audio_sector, track.start);
378       last_audio_sector = MAX (last_audio_sector, track.end);
379     }
380 #if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
381     if (NULL != cdtext)
382       track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), cdtext,
383           i + first_track);
384 #else
385     track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), src->cdio,
386         i + first_track);
387 #endif
388
389     gst_audio_cd_src_add_track (GST_AUDIO_CD_SRC (src), &track);
390   }
391
392   /* Try to detect if we need to byte-order swap the samples coming from the
393    * drive, which might be the case if the CD drive operates in a different
394    * endianness than the host CPU's endianness (happens on e.g. Powerbook G4) */
395   gst_cdio_cdda_src_detect_drive_endianness (src, first_audio_sector,
396       last_audio_sector);
397
398   return TRUE;
399
400   /* ERRORS */
401 open_failed:
402   {
403     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
404         (_("Could not open CD device for reading.")),
405         ("cdio_open() failed: %s", g_strerror (errno)));
406     return FALSE;
407   }
408 not_audio:
409   {
410     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
411         (_("Disc is not an Audio CD.")), ("discmode: %d", (gint) discmode));
412
413     cdio_destroy (src->cdio);
414     src->cdio = NULL;
415     return FALSE;
416   }
417 }
418
419 static void
420 gst_cdio_cdda_src_close (GstAudioCdSrc * audiocdsrc)
421 {
422   GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (audiocdsrc);
423
424   if (src->cdio) {
425     cdio_destroy (src->cdio);
426     src->cdio = NULL;
427   }
428 }
429
430 static void
431 gst_cdio_cdda_src_init (GstCdioCddaSrc * src)
432 {
433   src->read_speed = DEFAULT_READ_SPEED; /* don't need atomic access here */
434   src->cdio = NULL;
435 }
436
437 static void
438 gst_cdio_cdda_src_finalize (GObject * obj)
439 {
440   GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (obj);
441
442   if (src->cdio) {
443     cdio_destroy (src->cdio);
444     src->cdio = NULL;
445   }
446
447   G_OBJECT_CLASS (gst_cdio_cdda_src_parent_class)->finalize (obj);
448 }
449
450 static void
451 gst_cdio_cdda_src_class_init (GstCdioCddaSrcClass * klass)
452 {
453   GstAudioCdSrcClass *audiocdsrc_class = GST_AUDIO_CD_SRC_CLASS (klass);
454   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
455   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
456
457   gobject_class->set_property = gst_cdio_cdda_src_set_property;
458   gobject_class->get_property = gst_cdio_cdda_src_get_property;
459   gobject_class->finalize = gst_cdio_cdda_src_finalize;
460
461   audiocdsrc_class->open = gst_cdio_cdda_src_open;
462   audiocdsrc_class->close = gst_cdio_cdda_src_close;
463   audiocdsrc_class->read_sector = gst_cdio_cdda_src_read_sector;
464 #if 0
465   audiocdsrc_class->probe_devices = gst_cdio_cdda_src_probe_devices;
466   audiocdsrc_class->get_default_device = gst_cdio_cdda_src_get_default_device;
467 #endif
468
469   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_READ_SPEED,
470       g_param_spec_int ("read-speed", "Read speed",
471           "Read from device at the specified speed (-1 = default)", -1, 100,
472           DEFAULT_READ_SPEED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
473
474   gst_element_class_set_static_metadata (element_class,
475       "CD audio source (CDDA)", "Source/File",
476       "Read audio from CD using libcdio",
477       "Tim-Philipp Müller <tim centricular net>");
478 }
479
480 static void
481 gst_cdio_cdda_src_set_property (GObject * object, guint prop_id,
482     const GValue * value, GParamSpec * pspec)
483 {
484   GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
485
486   switch (prop_id) {
487     case PROP_READ_SPEED:{
488       gint speed;
489
490       speed = g_value_get_int (value);
491       g_atomic_int_set (&src->read_speed, speed);
492       break;
493     }
494     default:
495       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
496       break;
497   }
498 }
499
500 static void
501 gst_cdio_cdda_src_get_property (GObject * object, guint prop_id,
502     GValue * value, GParamSpec * pspec)
503 {
504   GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
505
506   switch (prop_id) {
507     case PROP_READ_SPEED:{
508       gint speed;
509
510       speed = g_atomic_int_get (&src->read_speed);
511       g_value_set_int (value, speed);
512       break;
513     }
514     default:
515       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
516       break;
517   }
518 }