gstcdaudio.c 15.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

20 21 22
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
23
#include <string.h>
24 25 26 27 28 29 30 31 32 33 34 35 36
#include <cdaudio.h>

#include <gst/gst.h>

#define GST_TYPE_CDAUDIO 		(gst_cdaudio_get_type())
#define GST_CDAUDIO(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CDAUDIO,GstCDAudio))
#define GST_CDAUDIO_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CDAUDIO,GstCDAudioClass))
#define GST_IS_CDAUDIO(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CDAUDIO))
#define GST_IS_CDAUDIO_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CDAUDIO))

typedef struct _GstCDAudio GstCDAudio;
typedef struct _GstCDAudioClass GstCDAudioClass;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37 38 39 40
struct _GstCDAudio
{
  GstBin element;

41
  /* properties */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
42
  gchar *device;
43
  gchar *uri;
44

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
45 46
  gint cd_desc;
  gulong discid;
47

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
  gboolean was_playing;
49

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
50 51
  struct disc_info info;
  struct disc_volume volume;
52

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
53
  GTimer *timer;
54 55
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
56 57 58
struct _GstCDAudioClass
{
  GstBinClass parent_class;
59

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
60
  void (*close_tray) (GstElement * element);
61
  /* signal callbacks */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
62
  void (*track_change) (GstElement * element, guint track);
63 64 65 66 67 68 69 70 71 72 73
};

/* props */
enum
{
  ARG_0,
  ARG_DEVICE,
  ARG_DISCID,
  ARG_VOLUME_FR,
  ARG_VOLUME_FL,
  ARG_VOLUME_BR,
74
  ARG_VOLUME_BL
75 76 77 78 79 80
};

/* signals */
enum
{
  TRACK_CHANGE,
81
  LAST_SIGNAL
82 83
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
84 85
static void gst_cdaudio_class_init (GstCDAudioClass * klass);
static void gst_cdaudio_init (GstCDAudio * cdaudio);
86
static void gst_cdaudio_finalize (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87 88 89 90 91

static void gst_cdaudio_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * spec);
static void gst_cdaudio_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * spec);
92 93
static GstStateChangeReturn gst_cdaudio_change_state (GstElement * element,
    GstStateChange transition);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
94 95 96 97 98 99 100 101 102 103

static const GstEventMask *gst_cdaudio_get_event_masks (GstElement * element);
static gboolean gst_cdaudio_send_event (GstElement * element, GstEvent * event);
static const GstFormat *gst_cdaudio_get_formats (GstElement * element);
static gboolean gst_cdaudio_convert (GstElement * element,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value);
static const GstQueryType *gst_cdaudio_get_query_types (GstElement * element);
static gboolean gst_cdaudio_query (GstElement * element, GstQueryType type,
    GstFormat * format, gint64 * value);
104

105 106
static void cdaudio_uri_handler_init (gpointer g_iface, gpointer iface_data);

107 108
static GstFormat track_format;
static GstFormat sector_format;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109

110
static GstBinClass *parent_class;
111 112 113 114 115 116
static guint gst_cdaudio_signals[LAST_SIGNAL] = { 0 };

static GstElementDetails gst_cdaudio_details = {
  "CD Player",
  "Generic/Bin",
  "Play CD audio through the CD Drive",
117
  "Wim Taymans <wim@fluendo.com>",
118 119 120
};


121 122
static void
_do_init (GType cdaudio_type)
123
{
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  static const GInterfaceInfo urihandler_info = {
    cdaudio_uri_handler_init,
    NULL,
    NULL,
  };

  g_type_add_interface_static (cdaudio_type, GST_TYPE_URI_HANDLER,
      &urihandler_info);
}


GST_BOILERPLATE_FULL (GstCDAudio, gst_cdaudio, GstBin, GST_TYPE_BIN, _do_init);

static void
gst_cdaudio_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
141

142 143 144 145 146
  gst_element_class_set_details (element_class, &gst_cdaudio_details);

  /* Register the track and sector format */
  track_format = gst_format_register ("track", "CD track");
  sector_format = gst_format_register ("sector", "CD sector");
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
}

static void
gst_cdaudio_class_init (GstCDAudioClass * klass)
{
  GObjectClass *gobject_klass;
  GstElementClass *gstelement_klass;
  GstBinClass *gstbin_klass;

  gobject_klass = (GObjectClass *) klass;
  gstelement_klass = (GstElementClass *) klass;
  gstbin_klass = (GstBinClass *) klass;

  parent_class = g_type_class_ref (gst_bin_get_type ());

  gobject_klass->set_property = gst_cdaudio_set_property;
  gobject_klass->get_property = gst_cdaudio_get_property;

  g_object_class_install_property (gobject_klass, ARG_DEVICE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
166
      g_param_spec_string ("device", "Device", "CDROM device",
167
          NULL, G_PARAM_READWRITE));
168
  g_object_class_install_property (gobject_klass, ARG_DISCID,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
169
      g_param_spec_ulong ("discid", "Disc ID", "CDDB Disc ID",
170
          0, G_MAXULONG, 0, G_PARAM_READABLE));
171
  g_object_class_install_property (gobject_klass, ARG_VOLUME_FL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172
      g_param_spec_int ("volume_fl", "Volume fl", "Front left volume",
173
          0, 255, 255, G_PARAM_READWRITE));
174
  g_object_class_install_property (gobject_klass, ARG_VOLUME_FR,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
175
      g_param_spec_int ("volume_fr", "Volume fr", "Front right volume",
176
          0, 255, 255, G_PARAM_READWRITE));
177
  g_object_class_install_property (gobject_klass, ARG_VOLUME_BL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
178
      g_param_spec_int ("volume_bl", "Volume bl", "Back left volume",
179
          0, 255, 255, G_PARAM_READWRITE));
180
  g_object_class_install_property (gobject_klass, ARG_VOLUME_BR,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
181
      g_param_spec_int ("volume_br", "Volume br", "Back right volume",
182
          0, 255, 255, G_PARAM_READWRITE));
183 184

  gst_cdaudio_signals[TRACK_CHANGE] =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
185 186 187
      g_signal_new ("track-change", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstCDAudioClass, track_change), NULL,
      NULL, gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
188

189
  gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_cdaudio_finalize);
190

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
191 192 193 194 195 196 197 198 199
  gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_cdaudio_change_state);
  gstelement_klass->get_event_masks =
      GST_DEBUG_FUNCPTR (gst_cdaudio_get_event_masks);
  gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_cdaudio_send_event);
  gstelement_klass->get_formats = GST_DEBUG_FUNCPTR (gst_cdaudio_get_formats);
  gstelement_klass->convert = GST_DEBUG_FUNCPTR (gst_cdaudio_convert);
  gstelement_klass->get_query_types =
      GST_DEBUG_FUNCPTR (gst_cdaudio_get_query_types);
  gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_cdaudio_query);
200 201 202
}

static void
203
gst_cdaudio_init (GstCDAudio * cdaudio, GstCDAudioClass * g_class)
204 205 206 207 208 209 210 211 212 213
{
  cdaudio->device = g_strdup ("/dev/cdrom");
  cdaudio->was_playing = FALSE;
  cdaudio->timer = g_timer_new ();

  GST_FLAG_SET (cdaudio, GST_BIN_FLAG_MANAGER);
  GST_FLAG_SET (cdaudio, GST_BIN_SELF_SCHEDULABLE);
}

static void
214
gst_cdaudio_finalize (GObject * object)
215
{
216
  GstCDAudio *cdaudio = GST_CDAUDIO (object);
217 218 219 220

  g_timer_destroy (cdaudio->timer);
  g_free (cdaudio->device);

221
  G_OBJECT_CLASS (parent_class)->finalize (object);
222 223 224
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
225 226
gst_cdaudio_set_property (GObject * object, guint prop_id, const GValue * value,
    GParamSpec * spec)
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
{
  GstCDAudio *cdaudio;

  g_return_if_fail (GST_IS_CDAUDIO (object));

  cdaudio = GST_CDAUDIO (object);

  switch (prop_id) {
    case ARG_DEVICE:
      break;
    case ARG_VOLUME_FR:
      break;
    case ARG_VOLUME_FL:
      break;
    case ARG_VOLUME_BR:
      break;
    case ARG_VOLUME_BL:
      break;
    default:
      break;
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
251 252
gst_cdaudio_get_property (GObject * object, guint prop_id, GValue * value,
    GParamSpec * spec)
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
{
  GstCDAudio *cdaudio;

  g_return_if_fail (GST_IS_CDAUDIO (object));

  cdaudio = GST_CDAUDIO (object);

  switch (prop_id) {
    case ARG_DEVICE:
      g_value_set_string (value, cdaudio->device);
      break;
    case ARG_DISCID:
      g_value_set_ulong (value, cdaudio->discid);
      break;
    case ARG_VOLUME_FR:
      g_value_set_int (value, cdaudio->volume.vol_front.right);
      break;
    case ARG_VOLUME_FL:
      g_value_set_int (value, cdaudio->volume.vol_front.left);
      break;
    case ARG_VOLUME_BR:
      g_value_set_int (value, cdaudio->volume.vol_back.right);
      break;
    case ARG_VOLUME_BL:
      g_value_set_int (value, cdaudio->volume.vol_back.left);
      break;
    default:
      break;
  }
}

static void
285
debug_track_info (GstCDAudio * cdaudio)
286 287 288
{
  gint i;

289 290 291 292 293 294 295
  for (i = 0; i < cdaudio->info.disc_total_tracks; i++) {
    GST_DEBUG_OBJECT (cdaudio, "%d %d %d %d:%02d", i,
        cdaudio->info.disc_track[i].track_length.frames,
        cdaudio->info.disc_track[i].track_pos.frames,
        cdaudio->info.disc_track[i].track_length.minutes,
        cdaudio->info.disc_track[i].track_length.seconds);
  }
296 297
}

298 299
static GstStateChangeReturn
gst_cdaudio_change_state (GstElement * element, GstStateChange transition)
300 301 302 303 304
{
  GstCDAudio *cdaudio;

  cdaudio = GST_CDAUDIO (element);

305 306
  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
307
      break;
308
    case GST_STATE_CHANGE_READY_TO_PAUSED:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
309 310
      cdaudio->cd_desc = cd_init_device (cdaudio->device);
      if (cdaudio->cd_desc < 0)
311
        return GST_STATE_CHANGE_FAILURE;
312 313 314

      /* close tray */
      if (cd_close (cdaudio->cd_desc) < 0)
315
        return GST_STATE_CHANGE_FAILURE;
316 317

      if (cd_stat (cdaudio->cd_desc, &cdaudio->info) < 0)
318
        return GST_STATE_CHANGE_FAILURE;
319

320
      debug_track_info (cdaudio);
321 322 323 324 325 326

      cdaudio->discid = cddb_discid (cdaudio->cd_desc);
      g_object_notify (G_OBJECT (cdaudio), "discid");

      cdaudio->was_playing = FALSE;
      break;
327
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
328 329 330 331
    {
      gint res;

      if (cdaudio->was_playing)
332
        res = cd_resume (cdaudio->cd_desc);
333
      else
334
        res = cd_play (cdaudio->cd_desc, 1);
335

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
336
      if (res < 0)
337
        return GST_STATE_CHANGE_FAILURE;
338 339 340 341 342

      cdaudio->was_playing = TRUE;
      g_timer_start (cdaudio->timer);
      break;
    }
343
    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
344
      if (cd_pause (cdaudio->cd_desc) < 0)
345
        return GST_STATE_CHANGE_FAILURE;
346 347
      g_timer_stop (cdaudio->timer);
      break;
348
    case GST_STATE_CHANGE_PAUSED_TO_READY:
349
      if (cd_stop (cdaudio->cd_desc) < 0)
350
        return GST_STATE_CHANGE_FAILURE;
351
      if (cd_finish (cdaudio->cd_desc) < 0)
352
        return GST_STATE_CHANGE_FAILURE;
353
      break;
354
    case GST_STATE_CHANGE_READY_TO_NULL:
355 356 357 358 359 360
      break;
    default:
      break;
  }

  if (GST_ELEMENT_CLASS (parent_class)->change_state) {
361
    return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
362 363
  }

364
  return GST_STATE_CHANGE_SUCCESS;
365 366
}

367 368 369 370 371 372 373 374 375 376
static const GstEventMask *
gst_cdaudio_get_event_masks (GstElement * element)
{
  static const GstEventMask masks[] = {
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
          GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
    {GST_EVENT_SEEK_SEGMENT, GST_SEEK_METHOD_SET |
          GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
    {0,}
  };
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
377

378 379
  return masks;
}
380

381
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
382
gst_cdaudio_send_event (GstElement * element, GstEvent * event)
383 384 385 386 387 388 389 390 391
{
  GstCDAudio *cdaudio;
  gboolean res = TRUE;

  cdaudio = GST_CDAUDIO (element);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      switch (GST_EVENT_SEEK_FORMAT (event)) {
392 393
        case GST_FORMAT_TIME:
        {
394 395 396
          if (cd_play_pos (cdaudio->cd_desc, 1,
                  (gint) (GST_EVENT_SEEK_OFFSET (event) / (GST_SECOND))) == -1)
            res = FALSE;
397 398 399 400 401
          break;
        }
        default:
          res = FALSE;
          break;
402 403 404 405 406 407 408 409 410 411
      }
      break;
    default:
      res = FALSE;
      break;
  }
  gst_event_unref (event);
  return res;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412 413
const GstFormat *
gst_cdaudio_get_formats (GstElement * element)
414 415 416 417
{
  static GstFormat formats[] = {
    GST_FORMAT_TIME,
    GST_FORMAT_BYTES,
Wim Taymans's avatar
Wim Taymans committed
418
    GST_FORMAT_DEFAULT,
419 420
    0,                          /* fillted below */
    0,                          /* fillted below */
421 422 423 424 425 426 427 428 429 430
    0,
  };

  formats[3] = track_format;
  formats[4] = sector_format;

  return formats;
}

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
431 432 433
gst_cdaudio_convert (GstElement * element,
    GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
434 435 436 437
{
  return FALSE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438 439
const GstQueryType *
gst_cdaudio_get_query_types (GstElement * element)
440 441 442 443 444 445 446 447
{
  static const GstQueryType query_types[] = {
    GST_QUERY_TOTAL,
    GST_QUERY_POSITION,
    GST_QUERY_START,
    GST_QUERY_SEGMENT_END,
    0
  };
448

449 450 451 452
  return query_types;
}

gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
453 454
gst_cdaudio_query (GstElement * element, GstQueryType type,
    GstFormat * format, gint64 * value)
455 456 457 458 459 460 461 462
{
  GstCDAudio *cdaudio;
  gboolean res = TRUE;
  gulong micros;
  gdouble seconds;

  cdaudio = GST_CDAUDIO (element);

463
  /* take new snapshot every 1000 miliseconds */
464
  seconds = g_timer_elapsed (cdaudio->timer, &micros);
465
  if (micros > 1000 || seconds > 1) {
466 467 468 469 470 471 472
    cd_stat (cdaudio->cd_desc, &cdaudio->info);
    g_timer_start (cdaudio->timer);
  }

  switch (type) {
    case GST_QUERY_TOTAL:
      switch (*format) {
473 474 475 476 477 478 479 480 481 482 483 484 485
        case GST_FORMAT_TIME:
          *value = (cdaudio->info.disc_length.minutes * 60 +
              cdaudio->info.disc_length.seconds) * GST_SECOND;
          break;
        default:
        {
          if (*format == track_format) {
            *value = cdaudio->info.disc_total_tracks;
          } else {
            res = FALSE;
          }
          break;
        }
486 487 488 489
      }
      break;
    case GST_QUERY_POSITION:
      switch (*format) {
490 491 492 493 494 495 496 497 498 499 500 501 502
        case GST_FORMAT_TIME:
          *value = (cdaudio->info.disc_time.minutes * 60 +
              cdaudio->info.disc_time.seconds) * GST_SECOND;
          break;
        default:
        {
          if (*format == track_format) {
            *value = cdaudio->info.disc_current_track;
          } else {
            res = FALSE;
          }
          break;
        }
503 504 505 506 507 508 509 510 511 512
      }
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}

static gboolean
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
plugin_init (GstPlugin * plugin)
{
  if (!gst_element_register (plugin, "cdaudio", GST_RANK_NONE,
          GST_TYPE_CDAUDIO))
    return FALSE;

  return TRUE;
}

/*** GSTURIHANDLER INTERFACE *************************************************/

static guint
cdaudio_uri_get_type (void)
{
  return GST_URI_SRC;
}
static gchar **
cdaudio_uri_get_protocols (void)
{
  static gchar *protocols[] = { "cd", NULL };

  return protocols;
}
static const gchar *
cdaudio_uri_get_uri (GstURIHandler * handler)
{
  GstCDAudio *cdaudio = GST_CDAUDIO (handler);

  return cdaudio->uri;
}

static gboolean
cdaudio_uri_set_uri (GstURIHandler * handler, const gchar * uri)
546
{
547 548
  gchar *protocol, *location;
  gboolean ret;
549

550
  ret = TRUE;
551

552
  //GstCDAudio *cdaudio = GST_CDAUDIO(handler);
553

554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
  protocol = gst_uri_get_protocol (uri);
  if (strcmp (protocol, "cd") != 0) {
    g_free (protocol);
    return FALSE;
  }
  g_free (protocol);

  location = gst_uri_get_location (uri);
  /*
     cdaudio->uri_track = strtol(location,NULL,10);
     if (cdaudio->uri_track > 0) {
     cdaudio->seek_request = cdaudio->uri_track;
     }
   */
  g_free (location);

  return ret;
571 572
}

573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
static void
cdaudio_uri_handler_init (gpointer g_iface, gpointer iface_data)
{
  GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;

  iface->get_type = cdaudio_uri_get_type;
  iface->get_protocols = cdaudio_uri_get_protocols;
  iface->get_uri = cdaudio_uri_get_uri;
  iface->set_uri = cdaudio_uri_set_uri;
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "cdaudio",
    "Play CD audio through the CD Drive",
    plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)