renatofilho@20
|
1 |
|
renatofilho@20
|
2 |
#include <gst/gst.h>
|
renatofilho@20
|
3 |
#include <gtk/gtk.h>
|
renatofilho@20
|
4 |
#include <gdk/gdkx.h>
|
renatofilho@20
|
5 |
#include <gst/interfaces/xoverlay.h>
|
renatofilho@20
|
6 |
#include <unistd.h>
|
renatofilho@20
|
7 |
|
renatofilho@20
|
8 |
#include "mmyth_ui.h"
|
renatofilho@20
|
9 |
#include "mmyth_videoplayer.h"
|
renatofilho@20
|
10 |
#include "mmyth_sdpreader.h"
|
renatofilho@20
|
11 |
|
renatofilho@20
|
12 |
#include "esg_database.h"
|
renatofilho@20
|
13 |
|
renatofilho@20
|
14 |
#include "ipd_demux.h"
|
renatofilho@20
|
15 |
#include "ipd_network.h"
|
renatofilho@20
|
16 |
|
renatofilho@20
|
17 |
#ifdef INDT_IP_DECAPSULATOR
|
renatofilho@20
|
18 |
#include <sys/types.h>
|
renatofilho@20
|
19 |
#include <signal.h>
|
renatofilho@20
|
20 |
#endif
|
renatofilho@20
|
21 |
|
renatofilho@20
|
22 |
/* Global flag/semaphore to control IP decapsulator threads */
|
renatofilho@20
|
23 |
gboolean state_keep_running = 0;
|
renatofilho@20
|
24 |
|
renatofilho@20
|
25 |
|
renatofilho@20
|
26 |
typedef struct _GstPlayerWindowStateChange
|
renatofilho@20
|
27 |
{
|
renatofilho@20
|
28 |
GstElement *play;
|
renatofilho@20
|
29 |
GstState old_state, new_state;
|
renatofilho@20
|
30 |
MMythUi *mmyth_ui;
|
renatofilho@20
|
31 |
} GstPlayerWindowStateChange;
|
renatofilho@20
|
32 |
|
renatofilho@20
|
33 |
typedef struct _GstPlayerWindowTagFound
|
renatofilho@20
|
34 |
{
|
renatofilho@20
|
35 |
GstElement *play;
|
renatofilho@20
|
36 |
GstTagList *taglist;
|
renatofilho@20
|
37 |
MMythUi *mmyth_ui;
|
renatofilho@20
|
38 |
} GstPlayerWindowTagFound;
|
renatofilho@20
|
39 |
|
renatofilho@20
|
40 |
|
renatofilho@20
|
41 |
GstElement *gst_pipeline, *gst_decoder, *gst_videosink;
|
renatofilho@20
|
42 |
|
renatofilho@20
|
43 |
static gboolean idle_state (gpointer data);
|
renatofilho@20
|
44 |
|
renatofilho@20
|
45 |
static gboolean
|
renatofilho@20
|
46 |
bus_call (GstBus * bus, GstMessage * msg, gpointer data)
|
renatofilho@20
|
47 |
{
|
renatofilho@20
|
48 |
MMythUi *mmyth_ui = (MMythUi *) data;
|
renatofilho@20
|
49 |
GMainLoop *loop = mmyth_ui->loop;
|
renatofilho@20
|
50 |
|
renatofilho@20
|
51 |
switch (GST_MESSAGE_TYPE (msg)) {
|
renatofilho@20
|
52 |
case GST_MESSAGE_EOS:
|
renatofilho@20
|
53 |
|
renatofilho@20
|
54 |
if (mmyth_ui->idle_id != 0) {
|
renatofilho@20
|
55 |
g_source_remove (mmyth_ui->idle_id);
|
renatofilho@20
|
56 |
mmyth_ui->idle_id = 0;
|
renatofilho@20
|
57 |
}
|
renatofilho@20
|
58 |
|
renatofilho@20
|
59 |
//g_idle_add ((GSourceFunc) idle_eos, data);
|
renatofilho@20
|
60 |
gst_element_set_state (GST_ELEMENT (GST_MESSAGE_SRC (msg)),
|
renatofilho@20
|
61 |
GST_STATE_READY);
|
renatofilho@20
|
62 |
break;
|
renatofilho@20
|
63 |
case GST_MESSAGE_ERROR:{
|
renatofilho@20
|
64 |
gchar *debug;
|
renatofilho@20
|
65 |
GError *err;
|
renatofilho@20
|
66 |
|
renatofilho@20
|
67 |
gst_message_parse_error (msg, &err, &debug);
|
renatofilho@20
|
68 |
g_free (debug);
|
renatofilho@20
|
69 |
|
renatofilho@20
|
70 |
printf ("Error: %s\n", err->message);
|
renatofilho@20
|
71 |
g_error_free (err);
|
renatofilho@20
|
72 |
|
renatofilho@20
|
73 |
g_main_loop_quit (loop);
|
renatofilho@20
|
74 |
}
|
renatofilho@20
|
75 |
break;
|
renatofilho@20
|
76 |
case GST_MESSAGE_STATE_CHANGED:{
|
renatofilho@20
|
77 |
GstState oldstate;
|
renatofilho@20
|
78 |
GstState newstate;
|
renatofilho@20
|
79 |
GstState pending;
|
renatofilho@20
|
80 |
GstPlayerWindowStateChange *st =
|
renatofilho@20
|
81 |
g_new (GstPlayerWindowStateChange, 1);
|
renatofilho@20
|
82 |
|
renatofilho@20
|
83 |
gst_message_parse_state_changed (msg,
|
renatofilho@20
|
84 |
&oldstate,
|
renatofilho@20
|
85 |
&newstate, &pending);
|
renatofilho@20
|
86 |
|
renatofilho@20
|
87 |
st->play = mmyth_ui->play;
|
renatofilho@20
|
88 |
gst_object_ref (GST_OBJECT (mmyth_ui->play));
|
renatofilho@20
|
89 |
st->old_state = oldstate;
|
renatofilho@20
|
90 |
st->new_state = newstate;
|
renatofilho@20
|
91 |
|
renatofilho@20
|
92 |
st->mmyth_ui = mmyth_ui;
|
renatofilho@20
|
93 |
|
renatofilho@20
|
94 |
/* State debug messages */
|
renatofilho@20
|
95 |
printf ("oldstate = %s, newstate = %s, pendingstate = %s\n",
|
renatofilho@20
|
96 |
gst_element_state_get_name (oldstate),
|
renatofilho@20
|
97 |
gst_element_state_get_name (newstate),
|
renatofilho@20
|
98 |
gst_element_state_get_name (pending));
|
renatofilho@20
|
99 |
|
renatofilho@20
|
100 |
g_idle_add ((GSourceFunc) idle_state, st);
|
renatofilho@20
|
101 |
|
renatofilho@20
|
102 |
}
|
renatofilho@20
|
103 |
break;
|
renatofilho@20
|
104 |
default:
|
renatofilho@20
|
105 |
printf (gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
|
renatofilho@20
|
106 |
printf ("\n");
|
renatofilho@20
|
107 |
break;
|
renatofilho@20
|
108 |
}
|
renatofilho@20
|
109 |
|
renatofilho@20
|
110 |
return TRUE;
|
renatofilho@20
|
111 |
}
|
renatofilho@20
|
112 |
|
renatofilho@20
|
113 |
|
renatofilho@20
|
114 |
static gboolean
|
renatofilho@20
|
115 |
cb_iterate (gpointer data)
|
renatofilho@20
|
116 |
{
|
renatofilho@20
|
117 |
MMythUi *mmyth_ui = (MMythUi *) data;
|
renatofilho@20
|
118 |
//gboolean res;
|
renatofilho@20
|
119 |
|
renatofilho@20
|
120 |
g_main_loop_run (mmyth_ui->loop);
|
renatofilho@20
|
121 |
/*
|
renatofilho@20
|
122 |
if (!GST_FLAG_IS_SET (mmyth_ui->play, GST_BIN_SELF_SCHEDULABLE)) {
|
renatofilho@20
|
123 |
res = gst_bin_iterate (GST_BIN (mmyth_ui->play));
|
renatofilho@20
|
124 |
} else {
|
renatofilho@20
|
125 |
g_usleep (100);
|
renatofilho@20
|
126 |
res = (gst_element_get_state (mmyth_ui->play) == GST_STATE_PLAYING);
|
renatofilho@20
|
127 |
}
|
renatofilho@20
|
128 |
|
renatofilho@20
|
129 |
if (!res)
|
renatofilho@20
|
130 |
mmyth_ui->idle_id = 0;
|
renatofilho@20
|
131 |
|
renatofilho@20
|
132 |
return res;
|
renatofilho@20
|
133 |
*/
|
renatofilho@20
|
134 |
return FALSE;
|
renatofilho@20
|
135 |
|
renatofilho@20
|
136 |
}
|
renatofilho@20
|
137 |
|
renatofilho@20
|
138 |
static gboolean
|
renatofilho@20
|
139 |
idle_state (gpointer data)
|
renatofilho@20
|
140 |
{
|
renatofilho@20
|
141 |
GstPlayerWindowStateChange *st = data;
|
renatofilho@20
|
142 |
|
renatofilho@20
|
143 |
if (st->old_state == GST_STATE_PLAYING) {
|
renatofilho@20
|
144 |
if (st->mmyth_ui->idle_id != 0) {
|
renatofilho@20
|
145 |
g_source_remove (st->mmyth_ui->idle_id);
|
renatofilho@20
|
146 |
st->mmyth_ui->idle_id = 0;
|
renatofilho@20
|
147 |
}
|
renatofilho@20
|
148 |
}
|
renatofilho@20
|
149 |
else if (st->new_state == GST_STATE_PLAYING) {
|
renatofilho@20
|
150 |
if (st->mmyth_ui->idle_id != 0)
|
renatofilho@20
|
151 |
g_source_remove (st->mmyth_ui->idle_id);
|
renatofilho@20
|
152 |
|
renatofilho@20
|
153 |
st->mmyth_ui->idle_id = g_idle_add (cb_iterate, st->mmyth_ui);
|
renatofilho@20
|
154 |
}
|
renatofilho@20
|
155 |
|
renatofilho@20
|
156 |
/* new movie loaded? */
|
renatofilho@20
|
157 |
if (st->old_state == GST_STATE_READY && st->new_state > GST_STATE_READY) {
|
renatofilho@20
|
158 |
|
renatofilho@20
|
159 |
/* gboolean have_video = FALSE; */
|
renatofilho@20
|
160 |
|
renatofilho@20
|
161 |
gtk_widget_show (st->mmyth_ui->videow);
|
renatofilho@20
|
162 |
|
renatofilho@20
|
163 |
gtk_window_resize (GTK_WINDOW (st->mmyth_ui->main_window), 1, 1);
|
renatofilho@20
|
164 |
|
renatofilho@20
|
165 |
}
|
renatofilho@20
|
166 |
|
renatofilho@20
|
167 |
/* discarded movie? */
|
renatofilho@20
|
168 |
if (st->old_state > GST_STATE_READY && st->new_state == GST_STATE_READY) {
|
renatofilho@20
|
169 |
|
renatofilho@20
|
170 |
if (st->mmyth_ui->tagcache) {
|
renatofilho@20
|
171 |
gst_tag_list_free (st->mmyth_ui->tagcache);
|
renatofilho@20
|
172 |
st->mmyth_ui->tagcache = NULL;
|
renatofilho@20
|
173 |
}
|
renatofilho@20
|
174 |
}
|
renatofilho@20
|
175 |
|
renatofilho@20
|
176 |
gst_object_unref (GST_OBJECT (st->play));
|
renatofilho@20
|
177 |
//g_object_unref (G_OBJECT (st->win));
|
renatofilho@20
|
178 |
g_free (st);
|
renatofilho@20
|
179 |
|
renatofilho@20
|
180 |
/* once */
|
renatofilho@20
|
181 |
return FALSE;
|
renatofilho@20
|
182 |
}
|
renatofilho@20
|
183 |
|
renatofilho@20
|
184 |
|
renatofilho@20
|
185 |
|
renatofilho@20
|
186 |
static gboolean
|
renatofilho@20
|
187 |
expose (GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
|
renatofilho@20
|
188 |
{
|
renatofilho@20
|
189 |
MMythUi *mmyth_ui = (MMythUi *) user_data;
|
renatofilho@20
|
190 |
|
renatofilho@20
|
191 |
gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (mmyth_ui->videosink),
|
renatofilho@20
|
192 |
GDK_WINDOW_XWINDOW (mmyth_ui->videow->
|
renatofilho@20
|
193 |
window));
|
renatofilho@20
|
194 |
return TRUE;
|
renatofilho@20
|
195 |
}
|
renatofilho@20
|
196 |
|
renatofilho@20
|
197 |
|
renatofilho@20
|
198 |
void
|
renatofilho@20
|
199 |
mmyth_player_init (MMythUi * mmyth_ui)
|
renatofilho@20
|
200 |
{
|
renatofilho@20
|
201 |
GstElement *play;
|
renatofilho@20
|
202 |
GstElement *source, *parser, *decoder, *videosink;
|
renatofilho@20
|
203 |
|
renatofilho@20
|
204 |
play = gst_pipeline_new ("video-player");
|
renatofilho@20
|
205 |
source = gst_element_factory_make ("udpsrc", "file-source");
|
renatofilho@20
|
206 |
parser = gst_element_factory_make ("rtph263pdepay", "rtp-demux");
|
renatofilho@20
|
207 |
decoder = gst_element_factory_make ("ffdec_h263", "mpeg-decoder");
|
renatofilho@20
|
208 |
videosink = gst_element_factory_make ("xvimagesink", "image-output");
|
renatofilho@20
|
209 |
gst_pipeline = play;
|
renatofilho@20
|
210 |
gst_decoder = decoder;
|
renatofilho@20
|
211 |
gst_videosink = videosink;
|
renatofilho@20
|
212 |
|
renatofilho@20
|
213 |
if (!(gst_pipeline && source && parser && decoder && videosink)) {
|
renatofilho@20
|
214 |
/* FIXME: hanlde the error correctly */
|
renatofilho@20
|
215 |
/* video_alignment is not being created (below)
|
renatofilho@20
|
216 |
and is causing problems to the ui */
|
renatofilho@20
|
217 |
g_print ("GstElement creation error!\n");
|
renatofilho@20
|
218 |
return;
|
renatofilho@20
|
219 |
}
|
renatofilho@20
|
220 |
|
renatofilho@20
|
221 |
g_object_set (G_OBJECT (videosink), "sync", FALSE, NULL);
|
renatofilho@20
|
222 |
|
renatofilho@20
|
223 |
gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline)),
|
renatofilho@20
|
224 |
bus_call, mmyth_ui);
|
renatofilho@20
|
225 |
|
renatofilho@20
|
226 |
gst_bin_add_many (GST_BIN (gst_pipeline), source, parser, decoder,
|
renatofilho@20
|
227 |
videosink, NULL);
|
renatofilho@20
|
228 |
|
renatofilho@20
|
229 |
{
|
renatofilho@20
|
230 |
GstCaps *rtpcaps = gst_caps_new_simple ("application/x-rtp", NULL);
|
renatofilho@20
|
231 |
gst_element_link_filtered(source, parser, rtpcaps);
|
renatofilho@20
|
232 |
}
|
renatofilho@20
|
233 |
|
renatofilho@20
|
234 |
gst_element_link_many (/*source,*/ parser, decoder, videosink, NULL);
|
renatofilho@20
|
235 |
|
renatofilho@20
|
236 |
/* actual window */
|
renatofilho@20
|
237 |
mmyth_ui->play = play;
|
renatofilho@20
|
238 |
mmyth_ui->videosink = videosink;
|
renatofilho@20
|
239 |
mmyth_ui->udpsource = source;
|
renatofilho@20
|
240 |
|
renatofilho@20
|
241 |
g_object_ref (mmyth_ui->play);
|
renatofilho@20
|
242 |
g_object_ref (mmyth_ui->videosink);
|
renatofilho@20
|
243 |
|
renatofilho@20
|
244 |
/* video widget */
|
renatofilho@20
|
245 |
//mmyth_ui->videow = gst_player_video_new (videosink, play);
|
renatofilho@20
|
246 |
mmyth_ui->videow = gtk_drawing_area_new ();
|
renatofilho@20
|
247 |
gtk_widget_set_size_request (mmyth_ui->videow, 300, 240);
|
renatofilho@20
|
248 |
|
renatofilho@20
|
249 |
mmyth_ui->logo = gdk_pixbuf_new_from_file ("logo.png", NULL);
|
renatofilho@20
|
250 |
|
renatofilho@20
|
251 |
|
renatofilho@20
|
252 |
mmyth_ui->video_alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
|
renatofilho@20
|
253 |
gtk_widget_hide (mmyth_ui->video_alignment);
|
renatofilho@20
|
254 |
|
renatofilho@20
|
255 |
gtk_container_add (GTK_CONTAINER (mmyth_ui->video_alignment),
|
renatofilho@20
|
256 |
mmyth_ui->videow);
|
renatofilho@20
|
257 |
|
renatofilho@20
|
258 |
g_signal_connect (mmyth_ui->videow, "expose-event", G_CALLBACK (expose),
|
renatofilho@20
|
259 |
mmyth_ui);
|
renatofilho@20
|
260 |
//g_signal_connect(mmyth_ui->videow, "size_request", G_CALLBACK(cb_preferred_video_size), mmyth_ui);
|
renatofilho@20
|
261 |
|
renatofilho@20
|
262 |
|
renatofilho@20
|
263 |
g_object_ref (mmyth_ui->videow);
|
renatofilho@20
|
264 |
g_object_ref (mmyth_ui->video_alignment);
|
renatofilho@20
|
265 |
|
renatofilho@20
|
266 |
//gnome_app_set_contents (app, videow);
|
renatofilho@20
|
267 |
//gtk_widget_show (mmyth_ui->videow);
|
renatofilho@20
|
268 |
|
renatofilho@20
|
269 |
//fail:
|
renatofilho@20
|
270 |
// gst_object_unref (GST_OBJECT (play));
|
renatofilho@20
|
271 |
// return NULL;
|
renatofilho@20
|
272 |
|
renatofilho@20
|
273 |
}
|
renatofilho@20
|
274 |
|
renatofilho@20
|
275 |
void
|
renatofilho@20
|
276 |
mmyth_player_stop (MMythUi * mmyth_ui)
|
renatofilho@20
|
277 |
{
|
renatofilho@20
|
278 |
gst_element_set_state (mmyth_ui->play, GST_STATE_NULL);
|
renatofilho@20
|
279 |
|
renatofilho@20
|
280 |
if(mmyth_ui->videow != NULL)
|
renatofilho@20
|
281 |
gtk_widget_hide (mmyth_ui->videow);
|
renatofilho@20
|
282 |
|
renatofilho@20
|
283 |
/* Disable IP Decapsulator playing threads */
|
renatofilho@20
|
284 |
state_keep_running = FALSE;
|
renatofilho@20
|
285 |
|
renatofilho@20
|
286 |
}
|
renatofilho@20
|
287 |
|
renatofilho@20
|
288 |
static gboolean
|
renatofilho@20
|
289 |
idle_play (gpointer data)
|
renatofilho@20
|
290 |
{
|
renatofilho@20
|
291 |
MMythUi *mmyth_ui = (MMythUi *) data;
|
renatofilho@20
|
292 |
|
renatofilho@20
|
293 |
sleep (2);
|
renatofilho@20
|
294 |
|
renatofilho@20
|
295 |
gst_element_set_state (mmyth_ui->play, GST_STATE_PLAYING);
|
renatofilho@20
|
296 |
|
renatofilho@20
|
297 |
return FALSE;
|
renatofilho@20
|
298 |
}
|
renatofilho@20
|
299 |
|
renatofilho@20
|
300 |
|
renatofilho@20
|
301 |
/* Run IP decapsulator play function in a new thread. The thread is finished setting the
|
renatofilho@20
|
302 |
* semaphore "state_keep_running" to FALSE*/
|
renatofilho@20
|
303 |
gpointer
|
renatofilho@20
|
304 |
run_ipd(gpointer data)
|
renatofilho@20
|
305 |
{
|
renatofilho@20
|
306 |
GString *ip_addr = (GString*) data;
|
renatofilho@20
|
307 |
|
renatofilho@20
|
308 |
state_keep_running = TRUE;
|
renatofilho@20
|
309 |
|
renatofilho@20
|
310 |
printf("Inside thread\n");
|
renatofilho@20
|
311 |
|
renatofilho@20
|
312 |
ipd_mpe_play_section (ip_addr->str, &state_keep_running);
|
renatofilho@20
|
313 |
|
renatofilho@20
|
314 |
return 0;
|
renatofilho@20
|
315 |
}
|
renatofilho@20
|
316 |
|
renatofilho@20
|
317 |
void
|
renatofilho@20
|
318 |
mmyth_play_channel (MMythUi * mmyth_ui, gint service_number)
|
renatofilho@20
|
319 |
{
|
renatofilho@20
|
320 |
ESGDatabase *esg_db;
|
renatofilho@20
|
321 |
DVBHService *service;
|
renatofilho@20
|
322 |
SDPData sdp_data;
|
renatofilho@20
|
323 |
GString *service_ip_addr;
|
renatofilho@20
|
324 |
|
renatofilho@20
|
325 |
/* First verifies if there is a tuned network */
|
renatofilho@20
|
326 |
if ( !ipd_network_is_tuned() ) {
|
renatofilho@20
|
327 |
/* FIXME: show alert and handle this error */
|
renatofilho@20
|
328 |
g_warning ("Play not possible, network not tuned");
|
renatofilho@20
|
329 |
return;
|
renatofilho@20
|
330 |
}
|
renatofilho@20
|
331 |
|
renatofilho@20
|
332 |
esg_db = esg_database_get_instance();
|
renatofilho@20
|
333 |
/* Gets the service structure */
|
renatofilho@20
|
334 |
service = (DVBHService *) esg_database_get_service_from_number
|
renatofilho@20
|
335 |
(esg_db, service_number);
|
renatofilho@20
|
336 |
|
renatofilho@20
|
337 |
/* Verifies if sdp fragment exists to this service */
|
renatofilho@20
|
338 |
if ( service->sdp_file == NULL ) {
|
renatofilho@20
|
339 |
/* FIXME: Implement error handling */
|
renatofilho@20
|
340 |
g_warning ("SDP fragment not available to access service");
|
renatofilho@20
|
341 |
return;
|
renatofilho@20
|
342 |
}
|
renatofilho@20
|
343 |
|
renatofilho@20
|
344 |
/* Parses the sdp to get ipv6 address and port */
|
renatofilho@20
|
345 |
mmyth_sdp_parse_file (service->sdp_file->str, &sdp_data);
|
renatofilho@20
|
346 |
|
renatofilho@20
|
347 |
/* Sets the gstreamer properties acording to the service access address */
|
renatofilho@20
|
348 |
/* FIXME: Develop sdp_bin in gstreamer to give sdp file as pipeline config */
|
renatofilho@20
|
349 |
g_object_set (G_OBJECT (mmyth_ui->udpsource), "multicast_group",
|
renatofilho@20
|
350 |
sdp_data.ip_addr->str, NULL);
|
renatofilho@20
|
351 |
g_object_set (G_OBJECT (mmyth_ui->udpsource), "port", sdp_data.video_port, NULL);
|
renatofilho@20
|
352 |
|
renatofilho@20
|
353 |
/* Shows the video widget on screen */
|
renatofilho@20
|
354 |
mmyth_ui_set_widget (mmyth_ui, mmyth_ui->video_uicommon);
|
renatofilho@20
|
355 |
|
renatofilho@20
|
356 |
gtk_widget_show (mmyth_ui->videow);
|
renatofilho@20
|
357 |
gtk_widget_queue_draw (mmyth_ui->videow);
|
renatofilho@20
|
358 |
|
renatofilho@20
|
359 |
#ifdef INDT_IP_DECAPSULATOR
|
renatofilho@20
|
360 |
/* Will be dealocated in run_ipd */
|
renatofilho@20
|
361 |
service_ip_addr = g_string_new (sdp_data.ip_addr->str);
|
renatofilho@20
|
362 |
|
renatofilho@20
|
363 |
/* Requests the IP decapsulator to play the channel stream*/
|
renatofilho@20
|
364 |
g_thread_create (run_ipd, (gpointer) service_ip_addr, FALSE, NULL);
|
renatofilho@20
|
365 |
#endif
|
renatofilho@20
|
366 |
|
renatofilho@20
|
367 |
//gst_element_set_state (mmyth_ui->play, GST_STATE_PLAYING);
|
renatofilho@20
|
368 |
g_idle_add (idle_play, mmyth_ui);
|
renatofilho@20
|
369 |
}
|