2 * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2005 Tim-Philipp Müller <tim centricular net>
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.
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.
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.
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.
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?
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?)
39 * SECTION:gstcddabasesrc
40 * @short_description: Base class for CD digital audio (CDDA) sources
44 * Provides a base class for CDDA sources, which handles things like seeking,
45 * querying, discid calculation, tags, and buffer timestamping.
47 * <title>Using GstCddaBaseSrc-based elements in applications</title>
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").
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
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.
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).
73 * <title>Tags and meta-information</title>
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,
89 #include <stdlib.h> /* for strtol */
91 #include "gstcddabasesrc.h"
92 #include "gst/gst-i18n-plugin.h"
94 GST_DEBUG_CATEGORY_STATIC (gst_cdda_base_src_debug);
95 #define GST_CAT_DEFAULT gst_cdda_base_src_debug
97 #define DEFAULT_DEVICE "/dev/cdrom"
99 #define CD_FRAMESIZE_RAW (2352)
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))
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,
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,
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);
138 GST_BOILERPLATE_FULL (GstCddaBaseSrc, gst_cdda_base_src, GstPushSrc,
139 GST_TYPE_PUSH_SRC, gst_cdda_base_src_setup_interfaces);
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" \
150 static GstStaticPadTemplate gst_cdda_base_src_src_template =
151 GST_STATIC_PAD_TEMPLATE ("src",
154 GST_STATIC_CAPS (SRC_CAPS)
157 /* our two formats */
158 static GstFormat track_format;
159 static GstFormat sector_format;
162 gst_cdda_base_src_mode_get_type (void)
164 static GType mode_type; /* 0 */
165 static const GEnumValue modes[] = {
166 {GST_CDDA_BASE_SRC_MODE_NORMAL, "Stream consists of a single track",
168 {GST_CDDA_BASE_SRC_MODE_CONTINUOUS, "Stream consists of the whole disc",
174 mode_type = g_enum_register_static ("GstCddaBaseSrcMode", modes);
180 gst_cdda_base_src_base_init (gpointer g_class)
182 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
184 gst_element_class_add_static_pad_template (element_class,
185 &gst_cdda_base_src_src_template);
187 /* our very own formats */
188 track_format = gst_format_register ("track", "CD track");
189 sector_format = gst_format_register ("sector", "CD sector");
191 /* register CDDA tags */
192 gst_tag_register_musicbrainz_tags ();
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??? ///////
199 GST_DEBUG_CATEGORY_INIT (gst_cdda_base_src_debug, "cddabasesrc", 0,
204 gst_cdda_base_src_class_init (GstCddaBaseSrcClass * klass)
206 GstElementClass *element_class;
207 GstPushSrcClass *pushsrc_class;
208 GstBaseSrcClass *basesrc_class;
209 GObjectClass *gobject_class;
211 gobject_class = (GObjectClass *) klass;
212 element_class = (GstElementClass *) klass;
213 basesrc_class = (GstBaseSrcClass *) klass;
214 pushsrc_class = (GstPushSrcClass *) klass;
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;
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));
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));
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
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));
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);
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);
261 pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_cdda_base_src_create);
265 gst_cdda_base_src_init (GstCddaBaseSrc * src, GstCddaBaseSrcClass * klass)
267 gst_pad_set_query_type_function (GST_BASE_SRC_PAD (src),
268 GST_DEBUG_FUNCPTR (gst_cdda_base_src_get_query_types));
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);
275 src->mode = GST_CDDA_BASE_SRC_MODE_NORMAL;
280 gst_cdda_base_src_finalize (GObject * obj)
282 GstCddaBaseSrc *cddasrc = GST_CDDA_BASE_SRC (obj);
284 g_free (cddasrc->uri);
285 g_free (cddasrc->device);
288 gst_object_unref (cddasrc->index);
290 G_OBJECT_CLASS (parent_class)->finalize (obj);
294 gst_cdda_base_src_set_device (GstCddaBaseSrc * src, const gchar * device)
297 g_free (src->device);
303 /* skip multiple slashes */
304 while (*device == '/' && *(device + 1) == '/')
309 * On Solaris, /dev/rdsk is used for accessing the CD device, but some
310 * applications pass in /dev/dsk, so correct.
312 if (strncmp (device, "/dev/dsk", 8) == 0) {
314 rdsk_value = g_strdup_printf ("/dev/rdsk%s", device + 8);
315 src->device = g_strdup (rdsk_value);
319 src->device = g_strdup (device);
326 gst_cdda_base_src_set_property (GObject * object, guint prop_id,
327 const GValue * value, GParamSpec * pspec)
329 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
331 GST_OBJECT_LOCK (src);
335 src->mode = g_value_get_enum (value);
339 const gchar *dev = g_value_get_string (value);
341 gst_cdda_base_src_set_device (src, dev);
345 guint track = g_value_get_uint (value);
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;
353 src->uri_track = track; /* seek will be done in start() */
357 case ARG_TOC_OFFSET:{
358 src->toc_offset = g_value_get_int (value);
362 src->toc_bias = g_value_get_boolean (value);
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
371 GST_OBJECT_UNLOCK (src);
375 gst_cdda_base_src_get_property (GObject * object, guint prop_id,
376 GValue * value, GParamSpec * pspec)
378 GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (object);
379 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (object);
381 GST_OBJECT_LOCK (src);
385 g_value_set_enum (value, src->mode);
388 if (src->device == NULL && klass->get_default_device != NULL) {
389 gchar *d = klass->get_default_device (src);
392 g_value_set_string (value, DEFAULT_DEVICE);
397 if (src->device == NULL)
398 g_value_set_string (value, DEFAULT_DEVICE);
400 g_value_set_string (value, src->device);
404 if (src->num_tracks <= 0 && src->uri_track > 0) {
405 g_value_set_uint (value, src->uri_track);
407 g_value_set_uint (value, src->cur_track + 1);
412 g_value_set_int (value, src->toc_offset);
415 g_value_set_boolean (value, src->toc_bias);
418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
423 GST_OBJECT_UNLOCK (src);
427 gst_cdda_base_src_get_track_from_sector (GstCddaBaseSrc * src, gint sector)
431 for (i = 0; i < src->num_tracks; ++i) {
432 if (sector >= src->tracks[i].start && sector <= src->tracks[i].end)
438 static const GstQueryType *
439 gst_cdda_base_src_get_query_types (GstPad * pad)
441 static const GstQueryType src_query_types[] = {
448 return src_query_types;
452 gst_cdda_base_src_convert (GstCddaBaseSrc * src, GstFormat src_format,
453 gint64 src_val, GstFormat dest_format, gint64 * dest_val)
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));
461 if (src_format == dest_format) {
466 started = GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED);
468 if (src_format == track_format) {
471 if (src_val < 0 || src_val >= src->num_tracks) {
472 GST_DEBUG_OBJECT (src, "track number %d out of bounds", (gint) src_val);
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;
482 if (src_format == dest_format) {
487 switch (src_format) {
488 case GST_FORMAT_BYTES:
489 /* convert to samples (4 bytes per sample) */
490 src_val = src_val >> 2;
492 case GST_FORMAT_DEFAULT:{
493 switch (dest_format) {
494 case GST_FORMAT_BYTES:{
496 GST_DEBUG_OBJECT (src, "sample source value negative");
499 *dest_val = src_val << 2; /* 4 bytes per sample */
502 case GST_FORMAT_TIME:{
503 *dest_val = gst_util_uint64_scale_int (src_val, GST_SECOND, 44100);
507 gint64 sector = src_val / SAMPLES_PER_SECTOR;
509 if (dest_format == sector_format) {
511 } else if (dest_format == track_format) {
514 *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
523 case GST_FORMAT_TIME:{
524 gint64 sample_offset;
526 if (src_val == GST_CLOCK_TIME_NONE) {
527 GST_DEBUG_OBJECT (src, "source time value invalid");
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 */
537 case GST_FORMAT_DEFAULT:{
538 *dest_val = sample_offset;
542 gint64 sector = sample_offset / SAMPLES_PER_SECTOR;
544 if (dest_format == sector_format) {
546 } else if (dest_format == track_format) {
549 *dest_val = gst_cdda_base_src_get_track_from_sector (src, sector);
565 GST_LOG_OBJECT (src, "returning %" G_GINT64_FORMAT, *dest_val);
571 GST_DEBUG_OBJECT (src, "conversion failed: %s", "unsupported format");
577 GST_DEBUG_OBJECT (src, "conversion failed: %s",
578 "source value not within allowed range");
584 GST_DEBUG_OBJECT (src, "conversion failed: %s",
585 "cannot do this conversion, device not open");
591 gst_cdda_base_src_query (GstBaseSrc * basesrc, GstQuery * query)
593 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
596 started = GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED);
598 GST_LOG_OBJECT (src, "handling %s query",
599 gst_query_type_get_name (GST_QUERY_TYPE (query)));
601 switch (GST_QUERY_TYPE (query)) {
602 case GST_QUERY_DURATION:{
603 GstFormat dest_format;
607 gst_query_parse_duration (query, &dest_format, NULL);
612 g_assert (src->tracks != NULL);
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);
620 if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
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;
627 sectors = src->tracks[src->num_tracks - 1].end -
628 src->tracks[0].start + 1;
631 /* ... and convert into final format */
632 if (!gst_cdda_base_src_convert (src, sector_format, sectors,
633 dest_format, &dest_val)) {
637 gst_query_set_duration (query, dest_format, dest_val);
639 GST_LOG ("duration: %u sectors, %" G_GINT64_FORMAT " in format %s",
640 sectors, dest_val, gst_format_get_name (dest_format));
643 case GST_QUERY_POSITION:{
644 GstFormat dest_format;
648 gst_query_parse_position (query, &dest_format, NULL);
653 g_assert (src->tracks != NULL);
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);
661 if (src->cur_track < 0 || src->cur_track >= src->num_tracks)
664 if (src->mode == GST_CDDA_BASE_SRC_MODE_NORMAL) {
665 pos_sector = src->cur_sector - src->tracks[src->cur_track].start;
667 pos_sector = src->cur_sector - src->tracks[0].start;
670 if (!gst_cdda_base_src_convert (src, sector_format, pos_sector,
671 dest_format, &dest_val)) {
675 gst_query_set_position (query, dest_format, dest_val);
677 GST_LOG ("position: sector %u, %" G_GINT64_FORMAT " in format %s",
678 (guint) pos_sector, dest_val, gst_format_get_name (dest_format));
681 case GST_QUERY_CONVERT:{
682 GstFormat src_format, dest_format;
683 gint64 src_val, dest_val;
685 gst_query_parse_convert (query, &src_format, &src_val, &dest_format,
688 if (!gst_cdda_base_src_convert (src, src_format, src_val, dest_format,
693 gst_query_set_convert (query, src_format, src_val, dest_format, dest_val);
697 GST_DEBUG_OBJECT (src, "unhandled query, chaining up to parent class");
698 return GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
706 gst_cdda_base_src_is_seekable (GstBaseSrc * basesrc)
712 gst_cdda_base_src_do_seek (GstBaseSrc * basesrc, GstSegment * segment)
714 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
717 GST_DEBUG_OBJECT (src, "segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
718 GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop));
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");
726 /* we should only really be called when open */
727 g_assert (src->cur_track >= 0 && src->cur_track < src->num_tracks);
730 case GST_CDDA_BASE_SRC_MODE_NORMAL:
731 seek_sector += src->tracks[src->cur_track].start;
733 case GST_CDDA_BASE_SRC_MODE_CONTINUOUS:
734 seek_sector += src->tracks[0].start;
737 g_return_val_if_reached (FALSE);
740 src->cur_sector = (gint) seek_sector;
742 GST_DEBUG_OBJECT (src, "seek'd to sector %d", src->cur_sector);
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)
752 GstBaseSrc *basesrc = GST_BASE_SRC (src);
755 if ((flags & GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) {
756 gint64 start_time = -1;
757 gint64 stop_time = -1;
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);
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",
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",
781 start_type = GST_SEEK_TYPE_SET;
783 case GST_SEEK_TYPE_NONE:
787 g_return_val_if_reached (FALSE);
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",
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",
806 stop_type = GST_SEEK_TYPE_SET;
808 case GST_SEEK_TYPE_NONE:
812 g_return_val_if_reached (FALSE);
815 GST_LOG_OBJECT (src, "seek segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
816 GST_TIME_ARGS (start_time), GST_TIME_ARGS (stop_time));
818 /* send fake segment seek event in TIME format to
819 * base class, which will hopefully handle the rest */
821 event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, start_type,
822 start_time, stop_type, stop_time);
824 return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
827 /* not a segment seek */
829 if (start_type == GST_SEEK_TYPE_NONE) {
830 GST_LOG_OBJECT (src, "start seek type is NONE, nothing to do");
834 if (stop_type != GST_SEEK_TYPE_NONE) {
835 GST_WARNING_OBJECT (src, "ignoring stop seek type (expected NONE)");
838 if (start < 0 || start >= src->num_tracks) {
839 GST_DEBUG_OBJECT (src, "invalid track %" G_GINT64_FORMAT, start);
843 GST_DEBUG_OBJECT (src, "seeking to track %" G_GINT64_FORMAT, start + 1);
845 src->cur_sector = src->tracks[start].start;
846 GST_DEBUG_OBJECT (src, "starting at sector %d", src->cur_sector);
848 if (src->cur_track != start) {
849 src->cur_track = (gint) start;
851 src->prev_track = -1;
853 gst_cdda_base_src_update_duration (src);
855 GST_DEBUG_OBJECT (src, "is current track, just seeking back to start");
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);
863 return GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
867 gst_cdda_base_src_handle_event (GstBaseSrc * basesrc, GstEvent * event)
869 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
870 gboolean ret = FALSE;
872 GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
874 switch (GST_EVENT_TYPE (event)) {
875 case GST_EVENT_SEEK:{
876 GstSeekType start_type, stop_type;
882 if (!GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED)) {
883 GST_DEBUG_OBJECT (src, "seek failed: device not open");
887 gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
890 if (format == sector_format) {
891 GST_DEBUG_OBJECT (src, "seek in sector format not supported");
895 if (format == track_format) {
896 ret = gst_cdda_base_src_handle_track_seek (src, rate, flags,
897 start_type, start, stop_type, stop);
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);
907 GST_LOG_OBJECT (src, "let base class handle event");
908 ret = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
917 gst_cdda_base_src_uri_get_type (void)
923 gst_cdda_base_src_uri_get_protocols (void)
925 static gchar *protocols[] = { (char *) "cdda", NULL };
931 gst_cdda_base_src_uri_get_uri (GstURIHandler * handler)
933 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
935 GST_OBJECT_LOCK (src);
939 if (GST_OBJECT_FLAG_IS_SET (GST_BASE_SRC (src), GST_BASE_SRC_STARTED)) {
941 g_strdup_printf ("cdda://%s#%d", src->device,
942 (src->uri_track > 0) ? src->uri_track : 1);
944 src->uri = g_strdup ("cdda://1");
947 GST_OBJECT_UNLOCK (src);
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) */
955 /* We accept URIs of the format cdda://(device#track)|(track) */
958 gst_cdda_base_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
960 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (handler);
962 const gchar *location;
965 GST_OBJECT_LOCK (src);
967 protocol = gst_uri_get_protocol (uri);
968 if (!protocol || g_ascii_strcasecmp (protocol, "cdda") != 0) {
975 track_number = g_strrstr (location, "#");
977 /* FIXME 0.11: ignore URI fragments that look like device paths for
978 * the benefit of rhythmbox and possibly other applications.
980 if (track_number && track_number[1] != '/') {
981 gchar *device, *nuri = g_strdup (uri);
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);
988 src->uri_track = strtol (track_number + 1, NULL, 10);
991 if (*location == '\0')
994 src->uri_track = strtol (location, NULL, 10);
997 if (src->uri_track < 1)
1000 if (src->num_tracks > 0
1001 && src->tracks != NULL && src->uri_track > src->num_tracks)
1004 if (src->uri_track > 0 && src->tracks != NULL) {
1005 GST_OBJECT_UNLOCK (src);
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));
1011 /* seek will be done in start() */
1012 GST_OBJECT_UNLOCK (src);
1015 GST_LOG_OBJECT (handler, "successfully handled uri '%s'", uri);
1021 GST_OBJECT_UNLOCK (src);
1022 GST_DEBUG_OBJECT (src, "cannot handle URI '%s'", uri);
1028 gst_cdda_base_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1030 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
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;
1039 gst_cdda_base_src_setup_interfaces (GType type)
1041 static const GInterfaceInfo urihandler_info = {
1042 gst_cdda_base_src_uri_handler_init,
1047 g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
1051 * gst_cdda_base_src_add_track:
1052 * @src: a #GstCddaBaseSrc
1053 * @track: address of #GstCddaBaseSrcTrack to add
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).
1060 * Returns: FALSE on error, otherwise TRUE.
1064 gst_cdda_base_src_add_track (GstCddaBaseSrc * src, GstCddaBaseSrcTrack * track)
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);
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);
1074 if (src->num_tracks > 0) {
1075 guint end_of_previous_track = src->tracks[src->num_tracks - 1].end;
1077 if (track->start <= end_of_previous_track) {
1078 GST_WARNING ("track %2u overlaps with previous tracks", track->num);
1083 GST_OBJECT_LOCK (src);
1086 src->tracks = g_renew (GstCddaBaseSrcTrack, src->tracks, src->num_tracks);
1087 src->tracks[src->num_tracks - 1] = *track;
1089 GST_OBJECT_UNLOCK (src);
1095 gst_cdda_base_src_update_duration (GstCddaBaseSrc * src)
1097 GstBaseSrc *basesrc;
1101 basesrc = GST_BASE_SRC (src);
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);
1107 gst_segment_set_duration (&basesrc->segment, GST_FORMAT_TIME, -1);
1108 duration = GST_CLOCK_TIME_NONE;
1111 gst_element_post_message (GST_ELEMENT (src),
1112 gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_TIME, -1));
1114 GST_LOG_OBJECT (src, "duration updated to %" GST_TIME_FORMAT,
1115 GST_TIME_ARGS (duration));
1118 #define CD_MSF_OFFSET 150
1120 /* the cddb hash function */
1135 gst_cddabasesrc_calculate_musicbrainz_discid (GstCddaBaseSrc * src)
1143 guint leadout_sector;
1146 s = g_string_new (NULL);
1148 leadout_sector = src->tracks[src->num_tracks - 1].end + 1 + CD_MSF_OFFSET;
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);
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);
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);
1164 for (i = 0; i < 99; i++) {
1165 if (i < src->num_tracks) {
1166 guint frame_offset = src->tracks[i].start + CD_MSF_OFFSET;
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);
1172 g_checksum_update (sha, (guchar *) "00000000", 8);
1176 g_checksum_get_digest (sha, (guint8 *) & digest, &digest_len);
1178 /* re-encode to base64 */
1179 ptr = g_base64_encode (digest, digest_len);
1180 g_checksum_free (sha);
1183 g_assert (i < sizeof (src->mb_discid) + 1);
1184 memcpy (src->mb_discid, ptr, i);
1185 src->mb_discid[i] = '\0';
1188 /* Replace '/', '+' and '=' by '_', '.' and '-' as specified on
1189 * http://musicbrainz.org/doc/DiscIDCalculation
1191 for (ptr = src->mb_discid; *ptr != '\0'; ptr++) {
1194 else if (*ptr == '+')
1196 else if (*ptr == '=')
1200 GST_DEBUG_OBJECT (src, "musicbrainz-discid = %s", src->mb_discid);
1201 GST_DEBUG_OBJECT (src, "musicbrainz-discid-full = %s", s->str);
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);
1207 g_string_free (s, TRUE);
1211 lba_to_msf (guint sector, guint * p_m, guint * p_s, guint * p_f, guint * p_secs)
1215 m = sector / SECTORS_PER_MINUTE;
1216 sector = sector % SECTORS_PER_MINUTE;
1217 s = sector / SECTORS_PER_SECOND;
1218 f = sector % SECTORS_PER_SECOND;
1227 *p_secs = s + (m * 60);
1231 gst_cdda_base_src_calculate_cddb_id (GstCddaBaseSrc * src)
1234 guint first_sector = 0, last_sector = 0;
1235 guint start_secs, end_secs, secs, len_secs;
1236 guint total_secs, num_audio_tracks;
1241 num_audio_tracks = 0;
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;
1250 last_sector = src->tracks[i].end + CD_MSF_OFFSET + 1;
1253 lba_to_msf (src->tracks[i].start + CD_MSF_OFFSET, NULL, NULL, NULL,
1256 len_secs = (src->tracks[i].end - src->tracks[i].start + 1) / 75;
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);
1263 id += cddb_sum (secs);
1264 total_secs += len_secs;
1268 /* first_sector = src->tracks[0].start + CD_MSF_OFFSET; */
1269 lba_to_msf (first_sector, NULL, NULL, NULL, &start_secs);
1271 /* last_sector = src->tracks[src->num_tracks-1].end + CD_MSF_OFFSET; */
1272 lba_to_msf (last_sector, NULL, NULL, NULL, &end_secs);
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);
1279 t = end_secs - start_secs;
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);
1285 src->discid = ((id % 0xff) << 24 | t << 8 | num_audio_tracks);
1287 s = g_string_new (NULL);
1288 g_string_append_printf (s, "%08x", src->discid);
1290 gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1291 GST_TAG_CDDA_CDDB_DISCID, s->str, NULL);
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);
1297 g_string_append_printf (s, " %u", t);
1299 gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1300 GST_TAG_CDDA_CDDB_DISCID_FULL, s->str, NULL);
1302 GST_DEBUG_OBJECT (src, "cddb discid = %s", s->str);
1304 g_string_free (s, TRUE);
1308 gst_cdda_base_src_add_tags (GstCddaBaseSrc * src)
1312 /* fill in details for each track */
1313 for (i = 0; i < src->num_tracks; ++i) {
1317 if (src->tracks[i].tags == NULL)
1318 src->tracks[i].tags = gst_tag_list_new ();
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);
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);
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 */
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 ?? *////////////////////////////////////////
1339 gst_tag_list_add (src->tags, GST_TAG_MERGE_REPLACE,
1340 GST_TAG_TRACK_COUNT, src->num_tracks, NULL);
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);
1348 GST_DEBUG ("src->tags = %" GST_PTR_FORMAT, src->tags);
1352 gst_cdda_base_src_add_index_associations (GstCddaBaseSrc * src)
1356 for (i = 0; i < src->num_tracks; i++) {
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,
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);
1370 gst_cdda_base_src_set_index (GstElement * element, GstIndex * index)
1372 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
1375 GST_OBJECT_LOCK (element);
1378 GST_OBJECT_UNLOCK (element);
1382 gst_object_ref (index);
1384 GST_OBJECT_UNLOCK (element);
1386 gst_object_unref (old);
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);
1397 gst_cdda_base_src_get_index (GstElement * element)
1399 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (element);
1402 GST_OBJECT_LOCK (element);
1403 if ((index = src->index))
1404 gst_object_ref (index);
1405 GST_OBJECT_UNLOCK (element);
1411 gst_cdda_base_src_track_sort_func (gconstpointer a, gconstpointer b,
1414 GstCddaBaseSrcTrack *track_a = ((GstCddaBaseSrcTrack *) a);
1415 GstCddaBaseSrcTrack *track_b = ((GstCddaBaseSrcTrack *) b);
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;
1421 if (track_a->is_audio) {
1429 gst_cdda_base_src_start (GstBaseSrc * basesrc)
1431 GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
1432 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
1434 gchar *device = NULL;
1437 src->mb_discid[0] = '\0';
1439 g_assert (klass->open != NULL);
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);
1448 device = g_strdup (DEFAULT_DEVICE);
1450 GST_LOG_OBJECT (basesrc, "opening device %s", device);
1452 src->tags = gst_tag_list_new ();
1454 ret = klass->open (src, device);
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);
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);
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);
1478 /* adjust sector offsets if necessary */
1479 if (src->toc_bias) {
1480 src->toc_offset -= src->tracks[0].start;
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;
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;
1492 g_qsort_with_data (src->tracks, src->num_tracks,
1493 sizeof (GstCddaBaseSrcTrack), gst_cdda_base_src_track_sort_func, NULL);
1495 while (src->num_tracks > 0 && !src->tracks[src->num_tracks - 1].is_audio)
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);
1506 gst_cdda_base_src_add_tags (src);
1508 if (src->index && GST_INDEX_IS_WRITABLE (src->index))
1509 gst_cdda_base_src_add_index_associations (src);
1512 src->prev_track = -1;
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;
1521 src->cur_sector = src->tracks[src->cur_track].start;
1522 GST_LOG_OBJECT (src, "starting at sector %d", src->cur_sector);
1524 gst_cdda_base_src_update_duration (src);
1530 gst_cdda_base_src_clear_tracks (GstCddaBaseSrc * src)
1532 if (src->tracks != NULL) {
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);
1540 g_free (src->tracks);
1543 src->num_tracks = 0;
1544 src->num_all_tracks = 0;
1548 gst_cdda_base_src_stop (GstBaseSrc * basesrc)
1550 GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (basesrc);
1551 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (basesrc);
1553 g_assert (klass->close != NULL);
1557 gst_cdda_base_src_clear_tracks (src);
1560 gst_tag_list_free (src->tags);
1564 src->prev_track = -1;
1565 src->cur_track = -1;
1571 static GstFlowReturn
1572 gst_cdda_base_src_create (GstPushSrc * pushsrc, GstBuffer ** buffer)
1574 GstCddaBaseSrcClass *klass = GST_CDDA_BASE_SRC_GET_CLASS (pushsrc);
1575 GstCddaBaseSrc *src = GST_CDDA_BASE_SRC (pushsrc);
1580 GstClockTime position = GST_CLOCK_TIME_NONE;
1581 GstClockTime duration = GST_CLOCK_TIME_NONE;
1582 gint64 qry_position;
1584 g_assert (klass->read_sector != NULL);
1586 switch (src->mode) {
1587 case GST_CDDA_BASE_SRC_MODE_NORMAL:
1588 eos = (src->cur_sector > src->tracks[src->cur_track].end);
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,
1596 g_return_val_if_reached (GST_FLOW_ERROR);
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;
1607 if (src->prev_track != src->cur_track) {
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;
1617 gst_cdda_base_src_update_duration (src);
1619 g_object_notify (G_OBJECT (src), "track");
1622 GST_LOG_OBJECT (src, "asking for sector %u", src->cur_sector);
1624 buf = klass->read_sector (src, src->cur_sector);
1627 GST_WARNING_OBJECT (src, "failed to read sector %u", src->cur_sector);
1628 return GST_FLOW_ERROR;
1631 if (GST_BUFFER_CAPS (buf) == NULL) {
1632 gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
1635 format = GST_FORMAT_TIME;
1636 if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &qry_position)) {
1639 position = (GstClockTime) qry_position;
1642 if (gst_pad_query_position (GST_BASE_SRC_PAD (src), &format, &next_ts)) {
1643 duration = (GstClockTime) (next_ts - qry_position);
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,
1654 GST_BUFFER_TIMESTAMP (buf) = position;
1655 GST_BUFFER_DURATION (buf) = duration;
1657 GST_LOG_OBJECT (src, "pushing sector %d with timestamp %" GST_TIME_FORMAT,
1658 src->cur_sector, GST_TIME_ARGS (position));