1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <math.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gst/gst.h>
26 #include <gthumb.h>
27 #include <extensions/gstreamer_utils/gstreamer-utils.h>
28 #include "actions.h"
29 #include "gth-media-viewer-page.h"
30 #include "preferences.h"
31
32
33 #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
34 #define PROGRESS_DELAY 500
35
36
37 static void gth_viewer_page_interface_init (GthViewerPageInterface *iface);
38
39
40 struct _GthMediaViewerPagePrivate {
41 GthBrowser *browser;
42 GSettings *settings;
43 GthFileData *file_data;
44 GFileInfo *updated_info;
45 GstElement *playbin;
46 GtkBuilder *builder;
47 GtkWidget *video_area;
48 GtkWidget *audio_area;
49 GtkWidget *area_box;
50 GtkWidget *area_overlay;
51 gboolean fit_if_larger;
52 gboolean visible;
53 gboolean playing;
54 gboolean paused;
55 gboolean loop;
56 gint64 duration;
57 int video_fps_n;
58 int video_fps_d;
59 int video_width;
60 int video_height;
61 gboolean has_video;
62 gboolean has_audio;
63 gulong update_progress_id;
64 gulong update_volume_id;
65 gdouble rate;
66 GtkWidget *mediabar;
67 GtkWidget *mediabar_revealer;
68 GdkPixbuf *icon;
69 PangoLayout *caption_layout;
70 GdkCursor *cursor;
71 GdkCursor *cursor_void;
72 gboolean cursor_visible;
73 GthScreensaver *screensaver;
74 GtkWidget *screenshot_button;
75 GtkWidget *fit_button;
76 gboolean background_painted;
77 };
78
79
80 G_DEFINE_TYPE_WITH_CODE (GthMediaViewerPage,
81 gth_media_viewer_page,
82 G_TYPE_OBJECT,
83 G_ADD_PRIVATE (GthMediaViewerPage)
84 G_IMPLEMENT_INTERFACE (GTH_TYPE_VIEWER_PAGE,
85 gth_viewer_page_interface_init))
86
87
88 static double default_rates[] = { 0.03, 0.06, 0.12, 0.25, 0.33, 0.50, 0.66, 1.0, 1.50, 2.0, 3.0, 4.0, 8.0, 16.0, 32.0 };
89
90
91 static const GActionEntry actions[] = {
92 { "video-screenshot", gth_browser_activate_video_screenshot },
93 { "toggle-play", gth_browser_activate_toggle_play },
94 { "video-zoom-fit", toggle_action_activated, NULL, "true", gth_browser_activate_video_zoom_fit },
95 };
96
97
98 static void
_gth_media_viewer_page_update_caption(GthMediaViewerPage * self)99 _gth_media_viewer_page_update_caption (GthMediaViewerPage *self)
100 {
101 if (self->priv->caption_layout == NULL)
102 return;
103
104 if (self->priv->file_data != NULL) {
105 GString *description;
106 GthMetadata *metadata;
107
108 description = g_string_new ("");
109 metadata = (GthMetadata *) g_file_info_get_attribute_object (self->priv->file_data->info, "general::title");
110 if (metadata != NULL) {
111 g_string_append (description, gth_metadata_get_formatted (metadata));
112 metadata = (GthMetadata *) g_file_info_get_attribute_object (self->priv->file_data->info, "audio-video::general::artist");
113 if (metadata != NULL) {
114 g_string_append (description, "\n");
115 g_string_append (description, gth_metadata_get_formatted (metadata));
116 }
117 }
118 else
119 g_string_append (description, g_file_info_get_display_name (self->priv->file_data->info));
120
121 pango_layout_set_text (self->priv->caption_layout, description->str, -1);
122
123 g_string_free (description, TRUE);
124 }
125 else
126 pango_layout_set_text (self->priv->caption_layout, "", -1);
127
128 gtk_widget_queue_draw (GTK_WIDGET (self->priv->audio_area));
129 }
130
131
132 static void
video_area_realize_cb(GtkWidget * widget,gpointer user_data)133 video_area_realize_cb (GtkWidget *widget,
134 gpointer user_data)
135 {
136 GthMediaViewerPage *self = user_data;
137
138 self->priv->cursor = _gdk_cursor_new_for_widget (widget, GDK_LEFT_PTR);
139 self->priv->cursor_void = _gdk_cursor_new_for_widget (self->priv->video_area, GDK_BLANK_CURSOR);
140
141 if (self->priv->cursor_visible)
142 gdk_window_set_cursor (gtk_widget_get_window (self->priv->video_area), self->priv->cursor);
143 else
144 gdk_window_set_cursor (gtk_widget_get_window (self->priv->video_area), self->priv->cursor_void);
145
146 self->priv->caption_layout = gtk_widget_create_pango_layout (widget, "");
147 pango_layout_set_alignment (self->priv->caption_layout, PANGO_ALIGN_CENTER);
148 _gth_media_viewer_page_update_caption (self);
149
150 self->priv->background_painted = FALSE;
151 }
152
153
154 static void
video_area_unrealize_cb(GtkWidget * widget,gpointer user_data)155 video_area_unrealize_cb (GtkWidget *widget,
156 gpointer user_data)
157 {
158 GthMediaViewerPage *self = user_data;
159
160 if (self->priv->cursor) {
161 g_object_unref (self->priv->cursor);
162 self->priv->cursor = NULL;
163 }
164
165 if (self->priv->cursor_void) {
166 g_object_unref (self->priv->cursor_void);
167 self->priv->cursor_void = NULL;
168 }
169
170 g_object_unref (self->priv->caption_layout);
171 self->priv->caption_layout = NULL;
172 }
173
174
175 static void
update_zoom_info(GthMediaViewerPage * self)176 update_zoom_info (GthMediaViewerPage *self)
177 {
178 GtkAllocation allocation;
179 double view_width;
180 double view_height;
181 int zoom;
182 char *text;
183
184 if (! self->priv->has_video) {
185 gth_statusbar_set_secondary_text (GTH_STATUSBAR (gth_browser_get_statusbar (self->priv->browser)), "");
186 return;
187 }
188
189 gtk_widget_get_allocation (self->priv->video_area, &allocation);
190
191 view_width = allocation.width;
192 view_height = (((double) self->priv->video_height / self->priv->video_width) * view_width);
193 if (view_height > allocation.height) {
194 view_height = allocation.height;
195 view_width = (((double) self->priv->video_width / self->priv->video_height) * view_height);
196 }
197
198 if (self->priv->video_width > 0)
199 zoom = (int) round ((double) view_width / self->priv->video_width * 100);
200 else if (self->priv->video_height > 0)
201 zoom = (int) round ((double) view_height / self->priv->video_height * 100);
202 else
203 zoom = 100;
204 text = g_strdup_printf (" %d%% ", zoom);
205 gth_statusbar_set_secondary_text (GTH_STATUSBAR (gth_browser_get_statusbar (self->priv->browser)), text);
206
207 g_free (text);
208 }
209
210
211 static void
video_area_size_allocate_cb(GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)212 video_area_size_allocate_cb (GtkWidget *widget,
213 GdkRectangle *allocation,
214 gpointer user_data)
215 {
216 GthMediaViewerPage *self = user_data;
217 update_zoom_info (self);
218 }
219
220
221 static gboolean
video_area_draw_cb(GtkWidget * widget,cairo_t * cr,gpointer user_data)222 video_area_draw_cb (GtkWidget *widget,
223 cairo_t *cr,
224 gpointer user_data)
225 {
226 GthMediaViewerPage *self = user_data;
227 GtkAllocation allocation;
228 GtkStyleContext *style_context;
229
230 if (self->priv->has_video && self->priv->background_painted)
231 return FALSE;
232
233 gtk_widget_get_allocation (widget, &allocation);
234 style_context = gtk_widget_get_style_context (widget);
235
236 if (self->priv->icon == NULL) {
237 char *type;
238 GIcon *icon;
239 int size;
240
241 type = NULL;
242 if (self->priv->file_data != NULL)
243 type = g_content_type_from_mime_type (gth_file_data_get_mime_type (self->priv->file_data));
244 if (type == NULL)
245 type = g_content_type_from_mime_type ("text/plain");
246 icon = g_content_type_get_icon (type);
247 size = allocation.width;
248 if (size > allocation.height)
249 size = allocation.height;
250 size = size / 3;
251 self->priv->icon = _g_icon_get_pixbuf (icon, size, _gtk_widget_get_icon_theme (widget));
252
253 g_object_unref (icon);
254 g_free (type);
255 }
256
257 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
258 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
259 cairo_fill (cr);
260
261 if (self->priv->icon != NULL) {
262 int icon_w, icon_h;
263 int text_w;
264 int icon_x, icon_y;
265 PangoRectangle logical_rect;
266 int x, y;
267 PangoFontDescription *font;
268
269 icon_w = gdk_pixbuf_get_width (self->priv->icon);
270 icon_h = gdk_pixbuf_get_height (self->priv->icon);
271
272 text_w = (icon_w * 3 / 2);
273 pango_layout_set_width (self->priv->caption_layout, PANGO_SCALE * text_w);
274 pango_layout_get_extents (self->priv->caption_layout, NULL, &logical_rect);
275
276 icon_x = (allocation.width - icon_w) / 2;
277 x = (allocation.width - text_w) / 2;
278
279 icon_y = (allocation.height - (icon_h + PANGO_PIXELS (logical_rect.height))) / 2;
280 y = icon_y + icon_h;
281
282 gdk_cairo_set_source_pixbuf (cr, self->priv->icon, icon_x, icon_y);
283 cairo_rectangle (cr, icon_x, icon_y, icon_w, icon_h);
284 cairo_fill (cr);
285
286 cairo_move_to (cr, x, y);
287 gtk_style_context_get (style_context, gtk_widget_get_state_flags (widget), "font", &font, NULL);
288 pango_layout_set_font_description (self->priv->caption_layout, font);
289 pango_cairo_layout_path (cr, self->priv->caption_layout);
290 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
291 cairo_fill (cr);
292 }
293
294 self->priv->background_painted = TRUE;
295
296 return TRUE;
297 }
298
299
300 static gboolean
video_area_button_press_cb(GtkWidget * widget,GdkEventButton * event,GthMediaViewerPage * self)301 video_area_button_press_cb (GtkWidget *widget,
302 GdkEventButton *event,
303 GthMediaViewerPage *self)
304 {
305 if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1) ) {
306 gtk_button_clicked (GTK_BUTTON (GET_WIDGET ("play_button")));
307 return TRUE;
308 }
309
310 return gth_browser_viewer_button_press_cb (self->priv->browser, event);
311 }
312
313
314 static gboolean
video_area_popup_menu_cb(GtkWidget * widget,GthMediaViewerPage * self)315 video_area_popup_menu_cb (GtkWidget *widget,
316 GthMediaViewerPage *self)
317 {
318 gth_browser_file_menu_popup (self->priv->browser, NULL);
319 return TRUE;
320 }
321
322
323 static gboolean
video_area_scroll_event_cb(GtkWidget * widget,GdkEventScroll * event,GthMediaViewerPage * self)324 video_area_scroll_event_cb (GtkWidget *widget,
325 GdkEventScroll *event,
326 GthMediaViewerPage *self)
327 {
328 return gth_browser_viewer_scroll_event_cb (self->priv->browser, event);
329 }
330
331
332 static void
volume_value_changed_cb(GtkAdjustment * adjustment,gpointer user_data)333 volume_value_changed_cb (GtkAdjustment *adjustment,
334 gpointer user_data)
335 {
336 GthMediaViewerPage *self = user_data;
337 double v;
338
339 if (self->priv->playbin == NULL)
340 return;
341
342 /* cubic in [0,1], linear in [1,2] */
343 v = gtk_adjustment_get_value (adjustment);
344 if (v <= 1.0)
345 v = (v * v * v);
346
347 g_object_set (self->priv->playbin,
348 "volume", v,
349 NULL);
350 }
351
352
353 static void position_value_changed_cb (GtkAdjustment *adjustment,
354 gpointer user_data);
355
356
357 static void
update_current_position_bar(GthMediaViewerPage * self)358 update_current_position_bar (GthMediaViewerPage *self)
359 {
360 GstFormat format;
361 gint64 current_value = 0;
362
363 format = GST_FORMAT_TIME;
364 if (gst_element_query_position (self->priv->playbin, format, ¤t_value)) {
365 char *s;
366
367 if (self->priv->duration <= 0) {
368 gst_element_query_duration (self->priv->playbin, format, &self->priv->duration);
369 s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (self->priv->duration));
370 gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_duration")), s);
371
372 g_free (s);
373 }
374
375 /*
376 g_print ("==> %" G_GINT64_FORMAT " / %" G_GINT64_FORMAT " (%0.3g)\n" ,
377 current_value,
378 self->priv->duration,
379 ((double) current_value / self->priv->duration) * 100.0);
380 */
381
382 g_signal_handlers_block_by_func(GET_WIDGET ("position_adjustment"), position_value_changed_cb, self);
383 gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("position_adjustment")), (self->priv->duration > 0) ? ((double) current_value / self->priv->duration) * 100.0 : 0.0);
384 g_signal_handlers_unblock_by_func(GET_WIDGET ("position_adjustment"), position_value_changed_cb, self);
385
386 s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
387 gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
388
389 g_free (s);
390 }
391 }
392
393
394 static void
position_value_changed_cb(GtkAdjustment * adjustment,gpointer user_data)395 position_value_changed_cb (GtkAdjustment *adjustment,
396 gpointer user_data)
397 {
398 GthMediaViewerPage *self = user_data;
399 gint64 current_value;
400 char *s;
401
402 if (self->priv->playbin == NULL)
403 return;
404
405 current_value = (gint64) (gtk_adjustment_get_value (adjustment) / 100.0 * self->priv->duration);
406 gst_element_seek (self->priv->playbin,
407 self->priv->rate,
408 GST_FORMAT_TIME,
409 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
410 GST_SEEK_TYPE_SET,
411 current_value,
412 GST_SEEK_TYPE_NONE,
413 0.0);
414
415 s = _g_format_duration_for_display (GST_TIME_AS_MSECONDS (current_value));
416 gtk_label_set_text (GTK_LABEL (GET_WIDGET ("label_position")), s);
417
418 g_free (s);
419 }
420
421
422 static void
update_playback_info(GthMediaViewerPage * self)423 update_playback_info (GthMediaViewerPage *self)
424 {
425 char *playback_info;
426
427 playback_info = g_strdup_printf ("@%2.2f", self->priv->rate);
428 g_file_info_set_attribute_string (gth_browser_get_current_file (self->priv->browser)->info, "gthumb::statusbar-extra-info", playback_info);
429 gth_browser_update_statusbar_file_info (self->priv->browser);
430
431 g_free (playback_info);
432 }
433
434
435 static void
update_player_rate(GthMediaViewerPage * self)436 update_player_rate (GthMediaViewerPage *self)
437 {
438 gint64 current_value;
439
440 self->priv->rate = CLAMP (self->priv->rate,
441 default_rates[0],
442 default_rates[G_N_ELEMENTS (default_rates) - 1]);
443
444 if (self->priv->playbin == NULL)
445 return;
446
447 update_playback_info (self);
448
449 if (! self->priv->playing)
450 return;
451
452 current_value = (gint64) (gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("position_adjustment"))) / 100.0 * self->priv->duration);
453 if (! gst_element_seek (self->priv->playbin,
454 self->priv->rate,
455 GST_FORMAT_TIME,
456 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
457 GST_SEEK_TYPE_SET,
458 current_value,
459 GST_SEEK_TYPE_NONE,
460 0.0))
461 {
462 g_warning ("seek failed");
463 }
464 }
465
466
467 static void
play_button_clicked_cb(GtkButton * button,gpointer user_data)468 play_button_clicked_cb (GtkButton *button,
469 gpointer user_data)
470 {
471 gth_media_viewer_page_toggle_play (GTH_MEDIA_VIEWER_PAGE (user_data));
472 }
473
474
475 static int
get_nearest_rate(double rate)476 get_nearest_rate (double rate)
477 {
478 int min_idx = -1;
479 double min_delta = 0;
480 int i;
481
482 for (i = 0; i < G_N_ELEMENTS (default_rates); i++) {
483 double delta;
484
485 delta = fabs (default_rates[i] - rate);
486 if ((i == 0) || (delta < min_delta)) {
487 min_delta = delta;
488 min_idx = i;
489 }
490 }
491
492 return min_idx;
493 }
494
495
496 static void
play_slower_button_clicked_cb(GtkButton * button,gpointer user_data)497 play_slower_button_clicked_cb (GtkButton *button,
498 gpointer user_data)
499 {
500 GthMediaViewerPage *self = user_data;
501 int i;
502
503 i = get_nearest_rate (self->priv->rate);
504 if (i > 0)
505 self->priv->rate = default_rates[i - 1];
506 else
507 self->priv->rate = default_rates[0];
508
509 update_player_rate (self);
510 }
511
512
513 static void
play_faster_button_clicked_cb(GtkButton * button,gpointer user_data)514 play_faster_button_clicked_cb (GtkButton *button,
515 gpointer user_data)
516 {
517 GthMediaViewerPage *self = user_data;
518 int i;
519
520 i = get_nearest_rate (self->priv->rate);
521 if (i < G_N_ELEMENTS (default_rates) - 1)
522 self->priv->rate = default_rates[i + 1];
523 else
524 self->priv->rate = default_rates[G_N_ELEMENTS (default_rates) - 1];
525
526 update_player_rate (self);
527 }
528
529
530 static void
loop_button_clicked_cb(GtkButton * button,gpointer user_data)531 loop_button_clicked_cb (GtkButton *button,
532 gpointer user_data)
533 {
534 GthMediaViewerPage *self = user_data;
535 self->priv->loop = ! self->priv->loop;
536 }
537
538
539 static gboolean
update_volume_from_playbin(GthMediaViewerPage * self)540 update_volume_from_playbin (GthMediaViewerPage *self)
541 {
542 double volume, v;
543
544 if (self->priv->update_volume_id != 0) {
545 g_source_remove (self->priv->update_volume_id);
546 self->priv->update_volume_id = 0;
547 }
548
549 if ((self->priv->builder == NULL) || (self->priv->playbin == NULL))
550 return FALSE;
551
552 g_object_get (self->priv->playbin, "volume", &volume, NULL);
553
554 /* cubic in [0,1], linear in [1,2] */
555 if (volume <= 1.0)
556 v = exp (1.0 / 3.0 * log (volume)); /* cube root of volume */
557 else
558 v = volume;
559
560 g_signal_handlers_block_by_func (GET_WIDGET ("volume_adjustment"), volume_value_changed_cb, self);
561 gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("volume_adjustment")), v);
562 g_signal_handlers_unblock_by_func (GET_WIDGET ("volume_adjustment"), volume_value_changed_cb, self);
563
564 return FALSE;
565 }
566
567
568 static gboolean
update_progress_cb(gpointer user_data)569 update_progress_cb (gpointer user_data)
570 {
571 GthMediaViewerPage *self = user_data;
572
573 if (self->priv->update_progress_id != 0) {
574 g_source_remove (self->priv->update_progress_id);
575 self->priv->update_progress_id = 0;
576 }
577
578 update_current_position_bar (self);
579
580 self->priv->update_progress_id = gdk_threads_add_timeout (PROGRESS_DELAY, update_progress_cb, self);
581
582 return FALSE;
583 }
584
585
586 static void
set_playing_state(GthMediaViewerPage * self,gboolean playing)587 set_playing_state (GthMediaViewerPage *self,
588 gboolean playing)
589 {
590 self->priv->playing = playing;
591 if (self->priv->playing)
592 gth_screensaver_inhibit (self->priv->screensaver,
593 GTK_WINDOW (self->priv->browser),
594 _("Playing video"));
595 else
596 gth_screensaver_uninhibit (self->priv->screensaver);
597 }
598
599
600 static void
update_play_button(GthMediaViewerPage * self,GstState new_state)601 update_play_button (GthMediaViewerPage *self,
602 GstState new_state)
603 {
604 if (! self->priv->playing && (new_state == GST_STATE_PLAYING)) {
605 set_playing_state (self, TRUE);
606 gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("play_button_image")), "media-playback-pause-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR);
607 gtk_widget_set_tooltip_text (GET_WIDGET ("play_button_image"), _("Pause"));
608
609 if (self->priv->update_progress_id == 0)
610 self->priv->update_progress_id = gdk_threads_add_timeout (PROGRESS_DELAY, update_progress_cb, self);
611
612 update_playback_info (self);
613 }
614 else if (self->priv->playing && (new_state != GST_STATE_PLAYING)) {
615 GtkWidget *play_button = GET_WIDGET ("play_button_image");
616
617 set_playing_state (self, FALSE);
618 gtk_image_set_from_icon_name (GTK_IMAGE (play_button),
619 "media-playback-start-symbolic",
620 GTK_ICON_SIZE_LARGE_TOOLBAR);
621 gtk_widget_set_tooltip_text (GET_WIDGET ("play_button_image"), _("Play"));
622
623 if (self->priv->update_progress_id != 0) {
624 g_source_remove (self->priv->update_progress_id);
625 self->priv->update_progress_id = 0;
626 }
627
628 update_playback_info (self);
629 }
630
631 gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
632 }
633
634
635 /*
636 static char *
637 state_description (GstState state)
638 {
639 switch (state) {
640 case GST_STATE_VOID_PENDING:
641 return "void pending";
642 case GST_STATE_NULL:
643 return "null";
644 case GST_STATE_READY:
645 return "ready";
646 case GST_STATE_PAUSED:
647 return "paused";
648 case GST_STATE_PLAYING:
649 return "playing";
650 }
651 return "error";
652 }
653 */
654
655
656 static void
reset_player_state(GthMediaViewerPage * self)657 reset_player_state (GthMediaViewerPage *self)
658 {
659 if (self->priv->update_progress_id != 0) {
660 g_source_remove (self->priv->update_progress_id);
661 self->priv->update_progress_id = 0;
662 }
663
664 update_play_button (self, GST_STATE_NULL);
665 self->priv->rate = 1.0;
666 set_playing_state (self, FALSE);
667 }
668
669
670 static void
update_stream_info(GthMediaViewerPage * self)671 update_stream_info (GthMediaViewerPage *self)
672 {
673 GstElement *audio_sink;
674 GstElement *video_sink;
675 GstPad *audio_pad;
676 GstPad *video_pad;
677
678 g_object_get (self->priv->playbin,
679 "audio-sink", &audio_sink,
680 "video-sink", &video_sink,
681 NULL);
682
683 self->priv->has_audio = FALSE;
684 self->priv->has_video = FALSE;
685
686 if (audio_sink != NULL) {
687 audio_pad = gst_element_get_static_pad (GST_ELEMENT (audio_sink), "sink");
688 if (audio_pad != NULL) {
689 GstCaps *caps;
690
691 if ((caps = gst_pad_get_current_caps (audio_pad)) != NULL) {
692 self->priv->has_audio = TRUE;
693 gst_caps_unref (caps);
694 }
695 }
696 }
697
698 if (video_sink != NULL) {
699 video_pad = gst_element_get_static_pad (GST_ELEMENT (video_sink), "sink");
700 if (video_pad != NULL) {
701 GstCaps *caps;
702
703 if ((caps = gst_pad_get_current_caps (video_pad)) != NULL) {
704 GstStructure *structure;
705 int video_width;
706 int video_height;
707
708 structure = gst_caps_get_structure (caps, 0);
709 gst_structure_get_fraction (structure, "framerate", &self->priv->video_fps_n, &self->priv->video_fps_d);
710 if (gst_structure_get_int (structure, "width", &video_width)
711 && gst_structure_get_int (structure, "height", &video_height))
712 {
713 g_file_info_set_attribute_int32 (self->priv->updated_info, "frame::width", video_width);
714 g_file_info_set_attribute_int32 (self->priv->updated_info, "frame::height", video_height);
715 self->priv->has_video = TRUE;
716 self->priv->video_width = video_width;
717 self->priv->video_height = video_height;
718 }
719
720 gst_caps_unref (caps);
721 }
722 }
723 }
724
725 gtk_stack_set_visible_child_name (GTK_STACK (self->priv->area_box), self->priv->has_video ? "video-area" : "audio-area");
726 update_zoom_info (self);
727 }
728
729
730 static void
bus_message_cb(GstBus * bus,GstMessage * message,gpointer user_data)731 bus_message_cb (GstBus *bus,
732 GstMessage *message,
733 gpointer user_data)
734 {
735 GthMediaViewerPage *self = user_data;
736
737 if (GST_MESSAGE_SRC (message) != GST_OBJECT (self->priv->playbin))
738 return;
739
740 switch (GST_MESSAGE_TYPE (message)) {
741 case GST_MESSAGE_STATE_CHANGED: {
742 GstState old_state;
743 GstState new_state;
744 GstState pending_state;
745
746 old_state = new_state = GST_STATE_NULL;
747 gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
748 if (old_state == new_state)
749 break;
750
751 /*
752 g_print ("old state: %s\n", state_description (old_state));
753 g_print ("new state: %s\n", state_description (new_state));
754 g_print ("pending state: %s\n", state_description (pending_state));
755 g_print ("\n");
756 */
757
758 self->priv->paused = (new_state == GST_STATE_PAUSED);
759 update_current_position_bar (self);
760
761 if ((old_state == GST_STATE_NULL) && (new_state == GST_STATE_READY) && (pending_state != GST_STATE_PAUSED)) {
762 update_stream_info (self);
763 gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
764 gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self), self->priv->file_data, self->priv->updated_info, TRUE);
765 }
766 if ((old_state == GST_STATE_READY) && (new_state == GST_STATE_PAUSED)) {
767 update_stream_info (self);
768 gth_viewer_page_update_sensitivity (GTH_VIEWER_PAGE (self));
769 gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self), self->priv->file_data, self->priv->updated_info, TRUE);
770 }
771 if ((old_state == GST_STATE_READY) || (new_state == GST_STATE_PAUSED))
772 update_volume_from_playbin (self);
773 if ((old_state == GST_STATE_PLAYING) || (new_state == GST_STATE_PLAYING))
774 update_play_button (self, new_state);
775 break;
776 }
777
778 case GST_MESSAGE_DURATION_CHANGED:
779 self->priv->duration = 0;
780 update_current_position_bar (self);
781 break;
782
783 case GST_MESSAGE_EOS:
784 if (self->priv->loop && self->priv->playing)
785 gst_element_seek (self->priv->playbin,
786 self->priv->rate,
787 GST_FORMAT_TIME,
788 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
789 GST_SEEK_TYPE_SET,
790 0.0,
791 GST_SEEK_TYPE_NONE,
792 0.0);
793 else
794 reset_player_state (self);
795 break;
796
797 case GST_MESSAGE_BUFFERING: {
798 int percent = 0;
799 gst_message_parse_buffering (message, &percent);
800 gst_element_set_state (self->priv->playbin, (percent == 100) ? GST_STATE_PLAYING : GST_STATE_PAUSED);
801 break;
802 }
803
804 case GST_MESSAGE_ERROR:
805 gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self), self->priv->file_data, NULL, FALSE);
806 break;
807
808 default:
809 break;
810 }
811 }
812
813
814 static void
playbin_notify_volume_cb(GObject * playbin,GParamSpec * pspec,gpointer user_data)815 playbin_notify_volume_cb (GObject *playbin,
816 GParamSpec *pspec,
817 gpointer user_data)
818 {
819 GthMediaViewerPage *self = user_data;
820
821 if (self->priv->update_volume_id == 0)
822 self->priv->update_volume_id = g_idle_add ((GSourceFunc) update_volume_from_playbin, user_data);
823 }
824
825
826 static void
create_playbin(GthMediaViewerPage * self)827 create_playbin (GthMediaViewerPage *self)
828 {
829 GstElement *video_sink;
830 GstBus *bus;
831
832 if (self->priv->playbin != NULL)
833 return;
834
835 self->priv->playbin = gst_element_factory_make ("playbin", "playbin");
836
837 if (g_settings_get_boolean (self->priv->settings, PREF_GSTREAMER_USE_HARDWARE_ACCEL)) {
838 GstElement *video_bin;
839 GstElement *glupload;
840 GstPad *pad;
841 GstPad *ghost_pad;
842
843 /* pipeline taken from GNOME Twitch */
844
845 video_sink = gst_element_factory_make ("gtkglsink", "sink");
846 video_bin = gst_bin_new ("video_bin");
847 glupload = gst_element_factory_make ("glupload", NULL);
848 gst_bin_add_many (GST_BIN(video_bin), glupload, video_sink, NULL);
849 gst_element_link_many (glupload, video_sink, NULL);
850 pad = gst_element_get_static_pad (glupload, "sink");
851 ghost_pad = gst_ghost_pad_new ("sink", pad);
852 gst_pad_set_active (ghost_pad, TRUE);
853 gst_element_add_pad (video_bin, ghost_pad);
854 g_object_set (self->priv->playbin, "video-sink", video_bin, NULL);
855 }
856 else {
857 video_sink = gst_element_factory_make ("gtksink", "sink");
858 g_object_set (self->priv->playbin, "video-sink", video_sink, NULL);
859 }
860
861 g_object_get (video_sink, "widget", &self->priv->video_area, NULL);
862 gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->video_area), "video-player");
863 gtk_widget_add_events (self->priv->video_area,
864 (gtk_widget_get_events (self->priv->video_area)
865 | GDK_EXPOSURE_MASK
866 | GDK_BUTTON_PRESS_MASK
867 | GDK_BUTTON_RELEASE_MASK
868 | GDK_POINTER_MOTION_MASK
869 | GDK_POINTER_MOTION_HINT_MASK
870 | GDK_BUTTON_MOTION_MASK
871 | GDK_SCROLL_MASK));
872 gtk_widget_set_can_focus (self->priv->video_area, TRUE);
873 gtk_widget_show (self->priv->video_area);
874
875 g_signal_connect (G_OBJECT (self->priv->video_area),
876 "realize",
877 G_CALLBACK (video_area_realize_cb),
878 self);
879 g_signal_connect (G_OBJECT (self->priv->video_area),
880 "unrealize",
881 G_CALLBACK (video_area_unrealize_cb),
882 self);
883 g_signal_connect (G_OBJECT (self->priv->video_area),
884 "size-allocate",
885 G_CALLBACK (video_area_size_allocate_cb),
886 self);
887
888 gtk_stack_add_named (GTK_STACK (self->priv->area_box), self->priv->video_area, "video-area");
889 gtk_widget_show (self->priv->video_area);
890
891 g_object_set (self->priv->playbin,
892 "volume", (double) g_settings_get_int (self->priv->settings, PREF_GSTREAMER_TOOLS_VOLUME) / 100.0,
893 "force-aspect-ratio", TRUE,
894 NULL);
895
896 bus = gst_pipeline_get_bus (GST_PIPELINE (self->priv->playbin));
897 gst_bus_add_signal_watch (bus);
898
899 g_signal_connect (self->priv->playbin,
900 "notify::volume",
901 G_CALLBACK (playbin_notify_volume_cb),
902 self);
903 g_signal_connect (bus,
904 "message",
905 G_CALLBACK (bus_message_cb),
906 self);
907 }
908
909
910 static void
gth_media_viewer_page_real_activate(GthViewerPage * base,GthBrowser * browser)911 gth_media_viewer_page_real_activate (GthViewerPage *base,
912 GthBrowser *browser)
913 {
914 GthMediaViewerPage *self;
915
916 if (! gstreamer_init ())
917 return;
918
919 self = (GthMediaViewerPage*) base;
920
921 self->priv->browser = browser;
922 g_action_map_add_action_entries (G_ACTION_MAP (browser),
923 actions,
924 G_N_ELEMENTS (actions),
925 browser);
926 self->priv->screenshot_button =
927 gth_browser_add_header_bar_button (browser,
928 GTH_BROWSER_HEADER_SECTION_VIEWER_VIEW,
929 "camera-photo-symbolic",
930 _("Take a screenshot"),
931 "win.video-screenshot",
932 NULL);
933 self->priv->fit_button =
934 gth_browser_add_header_bar_toggle_button (browser,
935 GTH_BROWSER_HEADER_SECTION_VIEWER_ZOOM,
936 "view-zoom-fit-symbolic",
937 _("Fit to window"),
938 "win.video-zoom-fit",
939 NULL);
940
941 /* audio area */
942
943 self->priv->audio_area = gtk_drawing_area_new ();
944 gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->audio_area), "video-player");
945 gtk_widget_add_events (self->priv->audio_area, (gtk_widget_get_events (self->priv->audio_area)
946 | GDK_EXPOSURE_MASK
947 | GDK_BUTTON_PRESS_MASK
948 | GDK_BUTTON_RELEASE_MASK
949 | GDK_POINTER_MOTION_MASK
950 | GDK_POINTER_MOTION_HINT_MASK
951 | GDK_BUTTON_MOTION_MASK
952 | GDK_SCROLL_MASK));
953 gtk_widget_set_can_focus (self->priv->audio_area, TRUE);
954 gtk_widget_show (self->priv->audio_area);
955
956 g_signal_connect (G_OBJECT (self->priv->audio_area),
957 "draw",
958 G_CALLBACK (video_area_draw_cb),
959 self);
960
961 /* mediabar */
962
963 self->priv->builder = _gtk_builder_new_from_file ("mediabar.ui", "gstreamer_tools");
964 self->priv->mediabar = GET_WIDGET ("mediabar");
965 gtk_widget_set_halign (self->priv->mediabar, GTK_ALIGN_FILL);
966 gtk_widget_set_valign (self->priv->mediabar, GTK_ALIGN_END);
967
968 gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("play_slower_image")),
969 "media-seek-backward-symbolic",
970 GTK_ICON_SIZE_MENU);
971 gtk_image_set_from_icon_name (GTK_IMAGE (GET_WIDGET ("play_faster_image")),
972 "media-seek-forward-symbolic",
973 GTK_ICON_SIZE_MENU);
974
975 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("loop_button")), self->priv->loop);
976
977 g_signal_connect (GET_WIDGET ("volume_adjustment"),
978 "value-changed",
979 G_CALLBACK (volume_value_changed_cb),
980 self);
981 g_signal_connect (GET_WIDGET ("position_adjustment"),
982 "value-changed",
983 G_CALLBACK (position_value_changed_cb),
984 self);
985 g_signal_connect (GET_WIDGET ("play_button"),
986 "clicked",
987 G_CALLBACK (play_button_clicked_cb),
988 self);
989 g_signal_connect (GET_WIDGET ("play_slower_button"),
990 "clicked",
991 G_CALLBACK (play_slower_button_clicked_cb),
992 self);
993 g_signal_connect (GET_WIDGET ("play_faster_button"),
994 "clicked",
995 G_CALLBACK (play_faster_button_clicked_cb),
996 self);
997 g_signal_connect (GET_WIDGET ("loop_button"),
998 "clicked",
999 G_CALLBACK (loop_button_clicked_cb),
1000 self);
1001
1002 self->priv->mediabar_revealer = gtk_revealer_new ();
1003 gtk_revealer_set_transition_type (GTK_REVEALER (self->priv->mediabar_revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
1004 gtk_widget_set_halign (self->priv->mediabar_revealer, GTK_ALIGN_FILL);
1005 gtk_widget_set_valign (self->priv->mediabar_revealer, GTK_ALIGN_END);
1006 gtk_widget_show (self->priv->mediabar_revealer);
1007 gtk_container_add (GTK_CONTAINER (self->priv->mediabar_revealer), self->priv->mediabar);
1008
1009 self->priv->area_box = gtk_stack_new ();
1010 gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->area_box), "video-player");
1011 gtk_stack_add_named (GTK_STACK (self->priv->area_box), self->priv->audio_area, "audio-area");
1012 gtk_widget_show (self->priv->area_box);
1013
1014 g_signal_connect (G_OBJECT (self->priv->area_box),
1015 "button_press_event",
1016 G_CALLBACK (video_area_button_press_cb),
1017 self);
1018 g_signal_connect (G_OBJECT (self->priv->area_box),
1019 "popup-menu",
1020 G_CALLBACK (video_area_popup_menu_cb),
1021 self);
1022 g_signal_connect (G_OBJECT (self->priv->area_box),
1023 "scroll_event",
1024 G_CALLBACK (video_area_scroll_event_cb),
1025 self);
1026
1027 self->priv->area_overlay = gtk_overlay_new ();
1028 gtk_container_add (GTK_CONTAINER (self->priv->area_overlay), self->priv->area_box);
1029 gtk_overlay_add_overlay (GTK_OVERLAY (self->priv->area_overlay), self->priv->mediabar_revealer);
1030 gtk_widget_show (self->priv->area_overlay);
1031
1032 gth_browser_set_viewer_widget (browser, self->priv->area_overlay);
1033
1034 gtk_widget_realize (self->priv->audio_area);
1035 gth_browser_register_viewer_control (self->priv->browser, self->priv->mediabar_revealer);
1036 gth_browser_register_viewer_control (self->priv->browser, gtk_scale_button_get_popup (GTK_SCALE_BUTTON (GET_WIDGET ("volumebutton"))));
1037
1038 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1039
1040 create_playbin (self);
1041
1042 gth_media_viewer_page_set_fit_if_larger (self, g_settings_get_boolean (self->priv->settings, PREF_GSTREAMER_ZOOM_TO_FIT));
1043 }
1044
1045
1046 static void
wait_playbin_state_change_to_complete(GthMediaViewerPage * self)1047 wait_playbin_state_change_to_complete (GthMediaViewerPage *self)
1048 {
1049 (void) gst_element_get_state (self->priv->playbin,
1050 NULL,
1051 NULL,
1052 GST_SECOND * 10);
1053 }
1054
1055
1056 static void
gth_media_viewer_page_real_deactivate(GthViewerPage * base)1057 gth_media_viewer_page_real_deactivate (GthViewerPage *base)
1058 {
1059 GthMediaViewerPage *self;
1060
1061 self = (GthMediaViewerPage*) base;
1062
1063 gth_browser_unregister_viewer_control (self->priv->browser, gtk_scale_button_get_popup (GTK_SCALE_BUTTON (GET_WIDGET ("volumebutton"))));
1064 gth_browser_unregister_viewer_control (self->priv->browser, self->priv->mediabar_revealer);
1065
1066 if (self->priv->builder != NULL) {
1067 g_object_unref (self->priv->builder);
1068 self->priv->builder = NULL;
1069 }
1070
1071 if (self->priv->update_progress_id != 0) {
1072 g_source_remove (self->priv->update_progress_id);
1073 self->priv->update_progress_id = 0;
1074 }
1075
1076 if (self->priv->update_volume_id != 0) {
1077 g_source_remove (self->priv->update_volume_id);
1078 self->priv->update_volume_id = 0;
1079 }
1080
1081 if (self->priv->playbin != NULL) {
1082 double volume;
1083
1084 g_object_get (self->priv->playbin, "volume", &volume, NULL);
1085 g_settings_set_int (self->priv->settings, PREF_GSTREAMER_TOOLS_VOLUME, (int) (volume * 100.0));
1086
1087 g_settings_set_boolean (self->priv->settings, PREF_GSTREAMER_ZOOM_TO_FIT, self->priv->fit_if_larger);
1088
1089 _g_signal_handlers_disconnect_by_data (self->priv->playbin, self);
1090 _g_signal_handlers_disconnect_by_data (self->priv->video_area, self);
1091
1092 gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
1093 wait_playbin_state_change_to_complete (self);
1094 gst_object_unref (GST_OBJECT (self->priv->playbin));
1095 self->priv->playbin = NULL;
1096 self->priv->video_area = NULL;
1097 self->priv->audio_area = NULL;
1098 }
1099
1100 gtk_widget_destroy (self->priv->screenshot_button);
1101 gtk_widget_destroy (self->priv->fit_button);
1102 self->priv->screenshot_button = NULL;
1103 self->priv->fit_button = NULL;
1104
1105 gth_browser_set_viewer_widget (self->priv->browser, NULL);
1106 }
1107
1108
1109 static void
_gth_media_viewer_page_set_uri(GthMediaViewerPage * self,const char * uri,GstState state)1110 _gth_media_viewer_page_set_uri (GthMediaViewerPage *self,
1111 const char *uri,
1112 GstState state)
1113 {
1114 g_return_if_fail (self->priv->playbin != NULL);
1115
1116 gst_element_set_state (self->priv->playbin, GST_STATE_NULL);
1117
1118 g_object_set (G_OBJECT (self->priv->playbin), "uri", uri, NULL);
1119 gst_element_set_state (self->priv->playbin, state);
1120 wait_playbin_state_change_to_complete (self);
1121 }
1122
1123
1124 static void
gth_media_viewer_page_real_show(GthViewerPage * base)1125 gth_media_viewer_page_real_show (GthViewerPage *base)
1126 {
1127 GthMediaViewerPage *self = GTH_MEDIA_VIEWER_PAGE (base);
1128
1129 self->priv->visible = TRUE;
1130 self->priv->background_painted = FALSE;
1131 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1132
1133 if (self->priv->file_data != NULL) {
1134 char *uri;
1135
1136 uri = g_file_get_uri (self->priv->file_data->file);
1137 _gth_media_viewer_page_set_uri (self, uri, GST_STATE_PLAYING);
1138
1139 g_free (uri);
1140 }
1141 }
1142
1143
1144 static void
gth_media_viewer_page_real_hide(GthViewerPage * base)1145 gth_media_viewer_page_real_hide (GthViewerPage *base)
1146 {
1147 GthMediaViewerPage *self;
1148
1149 self = (GthMediaViewerPage*) base;
1150
1151 self->priv->visible = FALSE;
1152 if ((self->priv->playbin != NULL) && self->priv->playing)
1153 gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
1154 }
1155
1156
1157 static gboolean
gth_media_viewer_page_real_can_view(GthViewerPage * base,GthFileData * file_data)1158 gth_media_viewer_page_real_can_view (GthViewerPage *base,
1159 GthFileData *file_data)
1160 {
1161 g_return_val_if_fail (file_data != NULL, FALSE);
1162
1163 return _g_mime_type_is_video (gth_file_data_get_mime_type (file_data)) || _g_mime_type_is_audio (gth_file_data_get_mime_type (file_data));
1164 }
1165
1166
1167 static void
gth_media_viewer_page_real_view(GthViewerPage * base,GthFileData * file_data)1168 gth_media_viewer_page_real_view (GthViewerPage *base,
1169 GthFileData *file_data)
1170 {
1171 GthMediaViewerPage *self;
1172 char *uri;
1173
1174 self = (GthMediaViewerPage*) base;
1175 g_return_if_fail (file_data != NULL);
1176 g_return_if_fail (self->priv->playbin != NULL);
1177
1178 gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
1179
1180 if ((self->priv->file_data != NULL)
1181 && g_file_equal (file_data->file, self->priv->file_data->file)
1182 && (gth_file_data_get_mtime (file_data) == gth_file_data_get_mtime (self->priv->file_data)))
1183 {
1184 return;
1185 }
1186
1187 /**/
1188
1189 _g_object_unref (self->priv->file_data);
1190 _g_object_unref (self->priv->updated_info);
1191 self->priv->file_data = gth_file_data_dup (file_data);
1192 self->priv->updated_info = g_file_info_new ();
1193
1194 self->priv->duration = 0;
1195 self->priv->has_audio = FALSE;
1196 self->priv->has_video = FALSE;
1197 self->priv->background_painted = FALSE;
1198
1199 _g_object_unref (self->priv->icon);
1200 self->priv->icon = NULL;
1201
1202 _gth_media_viewer_page_update_caption (self);
1203
1204 /**/
1205
1206 g_signal_handlers_block_by_func(GET_WIDGET ("position_adjustment"), position_value_changed_cb, self);
1207 gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("position_adjustment")), 0.0);
1208 g_signal_handlers_unblock_by_func(GET_WIDGET ("position_adjustment"), position_value_changed_cb, self);
1209 reset_player_state (self);
1210
1211 uri = g_file_get_uri (self->priv->file_data->file);
1212 _gth_media_viewer_page_set_uri (self, uri, self->priv->visible ? GST_STATE_PLAYING : GST_STATE_PAUSED);
1213
1214 g_free (uri);
1215 }
1216
1217
1218 static void
gth_media_viewer_page_real_focus(GthViewerPage * base)1219 gth_media_viewer_page_real_focus (GthViewerPage *base)
1220 {
1221 GthMediaViewerPage *self = (GthMediaViewerPage*) base;
1222 GtkWidget *widget;
1223
1224 widget = NULL;
1225 if (self->priv->has_video)
1226 widget = self->priv->video_area;
1227 else if (self->priv->has_audio)
1228 widget = self->priv->audio_area;
1229
1230 if ((widget != NULL) && gtk_widget_get_realized (widget) && gtk_widget_get_mapped (widget))
1231 gtk_widget_grab_focus (widget);
1232 }
1233
1234
1235 static void
gth_media_viewer_page_real_fullscreen(GthViewerPage * base,gboolean active)1236 gth_media_viewer_page_real_fullscreen (GthViewerPage *base,
1237 gboolean active)
1238 {
1239 /* void */
1240 }
1241
1242
1243 static void
gth_media_viewer_page_real_show_pointer(GthViewerPage * base,gboolean show)1244 gth_media_viewer_page_real_show_pointer (GthViewerPage *base,
1245 gboolean show)
1246 {
1247 GthMediaViewerPage *self = (GthMediaViewerPage*) base;
1248
1249 if (show == self->priv->cursor_visible)
1250 return;
1251
1252 self->priv->cursor_visible = show;
1253
1254 if (self->priv->video_area != NULL) {
1255 if (show && (self->priv->cursor != NULL))
1256 gdk_window_set_cursor (gtk_widget_get_window (self->priv->video_area), self->priv->cursor);
1257
1258 if (! show && gth_browser_get_is_fullscreen (self->priv->browser) && (self->priv->cursor_void != NULL))
1259 gdk_window_set_cursor (gtk_widget_get_window (self->priv->video_area), self->priv->cursor_void);
1260 }
1261
1262 gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->mediabar_revealer), show);
1263 }
1264
1265
1266 static void
gth_media_viewer_page_real_update_sensitivity(GthViewerPage * base)1267 gth_media_viewer_page_real_update_sensitivity (GthViewerPage *base)
1268 {
1269 GthMediaViewerPage *self = (GthMediaViewerPage *) base;
1270
1271 gtk_widget_set_sensitive (GET_WIDGET ("volume_box"), self->priv->has_audio);
1272 gtk_widget_set_sensitive (GET_WIDGET ("play_button"), self->priv->has_video || self->priv->has_audio);
1273 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "video-screenshot", self->priv->has_video);
1274 gth_window_enable_action (GTH_WINDOW (self->priv->browser), "video-zoom-fit", self->priv->has_video);
1275 }
1276
1277
1278 static gboolean
gth_media_viewer_page_real_can_save(GthViewerPage * base)1279 gth_media_viewer_page_real_can_save (GthViewerPage *base)
1280 {
1281 return FALSE;
1282 }
1283
1284
1285 static void
gth_media_viewer_page_real_save(GthViewerPage * base,GFile * file,FileSavedFunc func,gpointer user_data)1286 gth_media_viewer_page_real_save (GthViewerPage *base,
1287 GFile *file,
1288 FileSavedFunc func,
1289 gpointer user_data)
1290 {
1291 /* void */
1292 }
1293
1294
1295 static void
gth_media_viewer_page_real_save_as(GthViewerPage * base,FileSavedFunc func,gpointer user_data)1296 gth_media_viewer_page_real_save_as (GthViewerPage *base,
1297 FileSavedFunc func,
1298 gpointer user_data)
1299 {
1300 /* void */
1301 }
1302
1303
1304 static void
gth_media_viewer_page_real_revert(GthViewerPage * base)1305 gth_media_viewer_page_real_revert (GthViewerPage *base)
1306 {
1307 /* void */
1308 }
1309
1310
1311 static void
gth_media_viewer_page_real_update_info(GthViewerPage * base,GthFileData * file_data)1312 gth_media_viewer_page_real_update_info (GthViewerPage *base,
1313 GthFileData *file_data)
1314 {
1315 GthMediaViewerPage *self = GTH_MEDIA_VIEWER_PAGE (base);
1316
1317 if (! _g_file_equal (self->priv->file_data->file, file_data->file))
1318 return;
1319
1320 _g_object_unref (self->priv->file_data);
1321 self->priv->file_data = gth_file_data_dup (file_data);
1322 }
1323
1324
1325 static void
gth_media_viewer_page_finalize(GObject * obj)1326 gth_media_viewer_page_finalize (GObject *obj)
1327 {
1328 GthMediaViewerPage *self;
1329
1330 self = GTH_MEDIA_VIEWER_PAGE (obj);
1331
1332 if (self->priv->update_progress_id != 0) {
1333 g_source_remove (self->priv->update_progress_id);
1334 self->priv->update_progress_id = 0;
1335 }
1336
1337 if (self->priv->update_volume_id != 0) {
1338 g_source_remove (self->priv->update_volume_id);
1339 self->priv->update_volume_id = 0;
1340 }
1341
1342 _g_object_unref (self->priv->icon);
1343 _g_object_unref (self->priv->file_data);
1344 _g_object_unref (self->priv->updated_info);
1345 if (self->priv->screensaver != NULL) {
1346 gth_screensaver_uninhibit (self->priv->screensaver);
1347 g_object_unref (self->priv->screensaver);
1348 }
1349 _g_object_unref (self->priv->settings);
1350
1351 G_OBJECT_CLASS (gth_media_viewer_page_parent_class)->finalize (obj);
1352 }
1353
1354
1355 static void
gth_media_viewer_page_class_init(GthMediaViewerPageClass * klass)1356 gth_media_viewer_page_class_init (GthMediaViewerPageClass *klass)
1357 {
1358 G_OBJECT_CLASS (klass)->finalize = gth_media_viewer_page_finalize;
1359 }
1360
1361
1362 static void
gth_viewer_page_interface_init(GthViewerPageInterface * iface)1363 gth_viewer_page_interface_init (GthViewerPageInterface *iface)
1364 {
1365 iface->activate = gth_media_viewer_page_real_activate;
1366 iface->deactivate = gth_media_viewer_page_real_deactivate;
1367 iface->show = gth_media_viewer_page_real_show;
1368 iface->hide = gth_media_viewer_page_real_hide;
1369 iface->can_view = gth_media_viewer_page_real_can_view;
1370 iface->view = gth_media_viewer_page_real_view;
1371 iface->focus = gth_media_viewer_page_real_focus;
1372 iface->fullscreen = gth_media_viewer_page_real_fullscreen;
1373 iface->show_pointer = gth_media_viewer_page_real_show_pointer;
1374 iface->update_sensitivity = gth_media_viewer_page_real_update_sensitivity;
1375 iface->can_save = gth_media_viewer_page_real_can_save;
1376 iface->save = gth_media_viewer_page_real_save;
1377 iface->save_as = gth_media_viewer_page_real_save_as;
1378 iface->revert = gth_media_viewer_page_real_revert;
1379 iface->update_info = gth_media_viewer_page_real_update_info;
1380 }
1381
1382
1383 static void
pref_zoom_to_fit_changed(GSettings * settings,char * key,gpointer user_data)1384 pref_zoom_to_fit_changed (GSettings *settings,
1385 char *key,
1386 gpointer user_data)
1387 {
1388 GthMediaViewerPage *self = user_data;
1389 gth_media_viewer_page_set_fit_if_larger (self, g_settings_get_boolean (self->priv->settings, PREF_GSTREAMER_ZOOM_TO_FIT));
1390 }
1391
1392
1393 static void
gth_media_viewer_page_init(GthMediaViewerPage * self)1394 gth_media_viewer_page_init (GthMediaViewerPage *self)
1395 {
1396 self->priv = gth_media_viewer_page_get_instance_private (self);
1397 self->priv->settings = g_settings_new (GTHUMB_GSTREAMER_TOOLS_SCHEMA);
1398 self->priv->update_progress_id = 0;
1399 self->priv->update_volume_id = 0;
1400 self->priv->has_video = FALSE;
1401 self->priv->has_audio = FALSE;
1402 self->priv->video_fps_n = 0;
1403 self->priv->video_fps_d = 0;
1404 self->priv->icon = NULL;
1405 self->priv->cursor_visible = TRUE;
1406 self->priv->screensaver = gth_screensaver_new (NULL);
1407 self->priv->visible = FALSE;
1408 self->priv->screenshot_button = NULL;
1409 self->priv->background_painted = FALSE;
1410 self->priv->file_data = NULL;
1411 self->priv->updated_info = NULL;
1412 self->priv->loop = FALSE;
1413 self->priv->fit_if_larger = TRUE;
1414
1415 /* settings notifications */
1416
1417 g_signal_connect (self->priv->settings,
1418 "changed::" PREF_GSTREAMER_ZOOM_TO_FIT,
1419 G_CALLBACK (pref_zoom_to_fit_changed),
1420 self);
1421 }
1422
1423
1424 GthBrowser *
gth_media_viewer_page_get_browser(GthMediaViewerPage * self)1425 gth_media_viewer_page_get_browser (GthMediaViewerPage *self)
1426 {
1427 return self->priv->browser;
1428 }
1429
1430
1431 GstElement *
gth_media_viewer_page_get_playbin(GthMediaViewerPage * self)1432 gth_media_viewer_page_get_playbin (GthMediaViewerPage *self)
1433 {
1434 return self->priv->playbin;
1435 }
1436
1437
1438 gboolean
gth_media_viewer_page_is_playing(GthMediaViewerPage * self)1439 gth_media_viewer_page_is_playing (GthMediaViewerPage *self)
1440 {
1441 return self->priv->playing;
1442 }
1443
1444
1445 void
gth_media_viewer_page_get_video_fps(GthMediaViewerPage * self,int * video_fps_n,int * video_fps_d)1446 gth_media_viewer_page_get_video_fps (GthMediaViewerPage *self,
1447 int *video_fps_n,
1448 int *video_fps_d)
1449 {
1450 if (video_fps_n != NULL)
1451 *video_fps_n = self->priv->video_fps_n;
1452 if (video_fps_d != NULL)
1453 *video_fps_d = self->priv->video_fps_d;
1454 }
1455
1456
1457 GthFileData *
gth_media_viewer_page_get_file_data(GthMediaViewerPage * self)1458 gth_media_viewer_page_get_file_data (GthMediaViewerPage *self)
1459 {
1460 return self->priv->file_data;
1461 }
1462
1463
1464 void
gth_media_viewer_page_toggle_play(GthMediaViewerPage * self)1465 gth_media_viewer_page_toggle_play (GthMediaViewerPage *self)
1466 {
1467 if (self->priv->playbin == NULL)
1468 return;
1469
1470 if (! self->priv->playing) {
1471 if (! self->priv->paused) {
1472 gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
1473 gst_element_seek (self->priv->playbin,
1474 self->priv->rate,
1475 GST_FORMAT_TIME,
1476 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
1477 GST_SEEK_TYPE_SET,
1478 0.0,
1479 GST_SEEK_TYPE_NONE,
1480 0.0);
1481 }
1482 else {
1483 gint64 current_value;
1484
1485 current_value = (gint64) (gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("position_adjustment"))) / 100.0 * self->priv->duration);
1486 gst_element_seek (self->priv->playbin,
1487 self->priv->rate,
1488 GST_FORMAT_TIME,
1489 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
1490 GST_SEEK_TYPE_SET,
1491 current_value,
1492 GST_SEEK_TYPE_NONE,
1493 0.0);
1494 }
1495 gst_element_set_state (self->priv->playbin, GST_STATE_PLAYING);
1496 }
1497 else
1498 gst_element_set_state (self->priv->playbin, GST_STATE_PAUSED);
1499 }
1500
1501
1502 void
gth_media_viewer_page_set_fit_if_larger(GthMediaViewerPage * self,gboolean fit_if_larger)1503 gth_media_viewer_page_set_fit_if_larger (GthMediaViewerPage *self,
1504 gboolean fit_if_larger)
1505 {
1506 GtkAlign alignment;
1507
1508 self->priv->fit_if_larger = fit_if_larger;
1509 if (self->priv->video_area != NULL) {
1510 alignment = self->priv->fit_if_larger ? GTK_ALIGN_FILL : GTK_ALIGN_CENTER;
1511 gtk_widget_set_valign (self->priv->video_area, alignment);
1512 gtk_widget_set_halign (self->priv->video_area, alignment);
1513
1514 gth_window_change_action_state (GTH_WINDOW (self->priv->browser), "video-zoom-fit", self->priv->fit_if_larger);
1515 }
1516 }
1517