Random work from the last mounth...
[dictix.git] / libdictix / dix-player.c
blob2b84fdd1dcd721fc8783c1377220d37fbf5664b6
1 /**
2 * Dictix / DixPlayer - dix-player.c
4 * Copyright (C) Martin Blanchard 2011 <tinram@gmx.fr>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <time.h>
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <gio/gio.h>
32 #include <gst/gst.h>
34 #include "dix-player.h"
36 enum {
37 PLAYBACK_STARTED,
38 PLAYBACK_STOPPED,
39 PIPELINE_ERROR,
40 LAST_SIGNAL
43 static guint signals[LAST_SIGNAL] = { 0 };
45 struct _DixPlayerPrivate
47 gboolean now_playing;
48 gboolean stopping;
50 GstBus *bus;
52 GstElement *source;
53 GstElement *decoder;
54 GstElement *converter;
55 GstElement *resampler;
56 GstElement *sink;
59 #define DIX_PLAYER_GET_PRIVATE(obj) \
60 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), DIX_TYPE_PLAYER, DixPlayerPrivate))
62 G_DEFINE_TYPE (DixPlayer, dix_player, GST_TYPE_PIPELINE)
64 static void dix_player_error_cb (GstBus *bus, GstMessage *message, gpointer data);
65 static void dix_player_state_changed_cb (GstBus *bus, GstMessage *message, gpointer data);
66 static void dix_player_eos_cb (GstBus *bus, GstMessage *message, gpointer data);
68 static void
69 dix_player_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
71 DixPlayer *player = (DixPlayer *) object;
72 DixPlayerPrivate *priv = player->priv;
74 switch (prop_id) {
75 default: {
76 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81 static void
82 dix_player_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
84 DixPlayer *player = (DixPlayer *) object;
85 DixPlayerPrivate *priv = player->priv;
87 switch (prop_id) {
88 default: {
89 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
94 static void
95 dix_player_dispose (GObject *object)
97 DixPlayer *player = (DixPlayer *) object;
98 DixPlayerPrivate *priv = player->priv;
100 if (priv->bus != NULL) {
101 gst_object_unref (GST_OBJECT (priv->bus));
103 priv->bus = NULL;
105 G_OBJECT_CLASS (dix_player_parent_class)->dispose (object);
108 static void
109 dix_player_class_init (DixPlayerClass* klass)
111 GObjectClass *gobject_class = (GObjectClass *) klass;
113 gobject_class->set_property = dix_player_set_property;
114 gobject_class->get_property = dix_player_get_property;
115 gobject_class->dispose = dix_player_dispose;
117 signals[PLAYBACK_STARTED] = g_signal_new ("playback-started",
118 G_TYPE_FROM_CLASS (gobject_class),
119 G_SIGNAL_RUN_LAST,
120 G_STRUCT_OFFSET (DixPlayerClass, playback_started),
121 NULL, NULL,
122 g_cclosure_marshal_VOID__VOID,
123 G_TYPE_NONE, 0);
125 signals[PLAYBACK_STOPPED] = g_signal_new ("playback-stopped",
126 G_TYPE_FROM_CLASS (gobject_class),
127 G_SIGNAL_RUN_LAST,
128 G_STRUCT_OFFSET (DixPlayerClass, playback_stopped),
129 NULL, NULL,
130 g_cclosure_marshal_VOID__BOOLEAN,
131 G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
133 /*signals[PIPELINE_ERROR] = g_signal_new ("pipeline-error",
134 G_TYPE_FROM_CLASS (gobject_class),
135 G_SIGNAL_RUN_LAST,
136 G_STRUCT_OFFSET (DixPlayerClass, record_stopped),
137 NULL, NULL,
138 g_cclosure_marshal_VOID__POINTER,
139 1, DIX_ERROR, G_TYPE_NONE, 0);*/
141 g_type_class_add_private (gobject_class, sizeof (DixPlayerPrivate));
144 static void
145 dix_player_init (DixPlayer* player)
147 DixPlayerPrivate *priv = DIX_PLAYER_GET_PRIVATE (player);
148 player->priv = priv;
150 gst_object_set_name (GST_OBJECT(player), "DictixPlayer");
152 priv->now_playing = FALSE;
153 priv->stopping = FALSE;
155 priv->bus = NULL;
157 priv->source = NULL;
158 priv->decoder = NULL;
159 priv->converter = NULL;
160 priv->resampler = NULL;
161 priv->sink = NULL;
164 DixPlayer*
165 dix_player_new ()
167 return g_object_new (DIX_TYPE_PLAYER, NULL);
170 void
171 dix_player_start_playing (DixPlayer *player, gchar* uri, GError **error) /* GError ! */
173 g_return_if_fail (DIX_IS_PLAYER (player));
174 DixPlayerPrivate *priv = player->priv;
176 if (uri != NULL) {
177 if (g_str_has_suffix (uri, ".flac") == FALSE) {
178 g_warning ("Player only handles FLAC audio files.");
180 return;
184 if (G_UNLIKELY (priv->bus == NULL)) {
185 priv->source = gst_element_factory_make ("giosrc", "DictixPlayerSource");
186 priv->decoder = gst_element_factory_make ("flacdec", "DictixPlayerDecoder");
187 priv->converter = gst_element_factory_make ("audioconvert", "DictixPlayerConverter");
188 priv->resampler = gst_element_factory_make ("audioresample", "DictixPlayerResampler");
189 priv->sink = gst_element_factory_make ("pulsesink", "DictixPlayerSink");
191 if (priv->source == NULL || priv->decoder == NULL
192 || priv->converter == NULL || priv->resampler == NULL
193 || priv->sink == NULL) {
194 return;
197 gst_bin_add_many (GST_BIN (player),
198 priv->source,
199 priv->decoder,
200 priv->converter,
201 priv->resampler,
202 priv->sink,
203 NULL);
205 gst_element_link_many (priv->source,
206 priv->decoder,
207 priv->converter,
208 priv->resampler,
209 priv->sink,
210 NULL);
213 priv->bus = gst_pipeline_get_bus (GST_PIPELINE (player));
214 gst_bus_add_signal_watch (priv->bus);
215 g_signal_connect (priv->bus, "message::error",
216 G_CALLBACK (dix_player_error_cb), player);
217 g_signal_connect (priv->bus, "message::state-changed",
218 G_CALLBACK (dix_player_state_changed_cb), player);
219 g_signal_connect (priv->bus, "message::eos",
220 G_CALLBACK (dix_player_eos_cb), player);
223 if (uri != NULL) {
224 if (priv->now_playing == TRUE) {
225 gst_element_set_state (GST_ELEMENT (player), GST_STATE_NULL);
226 gst_element_get_state (GST_ELEMENT (player), NULL, NULL, -1); /*FIXME: Move it async !*/
229 g_object_set (G_OBJECT (priv->source), "location", uri, NULL);
232 gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
235 gint64
236 dix_player_query_duration (DixPlayer *player)
238 g_return_val_if_fail (DIX_IS_PLAYER (player), -1);
239 DixPlayerPrivate *priv = player->priv;
240 GstFormat format = GST_FORMAT_TIME;
241 gint64 duration = 0;
243 if (G_UNLIKELY (priv->now_playing == FALSE)) {
244 return -1;
247 if (gst_element_query_duration (GST_ELEMENT (player), &format, &duration)) {
248 if (format != GST_FORMAT_TIME) {
249 return -1;
252 return duration / GST_SECOND;
255 return -1;
258 gint64
259 dix_player_query_position (DixPlayer *player)
261 g_return_val_if_fail (DIX_IS_PLAYER (player), -1);
262 DixPlayerPrivate *priv = player->priv;
263 GstFormat format = GST_FORMAT_TIME;
264 gint64 position = 0;
266 if (G_UNLIKELY (priv->now_playing == FALSE)) {
267 return -1;
270 if (gst_element_query_position (GST_ELEMENT (player), &format, &position)) {
271 if (format != GST_FORMAT_TIME) {
272 return -1;
275 return position / GST_SECOND;
278 return -1;
281 gdouble
282 dix_player_query_progress (DixPlayer *player, gint64 *position)
284 g_return_val_if_fail (DIX_IS_PLAYER (player), -1.0);
285 DixPlayerPrivate *priv = player->priv;
286 GstFormat format = GST_FORMAT_TIME;
287 gint64 current = 0;
288 gint64 duration = 0;
290 if (G_UNLIKELY (priv->now_playing == FALSE)) {
291 return -1.0;
294 gst_element_query_position (GST_ELEMENT (player), &format, &current);
295 gst_element_query_duration (GST_ELEMENT (player), &format, &duration);
297 if (position != NULL) {
298 *position = current / GST_SECOND;
301 return (gdouble) current / (gdouble) duration;
304 void
305 dix_player_move (DixPlayer *player, gdouble location)
307 g_return_if_fail (DIX_IS_PLAYER (player));
308 DixPlayerPrivate *priv = player->priv;
309 GstFormat format = GST_FORMAT_TIME;
310 gint64 duration = 0;
312 if (G_UNLIKELY (priv->now_playing == FALSE)) {
313 return;
316 gst_element_query_duration (GST_ELEMENT (player), &format, &duration);
318 gst_element_seek (GST_ELEMENT (player),
319 1.0,
320 format,
321 GST_SEEK_FLAG_FLUSH,
322 GST_SEEK_TYPE_SET,
323 (gint64) ((gdouble) duration * location),
324 GST_SEEK_TYPE_NONE,
325 -1);
328 void
329 dix_player_pause (DixPlayer *player)
331 g_return_if_fail (DIX_IS_PLAYER (player));
332 DixPlayerPrivate *priv = player->priv;
334 if (G_UNLIKELY (priv->now_playing == FALSE)) {
335 return;
338 gst_element_set_state (GST_ELEMENT (player), GST_STATE_PAUSED);
341 void
342 dix_player_stop (DixPlayer *player)
344 g_return_if_fail (DIX_IS_PLAYER (player));
345 DixPlayerPrivate *priv = player->priv;
346 GstMessage *message = NULL, *fake;
348 if (G_UNLIKELY (priv->now_playing == FALSE)) {
349 return;
352 priv->stopping = TRUE;
354 gst_element_set_state (priv->source, GST_STATE_NULL);
355 gst_element_get_state (priv->source, NULL, NULL, GST_CLOCK_TIME_NONE);
356 gst_element_set_locked_state (priv->source, TRUE);
358 /* We have to simulate eos now that source isn't streaming anymore: */
359 fake = gst_message_new_eos (GST_OBJECT (priv->source));
360 gst_bus_post (priv->bus, fake);
362 do {
363 message = gst_bus_pop (priv->bus);
365 if (message != NULL) {
366 gst_bus_async_signal_func (priv->bus, message, player);
368 } while (message != NULL);
370 priv->stopping = FALSE;
373 void
374 dix_player_set_volume (DixPlayer *player, gdouble volume)
376 g_return_if_fail (DIX_IS_PLAYER (player));
377 g_return_if_fail (volume >= 0.0);
378 DixPlayerPrivate *priv = player->priv;
380 if (priv->bus != NULL) {
381 g_object_set (G_OBJECT (priv->sink), "volume", volume, NULL);
385 static void
386 dix_player_error_cb (GstBus *bus, GstMessage *message, gpointer data)
388 DixPlayer *player = (DixPlayer *) data;
389 DixPlayerPrivate *priv = player->priv;
390 GError *error;
392 /* error = g_error_new (DIX_ERROR, DIX_ERROR_GST_INTERNAL,*/
393 /* _("Audio engine internal error"));*/
396 static void
397 dix_player_state_changed_cb (GstBus *bus, GstMessage *message, gpointer data)
399 DixPlayer *player = (DixPlayer *) data;
400 DixPlayerPrivate *priv = player->priv;
401 GstState state;
403 gst_message_parse_state_changed (message, NULL, &state, NULL);
405 if (message->src != GST_OBJECT (player)) {
406 return;
409 switch (state) {
410 case GST_STATE_NULL: {
411 break;
412 } case GST_STATE_READY: {
413 break;
414 } case GST_STATE_PLAYING: {
415 priv->now_playing = TRUE;
416 g_signal_emit (player, signals[PLAYBACK_STARTED], 0);
417 break;
418 } case GST_STATE_PAUSED: {
419 if (priv->stopping == FALSE) {
420 g_signal_emit (player, signals[PLAYBACK_STOPPED], 0, FALSE);
422 break;
423 } default: {
424 break;
429 static void
430 dix_player_eos_cb (GstBus *bus, GstMessage *message, gpointer data)
432 DixPlayer *player = (DixPlayer *) data;
433 DixPlayerPrivate *priv = player->priv;
435 gst_element_set_state (GST_ELEMENT (player), GST_STATE_NULL);
436 gst_element_get_state (GST_ELEMENT (player), NULL, NULL, GST_CLOCK_TIME_NONE);
438 priv->now_playing = FALSE;
439 g_signal_emit (player, signals[PLAYBACK_STOPPED], 0, TRUE);
441 gst_element_set_locked_state (priv->source, FALSE);