Cleanup.
[jamendo] / src / player.c
1 /*
2  * player.c
3  *
4  *  Created on: 2009-10-22
5  *      Author: marcin
6  */
7
8 #include "player.h"
9 #include <gst/gst.h>
10 #include <hildon/hildon.h>
11 #include "resource_utils.h"
12
13 /****************************************************************************
14  * Simple playing functions
15  */
16 /** TODO: Get rid of global variable pipeline */
17 static GstElement *pipeline = NULL;
18 static GList* tracklist = 0;
19 static GList* tracklist_item = 0;
20
21 GtkWidget* toolbar;
22 GtkWidget* playing_label;
23 guint playing_label_timer = 0;
24 GtkToolButton* play_pause_button;
25 gboolean error=FALSE;
26
27 enum {
28         STOPPED,
29         PAUSED,
30         PLAYING
31 } state;
32
33 enum {
34         CMD_PREV,
35         CMD_PLAY,
36         CMD_PAUSE,
37         CMD_STOP,
38         CMD_NEXT
39 };
40
41 static struct {
42         const gchar* image;
43         const gchar* label;
44         const int cmd;
45         GtkWidget* icon;
46 } toolbar_data[] = {
47                 { "player_prev_light.png", "prev", CMD_PREV, NULL },
48                 { "player_play_light.png", "play", CMD_PLAY, NULL },
49                 { "player_pause_light.png", "pause", CMD_PAUSE, NULL },
50                 { "player_stop_light.png", "stop", CMD_STOP, NULL },
51                 { "player_next_light.png", "next", CMD_NEXT, NULL }
52 };
53
54 void pause_track();
55 void stop_track();
56 void prev_track();
57 void next_track();
58 void update_toolbar();
59
60 static void handle_eos(GstBus * bus, GstMessage * message, gpointer * appdata) {
61         if(!error) {
62                 next_track();
63         }
64 }
65
66 static void handle_error(GstBus * bus, GstMessage * msg, gpointer * appdata) {
67         gchar  *dbg;
68     GError *err;
69
70     error = TRUE;
71
72     gst_message_parse_error (msg, &err, &dbg);
73     g_debug("player: ERROR: %s\nDEBUG: %s",err->message,dbg);
74
75         hildon_banner_show_informationf(NULL,NULL,"Error: %s",err->message);
76 }
77
78 static void handle_buffering(GstBus * bus, GstMessage * msg, gpointer * appdata) {
79         gint percent;
80
81     gst_message_parse_buffering (msg, &percent);
82
83     if(percent!=100) {
84                 gchar* text = g_strdup_printf("Buffering %d%%", percent);
85                 gtk_label_set_text(GTK_LABEL(playing_label),text);
86                 g_free(text);
87     }
88 }
89
90 static void handle_any(GstBus * bus, GstMessage * msg, gchar *name) {
91         g_debug("handle_%s",name);
92         if(g_strcmp0(name,"duration")==0) {
93                 GstFormat format;
94                 gint64 duration;
95                 gst_message_parse_duration(msg, &format, &duration);
96                 g_debug("duration %d: %Ld",format,duration);
97         }
98 }
99
100 static gboolean handle_timer(gpointer* ptr) {
101         if (pipeline) {
102                 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
103                 gboolean res = gst_element_query(pipeline, query);
104                 if (res) {
105                         GstFormat format;
106                         gint64 cur;
107                         gst_query_parse_position(query, &format, &cur);
108                         int sec = cur / GST_SECOND;
109                         gchar *text = g_strdup_printf("%u:%02u:%02u", sec / 60 / 60, (sec / 60) % 60, sec % 60);
110                         gtk_label_set_text(GTK_LABEL(playing_label), text);
111                         g_free(text);
112                 }
113                 gst_query_unref(query);
114         }
115         return TRUE;
116 }
117
118 void play_track(Track* track) {
119         if (pipeline) {
120                 gst_element_set_state(pipeline, GST_STATE_NULL);
121                 g_object_unref(GST_OBJECT(pipeline));
122                 pipeline = NULL;
123         }
124
125         hildon_banner_show_informationf(NULL,NULL,"Playing %s by %s",track->name,track->artist_name);
126
127         if (0) {
128                 GstElement *src, *decoder, *sink;
129                 pipeline = gst_pipeline_new(NULL);
130                 src = gst_element_factory_make("souphttpsrc", NULL);
131                 g_object_set(G_OBJECT(src), "location", track->stream, NULL);
132                 decoder = gst_element_factory_make("decodebin", NULL);
133                 sink = gst_element_factory_make("alsasink", NULL);
134                 gst_bin_add_many(GST_BIN(pipeline), src, decoder, sink, NULL);
135                 gst_element_link_many(src, decoder, sink, NULL);
136         } else {
137                 pipeline = gst_element_factory_make("playbin2", NULL);
138                 g_object_set(G_OBJECT(pipeline), "uri", track->stream, NULL);
139         }
140
141         error = FALSE;
142         /* setup message handling */
143         GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
144         gst_bus_add_signal_watch_full(bus, G_PRIORITY_HIGH);
145         g_signal_connect(bus, "message::error", (GCallback) handle_error, NULL);
146         g_signal_connect(bus, "message::eos", (GCallback) handle_eos, NULL);
147         g_signal_connect(bus, "message::buffering", (GCallback) handle_buffering, NULL);
148         g_signal_connect(bus, "message::duration", (GCallback) handle_any, "duration");
149         gst_object_unref(GST_OBJECT(bus));
150
151         if(!playing_label_timer) {
152                 playing_label_timer = gdk_threads_add_timeout_seconds(1, (GSourceFunc)handle_timer, NULL);
153         }
154
155         g_debug("Playing %s",track->stream);
156         gst_element_set_state(pipeline, GST_STATE_PLAYING);
157
158         state = PLAYING;
159         update_toolbar();
160
161         if (tracklist_item->data != track) {
162                 tracklist_item = g_list_first(tracklist);
163                 while (tracklist_item && tracklist_item->data != track) {
164                         tracklist_item = g_list_next(tracklist_item);
165                 }
166         }
167 }
168
169 void pause_track() {
170         hildon_banner_show_information(NULL,NULL,"Paused");
171
172         gst_element_set_state(pipeline,GST_STATE_PAUSED);
173
174         state = PAUSED;
175         update_toolbar();
176 }
177
178
179 void resume_track() {
180         gst_element_set_state(pipeline, GST_STATE_PLAYING);
181
182         state = PLAYING;
183         update_toolbar();
184 }
185
186 void stop_track() {
187         if (pipeline) {
188                 hildon_banner_show_information(NULL,NULL,"Stopped");
189
190                 gst_element_set_state(pipeline, GST_STATE_NULL);
191                 g_object_unref(GST_OBJECT(pipeline));
192                 pipeline = NULL;
193         }
194
195         state = STOPPED;
196         update_toolbar();
197 }
198
199 void next_track() {
200         if (!tracklist_item) return;
201
202         tracklist_item = g_list_next(tracklist_item);
203         if (!tracklist_item) {
204                 tracklist_item = g_list_first(tracklist);
205         }
206         play_track(tracklist_item->data);
207 }
208
209 void prev_track() {
210         if (!tracklist_item) return;
211
212         tracklist_item = g_list_previous(tracklist_item);
213         if (!tracklist_item) {
214                 tracklist_item = g_list_last(tracklist);
215         }
216         play_track(tracklist_item->data);
217 }
218
219
220 void on_clicked(GtkToolButton *toolbutton, gint cmd) {
221         switch (cmd) {
222         case CMD_PREV:
223                 prev_track();
224                 break;
225         case CMD_PLAY:
226                 switch(state) {
227                 case STOPPED:
228                         if (tracklist_item) {
229                                 play_track(tracklist_item->data);
230                         }
231                         break;
232                 case PAUSED:
233                         resume_track();
234                         break;
235                 case PLAYING:
236                         pause_track();
237                         break;
238                 }
239                 break;
240         case CMD_STOP:
241                 stop_track();
242                 break;
243         case CMD_NEXT:
244                 next_track();
245                 break;
246         }
247 }
248
249 void update_toolbar() {
250         if (state == PLAYING) {
251                 gtk_tool_button_set_icon_widget(play_pause_button, toolbar_data[CMD_PAUSE].icon);
252                 gtk_tool_button_set_label(play_pause_button, toolbar_data[CMD_PAUSE].label);
253                 gtk_widget_show_all(GTK_WIDGET(play_pause_button));
254         }
255         else if (state == PAUSED) {
256                 gtk_tool_button_set_icon_widget(play_pause_button, toolbar_data[CMD_PLAY].icon);
257                 gtk_tool_button_set_label(play_pause_button, toolbar_data[CMD_PLAY].label);
258                 gtk_widget_show_all(GTK_WIDGET(play_pause_button));
259         }
260 }
261
262 GtkWidget* player_toolbar_create() {
263         if (!toolbar) {
264                 GtkToolItem *toolitem;
265                 int i,pos=0;
266
267                 /* Create a toolbar */
268                 toolbar = gtk_toolbar_new();
269
270                 /* Add items to the toolbar */
271                 for (i = 0; i < G_N_ELEMENTS(toolbar_data); i++) {
272                         toolbar_data[i].icon = resource_get_image(toolbar_data[i].image);
273                         g_object_ref(toolbar_data[i].icon);
274
275                         if (i != CMD_PAUSE) {
276                                 toolitem = gtk_tool_button_new(toolbar_data[i].icon, toolbar_data[i].label);
277                                 g_signal_connect (G_OBJECT (toolitem), "clicked", G_CALLBACK (on_clicked), (gpointer) toolbar_data[i].cmd);
278                                 gtk_widget_show_all(GTK_WIDGET(toolitem));
279                                 gtk_toolbar_insert(GTK_TOOLBAR (toolbar), toolitem, pos++);
280                         }
281                         if (i == CMD_PLAY) {
282                                 play_pause_button = GTK_TOOL_BUTTON(toolitem);
283                         }
284                 }
285
286                 toolitem = gtk_separator_tool_item_new();
287                 gtk_tool_item_set_expand(toolitem,TRUE);
288                 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
289                 gtk_toolbar_insert(GTK_TOOLBAR(toolbar),toolitem,pos++);
290
291                 playing_label= gtk_label_new("");
292                 toolitem = gtk_tool_button_new(NULL, NULL);
293                 gtk_widget_set_usize((GtkWidget*)playing_label, 200, -1);
294                 gtk_tool_button_set_label_widget(GTK_TOOL_BUTTON(toolitem), playing_label);
295                 gtk_toolbar_insert(GTK_TOOLBAR(toolbar),toolitem,pos++);
296
297                 gtk_widget_show_all(GTK_WIDGET(toolbar));
298         }
299         update_toolbar();
300         return toolbar;
301 }
302
303 void player_set_track_list(GList* track_list) {
304         g_debug("player_set_track_list length:%d",g_list_length(track_list));
305
306         if (tracklist) {
307                 track_list_free(tracklist);
308                 tracklist = NULL;
309         }
310
311         tracklist = track_list;
312         tracklist_item = g_list_first(tracklist);
313 }