Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / ext / cdaudio / gstcdaudio.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *               <2006> Wim Taymans <wim@fluendo.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <string.h>
25 #include <cdaudio.h>
26
27 #include <gst/gst.h>
28
29 GST_DEBUG_CATEGORY_STATIC (gst_cdaudio_debug);
30 #define GST_CAT_DEFAULT gst_cdaudio_debug
31
32 #define GST_TYPE_CDAUDIO                (gst_cdaudio_get_type())
33 #define GST_CDAUDIO(obj)                (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CDAUDIO,GstCDAudio))
34 #define GST_CDAUDIO_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CDAUDIO,GstCDAudioClass))
35 #define GST_IS_CDAUDIO(obj)             (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CDAUDIO))
36 #define GST_IS_CDAUDIO_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CDAUDIO))
37
38 typedef struct _GstCDAudio GstCDAudio;
39 typedef struct _GstCDAudioClass GstCDAudioClass;
40
41 struct _GstCDAudio
42 {
43   GstElement parent;
44
45   /* properties */
46   gchar *device;
47   gchar *uri;
48
49   gint cd_desc;
50   gulong discid;
51
52   gboolean was_playing;
53
54   struct disc_info info;
55   struct disc_volume volume;
56
57   GTimer *timer;
58 };
59
60 struct _GstCDAudioClass
61 {
62   GstElementClass parent_class;
63
64   void (*close_tray) (GstElement * element);
65   /* signal callbacks */
66   void (*track_change) (GstElement * element, guint track);
67 };
68
69 #define DEFAULT_DEVICE     "/dev/cdrom"
70 #define DEFAULT_VOLUME_FR  255
71 #define DEFAULT_VOLUME_FL  255
72 #define DEFAULT_VOLUME_BR  255
73 #define DEFAULT_VOLUME_BL  255
74
75 /* props */
76 enum
77 {
78   PROP_0,
79   PROP_DEVICE,
80   PROP_VOLUME_FR,
81   PROP_VOLUME_FL,
82   PROP_VOLUME_BR,
83   PROP_VOLUME_BL,
84   PROP_LAST
85 };
86
87 /* signals */
88 enum
89 {
90   TRACK_CHANGE,
91   LAST_SIGNAL
92 };
93
94 static void gst_cdaudio_finalize (GObject * object);
95
96 static void gst_cdaudio_set_property (GObject * object, guint prop_id,
97     const GValue * value, GParamSpec * spec);
98 static void gst_cdaudio_get_property (GObject * object, guint prop_id,
99     GValue * value, GParamSpec * spec);
100 static GstStateChangeReturn gst_cdaudio_change_state (GstElement * element,
101     GstStateChange transition);
102
103 static const GstQueryType *gst_cdaudio_get_query_types (GstElement * element);
104 static gboolean gst_cdaudio_query (GstElement * element, GstQuery * query);
105
106 static gboolean gst_cdaudio_send_event (GstElement * element, GstEvent * event);
107
108 static void cdaudio_uri_handler_init (gpointer g_iface, gpointer iface_data);
109
110 static GstFormat track_format;
111 static GstFormat sector_format;
112
113 static GstElementClass *parent_class;
114 static guint gst_cdaudio_signals[LAST_SIGNAL] = { 0 };
115
116 static void
117 _do_init (GType cdaudio_type)
118 {
119   static const GInterfaceInfo urihandler_info = {
120     cdaudio_uri_handler_init,
121     NULL,
122     NULL,
123   };
124
125   g_type_add_interface_static (cdaudio_type, GST_TYPE_URI_HANDLER,
126       &urihandler_info);
127 }
128
129 GType gst_cdaudio_get_type (void);
130 GST_BOILERPLATE_FULL (GstCDAudio, gst_cdaudio, GstElement, GST_TYPE_ELEMENT,
131     _do_init);
132
133 static void
134 gst_cdaudio_base_init (gpointer g_class)
135 {
136   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
137
138   gst_element_class_set_details_simple (element_class, "CD player",
139       "Generic/Bin",
140       "Play CD audio through the CD Drive", "Wim Taymans <wim@fluendo.com>");
141
142   /* Register the track and sector format */
143   track_format = gst_format_register ("track", "CD track");
144   sector_format = gst_format_register ("sector", "CD sector");
145 }
146
147 static void
148 gst_cdaudio_class_init (GstCDAudioClass * klass)
149 {
150   GObjectClass *gobject_klass;
151   GstElementClass *gstelement_klass;
152
153   gobject_klass = (GObjectClass *) klass;
154   gstelement_klass = (GstElementClass *) klass;
155
156   parent_class = g_type_class_peek_parent (klass);
157
158   gobject_klass->set_property = gst_cdaudio_set_property;
159   gobject_klass->get_property = gst_cdaudio_get_property;
160
161   g_object_class_install_property (gobject_klass, PROP_DEVICE,
162       g_param_spec_string ("device", "Device", "CDROM device",
163           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
164   g_object_class_install_property (gobject_klass, PROP_VOLUME_FL,
165       g_param_spec_int ("volume-fl", "Volume fl", "Front left volume",
166           0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167   g_object_class_install_property (gobject_klass, PROP_VOLUME_FR,
168       g_param_spec_int ("volume-fr", "Volume fr", "Front right volume",
169           0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
170   g_object_class_install_property (gobject_klass, PROP_VOLUME_BL,
171       g_param_spec_int ("volume-bl", "Volume bl", "Back left volume",
172           0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
173   g_object_class_install_property (gobject_klass, PROP_VOLUME_BR,
174       g_param_spec_int ("volume-br", "Volume br", "Back right volume",
175           0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
176
177   gst_cdaudio_signals[TRACK_CHANGE] =
178       g_signal_new ("track-change", G_TYPE_FROM_CLASS (klass),
179       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstCDAudioClass, track_change), NULL,
180       NULL, gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
181
182   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_cdaudio_finalize);
183
184   gstelement_klass->change_state = GST_DEBUG_FUNCPTR (gst_cdaudio_change_state);
185   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_cdaudio_send_event);
186   gstelement_klass->get_query_types =
187       GST_DEBUG_FUNCPTR (gst_cdaudio_get_query_types);
188   gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_cdaudio_query);
189
190   GST_DEBUG_CATEGORY_INIT (gst_cdaudio_debug, "cdaudio", 0, "CDAudio Element");
191 }
192
193 static void
194 gst_cdaudio_init (GstCDAudio * cdaudio, GstCDAudioClass * g_class)
195 {
196   cdaudio->device = g_strdup (DEFAULT_DEVICE);
197   cdaudio->volume.vol_front.right = DEFAULT_VOLUME_FR;
198   cdaudio->volume.vol_front.left = DEFAULT_VOLUME_FL;
199   cdaudio->volume.vol_back.right = DEFAULT_VOLUME_BR;
200   cdaudio->volume.vol_back.left = DEFAULT_VOLUME_BL;
201
202   cdaudio->was_playing = FALSE;
203   cdaudio->timer = g_timer_new ();
204
205   GST_OBJECT_FLAG_SET (cdaudio, GST_ELEMENT_IS_SINK);
206 }
207
208 static void
209 gst_cdaudio_finalize (GObject * object)
210 {
211   GstCDAudio *cdaudio = GST_CDAUDIO (object);
212
213   g_timer_destroy (cdaudio->timer);
214   g_free (cdaudio->device);
215
216   G_OBJECT_CLASS (parent_class)->finalize (object);
217 }
218
219 static void
220 gst_cdaudio_set_property (GObject * object, guint prop_id, const GValue * value,
221     GParamSpec * spec)
222 {
223   GstCDAudio *cdaudio;
224
225   cdaudio = GST_CDAUDIO (object);
226
227   switch (prop_id) {
228     case PROP_DEVICE:
229       g_free (cdaudio->device);
230       cdaudio->device = g_value_dup_string (value);
231       break;
232     case PROP_VOLUME_FR:
233       cdaudio->volume.vol_front.right = g_value_get_int (value);
234       break;
235     case PROP_VOLUME_FL:
236       cdaudio->volume.vol_front.left = g_value_get_int (value);
237       break;
238     case PROP_VOLUME_BR:
239       cdaudio->volume.vol_back.right = g_value_get_int (value);
240       break;
241     case PROP_VOLUME_BL:
242       cdaudio->volume.vol_back.left = g_value_get_int (value);
243       break;
244     default:
245       break;
246   }
247 }
248
249 static void
250 gst_cdaudio_get_property (GObject * object, guint prop_id, GValue * value,
251     GParamSpec * spec)
252 {
253   GstCDAudio *cdaudio;
254
255   cdaudio = GST_CDAUDIO (object);
256
257   switch (prop_id) {
258     case PROP_DEVICE:
259       g_value_set_string (value, cdaudio->device);
260       break;
261     case PROP_VOLUME_FR:
262       g_value_set_int (value, cdaudio->volume.vol_front.right);
263       break;
264     case PROP_VOLUME_FL:
265       g_value_set_int (value, cdaudio->volume.vol_front.left);
266       break;
267     case PROP_VOLUME_BR:
268       g_value_set_int (value, cdaudio->volume.vol_back.right);
269       break;
270     case PROP_VOLUME_BL:
271       g_value_set_int (value, cdaudio->volume.vol_back.left);
272       break;
273     default:
274       break;
275   }
276 }
277
278 static void
279 debug_track_info (GstCDAudio * cdaudio)
280 {
281   gint i;
282
283   for (i = 0; i < cdaudio->info.disc_total_tracks; i++) {
284     GST_DEBUG_OBJECT (cdaudio, "%d %d %d %d:%02d", i,
285         cdaudio->info.disc_track[i].track_length.frames,
286         cdaudio->info.disc_track[i].track_pos.frames,
287         cdaudio->info.disc_track[i].track_length.minutes,
288         cdaudio->info.disc_track[i].track_length.seconds);
289   }
290 }
291
292 static GstStateChangeReturn
293 gst_cdaudio_change_state (GstElement * element, GstStateChange transition)
294 {
295   GstCDAudio *cdaudio;
296   GstStateChangeReturn ret;
297   gint res;
298
299   cdaudio = GST_CDAUDIO (element);
300
301   switch (transition) {
302     case GST_STATE_CHANGE_NULL_TO_READY:
303       break;
304     case GST_STATE_CHANGE_READY_TO_PAUSED:
305       if ((res = cd_init_device (cdaudio->device)) < 0)
306         goto init_failed;
307
308       cdaudio->cd_desc = res;
309
310       /* close tray */
311       if ((res = cd_close (cdaudio->cd_desc)) < 0)
312         goto close_failed;
313
314       if ((res = cd_stat (cdaudio->cd_desc, &cdaudio->info)) < 0) {
315         /* we just give a warning here */
316         GST_ELEMENT_WARNING (cdaudio, LIBRARY, INIT,
317             ("Could not retrieve CD track info."), (NULL));
318       } else {
319         debug_track_info (cdaudio);
320         cdaudio->discid = cddb_discid (cdaudio->cd_desc);
321         /* FIXME, post message with discid */
322       }
323       cdaudio->was_playing = FALSE;
324       break;
325     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
326     {
327       if (cdaudio->was_playing)
328         res = cd_resume (cdaudio->cd_desc);
329       else
330         res = cd_play (cdaudio->cd_desc, 1);
331
332       if (res < 0)
333         goto play_failed;
334
335       cdaudio->was_playing = TRUE;
336       g_timer_start (cdaudio->timer);
337       break;
338     }
339     default:
340       break;
341   }
342
343   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
344
345   switch (transition) {
346     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
347       if ((res = cd_pause (cdaudio->cd_desc)) < 0)
348         goto pause_failed;
349       g_timer_stop (cdaudio->timer);
350       break;
351     case GST_STATE_CHANGE_PAUSED_TO_READY:
352       if ((res = cd_stop (cdaudio->cd_desc)) < 0)
353         goto stop_failed;
354       if ((res = cd_finish (cdaudio->cd_desc)) < 0)
355         goto finish_failed;
356       cdaudio->cd_desc = -1;
357       break;
358     case GST_STATE_CHANGE_READY_TO_NULL:
359       break;
360     default:
361       break;
362   }
363   return ret;
364
365   /* ERRORS */
366 init_failed:
367   {
368     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
369         ("Could not init CD device %s. (%d)", cdaudio->device, res), (NULL));
370     cdaudio->cd_desc = -1;
371     return GST_STATE_CHANGE_FAILURE;
372   }
373 close_failed:
374   {
375     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
376         ("Could not close CD tray for device %s. (%d)", cdaudio->device, res),
377         (NULL));
378     return GST_STATE_CHANGE_FAILURE;
379   }
380 play_failed:
381   {
382     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
383         ("Could not play CD device %s. (%d)", cdaudio->device, res), (NULL));
384     return GST_STATE_CHANGE_FAILURE;
385   }
386 pause_failed:
387   {
388     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
389         ("Could not pause CD device %s. (%d)", cdaudio->device, res), (NULL));
390     return GST_STATE_CHANGE_FAILURE;
391   }
392 stop_failed:
393   {
394     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
395         ("Could not stop CD device %s. (%d)", cdaudio->device, res), (NULL));
396     return GST_STATE_CHANGE_FAILURE;
397   }
398 finish_failed:
399   {
400     GST_ELEMENT_ERROR (cdaudio, LIBRARY, INIT,
401         ("Could not finish CD device %s. (%d)", cdaudio->device, res), (NULL));
402     return GST_STATE_CHANGE_FAILURE;
403   }
404 }
405
406 static gboolean
407 gst_cdaudio_send_event (GstElement * element, GstEvent * event)
408 {
409   GstCDAudio *cdaudio;
410   gboolean res = TRUE;
411
412   cdaudio = GST_CDAUDIO (element);
413
414   switch (GST_EVENT_TYPE (event)) {
415     case GST_EVENT_SEEK:
416     {
417       gdouble rate;
418       GstFormat format;
419       GstSeekFlags flags;
420       GstSeekType start_type, stop_type;
421       gint64 start, stop;
422       gint ret;
423
424       gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
425           &stop_type, &stop);
426
427       /* FIXME, implement more formats */
428       if (format != GST_FORMAT_TIME)
429         goto wrong_format;
430
431       if (rate != 1.0)
432         goto wrong_rate;
433
434       if (start_type != GST_SEEK_TYPE_SET)
435         goto unsupported;
436
437       ret = cd_play_pos (cdaudio->cd_desc, 1, start / GST_SECOND);
438       if (ret < 0)
439         goto seek_failed;
440     }
441     default:
442       res = FALSE;
443       break;
444   }
445
446 done:
447   gst_event_unref (event);
448
449   return res;
450
451   /* ERRORS */
452 wrong_format:
453   {
454     GST_DEBUG_OBJECT (cdaudio, "only seek in TIME is supported");
455     res = FALSE;
456     goto done;
457   }
458 wrong_rate:
459   {
460     GST_DEBUG_OBJECT (cdaudio, "only seek with 1.0 rate is supported");
461     res = FALSE;
462     goto done;
463   }
464 unsupported:
465   {
466     GST_DEBUG_OBJECT (cdaudio, "only seek SET is supported");
467     res = FALSE;
468     goto done;
469   }
470 seek_failed:
471   {
472     GST_DEBUG_OBJECT (cdaudio, "seek failed");
473     res = FALSE;
474     goto done;
475   }
476 }
477
478 static const GstQueryType *
479 gst_cdaudio_get_query_types (GstElement * element)
480 {
481   static const GstQueryType query_types[] = {
482     GST_QUERY_DURATION,
483     GST_QUERY_POSITION,
484     0
485   };
486
487   return query_types;
488 }
489
490 static gboolean
491 gst_cdaudio_query (GstElement * element, GstQuery * query)
492 {
493   GstCDAudio *cdaudio;
494   gboolean res = TRUE;
495   gulong micros;
496   gdouble seconds;
497
498   cdaudio = GST_CDAUDIO (element);
499
500   GST_LOG_OBJECT (element, "handling %s query",
501       gst_query_type_get_name (GST_QUERY_TYPE (query)));
502
503   /* take new snapshot every 1000 miliseconds */
504   seconds = g_timer_elapsed (cdaudio->timer, &micros);
505   if (micros > 1000 || seconds > 1) {
506     cd_stat (cdaudio->cd_desc, &cdaudio->info);
507     g_timer_start (cdaudio->timer);
508   }
509
510   switch (GST_QUERY_TYPE (query)) {
511     case GST_QUERY_DURATION:
512     {
513       GstFormat dest_format;
514       gint64 dest_val;
515
516       gst_query_parse_duration (query, &dest_format, NULL);
517
518       switch (dest_format) {
519         case GST_FORMAT_TIME:
520           dest_val = (cdaudio->info.disc_length.minutes * 60 +
521               cdaudio->info.disc_length.seconds) * GST_SECOND;
522           break;
523         default:
524         {
525           if (dest_format == track_format) {
526             dest_val = cdaudio->info.disc_total_tracks;
527           } else {
528             res = FALSE;
529           }
530           break;
531         }
532       }
533       if (res)
534         gst_query_set_duration (query, dest_format, dest_val);
535       break;
536     }
537     case GST_QUERY_POSITION:
538     {
539       GstFormat dest_format;
540       gint64 dest_val;
541
542       gst_query_parse_position (query, &dest_format, NULL);
543
544       switch (dest_format) {
545         case GST_FORMAT_TIME:
546           dest_val = (cdaudio->info.disc_time.minutes * 60 +
547               cdaudio->info.disc_time.seconds) * GST_SECOND;
548           break;
549         default:
550         {
551           if (dest_format == track_format) {
552             dest_val = cdaudio->info.disc_current_track;
553           } else {
554             res = FALSE;
555           }
556           break;
557         }
558       }
559       if (res)
560         gst_query_set_position (query, dest_format, dest_val);
561       break;
562     }
563     default:
564       res = FALSE;
565       break;
566   }
567   return res;
568 }
569
570
571 static gboolean
572 plugin_init (GstPlugin * plugin)
573 {
574   if (!gst_element_register (plugin, "cdaudio", GST_RANK_NONE,
575           GST_TYPE_CDAUDIO))
576     return FALSE;
577
578   return TRUE;
579 }
580
581 /*** GSTURIHANDLER INTERFACE *************************************************/
582
583 static guint
584 cdaudio_uri_get_type (void)
585 {
586   return GST_URI_SRC;
587 }
588
589 static gchar **
590 cdaudio_uri_get_protocols (void)
591 {
592   static gchar *protocols[] = { (char *) "cd", NULL };
593
594   return protocols;
595 }
596
597 static const gchar *
598 cdaudio_uri_get_uri (GstURIHandler * handler)
599 {
600   GstCDAudio *cdaudio = GST_CDAUDIO (handler);
601
602   return cdaudio->uri;
603 }
604
605 static gboolean
606 cdaudio_uri_set_uri (GstURIHandler * handler, const gchar * uri)
607 {
608   gchar *protocol, *location;
609   gboolean ret;
610
611   ret = TRUE;
612
613   //GstCDAudio *cdaudio = GST_CDAUDIO(handler);
614
615   protocol = gst_uri_get_protocol (uri);
616   if (strcmp (protocol, "cd") != 0)
617     goto wrong_protocol;
618
619   g_free (protocol);
620
621   location = gst_uri_get_location (uri);
622   /*
623      cdaudio->uri_track = strtol(location,NULL,10);
624      if (cdaudio->uri_track > 0) {
625      cdaudio->seek_request = cdaudio->uri_track;
626      }
627    */
628   g_free (location);
629
630   return ret;
631
632   /* ERRORS */
633 wrong_protocol:
634   {
635     g_free (protocol);
636     return FALSE;
637   }
638 }
639
640 static void
641 cdaudio_uri_handler_init (gpointer g_iface, gpointer iface_data)
642 {
643   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
644
645   iface->get_type = cdaudio_uri_get_type;
646   iface->get_protocols = cdaudio_uri_get_protocols;
647   iface->get_uri = cdaudio_uri_get_uri;
648   iface->set_uri = cdaudio_uri_set_uri;
649 }
650
651 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
652     GST_VERSION_MINOR,
653     "cdaudio",
654     "Play CD audio through the CD Drive",
655     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)