tizen 2.3 release
[framework/multimedia/gst-plugins-base0.10.git] / gst-libs / gst / cdda / gstcddabasesrc.c
1 /* GStreamer
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /* TODO:
22  *
23  *  - in ::start(), we want to post a tags message with an array or a list
24  *    of tagslists of all tracks, so that applications know at least the
25  *    number of tracks and all track durations immediately without having
26  *    to do any querying. We have to decide what type and name to use for
27  *    this array of track taglists.
28  *
29  *  - FIX cddb discid calculation algorithm for mixed mode CDs - do we use
30  *    offsets and duration of ALL tracks (data + audio) for the CDDB ID
31  *    calculation, or only audio tracks?
32  *
33  *  - Do we really need properties for the TOC bias/offset stuff? Wouldn't
34  *    environment variables make much more sense? Do we need this at all
35  *    (does it only affect ancient hardware?)
36  */
37
38 /**
39  * SECTION:gstcddabasesrc
40  * @short_description: Base class for CD digital audio (CDDA) sources
41  *
42  * <refsect2>
43  * <para>
44  * Provides a base class for CDDA sources, which handles things like seeking,
45  * querying, discid calculation, tags, and buffer timestamping.
46  * </para>
47  * <title>Using GstCddaBaseSrc-based elements in applications</title>
48  * <para>
49  * GstCddaBaseSrc registers two #GstFormat<!-- -->s of its own, namely
50  * the "track" format and the "sector" format. Applications will usually
51  * only find the "track" format interesting. You can retrieve that #GstFormat
52  * for use in seek events or queries with gst_format_get_by_nick("track").
53  * </para>
54  * <para>
55  * In order to query the number of tracks, for example, an application would
56  * set the CDDA source element to READY or PAUSED state and then query the
57  * the number of tracks via gst_element_query_duration() using the track
58  * format acquired above. Applications can query the currently playing track
59  * in the same way.
60  * </para>
61  * <para>
62  * Alternatively, applications may retrieve the currently playing track and
63  * the total number of tracks from the taglist that will posted on the bus
64  * whenever the CD is opened or the currently playing track changes. The
65  * taglist will contain GST_TAG_TRACK_NUMBER and GST_TAG_TRACK_COUNT tags.
66  * </para>
67  * <para>
68  * Applications playing back CD audio using playbin and cdda://n URIs should
69  * issue a seek command in track format to change between tracks, rather than
70  * setting a new cdda://n+1 URI on playbin (as setting a new URI on playbin
71  * involves closing and re-opening the CD device, which is much much slower).
72  * </para>
73  * <title>Tags and meta-information</title>
74  * <para>
75  * CDDA sources will automatically emit a number of tags, details about which
76  * can be found in the libgsttag documentation. Those tags are:
77  * #GST_TAG_CDDA_CDDB_DISCID, #GST_TAG_CDDA_CDDB_DISCID_FULL,
78  * #GST_TAG_CDDA_MUSICBRAINZ_DISCID, #GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL,
79  * among others.
80  * </para>
81  * </refsect2>
82  */
83
84 #ifdef HAVE_CONFIG_H
85 #include "config.h"
86 #endif
87
88 #include <string.h>
89 #include <stdlib.h>             /* for strtol */
90
91 #include "gstcddabasesrc.h"
92 #include "gst/gst-i18n-plugin.h"
93
94 GST_DEBUG_CATEGORY_STATIC (gst_cdda_base_src_debug);
95 #define GST_CAT_DEFAULT gst_cdda_base_src_debug
96
97 #define DEFAULT_DEVICE                       "/dev/cdrom"
98
99 #define CD_FRAMESIZE_RAW                     (2352)
100
101 #define SECTORS_PER_SECOND                   (75)
102 #define SECTORS_PER_MINUTE                   (75*60)
103 #define SAMPLES_PER_SECTOR                   (CD_FRAMESIZE_RAW >> 2)
104 #define TIME_INTERVAL_FROM_SECTORS(sectors)  ((SAMPLES_PER_SECTOR * sectors * GST_SECOND) / 44100)
105 #define SECTORS_FROM_TIME_INTERVAL(dtime)    (dtime * 44100 / (SAMPLES_PER_SECTOR * GST_SECOND))
106
107 enum
108 {
109   ARG_0,
110   ARG_MODE,
111   ARG_DEVICE,
112   ARG_TRACK,
113   ARG_TOC_OFFSET,
114   ARG_TOC_BIAS
115 };
116
117 static void gst_cdda_base_src_get_property (GObject * object, guint prop_id,
118     GValue * value, GParamSpec * pspec);
119 static void gst_cdda_base_src_set_property (GObject * object, guint prop_id,
120     const GValue * value, GParamSpec * pspec);
121 static void gst_cdda_base_src_finalize (GObject * obj);
122 static const GstQueryType *gst_cdda_base_src_get_query_types (GstPad * pad);
123 static gboolean gst_cdda_base_src_query (GstBaseSrc * src, GstQuery * query);
124 static gboolean gst_cdda_base_src_handle_event (GstBaseSrc * basesrc,
125     GstEvent * event);
126 static gboolean gst_cdda_base_src_do_seek (GstBaseSrc * basesrc,
127     GstSegment * segment);
128 static void gst_cdda_base_src_setup_interfaces (GType type);
129 static gboolean gst_cdda_base_src_start (GstBaseSrc * basesrc);
130 static gboolean gst_cdda_base_src_stop (GstBaseSrc * basesrc);
131 static GstFlowReturn gst_cdda_base_src_create (GstPushSrc * pushsrc,
132     GstBuffer ** buf);
133 static gboolean gst_cdda_base_src_is_seekable (GstBaseSrc * basesrc);
134 static void gst_cdda_base_src_update_duration (GstCddaBaseSrc * src);
135 static void gst_cdda_base_src_set_index (GstElement * src, GstIndex * index);
136 static GstIndex *gst_cdda_base_src_get_index (GstElement * src);
137
138 GST_BOILERPLATE_FULL (GstCddaBaseSrc, gst_cdda_base_src, GstPushSrc,
139     GST_TYPE_PUSH_SRC, gst_cdda_base_src_setup_interfaces);
140
141 #define SRC_CAPS \
142   "audio/x-raw-int, "               \
143   "endianness = (int) BYTE_ORDER, " \
144   "signed = (boolean) true, "       \
145   "width = (int) 16, "              \
146   "depth = (int) 16, "              \
147   "rate = (int) 44100, "            \
148   "channels = (int) 2"              \
149
150 static GstStaticPadTemplate gst_cdda_base_src_src_template =
151 GST_STATIC_PAD_TEMPLATE ("src",
152     GST_PAD_SRC,
153     GST_PAD_ALWAYS,
154     GST_STATIC_CAPS (SRC_CAPS)
155     );
156
157 /* our two formats */
158 static GstFormat track_format;
159 static GstFormat sector_format;
160
161 GType
162 gst_cdda_base_src_mode_get_type (void)
163 {
164   static GType mode_type;       /* 0 */
165   static const GEnumValue modes[] = {
166     {GST_CDDA_BASE_SRC_MODE_NORMAL, "Stream consists of a single track",
167         "normal"},
168     {GST_CDDA_BASE_SRC_MODE_CONTINUOUS, "Stream consists of the whole disc",
169         "continuous"},
170     {0, NULL, NULL}
171   };
172
173   if (mode_type == 0)
174     mode_type = g_enum_register_static ("GstCddaBaseSrcMode", modes);
175
176   return mode_type;
177 }
178
179 static void
180 gst_cdda_base_src_base_init (gpointer g_class)
181 {
182   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
183
184   gst_element_class_add_static_pad_template (element_class,
185       &gst_cdda_base_src_src_template);
186
187   /* our very own formats */
188   track_format = gst_format_register ("track", "CD track");
189   sector_format = gst_format_register ("sector", "CD sector");
190
191   /* register CDDA tags */
192   gst_tag_register_musicbrainz_tags ();
193
194 #if 0
195   ///// FIXME: what type to use here? ///////
196   gst_tag_register (GST_TAG_CDDA_TRACK_TAGS, GST_TAG_FLAG_META, GST_TYPE_TAG_LIST, "track-tags", "CDDA taglist for one track", gst_tag_merge_use_first);        ///////////// FIXME: right function??? ///////
197 #endif
198
199   GST_DEBUG_CATEGORY_INIT (gst_cdda_base_src_debug, "cddabasesrc", 0,
200       "CDDA Base Source");
201 }
202
203 static void
204 gst_cdda_base_src_class_init (GstCddaBaseSrcClass * klass)
205 {
206   GstElementClass *element_class;
207   GstPushSrcClass *pushsrc_class;
208   GstBaseSrcClass *basesrc_class;
209   GObjectClass *gobject_class;
210
211   gobject_class = (GObjectClass *) klass;
212   element_class = (GstElementClass *) klass;
213   basesrc_class = (GstBaseSrcClass *) klass;
214   pushsrc_class = (GstPushSrcClass *) klass;
215
216   gobject_class->set_property = gst_cdda_base_src_set_property;
217   gobject_class->get_property = gst_cdda_base_src_get_property;
218   gobject_class->finalize = gst_cdda_base_src_finalize;
219
220   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
221       g_param_spec_string ("device", "Device", "CD device location",
222           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MODE,
224       g_param_spec_enum ("mode", "Mode", "Mode", GST_TYPE_CDDA_BASE_SRC_MODE,
225           GST_CDDA_BASE_SRC_MODE_NORMAL,
226           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
227
228   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TRACK,
229       g_param_spec_uint ("track", "Track", "Track", 1, 99, 1,
230           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
231
232 #if 0
233   /* Do we really need this toc adjustment stuff as properties? does the user
234    * have a chance to set it in practice, e.g. when using sound-juicer, rb,
235    * totem, whatever? Shouldn't we rather use environment variables
236    * for this? (tpm) */
237
238   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOC_OFFSET,
239       g_param_spec_int ("toc-offset", "Table of contents offset",
240           "Add <n> sectors to the values reported", G_MININT, G_MAXINT, 0,
241           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TOC_BIAS,
243       g_param_spec_boolean ("toc-bias", "Table of contents bias",
244           "Assume that the beginning offset of track 1 as reported in the TOC "
245           "will be addressed as LBA 0.  Necessary for some Toshiba drives to "
246           "get track boundaries", FALSE,
247           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
248 #endif
249
250   element_class->set_index = GST_DEBUG_FUNCPTR (gst_cdda_base_src_set_index);
251   element_class->get_index = GST_DEBUG_FUNCPTR (gst_cdda_base_src_get_index);
252
253   basesrc_class->start = GST_DEBUG_FUNCPTR (gst_cdda_base_src_start);
254   basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_cdda_base_src_stop);
255   basesrc_class->query = GST_DEBUG_FUNCPTR (gst_cdda_base_src_query);
256   basesrc_class->event = GST_DEBUG_FUNCPTR (gst_cdda_base_src_handle_event);
257   basesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_cdda_base_src_do_seek);
258   basesrc_class->is_seekable =
259       GST_DEBUG_FUNCPTR (gst_cdda_base_src_is_seekable);
260
261   pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_cdda_base_src_create);
262 }
263
264 static void
265 gst_cdda_base_src_init (GstCddaBaseSrc * src, GstCddaBaseSrcClass * klass)
266 {
267   gst_pad_set_query_type_function (GST_BASE_SRC_PAD (src),
268       GST_DEBUG_FUNCPTR (gst_cdda_base_src_get_query_types));
269
270   /* we're not live and we operate in time */
271   gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
272   gst_base_src_set_live (GST_BASE_SRC (src), FALSE);
273
274   src->device = NULL;
275   src->mode = GST_CDDA_BASE_SRC_MODE_NORMAL;
276   src->uri_track = -1;
277 }
278
279 static void
280 gst_cdda_base_src_finalize (GObject * obj)
281 {
282   GstCddaBaseSrc *cddasrc = GST_CDDA_BASE_SRC (obj);
283
284   g_free (cddasrc->uri);
285   g_free (cddasrc->device);
286
287   if (cddasrc->index)
288     gst_object_unref (cddasrc->index);
289
290   G_OBJECT_CLASS (parent_class)->finalize (obj);
291 }
292
293 static void
294 gst_cdda_base_src_set_device (GstCddaBaseSrc * src, const gchar * device)
295 {
296   if (src->device)
297     g_free (src->device);
298   src->device = NULL;
299
300   if (!device)
301     return;
302
303   /* skip multiple slashes */
304   while (*device == '/' && *(device + 1) == '/')
305     device++;
306
307 #ifdef __sun
308   /*
309    * On Solaris, /dev/rdsk is used for accessing the CD device, but some
310    * applications pass in /dev/dsk, so correct.
311    */
312   if (strncmp (device, "/dev/dsk", 8) == 0) {
313     gchar *rdsk_value;
314     rdsk_value = g_strdup_printf ("/dev/rdsk%s", device + 8);
315     src->device = g_strdup (rdsk_value);
316     g_free (rdsk_value);
317   } else {
318 #endif
319     src->device = g_strdup (device);
320 #ifdef __sun
321   }
322 #endif
323 }
324
325 static void
326 gst_cdda_base_src_set_property (GObject * object, guint prop_id,
327     const GValue * value, GParamSpec * pspec)
328 {
329   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
330
331   GST_OBJECT_LOCK (src);
332
333   switch (prop_id) {
334     case ARG_MODE:{
335       src->mode = g_value_get_enum (value);
336       break;
337     }
338     case ARG_DEVICE:{
339       const gchar *dev = g_value_get_string (value);
340
341       gst_cdda_base_src_set_device (src, dev);
342       break;
343     }
344     case ARG_TRACK:{
345       guint track = g_value_get_uint (value);
346
347       if (src->num_tracks > 0 && track > src->num_tracks) {
348         g_warning ("Invalid track %u", track);
349       } else if (track > 0 && src->tracks != NULL) {
350         src->cur_sector = src->tracks[track - 1].start;
351         src->uri_track = track;
352       } else {
353         src->uri_track = track; /* seek will be done in start() */
354       }
355       break;
356     }
357     case ARG_TOC_OFFSET:{
358       src->toc_offset = g_value_get_int (value);
359       break;
360     }
361     case ARG_TOC_BIAS:{
362       src->toc_bias = g_value_get_boolean (value);
363       break;
364     }
365     default:{
366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367       break;
368     }
369   }
370
371   GST_OBJECT_UNLOCK (src);
372 }
373
374 static void
375 gst_cdda_base_src_get_property (GObject * object, guint prop_id,
376     GValue * value, GParamSpec * pspec)
377 {
378   GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (object);
379   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
380
381   GST_OBJECT_LOCK (src);
382
383   switch (prop_id) {
384     case ARG_MODE:
385       g_value_set_enum (value, src->mode);
386       break;
387     case ARG_DEVICE:{
388       if (src->device == NULL && klass->get_default_device != NULL) {
389         gchar *d = klass->get_default_device (src);
390
391         if (d != NULL) {
392           g_value_set_string (value, DEFAULT_DEVICE);
393           g_free (d);
394           break;
395         }
396       }
397       if (src->device == NULL)
398         g_value_set_string (value, DEFAULT_DEVICE);
399       else
400         g_value_set_string (value, src->device);
401       break;
402     }
403     case ARG_TRACK:{
404       if (src->num_tracks <= 0 && src->uri_track > 0) {
405         g_value_set_uint (value, src->uri_track);
406       } else {
407         g_value_set_uint (value, src->cur_track + 1);
408       }
409       break;
410     }
411     case ARG_TOC_OFFSET:
412       g_value_set_int (value, src->toc_offset);
413       break;
414     case ARG_TOC_BIAS:
415       g_value_set_boolean (value, src->toc_bias);
416       break;
417     default:{
418       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419       break;
420     }
421   }
422
423   GST_OBJECT_UNLOCK (src);
424 }
425
426 static gint
427 gst_cdda_base_src_get_track_from_sector (GstCddaBaseSrc * src, gint sector)
428 {
429   gint i;
430
431   for (i = 0; i < src->num_tracks; ++i) {
432     if (sector >= src->tracks[i].start && sector <= src->tracks[i].end)
433       return i;
434   }
435   return -1;
436 }
437
438 static const GstQueryType *
439 gst_cdda_base_src_get_query_types (GstPad * pad)
440 {
441   static const GstQueryType src_query_types[] = {
442     GST_QUERY_DURATION,
443     GST_QUERY_POSITION,
444     GST_QUERY_CONVERT,
445     0
446   };
447
448   return src_query_types;
449 }
450
451 static gboolean
452 gst_cdda_base_src_convert (GstCddaBaseSrc * src, GstFormat src_format,
453     gint64 src_val, GstFormat dest_format, gint64 * dest_val)
454 {
455   gboolean started;
456
457   GST_LOG_OBJECT (src, "converting value %" G_GINT64_FORMAT " from %s into %s",
458       src_val, gst_format_get_name (src_format),
459       gst_format_get_name (dest_format));
460
461   if (src_format == dest_format) {
462     *dest_val = src_val;
463     return TRUE;
464   }
465
466   started = GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED);
467
468   if (src_format == track_format) {
469     if (!started)
470       goto not_started;
471     if (src_val < 0 || src_val >= src->num_tracks) {
472       GST_DEBUG_OBJECT (src, "track number %d out of bounds", (gint) src_val);
473       goto wrong_value;
474     }
475     src_format = GST_FORMAT_DEFAULT;
476     src_val = src->tracks[src_val].start * SAMPLES_PER_SECTOR;
477   } else if (src_format == sector_format) {
478     src_format = GST_FORMAT_DEFAULT;
479     src_val = src_val * SAMPLES_PER_SECTOR;
480   }
481
482   if (src_format == dest_format) {
483     *dest_val = src_val;
484     goto done;
485   }
486
487   switch (src_format) {
488     case GST_FORMAT_BYTES:
489       /* convert to samples (4 bytes per sample) */
490       src_val = src_val >> 2;
491       /* fallthrough */
492     case GST_FORMAT_DEFAULT:{
493       switch (dest_format) {
494         case GST_FORMAT_BYTES:{
495           if (src_val < 0) {
496             GST_DEBUG_OBJECT (src, "sample source value negative");
497             goto wrong_value;
498           }
499           *dest_val = src_val << 2;     /* 4 bytes per sample */
500           break;
501         }
502         case GST_FORMAT_TIME:{
503           *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND, 44100);
504           break;
505         }
506         default:{
507           gint64 sector = src_val / SAMPLES_PER_SECTOR;
508
509           if (dest_format == sector_format) {
510             *dest_val = sector;
511           } else if (dest_format == track_format) {
512             if (!started)
513               goto not_started;
514             *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
515           } else {
516             goto unknown_format;
517           }
518           break;
519         }
520       }
521       break;
522     }
523     case GST_FORMAT_TIME:{
524       gint64 sample_offset;
525
526       if (src_val == GST_CLOCK_TIME_NONE) {
527         GST_DEBUG_OBJECT (src, "source time value invalid");
528         goto wrong_value;
529       }
530
531       sample_offset = gst_util_uint64_scale_int (src_val, 44100, GST_SECOND);
532       switch (dest_format) {
533         case GST_FORMAT_BYTES:{
534           *dest_val = sample_offset << 2;       /* 4 bytes per sample */
535           break;
536         }
537         case GST_FORMAT_DEFAULT:{
538           *dest_val = sample_offset;
539           break;
540         }
541         default:{
542           gint64 sector = sample_offset / SAMPLES_PER_SECTOR;
543
544           if (dest_format == sector_format) {
545             *dest_val = sector;
546           } else if (dest_format == track_format) {
547             if (!started)
548               goto not_started;
549             *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
550           } else {
551             goto unknown_format;
552           }
553           break;
554         }
555       }
556       break;
557     }
558     default:{
559       goto unknown_format;
560     }
561   }
562
563 done:
564   {
565     GST_LOG_OBJECT (src, "returning %" G_GINT64_FORMAT, *dest_val);
566     return TRUE;
567   }
568
569 unknown_format:
570   {
571     GST_DEBUG_OBJECT (src, "conversion failed: %s", "unsupported format");
572     return FALSE;
573   }
574
575 wrong_value:
576   {
577     GST_DEBUG_OBJECT (src, "conversion failed: %s",
578         "source value not within allowed range");
579     return FALSE;
580   }
581
582 not_started:
583   {
584     GST_DEBUG_OBJECT (src, "conversion failed: %s",
585         "cannot do this conversion, device not open");
586     return FALSE;
587   }
588 }
589
590 static gboolean
591 gst_cdda_base_src_query (GstBaseSrc * basesrc, GstQuery * query)
592 {
593   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
594   gboolean started;
595
596   started = GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED);
597
598   GST_LOG_OBJECT (src, "handling %s query",
599       gst_query_type_get_name (GST_QUERY_TYPE (query)));
600
601   switch (GST_QUERY_TYPE (query)) {
602     case GST_QUERY_DURATION:{
603       GstFormat dest_format;
604       gint64 dest_val;
605       guint sectors;
606
607       gst_query_parse_duration (query, &dest_format, NULL);
608
609       if (!started)
610         return FALSE;
611
612       g_assert (src->tracks != NULL);
613
614       if (dest_format == track_format) {
615         GST_LOG_OBJECT (src, "duration: %d tracks", src->num_tracks);
616         gst_query_set_duration (query, track_format, src->num_tracks);
617         return TRUE;
618       }
619
620       if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
621         return FALSE;
622
623       if (src->mode == GST_CDDA_BASE_SRC_MODE_NORMAL) {
624         sectors = src->tracks[src->cur_track].end -
625             src->tracks[src->cur_track].start + 1;
626       } else {
627         sectors = src->tracks[src->num_tracks - 1].end -
628             src->tracks[0].start + 1;
629       }
630
631       /* ... and convert into final format */
632       if (!gst_cdda_base_src_convert (src, sector_format, sectors,
633               dest_format, &dest_val)) {
634         return FALSE;
635       }
636
637       gst_query_set_duration (query, dest_format, dest_val);
638
639       GST_LOG ("duration: %u sectors, %" G_GINT64_FORMAT " in format %s",
640           sectors, dest_val, gst_format_get_name (dest_format));
641       break;
642     }
643     case GST_QUERY_POSITION:{
644       GstFormat dest_format;
645       gint64 pos_sector;
646       gint64 dest_val;
647
648       gst_query_parse_position (query, &dest_format, NULL);
649
650       if (!started)
651         return FALSE;
652
653       g_assert (src->tracks != NULL);
654
655       if (dest_format == track_format) {
656         GST_LOG_OBJECT (src, "position: track %d", src->cur_track);
657         gst_query_set_position (query, track_format, src->cur_track);
658         return TRUE;
659       }
660
661       if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
662         return FALSE;
663
664       if (src->mode == GST_CDDA_BASE_SRC_MODE_NORMAL) {
665         pos_sector = src->cur_sector - src->tracks[src->cur_track].start;
666       } else {
667         pos_sector = src->cur_sector - src->tracks[0].start;
668       }
669
670       if (!gst_cdda_base_src_convert (src, sector_format, pos_sector,
671               dest_format, &dest_val)) {
672         return FALSE;
673       }
674
675       gst_query_set_position (query, dest_format, dest_val);
676
677       GST_LOG ("position: sector %u, %" G_GINT64_FORMAT " in format %s",
678           (guint) pos_sector, dest_val, gst_format_get_name (dest_format));
679       break;
680     }
681     case GST_QUERY_CONVERT:{
682       GstFormat src_format, dest_format;
683       gint64 src_val, dest_val;
684
685       gst_query_parse_convert (query, &src_format, &src_val, &dest_format,
686           NULL);
687
688       if (!gst_cdda_base_src_convert (src, src_format, src_val, dest_format,
689               &dest_val)) {
690         return FALSE;
691       }
692
693       gst_query_set_convert (query, src_format, src_val, dest_format, dest_val);
694       break;
695     }
696     default:{
697       GST_DEBUG_OBJECT (src, "unhandled query, chaining up to parent class");
698       return GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
699     }
700   }
701
702   return TRUE;
703 }
704
705 static gboolean
706 gst_cdda_base_src_is_seekable (GstBaseSrc * basesrc)
707 {
708   return TRUE;
709 }
710
711 static gboolean
712 gst_cdda_base_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
713 {
714   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
715   gint64 seek_sector;
716
717   GST_DEBUG_OBJECT (src, "segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
718       GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop));
719
720   if (!gst_cdda_base_src_convert (src, GST_FORMAT_TIME, segment->start,
721           sector_format, &seek_sector)) {
722     GST_WARNING_OBJECT (src, "conversion failed");
723     return FALSE;
724   }
725
726   /* we should only really be called when open */
727   g_assert (src->cur_track >= 0 && src->cur_track < src->num_tracks);
728
729   switch (src->mode) {
730     case GST_CDDA_BASE_SRC_MODE_NORMAL:
731       seek_sector += src->tracks[src->cur_track].start;
732       break;
733     case GST_CDDA_BASE_SRC_MODE_CONTINUOUS:
734       seek_sector += src->tracks[0].start;
735       break;
736     default:
737       g_return_val_if_reached (FALSE);
738   }
739
740   src->cur_sector = (gint) seek_sector;
741
742   GST_DEBUG_OBJECT (src, "seek'd to sector %d", src->cur_sector);
743
744   return TRUE;
745 }
746
747 static gboolean
748 gst_cdda_base_src_handle_track_seek (GstCddaBaseSrc * src, gdouble rate,
749     GstSeekFlags flags, GstSeekType start_type, gint64 start,
750     GstSeekType stop_type, gint64 stop)
751 {
752   GstBaseSrc *basesrc = GST_BASE_SRC (src);
753   GstEvent *event;
754
755   if ((flags & GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) {
756     gint64 start_time = -1;
757     gint64 stop_time = -1;
758
759     if (src->mode != GST_CDDA_BASE_SRC_MODE_CONTINUOUS) {
760       GST_DEBUG_OBJECT (src, "segment seek in track format is only "
761           "supported in CONTINUOUS mode, not in mode %d", src->mode);
762       return FALSE;
763     }
764
765     switch (start_type) {
766       case GST_SEEK_TYPE_SET:
767         if (!gst_cdda_base_src_convert (src, track_format, start,
768                 GST_FORMAT_TIME, &start_time)) {
769           GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
770               (gint) start);
771           return FALSE;
772         }
773         break;
774       case GST_SEEK_TYPE_END:
775         if (!gst_cdda_base_src_convert (src, track_format,
776                 src->num_tracks - start - 1, GST_FORMAT_TIME, &start_time)) {
777           GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
778               (gint) start);
779           return FALSE;
780         }
781         start_type = GST_SEEK_TYPE_SET;
782         break;
783       case GST_SEEK_TYPE_NONE:
784         start_time = -1;
785         break;
786       default:
787         g_return_val_if_reached (FALSE);
788     }
789
790     switch (stop_type) {
791       case GST_SEEK_TYPE_SET:
792         if (!gst_cdda_base_src_convert (src, track_format, stop,
793                 GST_FORMAT_TIME, &stop_time)) {
794           GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
795               (gint) stop);
796           return FALSE;
797         }
798         break;
799       case GST_SEEK_TYPE_END:
800         if (!gst_cdda_base_src_convert (src, track_format,
801                 src->num_tracks - stop - 1, GST_FORMAT_TIME, &stop_time)) {
802           GST_DEBUG_OBJECT (src, "cannot convert track %d to time",
803               (gint) stop);
804           return FALSE;
805         }
806         stop_type = GST_SEEK_TYPE_SET;
807         break;
808       case GST_SEEK_TYPE_NONE:
809         stop_time = -1;
810         break;
811       default:
812         g_return_val_if_reached (FALSE);
813     }
814
815     GST_LOG_OBJECT (src, "seek segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
816         GST_TIME_ARGS (start_time), GST_TIME_ARGS (stop_time));
817
818     /* send fake segment seek event in TIME format to
819      * base class, which will hopefully handle the rest */
820
821     event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, start_type,
822         start_time, stop_type, stop_time);
823
824     return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
825   }
826
827   /* not a segment seek */
828
829   if (start_type == GST_SEEK_TYPE_NONE) {
830     GST_LOG_OBJECT (src, "start seek type is NONE, nothing to do");
831     return TRUE;
832   }
833
834   if (stop_type != GST_SEEK_TYPE_NONE) {
835     GST_WARNING_OBJECT (src, "ignoring stop seek type (expected NONE)");
836   }
837
838   if (start < 0 || start >= src->num_tracks) {
839     GST_DEBUG_OBJECT (src, "invalid track %" G_GINT64_FORMAT, start);
840     return FALSE;
841   }
842
843   GST_DEBUG_OBJECT (src, "seeking to track %" G_GINT64_FORMAT, start + 1);
844
845   src->cur_sector = src->tracks[start].start;
846   GST_DEBUG_OBJECT (src, "starting at sector %d", src->cur_sector);
847
848   if (src->cur_track != start) {
849     src->cur_track = (gint) start;
850     src->uri_track = -1;
851     src->prev_track = -1;
852
853     gst_cdda_base_src_update_duration (src);
854   } else {
855     GST_DEBUG_OBJECT (src, "is current track, just seeking back to start");
856   }
857
858   /* send fake segment seek event in TIME format to
859    * base class (so we get a newsegment etc.) */
860   event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
861       GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
862
863   return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
864 }
865
866 static gboolean
867 gst_cdda_base_src_handle_event (GstBaseSrc * basesrc, GstEvent * event)
868 {
869   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
870   gboolean ret = FALSE;
871
872   GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
873
874   switch (GST_EVENT_TYPE (event)) {
875     case GST_EVENT_SEEK:{
876       GstSeekType start_type, stop_type;
877       GstSeekFlags flags;
878       GstFormat format;
879       gdouble rate;
880       gint64 start, stop;
881
882       if (!GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED)) {
883         GST_DEBUG_OBJECT (src, "seek failed: device not open");
884         break;
885       }
886
887       gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
888           &stop_type, &stop);
889
890       if (format == sector_format) {
891         GST_DEBUG_OBJECT (src, "seek in sector format not supported");
892         break;
893       }
894
895       if (format == track_format) {
896         ret = gst_cdda_base_src_handle_track_seek (src, rate, flags,
897             start_type, start, stop_type, stop);
898       } else {
899         GST_LOG_OBJECT (src, "let base class handle seek in %s format",
900             gst_format_get_name (format));
901         event = gst_event_ref (event);
902         ret = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
903       }
904       break;
905     }
906     default:{
907       GST_LOG_OBJECT (src, "let base class handle event");
908       ret = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
909       break;
910     }
911   }
912
913   return ret;
914 }
915
916 static GstURIType
917 gst_cdda_base_src_uri_get_type (void)
918 {
919   return GST_URI_SRC;
920 }
921
922 static gchar **
923 gst_cdda_base_src_uri_get_protocols (void)
924 {
925   static gchar *protocols[] = { (char *) "cdda", NULL };
926
927   return protocols;
928 }
929
930 static const gchar *
931 gst_cdda_base_src_uri_get_uri (GstURIHandler * handler)
932 {
933   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
934
935   GST_OBJECT_LOCK (src);
936
937   g_free (src->uri);
938
939   if (GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED)) {
940     src->uri =
941         g_strdup_printf ("cdda://%s#%d", src->device,
942         (src->uri_track > 0) ? src->uri_track : 1);
943   } else {
944     src->uri = g_strdup ("cdda://1");
945   }
946
947   GST_OBJECT_UNLOCK (src);
948
949   return src->uri;
950 }
951
952 /* Note: gst_element_make_from_uri() might call us with just 'cdda://' as
953  * URI and expects us to return TRUE then (and this might be in any state) */
954
955 /* We accept URIs of the format cdda://(device#track)|(track) */
956
957 static gboolean
958 gst_cdda_base_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
959 {
960   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
961   gchar *protocol;
962   const gchar *location;
963   gchar *track_number;
964
965   GST_OBJECT_LOCK (src);
966
967   protocol = gst_uri_get_protocol (uri);
968   if (!protocol || g_ascii_strcasecmp (protocol, "cdda") != 0) {
969     g_free (protocol);
970     goto failed;
971   }
972   g_free (protocol);
973
974   location = uri + 7;
975   track_number = g_strrstr (location, "#");
976   src->uri_track = 0;
977   /* FIXME 0.11: ignore URI fragments that look like device paths for
978    * the benefit of rhythmbox and possibly other applications.
979    */
980   if (track_number && track_number[1] != '/') {
981     gchar *device, *nuri = g_strdup (uri);
982
983     track_number = nuri + (track_number - uri);
984     *track_number = '\0';
985     device = gst_uri_get_location (nuri);
986     gst_cdda_base_src_set_device (src, device);
987     g_free (device);
988     src->uri_track = strtol (track_number + 1, NULL, 10);
989     g_free (nuri);
990   } else {
991     if (*location == '\0')
992       src->uri_track = 1;
993     else
994       src->uri_track = strtol (location, NULL, 10);
995   }
996
997   if (src->uri_track < 1)
998     goto failed;
999
1000   if (src->num_tracks > 0
1001       && src->tracks != NULL && src->uri_track > src->num_tracks)
1002     goto failed;
1003
1004   if (src->uri_track > 0 && src->tracks != NULL) {
1005     GST_OBJECT_UNLOCK (src);
1006
1007     gst_pad_send_event (GST_BASE_SRC_PAD (src),
1008         gst_event_new_seek (1.0, track_format, GST_SEEK_FLAG_FLUSH,
1009             GST_SEEK_TYPE_SET, src->uri_track - 1, GST_SEEK_TYPE_NONE, -1));
1010   } else {
1011     /* seek will be done in start() */
1012     GST_OBJECT_UNLOCK (src);
1013   }
1014
1015   GST_LOG_OBJECT (handler, "successfully handled uri '%s'", uri);
1016
1017   return TRUE;
1018
1019 failed:
1020   {
1021     GST_OBJECT_UNLOCK (src);
1022     GST_DEBUG_OBJECT (src, "cannot handle URI '%s'", uri);
1023     return FALSE;
1024   }
1025 }
1026
1027 static void
1028 gst_cdda_base_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1029 {
1030   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1031
1032   iface->get_type = gst_cdda_base_src_uri_get_type;
1033   iface->get_uri = gst_cdda_base_src_uri_get_uri;
1034   iface->set_uri = gst_cdda_base_src_uri_set_uri;
1035   iface->get_protocols = gst_cdda_base_src_uri_get_protocols;
1036 }
1037
1038 static void
1039 gst_cdda_base_src_setup_interfaces (GType type)
1040 {
1041   static const GInterfaceInfo urihandler_info = {
1042     gst_cdda_base_src_uri_handler_init,
1043     NULL,
1044     NULL,
1045   };
1046
1047   g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
1048 }
1049
1050 /**
1051  * gst_cdda_base_src_add_track:
1052  * @src: a #GstCddaBaseSrc
1053  * @track: address of #GstCddaBaseSrcTrack to add
1054  * 
1055  * CDDA sources use this function from their start vfunc to announce the
1056  * available data and audio tracks to the base source class. The caller
1057  * should allocate @track on the stack, the base source will do a shallow
1058  * copy of the structure (and take ownership of the taglist if there is one).
1059  *
1060  * Returns: FALSE on error, otherwise TRUE.
1061  */
1062
1063 gboolean
1064 gst_cdda_base_src_add_track (GstCddaBaseSrc * src, GstCddaBaseSrcTrack * track)
1065 {
1066   g_return_val_if_fail (GST_IS_CDDA_BASE_SRC (src), FALSE);
1067   g_return_val_if_fail (track != NULL, FALSE);
1068   g_return_val_if_fail (track->num > 0, FALSE);
1069
1070   GST_DEBUG_OBJECT (src, "adding track %2u (%2u) [%6u-%6u] [%5s], tags: %"
1071       GST_PTR_FORMAT, src->num_tracks + 1, track->num, track->start,
1072       track->end, (track->is_audio) ? "AUDIO" : "DATA ", track->tags);
1073
1074   if (src->num_tracks > 0) {
1075     guint end_of_previous_track = src->tracks[src->num_tracks - 1].end;
1076
1077     if (track->start <= end_of_previous_track) {
1078       GST_WARNING ("track %2u overlaps with previous tracks", track->num);
1079       return FALSE;
1080     }
1081   }
1082
1083   GST_OBJECT_LOCK (src);
1084
1085   ++src->num_tracks;
1086   src->tracks = g_renew (GstCddaBaseSrcTrack, src->tracks, src->num_tracks);
1087   src->tracks[src->num_tracks - 1] = *track;
1088
1089   GST_OBJECT_UNLOCK (src);
1090
1091   return TRUE;
1092 }
1093
1094 static void
1095 gst_cdda_base_src_update_duration (GstCddaBaseSrc * src)
1096 {
1097   GstBaseSrc *basesrc;
1098   GstFormat format;
1099   gint64 duration;
1100
1101   basesrc = GST_BASE_SRC (src);
1102
1103   format = GST_FORMAT_TIME;
1104   if (gst_pad_query_duration (GST_BASE_SRC_PAD (src), &format, &duration)) {
1105     gst_segment_set_duration (&basesrc->segment, GST_FORMAT_TIME, duration);
1106   } else {
1107     gst_segment_set_duration (&basesrc->segment, GST_FORMAT_TIME, -1);
1108     duration = GST_CLOCK_TIME_NONE;
1109   }
1110
1111   gst_element_post_message (GST_ELEMENT (src),
1112       gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_TIME, -1));
1113
1114   GST_LOG_OBJECT (src, "duration updated to %" GST_TIME_FORMAT,
1115       GST_TIME_ARGS (duration));
1116 }
1117
1118 #define CD_MSF_OFFSET 150
1119
1120 /* the cddb hash function */
1121 static guint
1122 cddb_sum (gint n)
1123 {
1124   guint ret;
1125
1126   ret = 0;
1127   while (n > 0) {
1128     ret += (n % 10);
1129     n /= 10;
1130   }
1131   return ret;
1132 }
1133
1134 static void
1135 gst_cddabasesrc_calculate_musicbrainz_discid (GstCddaBaseSrc * src)
1136 {
1137   GString *s;
1138   GChecksum *sha;
1139   guchar digest[20];
1140   gchar *ptr;
1141   gchar tmp[9];
1142   gulong i;
1143   guint leadout_sector;
1144   gsize digest_len;
1145
1146   s = g_string_new (NULL);
1147
1148   leadout_sector = src->tracks[src->num_tracks - 1].end + 1 + CD_MSF_OFFSET;
1149
1150   /* generate SHA digest */
1151   sha = g_checksum_new (G_CHECKSUM_SHA1);
1152   g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[0].num);
1153   g_string_append_printf (s, "%02X", src->tracks[0].num);
1154   g_checksum_update (sha, (guchar *) tmp, 2);
1155
1156   g_snprintf (tmp, sizeof (tmp), "%02X", src->tracks[src->num_tracks - 1].num);
1157   g_string_append_printf (s, " %02X", src->tracks[src->num_tracks - 1].num);
1158   g_checksum_update (sha, (guchar *) tmp, 2);
1159
1160   g_snprintf (tmp, sizeof (tmp), "%08X", leadout_sector);
1161   g_string_append_printf (s, " %08X", leadout_sector);
1162   g_checksum_update (sha, (guchar *) tmp, 8);
1163
1164   for (i = 0; i < 99; i++) {
1165     if (i < src->num_tracks) {
1166       guint frame_offset = src->tracks[i].start + CD_MSF_OFFSET;
1167
1168       g_snprintf (tmp, sizeof (tmp), "%08X", frame_offset);
1169       g_string_append_printf (s, " %08X", frame_offset);
1170       g_checksum_update (sha, (guchar *) tmp, 8);
1171     } else {
1172       g_checksum_update (sha, (guchar *) "00000000", 8);
1173     }
1174   }
1175   digest_len = 20;
1176   g_checksum_get_digest (sha, (guint8 *) & digest, &digest_len);
1177
1178   /* re-encode to base64 */
1179   ptr = g_base64_encode (digest, digest_len);
1180   g_checksum_free (sha);
1181   i = strlen (ptr);
1182
1183   g_assert (i < sizeof (src->mb_discid) + 1);
1184   memcpy (src->mb_discid, ptr, i);
1185   src->mb_discid[i] = '\0';
1186   free (ptr);
1187
1188   /* Replace '/', '+' and '=' by '_', '.' and '-' as specified on
1189    * http://musicbrainz.org/doc/DiscIDCalculation
1190    */
1191   for (ptr = src->mb_discid; *ptr != '\0'; ptr++) {
1192     if (*ptr == '/')
1193       *ptr = '_';
1194     else if (*ptr == '+')
1195       *ptr = '.';
1196     else if (*ptr == '=')
1197       *ptr = '-';
1198   }
1199
1200   GST_DEBUG_OBJECT (src, "musicbrainz-discid      = %s", src->mb_discid);
1201   GST_DEBUG_OBJECT (src, "musicbrainz-discid-full = %s", s->str);
1202
1203   gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1204       GST_TAG_CDDA_MUSICBRAINZ_DISCID, src->mb_discid,
1205       GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL, s->str, NULL);
1206
1207   g_string_free (s, TRUE);
1208 }
1209
1210 static void
1211 lba_to_msf (guint sector, guint * p_m, guint * p_s, guint * p_f, guint * p_secs)
1212 {
1213   guint m, s, f;
1214
1215   m = sector / SECTORS_PER_MINUTE;
1216   sector = sector % SECTORS_PER_MINUTE;
1217   s = sector / SECTORS_PER_SECOND;
1218   f = sector % SECTORS_PER_SECOND;
1219
1220   if (p_m)
1221     *p_m = m;
1222   if (p_s)
1223     *p_s = s;
1224   if (p_f)
1225     *p_f = f;
1226   if (p_secs)
1227     *p_secs = s + (m * 60);
1228 }
1229
1230 static void
1231 gst_cdda_base_src_calculate_cddb_id (GstCddaBaseSrc * src)
1232 {
1233   GString *s;
1234   guint first_sector = 0, last_sector = 0;
1235   guint start_secs, end_secs, secs, len_secs;
1236   guint total_secs, num_audio_tracks;
1237   guint id, t, i;
1238
1239   id = 0;
1240   total_secs = 0;
1241   num_audio_tracks = 0;
1242
1243   /* FIXME: do we use offsets and duration of ALL tracks (data + audio)
1244    * for the CDDB ID calculation, or only audio tracks? */
1245   for (i = 0; i < src->num_tracks; ++i) {
1246     if (1) {                    /* src->tracks[i].is_audio) { */
1247       if (num_audio_tracks == 0) {
1248         first_sector = src->tracks[i].start + CD_MSF_OFFSET;
1249       }
1250       last_sector = src->tracks[i].end + CD_MSF_OFFSET + 1;
1251       ++num_audio_tracks;
1252
1253       lba_to_msf (src->tracks[i].start + CD_MSF_OFFSET, NULL, NULL, NULL,
1254           &secs);
1255
1256       len_secs = (src->tracks[i].end - src->tracks[i].start + 1) / 75;
1257
1258       GST_DEBUG_OBJECT (src, "track %02u: lsn %6u (%02u:%02u), "
1259           "length: %u seconds (%02u:%02u)",
1260           num_audio_tracks, src->tracks[i].start + CD_MSF_OFFSET,
1261           secs / 60, secs % 60, len_secs, len_secs / 60, len_secs % 60);
1262
1263       id += cddb_sum (secs);
1264       total_secs += len_secs;
1265     }
1266   }
1267
1268   /* first_sector = src->tracks[0].start + CD_MSF_OFFSET; */
1269   lba_to_msf (first_sector, NULL, NULL, NULL, &start_secs);
1270
1271   /* last_sector = src->tracks[src->num_tracks-1].end + CD_MSF_OFFSET; */
1272   lba_to_msf (last_sector, NULL, NULL, NULL, &end_secs);
1273
1274   GST_DEBUG_OBJECT (src, "first_sector = %u = %u secs (%02u:%02u)",
1275       first_sector, start_secs, start_secs / 60, start_secs % 60);
1276   GST_DEBUG_OBJECT (src, "last_sector  = %u = %u secs (%02u:%02u)",
1277       last_sector, end_secs, end_secs / 60, end_secs % 60);
1278
1279   t = end_secs - start_secs;
1280
1281   GST_DEBUG_OBJECT (src, "total length = %u secs (%02u:%02u), added title "
1282       "lengths = %u seconds (%02u:%02u)", t, t / 60, t % 60, total_secs,
1283       total_secs / 60, total_secs % 60);
1284
1285   src->discid = ((id % 0xff) << 24 | t << 8 | num_audio_tracks);
1286
1287   s = g_string_new (NULL);
1288   g_string_append_printf (s, "%08x", src->discid);
1289
1290   gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1291       GST_TAG_CDDA_CDDB_DISCID, s->str, NULL);
1292
1293   g_string_append_printf (s, " %u", src->num_tracks);
1294   for (i = 0; i < src->num_tracks; ++i) {
1295     g_string_append_printf (s, " %u", src->tracks[i].start + CD_MSF_OFFSET);
1296   }
1297   g_string_append_printf (s, " %u", t);
1298
1299   gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1300       GST_TAG_CDDA_CDDB_DISCID_FULL, s->str, NULL);
1301
1302   GST_DEBUG_OBJECT (src, "cddb discid = %s", s->str);
1303
1304   g_string_free (s, TRUE);
1305 }
1306
1307 static void
1308 gst_cdda_base_src_add_tags (GstCddaBaseSrc * src)
1309 {
1310   gint i;
1311
1312   /* fill in details for each track */
1313   for (i = 0; i < src->num_tracks; ++i) {
1314     gint64 duration;
1315     guint num_sectors;
1316
1317     if (src->tracks[i].tags == NULL)
1318       src->tracks[i].tags = gst_tag_list_new ();
1319
1320     num_sectors = src->tracks[i].end - src->tracks[i].start + 1;
1321     gst_cdda_base_src_convert (src, sector_format, num_sectors,
1322         GST_FORMAT_TIME, &duration);
1323
1324     gst_tag_list_add (src->tracks[i].tags,
1325         GST_TAG_MERGE_REPLACE,
1326         GST_TAG_TRACK_NUMBER, i + 1,
1327         GST_TAG_TRACK_COUNT, src->num_tracks, GST_TAG_DURATION, duration, NULL);
1328   }
1329
1330   /* now fill in per-album tags and include each track's tags
1331    * in the album tags, so that interested parties can retrieve
1332    * the relevant details for each track in one go */
1333
1334   /* /////////////////////////////// FIXME should we rather insert num_tracks
1335    * tags by the name of 'track-tags' and have the caller use
1336    * gst_tag_list_get_value_index() rather than use tag names incl.
1337    * the track number ?? *////////////////////////////////////////
1338
1339   gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1340       GST_TAG_TRACK_COUNT, src->num_tracks, NULL);
1341 #if 0
1342   for (i = 0; i < src->num_tracks; ++i) {
1343     gst_tag_list_add (src->tags, GST_TAG_MERGE_APPEND,
1344         GST_TAG_CDDA_TRACK_TAGS, src->tracks[i].tags, NULL);
1345   }
1346 #endif
1347
1348   GST_DEBUG ("src->tags = %" GST_PTR_FORMAT, src->tags);
1349 }
1350
1351 static void
1352 gst_cdda_base_src_add_index_associations (GstCddaBaseSrc * src)
1353 {
1354   gint i;
1355
1356   for (i = 0; i < src->num_tracks; i++) {
1357     gint64 sector;
1358
1359     sector = src->tracks[i].start;
1360     gst_index_add_association (src->index, src->index_id, GST_ASSOCIATION_FLAG_KEY_UNIT, track_format, i,       /* here we count from 0 */
1361         sector_format, sector,
1362         GST_FORMAT_TIME,
1363         (gint64) (((CD_FRAMESIZE_RAW >> 2) * sector * GST_SECOND) / 44100),
1364         GST_FORMAT_BYTES, (gint64) (sector << 2), GST_FORMAT_DEFAULT,
1365         (gint64) ((CD_FRAMESIZE_RAW >> 2) * sector), NULL);
1366   }
1367 }
1368
1369 static void
1370 gst_cdda_base_src_set_index (GstElement * element, GstIndex * index)
1371 {
1372   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
1373   GstIndex *old;
1374
1375   GST_OBJECT_LOCK (element);
1376   old = src->index;
1377   if (old == index) {
1378     GST_OBJECT_UNLOCK (element);
1379     return;
1380   }
1381   if (index)
1382     gst_object_ref (index);
1383   src->index = index;
1384   GST_OBJECT_UNLOCK (element);
1385   if (old)
1386     gst_object_unref (old);
1387
1388   if (index) {
1389     gst_index_get_writer_id (index, GST_OBJECT (src), &src->index_id);
1390     gst_index_add_format (index, src->index_id, track_format);
1391     gst_index_add_format (index, src->index_id, sector_format);
1392   }
1393 }
1394
1395
1396 static GstIndex *
1397 gst_cdda_base_src_get_index (GstElement * element)
1398 {
1399   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
1400   GstIndex *index;
1401
1402   GST_OBJECT_LOCK (element);
1403   if ((index = src->index))
1404     gst_object_ref (index);
1405   GST_OBJECT_UNLOCK (element);
1406
1407   return index;
1408 }
1409
1410 static gint
1411 gst_cdda_base_src_track_sort_func (gconstpointer a, gconstpointer b,
1412     gpointer foo)
1413 {
1414   GstCddaBaseSrcTrack *track_a = ((GstCddaBaseSrcTrack *) a);
1415   GstCddaBaseSrcTrack *track_b = ((GstCddaBaseSrcTrack *) b);
1416
1417   /* sort data tracks to the end, and audio tracks by track number */
1418   if (track_a->is_audio == track_b->is_audio)
1419     return (gint) track_a->num - (gint) track_b->num;
1420
1421   if (track_a->is_audio) {
1422     return -1;
1423   } else {
1424     return 1;
1425   }
1426 }
1427
1428 static gboolean
1429 gst_cdda_base_src_start (GstBaseSrc * basesrc)
1430 {
1431   GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
1432   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
1433   gboolean ret;
1434   gchar *device = NULL;
1435
1436   src->discid = 0;
1437   src->mb_discid[0] = '\0';
1438
1439   g_assert (klass->open != NULL);
1440
1441   if (src->device != NULL) {
1442     device = g_strdup (src->device);
1443   } else if (klass->get_default_device != NULL) {
1444     device = klass->get_default_device (src);
1445   }
1446
1447   if (device == NULL)
1448     device = g_strdup (DEFAULT_DEVICE);
1449
1450   GST_LOG_OBJECT (basesrc, "opening device %s", device);
1451
1452   src->tags = gst_tag_list_new ();
1453
1454   ret = klass->open (src, device);
1455   g_free (device);
1456   device = NULL;
1457
1458   if (!ret) {
1459     GST_DEBUG_OBJECT (basesrc, "failed to open device");
1460     /* subclass (should have) posted an error message with the details */
1461     gst_cdda_base_src_stop (basesrc);
1462     return FALSE;
1463   }
1464
1465   if (src->num_tracks == 0 || src->tracks == NULL) {
1466     GST_DEBUG_OBJECT (src, "no tracks");
1467     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
1468         (_("This CD has no audio tracks")), (NULL));
1469     gst_cdda_base_src_stop (basesrc);
1470     return FALSE;
1471   }
1472
1473   /* need to calculate disc IDs before we ditch the data tracks */
1474   gst_cdda_base_src_calculate_cddb_id (src);
1475   gst_cddabasesrc_calculate_musicbrainz_discid (src);
1476
1477 #if 0
1478   /* adjust sector offsets if necessary */
1479   if (src->toc_bias) {
1480     src->toc_offset -= src->tracks[0].start;
1481   }
1482   for (i = 0; i < src->num_tracks; ++i) {
1483     src->tracks[i].start += src->toc_offset;
1484     src->tracks[i].end += src->toc_offset;
1485   }
1486 #endif
1487
1488   /* now that we calculated the various disc IDs,
1489    * sort the data tracks to end and ignore them */
1490   src->num_all_tracks = src->num_tracks;
1491
1492   g_qsort_with_data (src->tracks, src->num_tracks,
1493       sizeof (GstCddaBaseSrcTrack), gst_cdda_base_src_track_sort_func, NULL);
1494
1495   while (src->num_tracks > 0 && !src->tracks[src->num_tracks - 1].is_audio)
1496     --src->num_tracks;
1497
1498   if (src->num_tracks == 0) {
1499     GST_DEBUG_OBJECT (src, "no audio tracks");
1500     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
1501         (_("This CD has no audio tracks")), (NULL));
1502     gst_cdda_base_src_stop (basesrc);
1503     return FALSE;
1504   }
1505
1506   gst_cdda_base_src_add_tags (src);
1507
1508   if (src->index && GST_INDEX_IS_WRITABLE (src->index))
1509     gst_cdda_base_src_add_index_associations (src);
1510
1511   src->cur_track = 0;
1512   src->prev_track = -1;
1513
1514   if (src->uri_track > 0 && src->uri_track <= src->num_tracks) {
1515     GST_LOG_OBJECT (src, "seek to track %d", src->uri_track);
1516     src->cur_track = src->uri_track - 1;
1517     src->uri_track = -1;
1518     src->mode = GST_CDDA_BASE_SRC_MODE_NORMAL;
1519   }
1520
1521   src->cur_sector = src->tracks[src->cur_track].start;
1522   GST_LOG_OBJECT (src, "starting at sector %d", src->cur_sector);
1523
1524   gst_cdda_base_src_update_duration (src);
1525
1526   return TRUE;
1527 }
1528
1529 static void
1530 gst_cdda_base_src_clear_tracks (GstCddaBaseSrc * src)
1531 {
1532   if (src->tracks != NULL) {
1533     gint i;
1534
1535     for (i = 0; i < src->num_all_tracks; ++i) {
1536       if (src->tracks[i].tags)
1537         gst_tag_list_free (src->tracks[i].tags);
1538     }
1539
1540     g_free (src->tracks);
1541     src->tracks = NULL;
1542   }
1543   src->num_tracks = 0;
1544   src->num_all_tracks = 0;
1545 }
1546
1547 static gboolean
1548 gst_cdda_base_src_stop (GstBaseSrc * basesrc)
1549 {
1550   GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
1551   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
1552
1553   g_assert (klass->close != NULL);
1554
1555   klass->close (src);
1556
1557   gst_cdda_base_src_clear_tracks (src);
1558
1559   if (src->tags) {
1560     gst_tag_list_free (src->tags);
1561     src->tags = NULL;
1562   }
1563
1564   src->prev_track = -1;
1565   src->cur_track = -1;
1566
1567   return TRUE;
1568 }
1569
1570
1571 static GstFlowReturn
1572 gst_cdda_base_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
1573 {
1574   GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (pushsrc);
1575   GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (pushsrc);
1576   GstBuffer *buf;
1577   GstFormat format;
1578   gboolean eos;
1579
1580   GstClockTime position = GST_CLOCK_TIME_NONE;
1581   GstClockTime duration = GST_CLOCK_TIME_NONE;
1582   gint64 qry_position;
1583
1584   g_assert (klass->read_sector != NULL);
1585
1586   switch (src->mode) {
1587     case GST_CDDA_BASE_SRC_MODE_NORMAL:
1588       eos = (src->cur_sector > src->tracks[src->cur_track].end);
1589       break;
1590     case GST_CDDA_BASE_SRC_MODE_CONTINUOUS:
1591       eos = (src->cur_sector > src->tracks[src->num_tracks - 1].end);
1592       src->cur_track = gst_cdda_base_src_get_track_from_sector (src,
1593           src->cur_sector);
1594       break;
1595     default:
1596       g_return_val_if_reached (GST_FLOW_ERROR);
1597   }
1598
1599   if (eos) {
1600     src->prev_track = -1;
1601     GST_DEBUG_OBJECT (src, "EOS at sector %d, cur_track=%d, mode=%d",
1602         src->cur_sector, src->cur_track, src->mode);
1603     /* base class will send EOS for us */
1604     return GST_FLOW_UNEXPECTED;
1605   }
1606
1607   if (src->prev_track != src->cur_track) {
1608     GstTagList *tags;
1609
1610     tags = gst_tag_list_merge (src->tags, src->tracks[src->cur_track].tags,
1611         GST_TAG_MERGE_REPLACE);
1612     GST_LOG_OBJECT (src, "announcing tags: %" GST_PTR_FORMAT, tags);
1613     gst_element_found_tags_for_pad (GST_ELEMENT (src),
1614         GST_BASE_SRC_PAD (src), tags);
1615     src->prev_track = src->cur_track;
1616
1617     gst_cdda_base_src_update_duration (src);
1618
1619     g_object_notify (G_OBJECT (src), "track");
1620   }
1621
1622   GST_LOG_OBJECT (src, "asking for sector %u", src->cur_sector);
1623
1624   buf = klass->read_sector (src, src->cur_sector);
1625
1626   if (buf == NULL) {
1627     GST_WARNING_OBJECT (src, "failed to read sector %u", src->cur_sector);
1628     return GST_FLOW_ERROR;
1629   }
1630
1631   if (GST_BUFFER_CAPS (buf) == NULL) {
1632     gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
1633   }
1634
1635   format = GST_FORMAT_TIME;
1636   if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &qry_position)) {
1637     gint64 next_ts = 0;
1638
1639     position = (GstClockTime) qry_position;
1640
1641     ++src->cur_sector;
1642     if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &next_ts)) {
1643       duration = (GstClockTime) (next_ts - qry_position);
1644     }
1645     --src->cur_sector;
1646   }
1647
1648   /* fallback duration: 4 bytes per sample, 44100 samples per second */
1649   if (duration == GST_CLOCK_TIME_NONE) {
1650     duration = gst_util_uint64_scale_int (GST_BUFFER_SIZE (buf) >> 2,
1651         GST_SECOND, 44100);
1652   }
1653
1654   GST_BUFFER_TIMESTAMP (buf) = position;
1655   GST_BUFFER_DURATION (buf) = duration;
1656
1657   GST_LOG_OBJECT (src, "pushing sector %d with timestamp %" GST_TIME_FORMAT,
1658       src->cur_sector, GST_TIME_ARGS (position));
1659
1660   ++src->cur_sector;
1661
1662   *buffer = buf;
1663
1664   return GST_FLOW_OK;
1665 }