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