aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheJackiMonster <thejackimonster@gmail.com>2022-12-04 18:40:40 +0100
committerTheJackiMonster <thejackimonster@gmail.com>2022-12-04 18:40:40 +0100
commit90aeae98ca55fffcfbd598255d62fd258d287e1d (patch)
treeec07af02efb42721f60fa96424423c162b2fee03
parent056af2fb44379238609a2a28ede614ee3c5defed (diff)
downloadmessenger-gtk-90aeae98ca55fffcfbd598255d62fd258d287e1d.tar.gz
messenger-gtk-90aeae98ca55fffcfbd598255d62fd258d287e1d.zip
Implement video playback from local file
Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>
-rw-r--r--resources/ui/play_media.ui64
-rw-r--r--src/ui/new_contact.c9
-rw-r--r--src/ui/new_contact.h2
-rw-r--r--src/ui/play_media.c354
-rw-r--r--src/ui/play_media.h18
5 files changed, 433 insertions, 14 deletions
diff --git a/resources/ui/play_media.ui b/resources/ui/play_media.ui
index 9e485df..ca1a1cf 100644
--- a/resources/ui/play_media.ui
+++ b/resources/ui/play_media.ui
@@ -25,7 +25,7 @@ Author: Tobias Frisch
25 <requires lib="libhandy" version="1.2"/> 25 <requires lib="libhandy" version="1.2"/>
26 <object class="HdyWindow" id="play_media_window"> 26 <object class="HdyWindow" id="play_media_window">
27 <property name="width-request">250</property> 27 <property name="width-request">250</property>
28 <property name="height-request">180</property> 28 <property name="height-request">250</property>
29 <property name="can-focus">False</property> 29 <property name="can-focus">False</property>
30 <property name="modal">True</property> 30 <property name="modal">True</property>
31 <property name="window-position">center-on-parent</property> 31 <property name="window-position">center-on-parent</property>
@@ -82,11 +82,64 @@ Author: Tobias Frisch
82 <property name="flap-position">end</property> 82 <property name="flap-position">end</property>
83 <property name="modal">False</property> 83 <property name="modal">False</property>
84 <child type="content"> 84 <child type="content">
85 <object class="GtkImage"> 85 <object class="GtkStack" id="preview_stack">
86 <property name="visible">True</property> 86 <property name="visible">True</property>
87 <property name="can-focus">False</property> 87 <property name="can-focus">False</property>
88 <property name="icon-name">folder-videos-symbolic</property> 88 <child>
89 <property name="icon_size">6</property> 89 <object class="GtkBox" id="video_box">
90 <property name="visible">True</property>
91 <property name="can-focus">False</property>
92 <property name="orientation">vertical</property>
93 <child>
94 <placeholder/>
95 </child>
96 </object>
97 <packing>
98 <property name="name">page0</property>
99 <property name="title" translatable="yes">page0</property>
100 </packing>
101 </child>
102 <child>
103 <object class="GtkBox" id="fail_box">
104 <property name="visible">True</property>
105 <property name="can-focus">False</property>
106 <property name="halign">center</property>
107 <property name="valign">center</property>
108 <property name="orientation">vertical</property>
109 <property name="spacing">8</property>
110 <child>
111 <object class="GtkImage">
112 <property name="visible">True</property>
113 <property name="can-focus">False</property>
114 <property name="pixel-size">64</property>
115 <property name="icon-name">action-unavailable-symbolic</property>
116 <property name="icon_size">3</property>
117 </object>
118 <packing>
119 <property name="expand">False</property>
120 <property name="fill">True</property>
121 <property name="position">0</property>
122 </packing>
123 </child>
124 <child>
125 <object class="GtkLabel">
126 <property name="visible">True</property>
127 <property name="can-focus">False</property>
128 <property name="label" translatable="yes">Video source invalid!</property>
129 </object>
130 <packing>
131 <property name="expand">False</property>
132 <property name="fill">True</property>
133 <property name="position">1</property>
134 </packing>
135 </child>
136 </object>
137 <packing>
138 <property name="name">page1</property>
139 <property name="title" translatable="yes">page1</property>
140 <property name="position">1</property>
141 </packing>
142 </child>
90 </object> 143 </object>
91 </child> 144 </child>
92 <child type="separator"> 145 <child type="separator">
@@ -109,6 +162,7 @@ Author: Tobias Frisch
109 <child> 162 <child>
110 <object class="GtkButton" id="play_pause_button"> 163 <object class="GtkButton" id="play_pause_button">
111 <property name="visible">True</property> 164 <property name="visible">True</property>
165 <property name="sensitive">False</property>
112 <property name="can-focus">True</property> 166 <property name="can-focus">True</property>
113 <property name="receives-default">True</property> 167 <property name="receives-default">True</property>
114 <property name="relief">none</property> 168 <property name="relief">none</property>
@@ -119,6 +173,7 @@ Author: Tobias Frisch
119 <child> 173 <child>
120 <object class="GtkImage"> 174 <object class="GtkImage">
121 <property name="visible">True</property> 175 <property name="visible">True</property>
176 <property name="sensitive">False</property>
122 <property name="can-focus">False</property> 177 <property name="can-focus">False</property>
123 <property name="icon-name">media-playback-start-symbolic</property> 178 <property name="icon-name">media-playback-start-symbolic</property>
124 </object> 179 </object>
@@ -149,6 +204,7 @@ Author: Tobias Frisch
149 <child> 204 <child>
150 <object class="GtkVolumeButton" id="volume_button"> 205 <object class="GtkVolumeButton" id="volume_button">
151 <property name="visible">True</property> 206 <property name="visible">True</property>
207 <property name="sensitive">False</property>
152 <property name="can-focus">True</property> 208 <property name="can-focus">True</property>
153 <property name="focus-on-click">False</property> 209 <property name="focus-on-click">False</property>
154 <property name="receives-default">True</property> 210 <property name="receives-default">True</property>
diff --git a/src/ui/new_contact.c b/src/ui/new_contact.c
index 562219e..90bb4b6 100644
--- a/src/ui/new_contact.c
+++ b/src/ui/new_contact.c
@@ -76,7 +76,10 @@ static void
76_disable_video_processing(UI_NEW_CONTACT_Handle *handle, 76_disable_video_processing(UI_NEW_CONTACT_Handle *handle,
77 gboolean drop_pipeline) 77 gboolean drop_pipeline)
78{ 78{
79 gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box); 79 GNUNET_assert(handle);
80
81 if (handle->preview_stack)
82 gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box);
80 83
81 if ((!(handle->pipeline)) || (!drop_pipeline)) 84 if ((!(handle->pipeline)) || (!drop_pipeline))
82 return; 85 return;
@@ -252,6 +255,8 @@ void
252ui_new_contact_dialog_init(MESSENGER_Application *app, 255ui_new_contact_dialog_init(MESSENGER_Application *app,
253 UI_NEW_CONTACT_Handle *handle) 256 UI_NEW_CONTACT_Handle *handle)
254{ 257{
258 GNUNET_assert((app) && (handle));
259
255 _setup_gst_pipeline(handle); 260 _setup_gst_pipeline(handle);
256 261
257 pthread_create( 262 pthread_create(
@@ -345,6 +350,8 @@ ui_new_contact_dialog_init(MESSENGER_Application *app,
345void 350void
346ui_new_contact_dialog_cleanup(UI_NEW_CONTACT_Handle *handle) 351ui_new_contact_dialog_cleanup(UI_NEW_CONTACT_Handle *handle)
347{ 352{
353 GNUNET_assert(handle);
354
348 pthread_join(handle->video_tid, NULL); 355 pthread_join(handle->video_tid, NULL);
349 356
350 g_object_unref(handle->builder); 357 g_object_unref(handle->builder);
diff --git a/src/ui/new_contact.h b/src/ui/new_contact.h
index 74c2ff4..f9d2a64 100644
--- a/src/ui/new_contact.h
+++ b/src/ui/new_contact.h
@@ -39,8 +39,6 @@ typedef struct UI_NEW_CONTACT_Handle
39 GstElement *scanner; 39 GstElement *scanner;
40 GstElement *sink; 40 GstElement *sink;
41 41
42 guint bus_watch;
43
44 GtkBuilder *builder; 42 GtkBuilder *builder;
45 GtkDialog *dialog; 43 GtkDialog *dialog;
46 44
diff --git a/src/ui/play_media.c b/src/ui/play_media.c
index dc41a3e..5dbfdec 100644
--- a/src/ui/play_media.c
+++ b/src/ui/play_media.c
@@ -27,6 +27,8 @@
27#include "../application.h" 27#include "../application.h"
28#include "../ui.h" 28#include "../ui.h"
29 29
30#include <gst/gst.h>
31
30static void 32static void
31handle_back_button_click(GtkButton *button, 33handle_back_button_click(GtkButton *button,
32 gpointer user_data) 34 gpointer user_data)
@@ -36,19 +38,131 @@ handle_back_button_click(GtkButton *button,
36} 38}
37 39
38static void 40static void
41_disable_video_processing(UI_PLAY_MEDIA_Handle *handle,
42 gboolean drop_pipeline)
43{
44 GNUNET_assert(handle);
45
46 if (handle->preview_stack)
47 gtk_stack_set_visible_child(handle->preview_stack, handle->fail_box);
48
49 if ((!(handle->pipeline)) || (!drop_pipeline))
50 return;
51
52 gst_element_set_state(handle->pipeline, GST_STATE_NULL);
53}
54
55static gboolean
56_adjust_playing_media_position(UI_PLAY_MEDIA_Handle *handle)
57{
58 gint64 pos, len;
59
60 if (!(handle->pipeline))
61 return FALSE;
62
63 if (!gst_element_query_position(handle->pipeline, GST_FORMAT_TIME, &pos))
64 return FALSE;
65
66 if (!gst_element_query_duration(handle->pipeline, GST_FORMAT_TIME, &len))
67 return FALSE;
68
69 if (handle->timeline_label)
70 {
71 GString *str = g_string_new(NULL);
72
73 guint pos_seconds = GST_TIME_AS_SECONDS(pos);
74 guint len_seconds = GST_TIME_AS_SECONDS(len);
75
76 g_string_append_printf(
77 str,
78 "%u:%02u / %u:%02u",
79 pos_seconds / 60,
80 pos_seconds % 60,
81 len_seconds / 60,
82 len_seconds % 60
83 );
84
85 ui_label_set_text(handle->timeline_label, str->str);
86 g_string_free(str, TRUE);
87 }
88
89 if (handle->timeline_progress_bar)
90 gtk_progress_bar_set_fraction(
91 handle->timeline_progress_bar,
92 1.0 * pos / len
93 );
94
95 return TRUE;
96}
97
98static void
99_adjust_playing_media_state(UI_PLAY_MEDIA_Handle *handle,
100 gboolean playing)
101{
102 handle->playing = playing;
103
104 if (!(handle->play_symbol_stack))
105 return;
106
107 gtk_stack_set_visible_child_name(
108 handle->play_symbol_stack,
109 handle->playing? "pause_page" : "play_page"
110 );
111
112 if (handle->timeline)
113 g_source_remove(handle->timeline);
114
115 if (handle->playing)
116 handle->timeline = g_timeout_add(
117 1000,
118 G_SOURCE_FUNC(_adjust_playing_media_position),
119 handle
120 );
121 else
122 handle->timeline = 0;
123
124 gtk_widget_set_sensitive(GTK_WIDGET(handle->play_pause_button), TRUE);
125
126 gtk_stack_set_visible_child(
127 handle->preview_stack,
128 handle->video_box
129 );
130}
131
132static void
39_pause_playing_media(UI_PLAY_MEDIA_Handle *handle) 133_pause_playing_media(UI_PLAY_MEDIA_Handle *handle)
40{ 134{
41 // 135 if (!(handle->pipeline))
136 _adjust_playing_media_state(handle, FALSE);
42 137
43 handle->playing = FALSE; 138 GstStateChangeReturn ret = gst_element_set_state(
139 handle->pipeline,
140 GST_STATE_PAUSED
141 );
142
143 if (GST_STATE_CHANGE_FAILURE == ret)
144 {
145 _disable_video_processing(handle, TRUE);
146 return;
147 }
44} 148}
45 149
46static void 150static void
47_continue_playing_media(UI_PLAY_MEDIA_Handle *handle) 151_continue_playing_media(UI_PLAY_MEDIA_Handle *handle)
48{ 152{
49 // 153 if (!(handle->pipeline))
154 _adjust_playing_media_state(handle, TRUE);
50 155
51 handle->playing = TRUE; 156 GstStateChangeReturn ret = gst_element_set_state(
157 handle->pipeline,
158 GST_STATE_PLAYING
159 );
160
161 if (GST_STATE_CHANGE_FAILURE == ret)
162 {
163 _disable_video_processing(handle, TRUE);
164 return;
165 }
52} 166}
53 167
54static void 168static void
@@ -57,14 +171,36 @@ handle_play_pause_button_click(GtkButton *button,
57{ 171{
58 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data; 172 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data;
59 173
174 gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
175
60 if (handle->playing) 176 if (handle->playing)
61 _pause_playing_media(handle); 177 _pause_playing_media(handle);
62 else 178 else
63 _continue_playing_media(handle); 179 _continue_playing_media(handle);
180}
64 181
65 gtk_stack_set_visible_child_name( 182static void
66 handle->play_symbol_stack, 183handle_volume_button_value_changed(GtkScaleButton *button,
67 handle->playing? "pause_page" : "play_page" 184 double value,
185 gpointer user_data)
186{
187 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) user_data;
188
189 if (!(handle->vol))
190 return;
191
192 g_object_set(
193 G_OBJECT(handle->vol),
194 "volume",
195 value,
196 NULL
197 );
198
199 g_object_set(
200 G_OBJECT(handle->vol),
201 "mute",
202 (value <= 0.0),
203 NULL
68 ); 204 );
69} 205}
70 206
@@ -157,12 +293,155 @@ handle_window_destroy(UNUSED GtkWidget *window,
157 ui_play_media_window_cleanup((UI_PLAY_MEDIA_Handle*) user_data); 293 ui_play_media_window_cleanup((UI_PLAY_MEDIA_Handle*) user_data);
158} 294}
159 295
296static void
297msg_error_cb(UNUSED GstBus *bus,
298 GstMessage *msg,
299 gpointer *data)
300{
301 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
302
303 GError* error;
304 gst_message_parse_error(msg, &error, NULL);
305
306 if (!error)
307 fprintf(stderr, "ERROR: Unknown error\n");
308 else if (error->message)
309 fprintf(stderr, "ERROR: %s (%d)\n", error->message, error->code);
310 else
311 fprintf(stderr, "ERROR: Unknown error (%d)\n", error->code);
312
313 _disable_video_processing(handle, TRUE);
314}
315
316static void
317msg_eos_cb(UNUSED GstBus *bus,
318 UNUSED GstMessage *msg,
319 gpointer *data)
320{
321 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
322
323 if (GST_MESSAGE_SRC(msg) == GST_OBJECT(handle->pipeline))
324 _adjust_playing_media_state(handle, FALSE);
325}
326
327static void
328msg_state_changed_cb(UNUSED GstBus *bus,
329 GstMessage *msg,
330 gpointer *data)
331{
332 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) data;
333
334 GstState old_state, new_state, pending_state;
335 gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
336
337 if ((GST_MESSAGE_SRC(msg) != GST_OBJECT(handle->pipeline)) ||
338 (new_state == old_state) || (!(handle->preview_stack)))
339 return;
340
341 if (!(handle->sink))
342 _disable_video_processing(handle, FALSE);
343 else if (GST_STATE_PLAYING == new_state)
344 _adjust_playing_media_state(handle, TRUE);
345 else if (GST_STATE_PAUSED == new_state)
346 _adjust_playing_media_state(handle, FALSE);
347}
348
349static void
350_setup_gst_pipeline(UI_PLAY_MEDIA_Handle *handle)
351{
352 handle->pipeline = gst_parse_launch(
353 "filesrc name=source"
354 " ! decodebin name=decode"
355 " ! videoconvert ! video/x-raw,format=RGB"
356 " ! videoconvert ! gtksink name=vsink decode."
357 " ! audioconvert ! audioresample"
358 " ! volume name=vol ! autoaudiosink",
359 NULL
360 );
361
362 if (!(handle->pipeline))
363 return;
364
365 handle->source = gst_bin_get_by_name(
366 GST_BIN(handle->pipeline), "source"
367 );
368
369 g_object_set(
370 G_OBJECT(handle->source),
371 "location",
372 "", // absolute path to video file
373 NULL
374 );
375
376 handle->decode = gst_bin_get_by_name(
377 GST_BIN(handle->pipeline), "decode"
378 );
379
380 handle->sink = gst_bin_get_by_name(
381 GST_BIN(handle->pipeline), "vsink"
382 );
383
384 handle->vol = gst_bin_get_by_name(
385 GST_BIN(handle->pipeline), "vol"
386 );
387
388 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(handle->pipeline));
389
390 if (!bus)
391 return;
392
393 gst_bus_add_signal_watch(bus);
394
395 g_signal_connect(
396 G_OBJECT(bus),
397 "message::error",
398 (GCallback) msg_error_cb,
399 handle
400 );
401
402 g_signal_connect(
403 G_OBJECT(bus),
404 "message::eos",
405 (GCallback) msg_eos_cb,
406 handle
407 );
408
409 g_signal_connect(
410 G_OBJECT(bus),
411 "message::state-changed",
412 (GCallback) msg_state_changed_cb,
413 handle
414 );
415
416 gst_object_unref(bus);
417}
418
419static void*
420_ui_play_media_video_thread(void *args)
421{
422 UI_PLAY_MEDIA_Handle *handle = (UI_PLAY_MEDIA_Handle*) args;
423
424 if (!handle->playing)
425 _continue_playing_media(handle);
426
427 return NULL;
428}
429
160void 430void
161ui_play_media_window_init(MESSENGER_Application *app, 431ui_play_media_window_init(MESSENGER_Application *app,
162 UI_PLAY_MEDIA_Handle *handle) 432 UI_PLAY_MEDIA_Handle *handle)
163{ 433{
164 GNUNET_assert((app) && (handle)); 434 GNUNET_assert((app) && (handle));
165 435
436 _setup_gst_pipeline(handle);
437
438 pthread_create(
439 &(handle->video_tid),
440 NULL,
441 _ui_play_media_video_thread,
442 handle
443 );
444
166 handle->parent = GTK_WINDOW(app->ui.messenger.main_window); 445 handle->parent = GTK_WINDOW(app->ui.messenger.main_window);
167 446
168 handle->builder = gtk_builder_new_from_resource( 447 handle->builder = gtk_builder_new_from_resource(
@@ -208,6 +487,40 @@ ui_play_media_window_init(MESSENGER_Application *app,
208 gtk_builder_get_object(handle->builder, "controls_flap") 487 gtk_builder_get_object(handle->builder, "controls_flap")
209 ); 488 );
210 489
490 handle->preview_stack = GTK_STACK(
491 gtk_builder_get_object(handle->builder, "preview_stack")
492 );
493
494 handle->fail_box = GTK_WIDGET(
495 gtk_builder_get_object(handle->builder, "fail_box")
496 );
497
498 handle->video_box = GTK_WIDGET(
499 gtk_builder_get_object(handle->builder, "video_box")
500 );
501
502 GtkWidget *widget;
503 if (handle->sink)
504 g_object_get(handle->sink, "widget", &widget, NULL);
505 else
506 widget = NULL;
507
508 if (widget)
509 {
510 gtk_box_pack_start(
511 GTK_BOX(handle->video_box),
512 widget,
513 true,
514 true,
515 0
516 );
517
518 g_object_unref(widget);
519 gtk_widget_realize(widget);
520
521 gtk_widget_show_all(handle->video_box);
522 }
523
211 handle->play_pause_button = GTK_BUTTON( 524 handle->play_pause_button = GTK_BUTTON(
212 gtk_builder_get_object(handle->builder, "play_pause_button") 525 gtk_builder_get_object(handle->builder, "play_pause_button")
213 ); 526 );
@@ -227,10 +540,21 @@ ui_play_media_window_init(MESSENGER_Application *app,
227 gtk_builder_get_object(handle->builder, "volume_button") 540 gtk_builder_get_object(handle->builder, "volume_button")
228 ); 541 );
229 542
543 g_signal_connect(
544 handle->volume_button,
545 "value-changed",
546 G_CALLBACK(handle_volume_button_value_changed),
547 handle
548 );
549
230 handle->timeline_label = GTK_LABEL( 550 handle->timeline_label = GTK_LABEL(
231 gtk_builder_get_object(handle->builder, "timeline_label") 551 gtk_builder_get_object(handle->builder, "timeline_label")
232 ); 552 );
233 553
554 handle->timeline_progress_bar = GTK_PROGRESS_BAR(
555 gtk_builder_get_object(handle->builder, "timeline_progress_bar")
556 );
557
234 handle->settings_button = GTK_BUTTON( 558 handle->settings_button = GTK_BUTTON(
235 gtk_builder_get_object(handle->builder, "settings_button") 559 gtk_builder_get_object(handle->builder, "settings_button")
236 ); 560 );
@@ -270,6 +594,11 @@ ui_play_media_window_init(MESSENGER_Application *app,
270 handle 594 handle
271 ); 595 );
272 596
597 gtk_scale_button_set_value(
598 GTK_SCALE_BUTTON(handle->volume_button),
599 1.0
600 );
601
273 gtk_widget_show_all(GTK_WIDGET(handle->window)); 602 gtk_widget_show_all(GTK_WIDGET(handle->window));
274} 603}
275 604
@@ -278,10 +607,21 @@ ui_play_media_window_cleanup(UI_PLAY_MEDIA_Handle *handle)
278{ 607{
279 GNUNET_assert(handle); 608 GNUNET_assert(handle);
280 609
610 pthread_join(handle->video_tid, NULL);
611
281 g_object_unref(handle->builder); 612 g_object_unref(handle->builder);
282 613
614 if (handle->timeline)
615 g_source_remove(handle->timeline);
616
283 if (handle->motion_lost) 617 if (handle->motion_lost)
284 g_source_remove(handle->motion_lost); 618 g_source_remove(handle->motion_lost);
285 619
620 if (handle->pipeline)
621 {
622 gst_element_set_state(handle->pipeline, GST_STATE_NULL);
623 gst_object_unref(GST_OBJECT(handle->pipeline));
624 }
625
286 memset(handle, 0, sizeof(*handle)); 626 memset(handle, 0, sizeof(*handle));
287} 627}
diff --git a/src/ui/play_media.h b/src/ui/play_media.h
index 29f8d1a..597bf7c 100644
--- a/src/ui/play_media.h
+++ b/src/ui/play_media.h
@@ -27,11 +27,20 @@
27 27
28#include "messenger.h" 28#include "messenger.h"
29 29
30#include <gstreamer-1.0/gst/gst.h>
31#include <pthread.h>
32
30typedef struct UI_PLAY_MEDIA_Handle 33typedef struct UI_PLAY_MEDIA_Handle
31{ 34{
32 gboolean playing; 35 gboolean playing;
33 gboolean fullscreen; 36 gboolean fullscreen;
34 37
38 GstElement *pipeline;
39 GstElement *source;
40 GstElement *decode;
41 GstElement *sink;
42 GstElement *vol;
43
35 GtkWindow *parent; 44 GtkWindow *parent;
36 45
37 GtkBuilder *builder; 46 GtkBuilder *builder;
@@ -42,18 +51,27 @@ typedef struct UI_PLAY_MEDIA_Handle
42 GtkButton *back_button; 51 GtkButton *back_button;
43 52
44 HdyFlap *controls_flap; 53 HdyFlap *controls_flap;
54
55 GtkStack *preview_stack;
56 GtkWidget *fail_box;
57 GtkWidget *video_box;
58
45 GtkButton *play_pause_button; 59 GtkButton *play_pause_button;
46 GtkStack *play_symbol_stack; 60 GtkStack *play_symbol_stack;
47 61
48 GtkVolumeButton *volume_button; 62 GtkVolumeButton *volume_button;
49 GtkLabel *timeline_label; 63 GtkLabel *timeline_label;
64 GtkProgressBar *timeline_progress_bar;
50 65
51 GtkButton *settings_button; 66 GtkButton *settings_button;
52 67
53 GtkButton *fullscreen_button; 68 GtkButton *fullscreen_button;
54 GtkStack *fullscreen_symbol_stack; 69 GtkStack *fullscreen_symbol_stack;
55 70
71 guint timeline;
56 guint motion_lost; 72 guint motion_lost;
73
74 pthread_t video_tid;
57} UI_PLAY_MEDIA_Handle; 75} UI_PLAY_MEDIA_Handle;
58 76
59/** 77/**