1 /*
2  * player.c - Simple media player based on GStreamer
3  *
4  * Copyright (C) 2007-2008 Johannes H. Jensen <joh@pseudoberries.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
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.
15  *
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
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  * Authors:
21  * 		Johannes H. Jensen <joh@pseudoberries.com>
22  */
23 
24 #include <gst/gst.h>
25 
26 #include "player.h"
27 
28 /**
29  * Create a new media player.
30  *
31  * @uri				The file to play.
32  * @loop			Wether to loop or not.
33  * @state_callback	An optional #MediaPlayerStateChangeCallback which will be
34  * 					notified when the state of the player changes.
35  * @data			Data for the state_callback
36  * @error_handler	An optional #MediaPlayerErrorHandler which will be notified
37  * 					if an error occurs.
38  * @error_data		Data for the error_handler.
39  */
40 MediaPlayer *
media_player_new(const gchar * uri,gboolean loop,MediaPlayerStateChangeCallback state_callback,gpointer data,MediaPlayerErrorHandler error_handler,gpointer error_data)41 media_player_new (const gchar *uri, gboolean loop,
42 				  MediaPlayerStateChangeCallback state_callback, gpointer data,
43 				  MediaPlayerErrorHandler error_handler, gpointer error_data)
44 {
45 	MediaPlayer *player;
46 	GstElement *audiosink, *videosink;
47 
48 	// Initialize struct
49 	player = g_new (MediaPlayer, 1);
50 
51 	player->loop	 = loop;
52 	player->state	 = MEDIA_PLAYER_STOPPED;
53 	player->watch_id = 0;
54 
55 	player->state_changed 		= state_callback;
56 	player->state_changed_data	= data;
57 	player->error_handler		= error_handler;
58 	player->error_handler_data	= error_data;
59 
60 	// Initialize GStreamer
61 	gst_init (NULL, NULL);
62 
63 	/* Set up player */
64 	player->player	= gst_element_factory_make ("playbin", "player");
65 	audiosink 		= gst_element_factory_make ("autoaudiosink", "player-audiosink");
66 	videosink 		= gst_element_factory_make ("autovideosink", "player-videosink");
67 
68 	if (!player->player || !audiosink || !videosink) {
69 		g_critical ("Could not create player.");
70 		return NULL;
71 	}
72 
73 	// Set uri and sinks
74 	g_object_set (player->player,
75 				  "uri", uri,
76 				  "audio-sink", audiosink,
77 				  "video-sink", videosink,
78 				  NULL);
79 
80 	return player;
81 }
82 
83 /**
84  * Free a media player.
85  */
86 void
media_player_free(MediaPlayer * player)87 media_player_free (MediaPlayer *player)
88 {
89 	g_assert(player);
90 
91 	if (player->player)
92 		gst_object_unref (GST_OBJECT (player->player));
93 
94 	g_free (player);
95 }
96 
97 /**
98  * Set the uri of player.
99  */
100 void
media_player_set_uri(MediaPlayer * player,const gchar * uri)101 media_player_set_uri (MediaPlayer *player, const gchar *uri)
102 {
103 	g_assert(player);
104 
105 	g_object_set (player->player, "uri", uri, NULL);
106 }
107 
108 /**
109  * Get the uri of player.
110  *
111  * Free with g_free()
112  */
113 gchar *
media_player_get_uri(MediaPlayer * player)114 media_player_get_uri (MediaPlayer *player)
115 {
116 	gchar *uri;
117 
118 	g_assert(player);
119 
120 	g_object_get (player->player, "uri", &uri, NULL);
121 
122 	return uri;
123 }
124 
125 /**
126  * Set media player state.
127  */
128 void
media_player_set_state(MediaPlayer * player,MediaPlayerState state)129 media_player_set_state (MediaPlayer *player, MediaPlayerState state)
130 {
131 	g_assert(player);
132 
133 	MediaPlayerState old = player->state;
134 
135 	player->state = state;
136 
137 	// Notify state change handler
138 	if (old != state && player->state_changed)
139 		player->state_changed(player, player->state, player->state_changed_data);
140 }
141 
142 
143 /**
144  * Check for errors & call error handler
145  */
146 static gboolean
media_player_bus_check_errors(MediaPlayer * player,GstMessage * message)147 media_player_bus_check_errors (MediaPlayer *player, GstMessage *message)
148 {
149 //	g_debug ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
150 
151 	switch (GST_MESSAGE_TYPE (message)) {
152 	case GST_MESSAGE_ERROR: {
153 		GError *err;
154 		gchar *debug;
155 
156 		gst_message_parse_error (message, &err, &debug);
157 
158 		if (player->error_handler)
159 			player->error_handler (player, err, player->error_handler_data);
160 
161 		g_error_free (err);
162 		g_free (debug);
163 
164 		return FALSE;
165 		break;
166 	}
167 	default:
168 		break;
169 	}
170 
171 	// No errors
172 	return TRUE;
173 }
174 
175 /**
176  * GST bus callback.
177  */
178 static gboolean
media_player_bus_cb(GstBus * bus,GstMessage * message,MediaPlayer * player)179 media_player_bus_cb (GstBus     *bus,
180                      GstMessage *message,
181                      MediaPlayer *player)
182 {
183     GstState state;
184 //	g_debug ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
185 
186     if (!media_player_bus_check_errors (player, message)) {
187         // There were errors
188         media_player_stop (player);
189 
190         return FALSE;
191     }
192 
193     switch (GST_MESSAGE_TYPE(message))
194     {
195         case GST_MESSAGE_ASYNC_DONE:
196             g_debug("GST_MESSAGE_ASYNC_DONE");
197             gst_element_get_state(player->player, &state, NULL, GST_CLOCK_TIME_NONE);
198             if (state == GST_STATE_PAUSED) {
199                 gst_element_seek (player->player, 1.0, GST_FORMAT_TIME,
200                                   GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT,
201                                   GST_SEEK_TYPE_SET, 0,
202                                   GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
203                 gst_element_set_state (player->player, GST_STATE_PLAYING);
204             }
205             break;
206         case GST_MESSAGE_SEGMENT_DONE:
207             g_debug("GST_MESSAGE_SEGMENT_DONE");
208             // End of segment. Do we loop?
209             if (player->loop) {
210                 // Perform a segment seek to the beginning of the stream
211                 gst_element_seek (player->player, 1.0, GST_FORMAT_TIME,
212                                   GST_SEEK_FLAG_SEGMENT,
213                                   GST_SEEK_TYPE_SET, 0,
214                                   GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
215             } else {
216                 // Perform a normal seek so we reach EOS
217                 gst_element_seek (player->player, 1.0, GST_FORMAT_TIME,
218                                   GST_SEEK_FLAG_NONE,
219                                   GST_SEEK_TYPE_NONE, 0,
220                                   GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
221             }
222 
223             break;
224         case GST_MESSAGE_EOS:
225             g_debug("GST_MESSAGE_EOS");
226             media_player_stop (player);
227             break;
228         default:
229             break;
230     }
231 
232     return TRUE;
233 }
234 
235 /**
236  * Start media player
237  */
238 void
media_player_start(MediaPlayer * player)239 media_player_start (MediaPlayer *player)
240 {
241 	GstBus *bus;
242 
243 	g_assert(player);
244 
245 	// Attach bus watcher
246 	bus = gst_pipeline_get_bus (GST_PIPELINE (player->player));
247 	player->watch_id = gst_bus_add_watch (bus, (GstBusFunc) media_player_bus_cb, player);
248 	gst_object_unref (bus);
249 
250 	gst_element_set_state (player->player, GST_STATE_PAUSED);
251 	media_player_set_state (player, MEDIA_PLAYER_PLAYING);
252 }
253 
254 /**
255  * Stop player
256  */
257 void
media_player_stop(MediaPlayer * player)258 media_player_stop (MediaPlayer *player)
259 {
260 	g_assert(player);
261 
262 	if (player->watch_id) {
263 		g_source_remove (player->watch_id);
264 
265 		player->watch_id = 0;
266 	}
267 
268 	if (player->player != NULL) {
269 		gst_element_set_state (player->player, GST_STATE_NULL);
270 	}
271 
272 	media_player_set_state (player, MEDIA_PLAYER_STOPPED);
273 }
274 
275 /*
276  * }} Media player
277  */
278