/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Goo
*
* Copyright (C) 2004, 2007 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
#include
#include
#include
#include
#include
#include "goo-error.h"
#include "goo-player.h"
#include "goo-marshal.h"
#include "glib-utils.h"
#include "gth-user-dir.h"
#include "main.h"
#include "metadata.h"
#define TOC_OFFSET 150
#define SECTORS_PER_SEC 75
#define POLL_TIMEOUT 1000
#define REFRESH_RATE 5
#define PROGRESS_DELAY 400
#define QUEUE_SIZE 16384U /*131072U*/
#define PIPELINE_VOLUME(x) ((x) / 100.0)
struct _GooPlayerPrivate {
BraseroDrive *drive;
gulong medium_added_event;
gulong medium_removed_event;
GooPlayerState state;
GooPlayerAction action;
double volume_value;
gboolean is_busy;
gboolean audio_cd;
gboolean hibernate;
GstElement *pipeline;
char *discid;
AlbumInfo *album;
TrackInfo *current_track;
int current_track_n;
int next_track_n;
guint update_state_id;
guint update_progress_id;
GMutex data_mutex;
gboolean exiting;
GCancellable *cancellable;
GList *albums;
};
enum {
START,
DONE,
PROGRESS,
MESSAGE,
STATE_CHANGED,
LAST_SIGNAL
};
static guint goo_player_signals[LAST_SIGNAL] = { 0 };
static void goo_player_finalize (GObject *object);
G_DEFINE_TYPE_WITH_CODE (GooPlayer, goo_player, G_TYPE_OBJECT,
G_ADD_PRIVATE (GooPlayer))
static void
destroy_pipeline (GooPlayer *player)
{
if (player->priv->pipeline != NULL) {
gst_element_set_state (player->priv->pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (player->priv->pipeline));
player->priv->pipeline = NULL;
}
if (player->priv->update_progress_id != 0) {
g_source_remove (player->priv->update_progress_id);
player->priv->update_progress_id = 0;
}
}
static void
action_start (GooPlayer *self,
GooPlayerAction action)
{
g_signal_emit (G_OBJECT (self),
goo_player_signals[START],
0,
action,
NULL);
}
static void
action_done (GooPlayer *self,
GooPlayerAction action)
{
g_signal_emit (G_OBJECT (self),
goo_player_signals[DONE],
0,
action,
NULL);
}
static void
action_done_with_error (GooPlayer *self,
GooPlayerAction action,
GError *error)
{
g_signal_emit_by_name (G_OBJECT (self), "done", action, error);
g_error_free (error);
}
static TrackInfo*
get_track (GooPlayer *player,
guint n)
{
GList *scan;
for (scan = player->priv->album->tracks; scan; scan = scan->next) {
TrackInfo *track = scan->data;
if (track->number == n)
return track;
}
return NULL;
}
static gboolean
set_current_track (GooPlayer *player,
int track_to_play)
{
GstStateChangeReturn ret;
if (track_to_play == -1)
return FALSE;
player->priv->current_track_n = CLAMP (track_to_play, 0, player->priv->album->n_tracks - 1);
player->priv->current_track = get_track (player, player->priv->current_track_n);
g_return_val_if_fail (player->priv->current_track != NULL, FALSE);
debug (DEBUG_INFO, "seek to track %d\n", player->priv->current_track_n);
ret = gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
while (ret == GST_STATE_CHANGE_ASYNC)
ret = gst_element_get_state (player->priv->pipeline, NULL, NULL, GST_MSECOND);
if (ret != GST_STATE_CHANGE_SUCCESS)
return FALSE;
return gst_element_seek (player->priv->pipeline,
1.0,
gst_format_get_by_nick ("track"),
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
GST_SEEK_TYPE_SET,
track_to_play,
GST_SEEK_TYPE_NONE,
-1);
}
static gboolean
player_done_cb (gpointer user_data)
{
GooPlayer *self = user_data;
if (set_current_track (self, self->priv->next_track_n)) {
gst_element_set_state (self->priv->pipeline, GST_STATE_PLAYING);
action_done (self, GOO_PLAYER_ACTION_SEEK_SONG);
action_done (self, GOO_PLAYER_ACTION_STARTED_NEXT);
}
else {
if (self->priv->update_progress_id != 0) {
g_source_remove (self->priv->update_progress_id);
self->priv->update_progress_id = 0;
}
action_done (self, GOO_PLAYER_ACTION_PLAY);
}
return FALSE;
}
static void
pipeline_eos_cb (GstBus *bus,
GstMessage *message,
gpointer user_data)
{
g_idle_add (player_done_cb, user_data);
}
static void
pipeline_source_setup_cb (GstElement *playbin,
GstElement *source,
gpointer user_data)
{
GooPlayer *self = user_data;
if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed") != NULL) {
g_object_set (G_OBJECT (source),
"read-speed", 2,
NULL);
}
/* Disable paranoia in playback mode */
if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
g_object_set (source, "paranoia-mode", 0, NULL);
debug (DEBUG_INFO, "DEVICE: %s\n", brasero_drive_get_device (self->priv->drive));
g_object_set (G_OBJECT (source),
"device", brasero_drive_get_device (self->priv->drive),
NULL);
}
typedef enum {
_GST_PLAY_FLAG_VIDEO = (1 << 0),
_GST_PLAY_FLAG_AUDIO = (1 << 1),
_GST_PLAY_FLAG_TEXT = (1 << 2),
_GST_PLAY_FLAG_VIS = (1 << 3),
_GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4),
_GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5),
_GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6),
_GST_PLAY_FLAG_DOWNLOAD = (1 << 7),
_GST_PLAY_FLAG_BUFFERING = (1 << 8),
_GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
_GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
} _GstPlayFlags;
static gboolean
create_pipeline (GooPlayer *self)
{
GstElement *audio_sink;
GstElement *vis_plugin;
_GstPlayFlags flags;
GstBus *bus;
if (self->priv->pipeline != NULL)
return TRUE;
self->priv->pipeline = gst_element_factory_make ("playbin", "playbin");
if (self->priv->pipeline == NULL)
return FALSE;
audio_sink = gst_element_factory_make ("autoaudiosink", "audiosink");
vis_plugin = NULL; /*gst_element_factory_make ("monoscope", "visplugin");*/
flags = _GST_PLAY_FLAG_AUDIO;
if (vis_plugin != NULL)
flags |= _GST_PLAY_FLAG_VIS;
g_object_set (self->priv->pipeline,
"audio-sink", audio_sink,
"vis-plugin", vis_plugin,
"flags", flags,
"uri", "cdda://",
"volume", PIPELINE_VOLUME (self->priv->volume_value),
"buffer-duration", (guint64) 10 * GST_SECOND,
NULL);
g_signal_connect (self->priv->pipeline,
"source-setup",
G_CALLBACK (pipeline_source_setup_cb),
self);
bus = gst_element_get_bus (self->priv->pipeline);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus,
"message::eos",
G_CALLBACK (pipeline_eos_cb),
self);
return TRUE;
}
static void
goo_player_empty_list (GooPlayer *player)
{
album_info_unref (player->priv->album);
player->priv->album = album_info_new ();
player->priv->current_track = NULL;
player->priv->current_track_n = -1;
player->priv->next_track_n = -1;
}
static void
goo_player_set_state (GooPlayer *self,
GooPlayerState state,
gboolean notify)
{
self->priv->state = state;
if (notify)
g_signal_emit (G_OBJECT (self),
goo_player_signals[STATE_CHANGED],
0,
NULL);
}
static void
goo_player_class_init (GooPlayerClass *class)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = goo_player_finalize;
goo_player_signals[START] =
g_signal_new ("start",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, start),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE,
1,
G_TYPE_INT);
goo_player_signals[DONE] =
g_signal_new ("done",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, done),
NULL, NULL,
goo_marshal_VOID__INT_BOXED,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_ERROR);
goo_player_signals[PROGRESS] =
g_signal_new ("progress",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, progress),
NULL, NULL,
g_cclosure_marshal_VOID__DOUBLE,
G_TYPE_NONE, 1,
G_TYPE_DOUBLE);
goo_player_signals[MESSAGE] =
g_signal_new ("message",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, message),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
goo_player_signals[STATE_CHANGED] =
g_signal_new ("state_changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GooPlayerClass, state_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
goo_player_init (GooPlayer *self)
{
self->priv = goo_player_get_instance_private (self);
self->priv->state = GOO_PLAYER_STATE_NO_DISC;
self->priv->action = GOO_PLAYER_ACTION_NONE;
self->priv->is_busy = FALSE;
self->priv->hibernate = FALSE;
g_mutex_init (&self->priv->data_mutex);
self->priv->exiting = FALSE,
self->priv->discid = NULL;
self->priv->album = album_info_new ();
self->priv->current_track_n = -1;
self->priv->next_track_n = -1;
self->priv->volume_value = 1.0;
self->priv->update_progress_id = 0;
self->priv->albums = NULL;
self->priv->cancellable = g_cancellable_new ();
}
static void
goo_player_finalize (GObject *object)
{
GooPlayer *self;
g_return_if_fail (object != NULL);
g_return_if_fail (GOO_IS_PLAYER (object));
self = GOO_PLAYER (object);
g_mutex_lock (&self->priv->data_mutex);
self->priv->exiting = TRUE;
g_mutex_unlock (&self->priv->data_mutex);
if (self->priv->medium_added_event != 0)
g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
if (self->priv->medium_removed_event != 0)
g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
g_object_unref (self->priv->drive);
if (self->priv->update_progress_id != 0) {
g_source_remove (self->priv->update_progress_id);
self->priv->update_progress_id = 0;
}
destroy_pipeline (self);
g_mutex_clear (&self->priv->data_mutex);
g_free (self->priv->discid);
album_info_unref (self->priv->album);
g_object_unref (self->priv->cancellable);
G_OBJECT_CLASS (goo_player_parent_class)->finalize (object);
}
static void
drive_medium_added_cb (BraseroDrive *drive,
BraseroMedium *medium,
gpointer user_data)
{
GooPlayer *self = user_data;
action_done (self, GOO_PLAYER_ACTION_MEDIUM_ADDED);
goo_player_update (self);
}
static void
drive_medium_removed_cb (BraseroDrive *drive,
BraseroMedium *medium,
gpointer user_data)
{
GooPlayer *self = user_data;
action_done (self, GOO_PLAYER_ACTION_MEDIUM_REMOVED);
goo_player_update (self);
}
GooPlayer *
goo_player_new (BraseroDrive *drive)
{
GooPlayer *self;
self = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
goo_player_set_drive (self, drive);
return self;
}
static void
notify_action_start (GooPlayer *self)
{
g_signal_emit (G_OBJECT (self),
goo_player_signals[START],
0,
self->priv->action,
NULL);
}
static void
goo_player_set_is_busy (GooPlayer *self,
gboolean is_busy)
{
self->priv->is_busy = is_busy;
}
/* -- goo_player_list -- */
void
goo_player_set_album (GooPlayer *self,
AlbumInfo *album)
{
if (self->priv->album == NULL)
return;
album_info_copy_metadata (self->priv->album, album);
album_info_save_to_cache (self->priv->album, self->priv->discid);
action_done (self, GOO_PLAYER_ACTION_METADATA);
}
gboolean
goo_player_is_audio_cd (GooPlayer *self)
{
return self->priv->audio_cd;
}
void
goo_player_hibernate (GooPlayer *self,
gboolean hibernate)
{
self->priv->hibernate = hibernate;
}
gboolean
goo_player_is_hibernate (GooPlayer *self)
{
return self->priv->hibernate;
}
void
goo_player_update (GooPlayer *self)
{
BraseroMedium *medium;
if (self->priv->hibernate)
return;
self->priv->audio_cd = FALSE;
medium = brasero_drive_get_medium (self->priv->drive);
if (medium == NULL) {
goo_player_stop (self);
goo_player_set_state (self, GOO_PLAYER_STATE_NO_DISC, TRUE);
goo_player_empty_list (self);
action_done (self, GOO_PLAYER_ACTION_LIST);
}
else if ((BRASERO_MEDIUM_IS (brasero_medium_get_status (medium), BRASERO_MEDIUM_CD | BRASERO_MEDIUM_HAS_AUDIO))) {
self->priv->audio_cd = TRUE;
goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
goo_player_list (self);
}
else {
goo_player_stop (self);
goo_player_set_state (self, GOO_PLAYER_STATE_DATA_DISC, TRUE);
goo_player_empty_list (self);
action_done (self, GOO_PLAYER_ACTION_LIST);
}
}
static void
album_info_from_disc_id_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GooPlayer *player = user_data;
GList *albums;
GError *error = NULL;
albums = metadata_get_album_info_from_disc_id_finish (result, &error);
if (albums != NULL) {
AlbumInfo *first_album = albums->data;
/* FIXME: ask the user which album to use if the query
* returned more than one album. */
goo_player_set_album (player, first_album);
album_info_save_to_cache (player->priv->album, player->priv->discid);
album_list_free (albums);
}
else
action_done (player, GOO_PLAYER_ACTION_METADATA);
}
static void
get_cd_info_from_device_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GooPlayer *player = user_data;
AlbumInfo *album = NULL;
GError *error = NULL;
if (metadata_get_cd_info_from_device_finish (result,
&player->priv->discid,
&album,
&error))
{
album_info_set_tracks (player->priv->album, album->tracks);
}
destroy_pipeline (player);
goo_player_set_is_busy (player, FALSE);
goo_player_set_state (player, GOO_PLAYER_STATE_STOPPED, TRUE);
action_done (player, GOO_PLAYER_ACTION_LIST);
if (player->priv->discid == NULL)
return;
if (album_info_load_from_cache (player->priv->album, player->priv->discid)) {
action_done (player, GOO_PLAYER_ACTION_METADATA);
return;
}
action_start (player, GOO_PLAYER_ACTION_METADATA);
metadata_get_album_info_from_disc_id (player->priv->discid,
player->priv->cancellable,
album_info_from_disc_id_ready_cb,
player);
album_info_unref (album);
}
void
goo_player_list (GooPlayer *player)
{
if (goo_player_get_is_busy (player))
return;
player->priv->action = GOO_PLAYER_ACTION_LIST;
player->priv->state = GOO_PLAYER_STATE_LISTING;
notify_action_start (player);
goo_player_empty_list (player);
goo_player_set_is_busy (player, TRUE);
#if 0
create_pipeline (player);
if (player->priv->pipeline != NULL)
gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
#endif
g_free (player->priv->discid);
player->priv->discid = NULL;
metadata_get_cd_info_from_device (goo_player_get_device (player),
player->priv->cancellable,
get_cd_info_from_device_ready_cb,
player);
}
void
goo_player_seek_track (GooPlayer *player,
int track_to_play)
{
if (goo_player_get_is_busy (player))
return;
player->priv->action = GOO_PLAYER_ACTION_SEEK_SONG;
player->priv->state = GOO_PLAYER_STATE_SEEKING;
notify_action_start (player);
if (player->priv->album->n_tracks == 0) {
action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
return;
}
goo_player_stop (player);
if (! create_pipeline (player)) {
GError *error = g_error_new (GOO_ERROR, GOO_ERROR_GENERIC, "Could not create the pipeline");
action_done_with_error (player, GOO_PLAYER_ACTION_SEEK_SONG, error);
return;
}
/* seek to track */
goo_player_set_state (player, GOO_PLAYER_STATE_SEEKING, TRUE);
set_current_track (player, track_to_play);
action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
goo_player_play (player);
}
int
goo_player_get_current_track (GooPlayer *player)
{
return player->priv->current_track_n;
}
void
goo_player_set_next_track (GooPlayer *self,
int next_track_to_play)
{
self->priv->next_track_n = next_track_to_play;
}
void
goo_player_skip_to (GooPlayer *player,
guint seconds)
{
GstState state;
if (goo_player_get_is_busy (player))
return;
if (player->priv->pipeline == NULL)
return;
gst_element_get_state (player->priv->pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
gst_element_seek_simple (player->priv->pipeline,
GST_FORMAT_TIME,
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
G_GINT64_CONSTANT (1000000000) * seconds);
gst_element_get_state (player->priv->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
gst_element_set_state (player->priv->pipeline, (state == GST_STATE_PLAYING) ? GST_STATE_PLAYING : GST_STATE_PAUSED);
}
void
goo_player_set_drive (GooPlayer *self,
BraseroDrive *drive)
{
if (self->priv->drive == drive)
return;
if (self->priv->drive != NULL) {
if (self->priv->medium_added_event != 0) {
g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
self->priv->medium_added_event = 0;
}
if (self->priv->medium_removed_event != 0) {
g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
self->priv->medium_removed_event = 0;
}
g_object_unref (self->priv->drive);
}
self->priv->drive = _g_object_ref (drive);
if (self->priv->drive == NULL)
return;
self->priv->medium_added_event =
g_signal_connect (self->priv->drive,
"medium-added",
G_CALLBACK (drive_medium_added_cb),
self);
self->priv->medium_removed_event =
g_signal_connect (self->priv->drive,
"medium-removed",
G_CALLBACK (drive_medium_removed_cb),
self);
}
BraseroDrive *
goo_player_get_drive (GooPlayer *self)
{
return self->priv->drive;
}
const char *
goo_player_get_device (GooPlayer *self)
{
return brasero_drive_get_device (self->priv->drive);
}
static gboolean
update_progress_cb (gpointer user_data)
{
GooPlayer *self = user_data;
gint64 current_time = 0;
if (self->priv->update_progress_id != 0) {
g_source_remove (self->priv->update_progress_id);
self->priv->update_progress_id = 0;
}
if (self->priv->current_track == NULL)
return FALSE;
if (gst_element_query_position (self->priv->pipeline,
GST_FORMAT_TIME,
¤t_time))
{
g_signal_emit_by_name (G_OBJECT (self),
"progress",
(double) current_time / self->priv->current_track->time,
NULL);
}
self->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, user_data);
return FALSE;
}
void
goo_player_play (GooPlayer *player)
{
if (goo_player_get_is_busy (player))
return;
if (player->priv->state == GOO_PLAYER_STATE_PLAYING)
return;
player->priv->action = GOO_PLAYER_ACTION_PLAY;
notify_action_start (player);
if (player->priv->album->n_tracks == 0) {
action_done (player, GOO_PLAYER_ACTION_PLAY);
return;
}
#if 0
if (! ((player->priv->pipeline != NULL)
&& ((goo_player_get_state (player) == GOO_PLAYER_STATE_PAUSED)
|| (goo_player_get_state (player) == GOO_PLAYER_STATE_SEEKING))))
{
create_pipeline (player);
}
#endif
if (! create_pipeline (player))
return;
/*g_object_set (G_OBJECT (player->priv->pipeline), "volume", goo_player_get_audio_volume (player), NULL);*/
gst_element_set_state (player->priv->pipeline, GST_STATE_PLAYING);
goo_player_set_state (player, GOO_PLAYER_STATE_PLAYING, TRUE);
player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, player);
}
void
goo_player_pause (GooPlayer *player)
{
if (goo_player_get_is_busy (player))
return;
if (player->priv->state == GOO_PLAYER_STATE_PAUSED)
return;
if (player->priv->pipeline == NULL)
return;
if (player->priv->update_progress_id != 0) {
g_source_remove (player->priv->update_progress_id);
player->priv->update_progress_id = 0;
}
gst_element_set_state (player->priv->pipeline, GST_STATE_PAUSED);
goo_player_set_state (GOO_PLAYER (player), GOO_PLAYER_STATE_PAUSED, TRUE);
action_done (player, GOO_PLAYER_ACTION_PAUSE);
}
void
goo_player_stop (GooPlayer *player)
{
if (goo_player_get_is_busy (player))
return;
if (player->priv->state == GOO_PLAYER_STATE_STOPPED)
return;
if (player->priv->pipeline == NULL)
return;
destroy_pipeline (player);
goo_player_set_state (GOO_PLAYER (player), GOO_PLAYER_STATE_STOPPED, TRUE);
action_done (player, GOO_PLAYER_ACTION_STOP);
}
static void
eject_ready_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GooPlayer *self = user_data;
GError *error = NULL;
if (! g_drive_eject_with_operation_finish (G_DRIVE (source_object), result, &error))
g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_MEDIUM_REMOVED, error);
else
g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_MEDIUM_REMOVED, NULL);
goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
}
void
goo_player_eject (GooPlayer *self)
{
GDrive *gdrive;
if (self->priv->hibernate)
return;
g_signal_emit_by_name (G_OBJECT (self), "start", GOO_PLAYER_ACTION_MEDIUM_REMOVED);
gdrive = brasero_drive_get_gdrive (self->priv->drive);
g_drive_eject_with_operation (gdrive,
G_MOUNT_UNMOUNT_NONE,
NULL,
NULL,
eject_ready_cb,
self);
g_object_unref (gdrive);
}
GooPlayerAction
goo_player_get_action (GooPlayer *player)
{
return player->priv->action;
}
GooPlayerState
goo_player_get_state (GooPlayer *player)
{
return player->priv->state;
}
double
goo_player_get_audio_volume (GooPlayer *player)
{
return player->priv->volume_value;
}
void
goo_player_set_audio_volume (GooPlayer *player,
double vol)
{
if (goo_player_get_is_busy (player))
return;
player->priv->volume_value = vol;
if (player->priv->pipeline != NULL)
g_object_set (G_OBJECT (player->priv->pipeline), "volume", PIPELINE_VOLUME (player->priv->volume_value), NULL);
}
gboolean
goo_player_get_is_busy (GooPlayer *self)
{
return self->priv->is_busy || self->priv->hibernate;
}
const char *
goo_player_get_discid (GooPlayer *player)
{
return player->priv->discid;
}
AlbumInfo *
goo_player_get_album (GooPlayer *player)
{
return player->priv->album;
}