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 					&current_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