Coverage Report

Created: 2026-01-22 06:44

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