1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * Goo
5 *
6 * Copyright (C) 2004, 2007 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <math.h>
24 #include <string.h>
25 #include <glib/gi18n.h>
26 #include <gst/gst.h>
27 #include "goo-error.h"
28 #include "goo-player.h"
29 #include "goo-marshal.h"
30 #include "glib-utils.h"
31 #include "gth-user-dir.h"
32 #include "main.h"
33 #include "metadata.h"
34
35 #define TOC_OFFSET 150
36 #define SECTORS_PER_SEC 75
37 #define POLL_TIMEOUT 1000
38 #define REFRESH_RATE 5
39 #define PROGRESS_DELAY 400
40 #define QUEUE_SIZE 16384U /*131072U*/
41 #define PIPELINE_VOLUME(x) ((x) / 100.0)
42
43
44 struct _GooPlayerPrivate {
45 BraseroDrive *drive;
46 gulong medium_added_event;
47 gulong medium_removed_event;
48
49 GooPlayerState state;
50 GooPlayerAction action;
51 double volume_value;
52 gboolean is_busy;
53 gboolean audio_cd;
54 gboolean hibernate;
55
56 GstElement *pipeline;
57 char *discid;
58 AlbumInfo *album;
59 TrackInfo *current_track;
60 int current_track_n;
61 int next_track_n;
62
63 guint update_state_id;
64 guint update_progress_id;
65
66 GMutex data_mutex;
67 gboolean exiting;
68 GCancellable *cancellable;
69 GList *albums;
70 };
71
72 enum {
73 START,
74 DONE,
75 PROGRESS,
76 MESSAGE,
77 STATE_CHANGED,
78 LAST_SIGNAL
79 };
80
81 static guint goo_player_signals[LAST_SIGNAL] = { 0 };
82
83 static void goo_player_finalize (GObject *object);
84
85
G_DEFINE_TYPE_WITH_CODE(GooPlayer,goo_player,G_TYPE_OBJECT,G_ADD_PRIVATE (GooPlayer))86 G_DEFINE_TYPE_WITH_CODE (GooPlayer, goo_player, G_TYPE_OBJECT,
87 G_ADD_PRIVATE (GooPlayer))
88
89
90 static void
91 destroy_pipeline (GooPlayer *player)
92 {
93 if (player->priv->pipeline != NULL) {
94 gst_element_set_state (player->priv->pipeline, GST_STATE_NULL);
95 gst_object_unref (GST_OBJECT (player->priv->pipeline));
96 player->priv->pipeline = NULL;
97 }
98
99 if (player->priv->update_progress_id != 0) {
100 g_source_remove (player->priv->update_progress_id);
101 player->priv->update_progress_id = 0;
102 }
103 }
104
105
106 static void
action_start(GooPlayer * self,GooPlayerAction action)107 action_start (GooPlayer *self,
108 GooPlayerAction action)
109 {
110 g_signal_emit (G_OBJECT (self),
111 goo_player_signals[START],
112 0,
113 action,
114 NULL);
115 }
116
117
118 static void
action_done(GooPlayer * self,GooPlayerAction action)119 action_done (GooPlayer *self,
120 GooPlayerAction action)
121 {
122 g_signal_emit (G_OBJECT (self),
123 goo_player_signals[DONE],
124 0,
125 action,
126 NULL);
127 }
128
129
130 static void
action_done_with_error(GooPlayer * self,GooPlayerAction action,GError * error)131 action_done_with_error (GooPlayer *self,
132 GooPlayerAction action,
133 GError *error)
134 {
135 g_signal_emit_by_name (G_OBJECT (self), "done", action, error);
136 g_error_free (error);
137 }
138
139
140 static TrackInfo*
get_track(GooPlayer * player,guint n)141 get_track (GooPlayer *player,
142 guint n)
143 {
144 GList *scan;
145
146 for (scan = player->priv->album->tracks; scan; scan = scan->next) {
147 TrackInfo *track = scan->data;
148
149 if (track->number == n)
150 return track;
151 }
152
153 return NULL;
154 }
155
156
157 static gboolean
set_current_track(GooPlayer * player,int track_to_play)158 set_current_track (GooPlayer *player,
159 int track_to_play)
160 {
161 GstStateChangeReturn ret;
162
163 if (track_to_play == -1)
164 return FALSE;
165
166 player->priv->current_track_n = CLAMP (track_to_play, 0, player->priv->album->n_tracks - 1);
167 player->priv->current_track = get_track (player, player->priv->current_track_n);
168 g_return_val_if_fail (player->priv->current_track != NULL, FALSE);
169
170 debug (DEBUG_INFO, "seek to track %d\n", player->priv->current_track_n);
171
172 ret = gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
173 while (ret == GST_STATE_CHANGE_ASYNC)
174 ret = gst_element_get_state (player->priv->pipeline, NULL, NULL, GST_MSECOND);
175
176 if (ret != GST_STATE_CHANGE_SUCCESS)
177 return FALSE;
178
179 return gst_element_seek (player->priv->pipeline,
180 1.0,
181 gst_format_get_by_nick ("track"),
182 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
183 GST_SEEK_TYPE_SET,
184 track_to_play,
185 GST_SEEK_TYPE_NONE,
186 -1);
187 }
188
189
190 static gboolean
player_done_cb(gpointer user_data)191 player_done_cb (gpointer user_data)
192 {
193 GooPlayer *self = user_data;
194
195 if (set_current_track (self, self->priv->next_track_n)) {
196 gst_element_set_state (self->priv->pipeline, GST_STATE_PLAYING);
197 action_done (self, GOO_PLAYER_ACTION_SEEK_SONG);
198 action_done (self, GOO_PLAYER_ACTION_STARTED_NEXT);
199 }
200 else {
201 if (self->priv->update_progress_id != 0) {
202 g_source_remove (self->priv->update_progress_id);
203 self->priv->update_progress_id = 0;
204 }
205 action_done (self, GOO_PLAYER_ACTION_PLAY);
206 }
207
208 return FALSE;
209 }
210
211
212 static void
pipeline_eos_cb(GstBus * bus,GstMessage * message,gpointer user_data)213 pipeline_eos_cb (GstBus *bus,
214 GstMessage *message,
215 gpointer user_data)
216 {
217 g_idle_add (player_done_cb, user_data);
218 }
219
220
221 static void
pipeline_source_setup_cb(GstElement * playbin,GstElement * source,gpointer user_data)222 pipeline_source_setup_cb (GstElement *playbin,
223 GstElement *source,
224 gpointer user_data)
225 {
226 GooPlayer *self = user_data;
227
228 if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed") != NULL) {
229 g_object_set (G_OBJECT (source),
230 "read-speed", 2,
231 NULL);
232 }
233
234 /* Disable paranoia in playback mode */
235 if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
236 g_object_set (source, "paranoia-mode", 0, NULL);
237
238 debug (DEBUG_INFO, "DEVICE: %s\n", brasero_drive_get_device (self->priv->drive));
239 g_object_set (G_OBJECT (source),
240 "device", brasero_drive_get_device (self->priv->drive),
241 NULL);
242 }
243
244
245 typedef enum {
246 _GST_PLAY_FLAG_VIDEO = (1 << 0),
247 _GST_PLAY_FLAG_AUDIO = (1 << 1),
248 _GST_PLAY_FLAG_TEXT = (1 << 2),
249 _GST_PLAY_FLAG_VIS = (1 << 3),
250 _GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
251 _GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
252 _GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
253 _GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
254 _GST_PLAY_FLAG_BUFFERING = (1 << 8),
255 _GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
256 _GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
257 } _GstPlayFlags;
258
259
260 static gboolean
create_pipeline(GooPlayer * self)261 create_pipeline (GooPlayer *self)
262 {
263 GstElement *audio_sink;
264 GstElement *vis_plugin;
265 _GstPlayFlags flags;
266 GstBus *bus;
267
268 if (self->priv->pipeline != NULL)
269 return TRUE;
270
271 self->priv->pipeline = gst_element_factory_make ("playbin", "playbin");
272 if (self->priv->pipeline == NULL)
273 return FALSE;
274
275 audio_sink = gst_element_factory_make ("autoaudiosink", "audiosink");
276 vis_plugin = NULL; /*gst_element_factory_make ("monoscope", "visplugin");*/
277
278 flags = _GST_PLAY_FLAG_AUDIO;
279 if (vis_plugin != NULL)
280 flags |= _GST_PLAY_FLAG_VIS;
281
282 g_object_set (self->priv->pipeline,
283 "audio-sink", audio_sink,
284 "vis-plugin", vis_plugin,
285 "flags", flags,
286 "uri", "cdda://",
287 "volume", PIPELINE_VOLUME (self->priv->volume_value),
288 "buffer-duration", (guint64) 10 * GST_SECOND,
289 NULL);
290
291 g_signal_connect (self->priv->pipeline,
292 "source-setup",
293 G_CALLBACK (pipeline_source_setup_cb),
294 self);
295
296 bus = gst_element_get_bus (self->priv->pipeline);
297 gst_bus_add_signal_watch (bus);
298
299 g_signal_connect (bus,
300 "message::eos",
301 G_CALLBACK (pipeline_eos_cb),
302 self);
303
304 return TRUE;
305 }
306
307
308 static void
goo_player_empty_list(GooPlayer * player)309 goo_player_empty_list (GooPlayer *player)
310 {
311 album_info_unref (player->priv->album);
312 player->priv->album = album_info_new ();
313 player->priv->current_track = NULL;
314 player->priv->current_track_n = -1;
315 player->priv->next_track_n = -1;
316 }
317
318
319 static void
goo_player_set_state(GooPlayer * self,GooPlayerState state,gboolean notify)320 goo_player_set_state (GooPlayer *self,
321 GooPlayerState state,
322 gboolean notify)
323 {
324 self->priv->state = state;
325 if (notify)
326 g_signal_emit (G_OBJECT (self),
327 goo_player_signals[STATE_CHANGED],
328 0,
329 NULL);
330 }
331
332
333 static void
goo_player_class_init(GooPlayerClass * class)334 goo_player_class_init (GooPlayerClass *class)
335 {
336 GObjectClass *gobject_class;
337
338 gobject_class = G_OBJECT_CLASS (class);
339 gobject_class->finalize = goo_player_finalize;
340
341 goo_player_signals[START] =
342 g_signal_new ("start",
343 G_TYPE_FROM_CLASS (class),
344 G_SIGNAL_RUN_LAST,
345 G_STRUCT_OFFSET (GooPlayerClass, start),
346 NULL, NULL,
347 g_cclosure_marshal_VOID__INT,
348 G_TYPE_NONE,
349 1,
350 G_TYPE_INT);
351 goo_player_signals[DONE] =
352 g_signal_new ("done",
353 G_TYPE_FROM_CLASS (class),
354 G_SIGNAL_RUN_LAST,
355 G_STRUCT_OFFSET (GooPlayerClass, done),
356 NULL, NULL,
357 goo_marshal_VOID__INT_BOXED,
358 G_TYPE_NONE, 2,
359 G_TYPE_INT,
360 G_TYPE_ERROR);
361 goo_player_signals[PROGRESS] =
362 g_signal_new ("progress",
363 G_TYPE_FROM_CLASS (class),
364 G_SIGNAL_RUN_LAST,
365 G_STRUCT_OFFSET (GooPlayerClass, progress),
366 NULL, NULL,
367 g_cclosure_marshal_VOID__DOUBLE,
368 G_TYPE_NONE, 1,
369 G_TYPE_DOUBLE);
370 goo_player_signals[MESSAGE] =
371 g_signal_new ("message",
372 G_TYPE_FROM_CLASS (class),
373 G_SIGNAL_RUN_LAST,
374 G_STRUCT_OFFSET (GooPlayerClass, message),
375 NULL, NULL,
376 g_cclosure_marshal_VOID__STRING,
377 G_TYPE_NONE, 1,
378 G_TYPE_STRING);
379 goo_player_signals[STATE_CHANGED] =
380 g_signal_new ("state_changed",
381 G_TYPE_FROM_CLASS (class),
382 G_SIGNAL_RUN_LAST,
383 G_STRUCT_OFFSET (GooPlayerClass, state_changed),
384 NULL, NULL,
385 g_cclosure_marshal_VOID__VOID,
386 G_TYPE_NONE, 0);
387 }
388
389
390 static void
goo_player_init(GooPlayer * self)391 goo_player_init (GooPlayer *self)
392 {
393 self->priv = goo_player_get_instance_private (self);
394 self->priv->state = GOO_PLAYER_STATE_NO_DISC;
395 self->priv->action = GOO_PLAYER_ACTION_NONE;
396 self->priv->is_busy = FALSE;
397 self->priv->hibernate = FALSE;
398 g_mutex_init (&self->priv->data_mutex);
399 self->priv->exiting = FALSE,
400 self->priv->discid = NULL;
401 self->priv->album = album_info_new ();
402 self->priv->current_track_n = -1;
403 self->priv->next_track_n = -1;
404 self->priv->volume_value = 1.0;
405 self->priv->update_progress_id = 0;
406 self->priv->albums = NULL;
407 self->priv->cancellable = g_cancellable_new ();
408 }
409
410
411 static void
goo_player_finalize(GObject * object)412 goo_player_finalize (GObject *object)
413 {
414 GooPlayer *self;
415
416 g_return_if_fail (object != NULL);
417 g_return_if_fail (GOO_IS_PLAYER (object));
418
419 self = GOO_PLAYER (object);
420
421 g_mutex_lock (&self->priv->data_mutex);
422 self->priv->exiting = TRUE;
423 g_mutex_unlock (&self->priv->data_mutex);
424
425 if (self->priv->medium_added_event != 0)
426 g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
427 if (self->priv->medium_removed_event != 0)
428 g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
429 g_object_unref (self->priv->drive);
430
431 if (self->priv->update_progress_id != 0) {
432 g_source_remove (self->priv->update_progress_id);
433 self->priv->update_progress_id = 0;
434 }
435
436 destroy_pipeline (self);
437 g_mutex_clear (&self->priv->data_mutex);
438 g_free (self->priv->discid);
439 album_info_unref (self->priv->album);
440 g_object_unref (self->priv->cancellable);
441
442 G_OBJECT_CLASS (goo_player_parent_class)->finalize (object);
443 }
444
445
446 static void
drive_medium_added_cb(BraseroDrive * drive,BraseroMedium * medium,gpointer user_data)447 drive_medium_added_cb (BraseroDrive *drive,
448 BraseroMedium *medium,
449 gpointer user_data)
450 {
451 GooPlayer *self = user_data;
452
453 action_done (self, GOO_PLAYER_ACTION_MEDIUM_ADDED);
454 goo_player_update (self);
455 }
456
457
458 static void
drive_medium_removed_cb(BraseroDrive * drive,BraseroMedium * medium,gpointer user_data)459 drive_medium_removed_cb (BraseroDrive *drive,
460 BraseroMedium *medium,
461 gpointer user_data)
462 {
463 GooPlayer *self = user_data;
464
465 action_done (self, GOO_PLAYER_ACTION_MEDIUM_REMOVED);
466 goo_player_update (self);
467 }
468
469
470 GooPlayer *
goo_player_new(BraseroDrive * drive)471 goo_player_new (BraseroDrive *drive)
472 {
473 GooPlayer *self;
474
475 self = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
476 goo_player_set_drive (self, drive);
477
478 return self;
479 }
480
481
482 static void
notify_action_start(GooPlayer * self)483 notify_action_start (GooPlayer *self)
484 {
485 g_signal_emit (G_OBJECT (self),
486 goo_player_signals[START],
487 0,
488 self->priv->action,
489 NULL);
490 }
491
492
493 static void
goo_player_set_is_busy(GooPlayer * self,gboolean is_busy)494 goo_player_set_is_busy (GooPlayer *self,
495 gboolean is_busy)
496 {
497 self->priv->is_busy = is_busy;
498 }
499
500
501 /* -- goo_player_list -- */
502
503
504 void
goo_player_set_album(GooPlayer * self,AlbumInfo * album)505 goo_player_set_album (GooPlayer *self,
506 AlbumInfo *album)
507 {
508 if (self->priv->album == NULL)
509 return;
510 album_info_copy_metadata (self->priv->album, album);
511 album_info_save_to_cache (self->priv->album, self->priv->discid);
512 action_done (self, GOO_PLAYER_ACTION_METADATA);
513 }
514
515
516 gboolean
goo_player_is_audio_cd(GooPlayer * self)517 goo_player_is_audio_cd (GooPlayer *self)
518 {
519 return self->priv->audio_cd;
520 }
521
522
523 void
goo_player_hibernate(GooPlayer * self,gboolean hibernate)524 goo_player_hibernate (GooPlayer *self,
525 gboolean hibernate)
526 {
527 self->priv->hibernate = hibernate;
528 }
529
530
531 gboolean
goo_player_is_hibernate(GooPlayer * self)532 goo_player_is_hibernate (GooPlayer *self)
533 {
534 return self->priv->hibernate;
535 }
536
537
538 void
goo_player_update(GooPlayer * self)539 goo_player_update (GooPlayer *self)
540 {
541 BraseroMedium *medium;
542
543 if (self->priv->hibernate)
544 return;
545
546 self->priv->audio_cd = FALSE;
547
548 medium = brasero_drive_get_medium (self->priv->drive);
549 if (medium == NULL) {
550 goo_player_stop (self);
551 goo_player_set_state (self, GOO_PLAYER_STATE_NO_DISC, TRUE);
552 goo_player_empty_list (self);
553 action_done (self, GOO_PLAYER_ACTION_LIST);
554 }
555 else if ((BRASERO_MEDIUM_IS (brasero_medium_get_status (medium), BRASERO_MEDIUM_CD | BRASERO_MEDIUM_HAS_AUDIO))) {
556 self->priv->audio_cd = TRUE;
557 goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
558 goo_player_list (self);
559 }
560 else {
561 goo_player_stop (self);
562 goo_player_set_state (self, GOO_PLAYER_STATE_DATA_DISC, TRUE);
563 goo_player_empty_list (self);
564 action_done (self, GOO_PLAYER_ACTION_LIST);
565 }
566 }
567
568
569 static void
album_info_from_disc_id_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)570 album_info_from_disc_id_ready_cb (GObject *source_object,
571 GAsyncResult *result,
572 gpointer user_data)
573 {
574 GooPlayer *player = user_data;
575 GList *albums;
576 GError *error = NULL;
577
578 albums = metadata_get_album_info_from_disc_id_finish (result, &error);
579 if (albums != NULL) {
580 AlbumInfo *first_album = albums->data;
581
582 /* FIXME: ask the user which album to use if the query
583 * returned more than one album. */
584
585 goo_player_set_album (player, first_album);
586 album_info_save_to_cache (player->priv->album, player->priv->discid);
587
588 album_list_free (albums);
589 }
590 else
591 action_done (player, GOO_PLAYER_ACTION_METADATA);
592 }
593
594
595 static void
get_cd_info_from_device_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)596 get_cd_info_from_device_ready_cb (GObject *source_object,
597 GAsyncResult *result,
598 gpointer user_data)
599 {
600 GooPlayer *player = user_data;
601 AlbumInfo *album = NULL;
602 GError *error = NULL;
603
604 if (metadata_get_cd_info_from_device_finish (result,
605 &player->priv->discid,
606 &album,
607 &error))
608 {
609 album_info_set_tracks (player->priv->album, album->tracks);
610 }
611
612 destroy_pipeline (player);
613 goo_player_set_is_busy (player, FALSE);
614 goo_player_set_state (player, GOO_PLAYER_STATE_STOPPED, TRUE);
615 action_done (player, GOO_PLAYER_ACTION_LIST);
616
617 if (player->priv->discid == NULL)
618 return;
619
620 if (album_info_load_from_cache (player->priv->album, player->priv->discid)) {
621 action_done (player, GOO_PLAYER_ACTION_METADATA);
622 return;
623 }
624
625 action_start (player, GOO_PLAYER_ACTION_METADATA);
626
627 metadata_get_album_info_from_disc_id (player->priv->discid,
628 player->priv->cancellable,
629 album_info_from_disc_id_ready_cb,
630 player);
631
632 album_info_unref (album);
633 }
634
635
636 void
goo_player_list(GooPlayer * player)637 goo_player_list (GooPlayer *player)
638 {
639 if (goo_player_get_is_busy (player))
640 return;
641
642 player->priv->action = GOO_PLAYER_ACTION_LIST;
643 player->priv->state = GOO_PLAYER_STATE_LISTING;
644 notify_action_start (player);
645
646 goo_player_empty_list (player);
647 goo_player_set_is_busy (player, TRUE);
648
649 #if 0
650 create_pipeline (player);
651 if (player->priv->pipeline != NULL)
652 gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
653 #endif
654
655 g_free (player->priv->discid);
656 player->priv->discid = NULL;
657
658 metadata_get_cd_info_from_device (goo_player_get_device (player),
659 player->priv->cancellable,
660 get_cd_info_from_device_ready_cb,
661 player);
662 }
663
664
665 void
goo_player_seek_track(GooPlayer * player,int track_to_play)666 goo_player_seek_track (GooPlayer *player,
667 int track_to_play)
668 {
669 if (goo_player_get_is_busy (player))
670 return;
671
672 player->priv->action = GOO_PLAYER_ACTION_SEEK_SONG;
673 player->priv->state = GOO_PLAYER_STATE_SEEKING;
674 notify_action_start (player);
675
676 if (player->priv->album->n_tracks == 0) {
677 action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
678 return;
679 }
680
681 goo_player_stop (player);
682 if (! create_pipeline (player)) {
683 GError *error = g_error_new (GOO_ERROR, GOO_ERROR_GENERIC, "Could not create the pipeline");
684 action_done_with_error (player, GOO_PLAYER_ACTION_SEEK_SONG, error);
685 return;
686 }
687
688 /* seek to track */
689
690 goo_player_set_state (player, GOO_PLAYER_STATE_SEEKING, TRUE);
691 set_current_track (player, track_to_play);
692 action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
693 goo_player_play (player);
694 }
695
696
697 int
goo_player_get_current_track(GooPlayer * player)698 goo_player_get_current_track (GooPlayer *player)
699 {
700 return player->priv->current_track_n;
701 }
702
703
704 void
goo_player_set_next_track(GooPlayer * self,int next_track_to_play)705 goo_player_set_next_track (GooPlayer *self,
706 int next_track_to_play)
707 {
708 self->priv->next_track_n = next_track_to_play;
709 }
710
711
712 void
goo_player_skip_to(GooPlayer * player,guint seconds)713 goo_player_skip_to (GooPlayer *player,
714 guint seconds)
715 {
716 GstState state;
717
718 if (goo_player_get_is_busy (player))
719 return;
720
721 if (player->priv->pipeline == NULL)
722 return;
723
724 gst_element_get_state (player->priv->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
725 gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
726 gst_element_seek_simple (player->priv->pipeline,
727 GST_FORMAT_TIME,
728 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
729 G_GINT64_CONSTANT (1000000000) * seconds);
730 gst_element_get_state (player->priv->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
731 gst_element_set_state (player->priv->pipeline, (state == GST_STATE_PLAYING) ? GST_STATE_PLAYING : GST_STATE_PAUSED);
732 }
733
734
735 void
goo_player_set_drive(GooPlayer * self,BraseroDrive * drive)736 goo_player_set_drive (GooPlayer *self,
737 BraseroDrive *drive)
738 {
739 if (self->priv->drive == drive)
740 return;
741
742 if (self->priv->drive != NULL) {
743 if (self->priv->medium_added_event != 0) {
744 g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
745 self->priv->medium_added_event = 0;
746 }
747 if (self->priv->medium_removed_event != 0) {
748 g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
749 self->priv->medium_removed_event = 0;
750 }
751 g_object_unref (self->priv->drive);
752 }
753
754 self->priv->drive = _g_object_ref (drive);
755 if (self->priv->drive == NULL)
756 return;
757
758 self->priv->medium_added_event =
759 g_signal_connect (self->priv->drive,
760 "medium-added",
761 G_CALLBACK (drive_medium_added_cb),
762 self);
763 self->priv->medium_removed_event =
764 g_signal_connect (self->priv->drive,
765 "medium-removed",
766 G_CALLBACK (drive_medium_removed_cb),
767 self);
768 }
769
770
771 BraseroDrive *
goo_player_get_drive(GooPlayer * self)772 goo_player_get_drive (GooPlayer *self)
773 {
774 return self->priv->drive;
775 }
776
777
778 const char *
goo_player_get_device(GooPlayer * self)779 goo_player_get_device (GooPlayer *self)
780 {
781 return brasero_drive_get_device (self->priv->drive);
782 }
783
784
785 static gboolean
update_progress_cb(gpointer user_data)786 update_progress_cb (gpointer user_data)
787 {
788 GooPlayer *self = user_data;
789 gint64 current_time = 0;
790
791 if (self->priv->update_progress_id != 0) {
792 g_source_remove (self->priv->update_progress_id);
793 self->priv->update_progress_id = 0;
794 }
795
796 if (self->priv->current_track == NULL)
797 return FALSE;
798
799 if (gst_element_query_position (self->priv->pipeline,
800 GST_FORMAT_TIME,
801 ¤t_time))
802 {
803 g_signal_emit_by_name (G_OBJECT (self),
804 "progress",
805 (double) current_time / self->priv->current_track->time,
806 NULL);
807 }
808
809 self->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, user_data);
810
811 return FALSE;
812 }
813
814
815 void
goo_player_play(GooPlayer * player)816 goo_player_play (GooPlayer *player)
817 {
818 if (goo_player_get_is_busy (player))
819 return;
820 if (player->priv->state == GOO_PLAYER_STATE_PLAYING)
821 return;
822
823 player->priv->action = GOO_PLAYER_ACTION_PLAY;
824 notify_action_start (player);
825
826 if (player->priv->album->n_tracks == 0) {
827 action_done (player, GOO_PLAYER_ACTION_PLAY);
828 return;
829 }
830
831 #if 0
832 if (! ((player->priv->pipeline != NULL)
833 && ((goo_player_get_state (player) == GOO_PLAYER_STATE_PAUSED)
834 || (goo_player_get_state (player) == GOO_PLAYER_STATE_SEEKING))))
835 {
836 create_pipeline (player);
837 }
838 #endif
839
840 if (! create_pipeline (player))
841 return;
842
843 /*g_object_set (G_OBJECT (player->priv->pipeline), "volume", goo_player_get_audio_volume (player), NULL);*/
844
845 gst_element_set_state (player->priv->pipeline, GST_STATE_PLAYING);
846 goo_player_set_state (player, GOO_PLAYER_STATE_PLAYING, TRUE);
847
848 player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, player);
849 }
850
851
852 void
goo_player_pause(GooPlayer * player)853 goo_player_pause (GooPlayer *player)
854 {
855 if (goo_player_get_is_busy (player))
856 return;
857 if (player->priv->state == GOO_PLAYER_STATE_PAUSED)
858 return;
859 if (player->priv->pipeline == NULL)
860 return;
861
862 if (player->priv->update_progress_id != 0) {
863 g_source_remove (player->priv->update_progress_id);
864 player->priv->update_progress_id = 0;
865 }
866
867 gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
868 goo_player_set_state (GOO_PLAYER (player), GOO_PLAYER_STATE_PAUSED, TRUE);
869
870 action_done (player, GOO_PLAYER_ACTION_PAUSE);
871 }
872
873
874 void
goo_player_stop(GooPlayer * player)875 goo_player_stop (GooPlayer *player)
876 {
877 if (goo_player_get_is_busy (player))
878 return;
879 if (player->priv->state == GOO_PLAYER_STATE_STOPPED)
880 return;
881 if (player->priv->pipeline == NULL)
882 return;
883
884 destroy_pipeline (player);
885 goo_player_set_state (GOO_PLAYER (player), GOO_PLAYER_STATE_STOPPED, TRUE);
886
887 action_done (player, GOO_PLAYER_ACTION_STOP);
888 }
889
890
891
892 static void
eject_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)893 eject_ready_cb (GObject *source_object,
894 GAsyncResult *result,
895 gpointer user_data)
896 {
897 GooPlayer *self = user_data;
898 GError *error = NULL;
899
900 if (! g_drive_eject_with_operation_finish (G_DRIVE (source_object), result, &error))
901 g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_MEDIUM_REMOVED, error);
902 else
903 g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_MEDIUM_REMOVED, NULL);
904
905 goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
906 }
907
908
909 void
goo_player_eject(GooPlayer * self)910 goo_player_eject (GooPlayer *self)
911 {
912 GDrive *gdrive;
913
914 if (self->priv->hibernate)
915 return;
916
917 g_signal_emit_by_name (G_OBJECT (self), "start", GOO_PLAYER_ACTION_MEDIUM_REMOVED);
918
919 gdrive = brasero_drive_get_gdrive (self->priv->drive);
920 g_drive_eject_with_operation (gdrive,
921 G_MOUNT_UNMOUNT_NONE,
922 NULL,
923 NULL,
924 eject_ready_cb,
925 self);
926
927 g_object_unref (gdrive);
928 }
929
930
931 GooPlayerAction
goo_player_get_action(GooPlayer * player)932 goo_player_get_action (GooPlayer *player)
933 {
934 return player->priv->action;
935 }
936
937
938 GooPlayerState
goo_player_get_state(GooPlayer * player)939 goo_player_get_state (GooPlayer *player)
940 {
941 return player->priv->state;
942 }
943
944
945 double
goo_player_get_audio_volume(GooPlayer * player)946 goo_player_get_audio_volume (GooPlayer *player)
947 {
948 return player->priv->volume_value;
949 }
950
951
952 void
goo_player_set_audio_volume(GooPlayer * player,double vol)953 goo_player_set_audio_volume (GooPlayer *player,
954 double vol)
955 {
956 if (goo_player_get_is_busy (player))
957 return;
958
959 player->priv->volume_value = vol;
960 if (player->priv->pipeline != NULL)
961 g_object_set (G_OBJECT (player->priv->pipeline), "volume", PIPELINE_VOLUME (player->priv->volume_value), NULL);
962 }
963
964
965 gboolean
goo_player_get_is_busy(GooPlayer * self)966 goo_player_get_is_busy (GooPlayer *self)
967 {
968 return self->priv->is_busy || self->priv->hibernate;
969 }
970
971
972 const char *
goo_player_get_discid(GooPlayer * player)973 goo_player_get_discid (GooPlayer *player)
974 {
975 return player->priv->discid;
976 }
977
978
979 AlbumInfo *
goo_player_get_album(GooPlayer * player)980 goo_player_get_album (GooPlayer *player)
981 {
982 return player->priv->album;
983 }
984