2 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
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.
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.
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.
21 * SECTION:element-cdiocddasrc
23 * @see_also: GstCdParanoiaSrc, GstAudioCdSrc
25 * cdiocddasrc reads and extracts raw audio from Audio CDs. It can operate
26 * in one of two modes:
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
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.
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.
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.
45 * cdiocddasrc supports the GstUriHandler interface, so applications can use
46 * playbin with cdda://<track-number> 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).
53 * ## Example launch line
56 * gst-launch-1.0 cdiocddasrc track=5 device=/dev/cdrom ! audioconvert ! vorbisenc ! oggmux ! filesink location=track5.ogg
58 * This pipeline extracts track 5 of the audio CD and encodes it into an
68 #include "gstcdiocddasrc.h"
71 #include "gst/gst-i18n-plugin.h"
73 #include <sys/types.h>
78 #define SAMPLES_PER_SECTOR (CDIO_CD_FRAMESIZE_RAW / sizeof (gint16))
80 #define DEFAULT_READ_SPEED -1
88 GST_DEBUG_CATEGORY (gst_cdio_debug);
91 G_DEFINE_TYPE (GstCdioCddaSrc, gst_cdio_cdda_src, GST_TYPE_AUDIO_CD_SRC);
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);
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,
105 static gboolean gst_cdio_cdda_src_open (GstAudioCdSrc * src,
106 const gchar * device);
107 static void gst_cdio_cdda_src_close (GstAudioCdSrc * src);
111 gst_cdio_cdda_src_get_default_device (GstAudioCdSrc * audiocdsrc)
114 gchar *default_device, *ret;
116 src = GST_CDIO_CDDA_SRC (audiocdsrc);
118 /* src->cdio may be NULL here */
119 default_device = cdio_get_default_device (src->cdio);
121 ret = g_strdup (default_device);
122 free (default_device);
124 GST_LOG_OBJECT (src, "returning default device: %s", GST_STR_NULL (ret));
130 gst_cdio_cdda_src_probe_devices (GstAudioCdSrc * audiocdsrc)
132 char **devices, **ret, **d;
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);
141 if (*devices == NULL)
144 ret = g_strdupv (devices);
145 for (d = devices; *d != NULL; ++d) {
146 GST_DEBUG_OBJECT (audiocdsrc, "device: %s", GST_STR_NULL (*d));
156 GST_DEBUG_OBJECT (audiocdsrc, "no devices found");
161 GST_DEBUG_OBJECT (audiocdsrc, "empty device list found");
169 gst_cdio_cdda_src_read_sector (GstAudioCdSrc * audiocdsrc, gint sector)
174 src = GST_CDIO_CDDA_SRC (audiocdsrc);
176 data = g_malloc (CDIO_CD_FRAMESIZE_RAW);
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)
182 if (src->swap_le_be) {
183 gint16 *pcm_data = (gint16 *) data;
186 for (i = 0; i < SAMPLES_PER_SECTOR; ++i)
187 pcm_data[i] = GUINT16_SWAP_LE_BE (pcm_data[i]);
190 return gst_buffer_new_wrapped (data, CDIO_CD_FRAMESIZE_RAW);
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)));
206 gst_cdio_cdda_src_do_detect_drive_endianness (GstCdioCddaSrc * src, gint from,
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;
216 ne_sumd0 = ne_sumd1 = 0.0;
217 oe_sumd0 = oe_sumd1 = 0.0;
221 GST_LOG_OBJECT (src, "checking sector %d to %d", from, to);
223 for (sector = from; sector < to; ++sector) {
224 if (cdio_read_audio_sector (src->cdio, pcm_data, sector) != 0)
227 /* only evaluate samples for left channel */
228 for (i = 0; i < SAMPLES_PER_SECTOR; i += 2) {
231 /* Native endianness first */
233 ne_sumd0 += abs (pcm);
234 ne_sumd1 += abs (pcm - last_pcm_ne);
237 /* other endianness next */
238 pcm = GUINT16_SWAP_LE_BE (pcm);
239 oe_sumd0 += abs (pcm);
240 oe_sumd1 += abs (pcm - last_pcm_oe);
246 ne_factor = (ne_sumd1 / ne_sumd0);
247 oe_factor = (oe_sumd1 / oe_sumd0);
248 diff = ne_factor - oe_factor;
250 GST_DEBUG_OBJECT (src, "Native: %.2f, Other: %.2f, diff: %.2f",
251 ne_factor, oe_factor, diff);
254 GST_INFO_OBJECT (src, "Drive produces samples in other endianness");
255 src->swap_le_be = TRUE;
257 } else if (diff < -0.5) {
258 GST_INFO_OBJECT (src, "Drive produces samples in host endianness");
259 src->swap_le_be = FALSE;
262 GST_INFO_OBJECT (src, "Inconclusive, assuming host endianness");
263 src->swap_le_be = FALSE;
270 GST_WARNING_OBJECT (src, "could not read sector %d", sector);
271 src->swap_le_be = FALSE;
277 gst_cdio_cdda_src_detect_drive_endianness (GstCdioCddaSrc * src, gint first,
282 GST_INFO ("Detecting drive endianness");
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))
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))
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))
301 /* if that's still inconclusive, we give up and assume host endianness */
306 notcdio_track_is_audio_track (const CdIo * p_cdio, track_t i_track)
308 return (cdio_get_track_format (p_cdio, i_track) == TRACK_FORMAT_AUDIO);
312 gst_cdio_cdda_src_open (GstAudioCdSrc * audiocdsrc, const gchar * device)
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
322 src = GST_CDIO_CDDA_SRC (audiocdsrc);
324 g_assert (device != NULL);
325 g_assert (src->cdio == NULL);
327 GST_LOG_OBJECT (src, "trying to open device %s", device);
329 if (!(src->cdio = cdio_open (device, DRIVER_UNKNOWN)))
332 discmode = cdio_get_discmode (src->cdio);
333 GST_LOG_OBJECT (src, "discmode: %d", (gint) discmode);
335 if (discmode != CDIO_DISC_MODE_CD_DA && discmode != CDIO_DISC_MODE_CD_MIXED)
338 first_track = cdio_get_first_track_num (src->cdio);
339 num_tracks = cdio_get_num_tracks (src->cdio);
341 if (num_tracks <= 0 || first_track < 0)
342 return TRUE; /* base class will generate 'has no tracks' error */
344 if (src->read_speed != -1)
345 cdio_set_speed (src->cdio, src->read_speed);
347 #if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
348 cdtext = cdio_get_cdtext (src->cdio);
351 GST_DEBUG_OBJECT (src, "no CD-TEXT on disc");
353 gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), cdtext,
356 gst_cdio_add_cdtext_album_tags (GST_OBJECT_CAST (src), src->cdio,
360 GST_LOG_OBJECT (src, "%u tracks, first track: %d", num_tracks, first_track);
362 for (i = 0; i < num_tracks; ++i) {
363 GstAudioCdSrcTrack track = { 0, };
366 len_sectors = cdio_get_track_sec_count (src->cdio, i + first_track);
368 track.num = i + first_track;
369 track.is_audio = notcdio_track_is_audio_track (src->cdio, i + first_track);
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? */
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);
380 #if LIBCDIO_VERSION_NUM > 83 || LIBCDIO_VERSION_NUM < 76
382 track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), cdtext,
385 track.tags = gst_cdio_get_cdtext (GST_OBJECT (src), src->cdio,
389 gst_audio_cd_src_add_track (GST_AUDIO_CD_SRC (src), &track);
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,
403 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
404 (_("Could not open CD device for reading.")),
405 ("cdio_open() failed: %s", g_strerror (errno)));
410 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
411 (_("Disc is not an Audio CD.")), ("discmode: %d", (gint) discmode));
413 cdio_destroy (src->cdio);
420 gst_cdio_cdda_src_close (GstAudioCdSrc * audiocdsrc)
422 GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (audiocdsrc);
425 cdio_destroy (src->cdio);
431 gst_cdio_cdda_src_init (GstCdioCddaSrc * src)
433 src->read_speed = DEFAULT_READ_SPEED; /* don't need atomic access here */
438 gst_cdio_cdda_src_finalize (GObject * obj)
440 GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (obj);
443 cdio_destroy (src->cdio);
447 G_OBJECT_CLASS (gst_cdio_cdda_src_parent_class)->finalize (obj);
451 gst_cdio_cdda_src_class_init (GstCdioCddaSrcClass * klass)
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);
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;
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;
465 audiocdsrc_class->probe_devices = gst_cdio_cdda_src_probe_devices;
466 audiocdsrc_class->get_default_device = gst_cdio_cdda_src_get_default_device;
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));
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>");
481 gst_cdio_cdda_src_set_property (GObject * object, guint prop_id,
482 const GValue * value, GParamSpec * pspec)
484 GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
487 case PROP_READ_SPEED:{
490 speed = g_value_get_int (value);
491 g_atomic_int_set (&src->read_speed, speed);
495 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
501 gst_cdio_cdda_src_get_property (GObject * object, guint prop_id,
502 GValue * value, GParamSpec * pspec)
504 GstCdioCddaSrc *src = GST_CDIO_CDDA_SRC (object);
507 case PROP_READ_SPEED:{
510 speed = g_atomic_int_get (&src->read_speed);
511 g_value_set_int (value, speed);
515 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);