summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis de Bethencourt <luis@debethencourt.com>2011-03-06 19:53:00 +0100
committerLuis de Bethencourt <luis@debethencourt.com>2011-03-06 19:55:46 +0100
commit8cdda32d5317dd1479d77cad7936375258ae6ca2 (patch)
tree31f61d7d05301dd21babc884ec95e3c5b8cb1660
parent078650355e458e45b8f7be66d7e67bbc8d3872fc (diff)
Port the Python prototype to C.
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL0
-rw-r--r--NEWS0
-rw-r--r--README0
-rwxr-xr-xdogme14
-rw-r--r--src/dogme.c636
-rw-r--r--src/dogme.h78
-rwxr-xr-xsrc/dogme.py198
-rw-r--r--src/ui.py143
9 files changed, 714 insertions, 355 deletions
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/INSTALL
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/dogme b/dogme
deleted file mode 100755
index 8ccb89e..0000000
--- a/dogme
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-######## CONFIGURATION OPTIONS ########
-#OPTIONS="/home/username/.config/dogme/"
-# not being used yet
-#######################################
-
-# get the app dir if not already defined
-if [ -z "$PROGRAM_DIR" ]; then
- PROGRAM_DIR=`dirname "$0"`
-fi
-
-#cd ${PROGRAM_DIR}
-exec python -O src/dogme.py "$@"
diff --git a/src/dogme.c b/src/dogme.c
new file mode 100644
index 0000000..7a78b5a
--- /dev/null
+++ b/src/dogme.c
@@ -0,0 +1,636 @@
+/*
+ * gcc `pkg-config --libs --cflags clutter-1.0 clutter-glx-1.0 gstreamer-0.10 clutter-gst-0.10` dogme.c -o dogme
+ * or (if your pkg-config has clutter-gst-1.0 instead of clutter-gst-0.10
+ * gcc `pkg-config --libs --cflags clutter-1.0 clutter-glx-1.0 gstreamer-0.10 clutter-gst-1.0` dogme.c -o dogme
+ *
+ * Dogme video player.
+ *
+ * Copyright (C) 2011 Collabora Multimedia Ltd.
+ * <luis.debethencourt@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-13 01
+ * USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <clutter/clutter.h>
+#include <clutter-gst/clutter-gst.h>
+#include "dogme.h"
+
+static void
+show_controls (UserInterface *ui, gboolean vis)
+{
+ if (vis == TRUE && ui->controls_showing == TRUE)
+ {
+ if (ui->controls_timeout == 0)
+ {
+ ui->controls_timeout =
+ g_timeout_add_seconds (3, controls_timeout_cb, ui);
+ }
+
+ return;
+ }
+
+ if (vis == TRUE && ui->controls_showing == FALSE)
+ {
+ ui->controls_showing = TRUE;
+
+ clutter_stage_show_cursor (CLUTTER_STAGE (ui->stage));
+ clutter_actor_animate (ui->control, CLUTTER_EASE_OUT_QUINT, 250,
+ "opacity", 224,
+ NULL);
+
+ return;
+ }
+
+ if (vis == FALSE && ui->controls_showing == TRUE)
+ {
+ ui->controls_showing = FALSE;
+
+ clutter_stage_hide_cursor (CLUTTER_STAGE (ui->stage));
+ clutter_actor_animate (ui->control, CLUTTER_EASE_OUT_QUINT, 250,
+ "opacity", 0,
+ NULL);
+ return;
+ }
+}
+
+static gboolean
+controls_timeout_cb (gpointer data)
+{
+ UserInterface *ui = data;
+
+ ui->controls_timeout = 0;
+ show_controls (ui, FALSE);
+
+ return FALSE;
+}
+
+static void
+center_controls (UserInterface *ui)
+{
+ gfloat x, y;
+
+ x = (ui->stage_width - clutter_actor_get_width (ui->control)) / 2;
+ y = ui->stage_height - (ui->stage_height / 3);
+
+ g_debug ("stage width = %.2d, height = %.2d\n", ui->stage_width,
+ ui->stage_height);
+ g_debug ("setting x = %.2f, y = %.2f, width = %.2f\n",
+ x, y, clutter_actor_get_width (ui->control));
+
+ clutter_actor_set_position (ui->control, x, y);
+}
+
+static void
+toggle_playing (UserInterface *ui, GstEngine *engine)
+{
+ if (engine->playing) {
+ gst_element_set_state (engine->player, GST_STATE_PAUSED);
+ engine->playing = FALSE;
+ clutter_actor_hide (ui->control_pause);
+ clutter_actor_show (ui->control_play);
+ } else {
+ gst_element_set_state (engine->player, GST_STATE_PLAYING);
+ engine->playing = TRUE;
+ clutter_actor_hide (ui->control_play);
+ clutter_actor_show (ui->control_pause);
+ }
+}
+
+static void
+toggle_fullscreen (UserInterface *ui)
+{
+ if (ui->fullscreen) {
+ clutter_stage_set_fullscreen (CLUTTER_STAGE (ui->stage), FALSE);
+ ui->fullscreen = FALSE;
+ } else {
+ clutter_stage_set_fullscreen (CLUTTER_STAGE (ui->stage), TRUE);
+ ui->fullscreen = TRUE;
+ }
+}
+
+static void
+size_change (ClutterStage *stage,
+ gpointer *data)
+{
+ UserInterface *ui = (UserInterface*)data;
+
+ gfloat stage_width, stage_height;
+ gfloat new_width, new_height;
+ gfloat video_width, video_height;
+ gfloat center, aratio;
+
+ video_width = ui->engine->video_width;
+ video_height = ui->engine->video_height;
+
+ stage_width = clutter_actor_get_width (ui->stage);
+ stage_height = clutter_actor_get_height (ui->stage);
+ ui->stage_width = stage_width;
+ ui->stage_height = stage_height;
+
+ new_width = stage_width;
+ new_height = stage_height;
+ if (video_height <= video_width)
+ {
+ aratio = video_height / video_width;
+ new_height = new_width * aratio;
+ center = (stage_height - new_height) / 2;
+ clutter_actor_set_position (CLUTTER_ACTOR (ui->texture), 0, center);
+ } else {
+ aratio = video_width / video_height;
+ new_width = new_height * aratio;
+ center = (stage_width - new_width) / 2;
+ clutter_actor_set_position (CLUTTER_ACTOR (ui->texture), center, 0);
+ }
+
+ clutter_actor_set_size (CLUTTER_ACTOR (ui->texture),
+ new_width, new_height);
+ center_controls(ui);
+}
+
+static gboolean
+event_cb (ClutterStage *stage,
+ ClutterEvent *event,
+ gpointer data)
+{
+ UserInterface *ui = (UserInterface*)data;
+ gboolean handled = FALSE;
+
+ switch (event->type) {
+ case CLUTTER_KEY_PRESS:
+ {
+ ClutterVertex center = { 0, };
+ ClutterAnimation *animation = NULL;
+
+ center.x - clutter_actor_get_width (ui->texture) / 2;
+ guint keyval = clutter_event_get_key_symbol (event);
+ switch (keyval) {
+ case CLUTTER_q:
+ case CLUTTER_Escape:
+ clutter_main_quit ();
+ break;
+ case CLUTTER_f:
+ // Fullscreen button
+ toggle_fullscreen (ui);
+ handled = TRUE;
+ break;
+ case CLUTTER_8:
+ {
+ // Mute button
+ gboolean muteval;
+ g_object_get (G_OBJECT (ui->engine->player), "mute",
+ &muteval, NULL);
+ g_object_set (G_OBJECT (ui->engine->player), "mute",
+ ! muteval, NULL);
+ if (muteval) {
+ g_debug ("Unmute stream\n");
+ } else {
+ g_debug ("Mute stream\n");
+ }
+ handled = TRUE;
+ break;
+ }
+
+ case CLUTTER_9:
+ case CLUTTER_0:
+ {
+ gdouble volume;
+ g_object_get (G_OBJECT (ui->engine->player), "volume",
+ &volume, NULL);
+ // Volume Down
+ if (keyval == CLUTTER_9 && volume > 0.0) {
+ g_object_set (G_OBJECT (ui->engine->player), "volume",
+ volume -= 0.05, NULL);
+ g_debug ("Volume down: %f", volume);
+
+ // Volume Up
+ } else if (keyval == CLUTTER_0 && volume < 1.0) {
+ g_object_set (G_OBJECT (ui->engine->player), "volume",
+ volume += 0.05, NULL);
+ g_debug ("Volume up: %f", volume);
+ }
+ handled = TRUE;
+ break;
+ }
+
+ case CLUTTER_Up:
+ case CLUTTER_Down:
+ case CLUTTER_Left:
+ case CLUTTER_Right:
+ {
+ gint64 pos;
+ GstFormat fmt = GST_FORMAT_TIME;
+ gst_element_query_position (ui->engine->player, &fmt,
+ &pos);
+ // Seek 1 minute foward
+ if (keyval == CLUTTER_Up) {
+ pos += 60 * GST_SECOND;
+ g_debug("Skipping 1 minute ahead in the stream\n");
+
+ // Seek 1 minute back
+ } else if (keyval == CLUTTER_Down) {
+ pos -= 60 * GST_SECOND;
+ g_debug("Moving 1 minute back in the stream\n");
+
+ // Seek 10 seconds back
+ } else if (keyval == CLUTTER_Left) {
+ pos -= 10 * GST_SECOND;
+ g_debug("Moving 10 seconds back in the stream\n");
+
+ // Seek 10 seconds foward
+ } else if (keyval == CLUTTER_Right) {
+ pos += 10 * GST_SECOND;
+ g_debug("Skipping 10 seconds ahead in the stream\n");
+ }
+
+ gst_element_seek_simple (ui->engine->player,
+ fmt, GST_SEEK_FLAG_FLUSH,
+ pos);
+
+ handled = TRUE;
+ break;
+ }
+ case CLUTTER_r:
+ // rotate video 90 degrees.
+ handled = TRUE;
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+ }
+
+ case CLUTTER_BUTTON_PRESS:
+ {
+ if (ui->controls_showing) {
+ ClutterActor *actor;
+ ClutterButtonEvent *bev = (ClutterButtonEvent *) event;
+
+ actor = clutter_stage_get_actor_at_pos (stage,
+ CLUTTER_PICK_ALL, bev->x, bev->y);
+ if (actor == ui->control_pause || actor == ui->control_play) {
+ toggle_playing (ui, ui->engine);
+ }
+ else if (actor == ui->control_seek1 ||
+ actor == ui->control_seek2 ||
+ actor == ui->control_seekbar) {
+ gfloat x, y, dist;
+ gint64 progress;
+
+ clutter_actor_get_transformed_position (ui->control_seekbar,
+ &x, &y);
+ dist = bev->x - x;
+ dist = CLAMP (dist, 0, SEEK_WIDTH);
+
+ if (ui->engine->video_duration == -1)
+ {
+ update_video_duration (ui->engine);
+ }
+
+ progress = ui->engine->video_duration * (dist / SEEK_WIDTH);
+ gst_element_seek_simple (ui->engine->player,
+ GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ progress);
+ clutter_actor_set_size (ui->control_seekbar,
+ dist, SEEK_HEIGHT);
+ }
+ }
+ handled = TRUE;
+ break;
+ }
+
+ case CLUTTER_MOTION:
+ {
+ show_controls (ui, TRUE);
+ handled = TRUE;
+ break;
+ }
+ }
+
+ return handled;
+}
+
+static gboolean
+update_video_duration (GstEngine *engine)
+{
+ gboolean success = FALSE;
+
+ GstFormat fmt = GST_FORMAT_TIME;
+ if (gst_element_query_duration (engine->player, &fmt,
+ &engine->video_duration))
+ {
+ if (engine->video_duration != -1 && fmt == GST_FORMAT_TIME) {
+ g_debug ("Media duration: %ld\n", engine->video_duration);
+ success = TRUE;
+ } else {
+ g_debug ("Could not get media's duration\n");
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
+static gboolean
+progress_update (gpointer data)
+{
+ UserInterface *ui = (UserInterface*)data;
+ GstEngine *engine = ui->engine;
+ gfloat progress = 0.0;
+
+ if (engine->video_duration == -1)
+ {
+ update_video_duration (engine);
+ }
+
+ gint64 pos;
+ GstFormat fmt = GST_FORMAT_TIME;
+ gst_element_query_position (engine->player, &fmt, &pos);
+ progress = (float) pos / engine->video_duration;
+ g_debug ("playback position progress: %f\n", progress);
+
+ clutter_actor_set_size (ui->control_seekbar, progress * SEEK_WIDTH,
+ SEEK_HEIGHT);
+
+ return TRUE;
+}
+
+static gboolean
+bus_call (GstBus *bus, GstMessage *msg, gpointer data)
+{
+ GstEngine *engine = (GstEngine*)data;
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:
+ g_debug ("End-of-stream\n");
+ break;
+ case GST_MESSAGE_ERROR: {
+ gchar *debug = NULL;
+ GError *err = NULL;
+
+ gst_message_parse_error (msg, &err, &debug);
+
+ g_debug ("Error: %s\n", err->message);
+ g_error_free (err);
+
+ if (debug) {
+ g_debug ("Debug details: %s\n", debug);
+ g_free (debug);
+ }
+
+ break;
+ }
+ case GST_MESSAGE_STATE_CHANGED:
+ {
+ GstState old, new, pending;
+ gst_message_parse_state_changed (msg, &old, &new, &pending);
+ if (new == GST_STATE_PAUSED)
+ {
+ if (engine->video_width == -1)
+ {
+ GstPad *p = gst_element_get_pad (engine->sink, "sink");
+ GstCaps *c = gst_pad_get_negotiated_caps (p);
+ if (c)
+ {
+ GstStructure *s = gst_caps_get_structure (c, 0);
+ const GValue *widthval, *heightval;
+ widthval = gst_structure_get_value (s, "width");
+ heightval = gst_structure_get_value (s, "height");
+ if (G_VALUE_HOLDS (widthval, G_TYPE_INT))
+ {
+ gint width, height;
+ width = g_value_get_int (widthval);
+ height = g_value_get_int (heightval);
+ g_debug ("Setting width: %d, height: %d\n", width,
+ height);
+ engine->video_width = width;
+ engine->video_height = height;
+ load_user_interface (engine->ui);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gchar *
+cut_long_filename(gchar *filename)
+{
+ gchar *ret;
+ gint c;
+ gint max_size = 32;
+ for (c = 0; filename[c] != '\0'; c++);
+
+ if (c > max_size)
+ {
+ gchar short_filename[max_size];
+
+ for (c = 0; c < (max_size -1); c++)
+ {
+ short_filename[c] = filename[c];
+ }
+ short_filename[max_size - 1] = '\0';
+ ret = g_locale_to_utf8 (short_filename, max_size, NULL, NULL, NULL);
+ } else {
+ ret = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
+ }
+
+ return ret;
+}
+
+static void
+load_user_interface (UserInterface *ui)
+{
+ char *env;
+ env = getenv("PWD");
+
+ // Stage
+ ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 };
+ ClutterColor control_color1 = { 73, 74, 77, 0xee };
+ ClutterColor control_color2 = { 0xcc, 0xcc, 0xcc, 0xff };
+ ui->filename = g_path_get_basename (ui->filepath);
+
+ ui->video_width = ui->engine->video_width;
+ ui->video_height = ui->engine->video_height;
+ ui->stage_width = ui->engine->video_width;
+ ui->stage_height = ui->engine->video_height;
+ ui->stage = clutter_stage_get_default();
+ clutter_stage_set_color (CLUTTER_STAGE (ui->stage), &stage_color);
+ clutter_stage_set_minimum_size (CLUTTER_STAGE (ui->stage),
+ ui->stage_width, ui->stage_height);
+ clutter_stage_set_title (CLUTTER_STAGE (ui->stage), ui->filename);
+
+ if (ui->fullscreen) {
+ clutter_stage_set_fullscreen (CLUTTER_STAGE (ui->stage), TRUE);
+ } else {
+ clutter_actor_set_size (CLUTTER_ACTOR (ui->stage), ui->stage_width,
+ ui->stage_height);
+ }
+
+ // Controls
+ char *vid_panel_png = malloc (strlen (env) + strlen("/../img/vid-panel.png") + 2);
+ sprintf (vid_panel_png, "%s%s", env, "/../img/vid-panel.png");
+ char *play_png = malloc (strlen (env) + strlen("/../img/media-actions-start.png") + 2);
+ sprintf (play_png, "%s%s", env, "/../img/media-actions-start.png");
+ char *pause_png = malloc (strlen (env) + strlen("/../img/media-actions-pause.png") + 2);
+ sprintf (pause_png, "%s%s", env, "/../img/media-actions-pause.png");
+
+ ui->control = clutter_group_new ();
+ ui->control_bg =
+ clutter_texture_new_from_file (vid_panel_png, NULL);
+ ui->control_play =
+ clutter_texture_new_from_file (play_png, NULL);
+ ui->control_pause =
+ clutter_texture_new_from_file (pause_png, NULL);
+
+ g_assert (ui->control_bg && ui->control_play && ui->control_pause);
+
+ ui->control_seek1 = clutter_rectangle_new_with_color (&control_color1);
+ ui->control_seek2 = clutter_rectangle_new_with_color (&control_color2);
+ ui->control_seekbar = clutter_rectangle_new_with_color (&control_color1);
+ clutter_actor_set_opacity (ui->control_seekbar, 0x99);
+
+ ui->control_label =
+ clutter_text_new_full ("Sans Bold 24", cut_long_filename (ui->filename),
+ &control_color1);
+
+ clutter_actor_hide (ui->control_play);
+
+ clutter_container_add (CLUTTER_CONTAINER (ui->control),
+ ui->control_bg,
+ ui->control_play,
+ ui->control_pause,
+ ui->control_seek1,
+ ui->control_seek2,
+ ui->control_seekbar,
+ ui->control_label,
+ NULL);
+
+ clutter_actor_set_opacity (ui->control, 0xee);
+
+ clutter_actor_set_position (ui->control_play, 30, 30);
+ clutter_actor_set_position (ui->control_pause, 30, 30);
+
+ clutter_actor_set_size (ui->control_seek1, SEEK_WIDTH+10, SEEK_HEIGHT+10);
+ clutter_actor_set_position (ui->control_seek1, 200, 100);
+ clutter_actor_set_size (ui->control_seek2, SEEK_WIDTH, SEEK_HEIGHT);
+ clutter_actor_set_position (ui->control_seek2, 205, 105);
+ clutter_actor_set_size (ui->control_seekbar, 0, SEEK_HEIGHT);
+ clutter_actor_set_position (ui->control_seekbar, 205, 105);
+
+ clutter_actor_set_position (ui->control_label, 200, 40);
+
+ // Add control UI to stage
+ clutter_container_add (CLUTTER_CONTAINER (ui->stage),
+ ui->texture,
+ ui->control,
+ NULL);
+
+ clutter_stage_hide_cursor (CLUTTER_STAGE (ui->stage));
+ clutter_actor_animate (ui->control, CLUTTER_EASE_OUT_QUINT, 1000,
+ "opacity", 0, NULL);
+
+ g_signal_connect (CLUTTER_TEXTURE (ui->stage), "fullscreen",
+ G_CALLBACK (size_change), ui);
+ g_signal_connect (CLUTTER_TEXTURE (ui->stage), "unfullscreen",
+ G_CALLBACK (size_change), ui);
+ g_signal_connect (ui->stage, "event", G_CALLBACK (event_cb), ui);
+
+ g_timeout_add (2000, progress_update, ui);
+
+ center_controls (ui);
+ clutter_actor_show(ui->stage);
+}
+
+int main (int argc, char *argv[])
+{
+ // Command line arguments.
+ if (argc < 2)
+ {
+ g_print ("Usage: %s [options] <media_file>\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ gboolean fullscreen = FALSE;
+ guint c, index, pos = 0;
+ gchar *file_list[argc];
+ while ((c = getopt (argc, argv, "f")) != -1)
+ switch (c)
+ {
+ case 'f':
+ g_debug ("fullscreen!\n");
+ fullscreen = TRUE;
+ break;
+ }
+ for (index = optind; index < argc; index++)
+ {
+ g_debug ("Adding file: %s\n", argv[index]);
+ file_list[pos] = argv[index];
+ pos++;
+ }
+
+ // User Interface
+ UserInterface *ui = NULL;
+ ui = g_new0(UserInterface, 1);
+ ui->fullscreen = fullscreen;
+
+ clutter_gst_init (&argc, &argv);
+ // Gstreamer
+ GstEngine *engine = NULL;
+ engine = g_new0(GstEngine, 1);
+ engine->video_width = -1;
+ engine->video_height = -1;
+ engine->ui = ui;
+ ui->engine = engine;
+
+ engine->player = gst_element_factory_make ("playbin2", "playbin2");
+ if (engine->player == NULL){
+ g_print ("ERROR: Failed to create playbin element\n");
+ return 1;
+ }
+ ClutterActor *texture = clutter_texture_new ();
+ engine->sink = clutter_gst_video_sink_new (CLUTTER_TEXTURE (texture));
+ g_object_set (G_OBJECT (engine->player), "video-sink", engine->sink, NULL);
+ engine->bus = gst_pipeline_get_bus (GST_PIPELINE (engine->player));
+ gst_bus_add_watch (engine->bus, bus_call, engine);
+ gst_object_unref (engine->bus);
+ ui->texture = texture;
+
+ gchar *filepath = file_list[0];
+ engine->uri = NULL;
+ asprintf(&engine->uri, "file://%s", filepath);
+ g_object_set (G_OBJECT (engine->player), "uri", engine->uri, NULL);
+ engine->filepath = filepath;
+ ui->filepath = filepath;
+ GstStateChange ret;
+ gst_element_set_state (engine->player, GST_STATE_PAUSED);
+ engine->playing = FALSE;
+ engine->video_duration = -1;
+
+ gst_element_set_state (engine->player, GST_STATE_PLAYING);
+ clutter_main ();
+
+ return 0;
+}
diff --git a/src/dogme.h b/src/dogme.h
new file mode 100644
index 0000000..bad7b14
--- /dev/null
+++ b/src/dogme.h
@@ -0,0 +1,78 @@
+/*
+ * Dogme video player.
+ *
+ * Copyright (C) 2011 Collabora Multimedia Ltd.
+ * <luis.debethencourt@collabora.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-13 01
+ * USA
+ */
+
+#define SEEK_HEIGHT 20
+#define SEEK_WIDTH 640
+
+typedef struct GstEngine GstEngine;
+typedef struct UserInterface UserInterface;
+
+struct GstEngine
+{
+ gchar *uri, *filepath;
+ guint video_width, video_height;
+ gboolean playing;
+ gint64 video_duration;
+
+ GstElement *player;
+ GstElement *sink;
+
+ GstBus *bus;
+ UserInterface *ui;
+};
+
+struct UserInterface
+{
+ gchar *filename, *filepath;
+ ClutterActor *stage;
+
+ ClutterColor stage_color, control_color1, control_color2;
+
+ ClutterActor *texture;
+ ClutterActor *control;
+ ClutterActor *control_bg, *control_label, *control_play,
+ *control_pause, *control_seek1, *control_seek2,
+ *control_seekbar;
+
+ gboolean controls_showing, fullscreen;
+ guint controls_timeout;
+
+ guint video_width, video_height;
+ guint stage_width, stage_height;
+
+ GstEngine *engine;
+
+};
+
+static void show_controls (UserInterface *ui, gboolean vis);
+static gboolean controls_timeout_cb (gpointer data);
+static void center_controls (UserInterface *ui);
+static void toggle_playing (UserInterface *ui, GstEngine *engine);
+static void toggle_fullscreen (UserInterface *ui);
+static void size_change (ClutterStage *stage, gpointer *data);
+static gboolean event_cb (ClutterStage *stage, ClutterEvent *event,
+ gpointer data);
+static gboolean update_video_duration (GstEngine *engine);
+static gboolean progress_update (gpointer data);
+static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data);
+static gchar * cut_long_filename (gchar *filename);
+static void load_user_interface (UserInterface *ui);
diff --git a/src/dogme.py b/src/dogme.py
deleted file mode 100755
index ecc2599..0000000
--- a/src/dogme.py
+++ /dev/null
@@ -1,198 +0,0 @@
-#!/usr/bin/env python
-
-# Dogme video player.
-
-# Copyright (C) 2011 Collabora Multimedia Ltd.
-# <luis.debethencourt@collabora.co.uk>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
-# USA
-
-""" dogme video player's main file """
-
-import gobject
-gobject.threads_init()
-import gst
-import clutter
-import cluttergst
-import os, optparse
-
-from ui import UI
-
-class Dogme:
- def toggle_playing_state(self):
- if self.playing:
- self.player.set_state(gst.STATE_PAUSED)
- self.playing = False
- self.ui.control_pause.hide()
- self.ui.control_play.show()
-
- else:
- self.player.set_state(gst.STATE_PLAYING)
- self.playing = True
- self.ui.control_play.hide()
- self.ui.control_pause.show()
-
- def toggle_fullscreen(self):
- new_state = not self.fullscreen
- self.ui.toggle_fullscreen(new_state)
- self.fullscreen = new_state
-
- def event (self, stage, event):
- # Key pressed event.
- if event.type == clutter.KEY_PRESS:
- #print "key pressed ", event.keyval, "."
- if event.keyval == 65363:
- pos = self.player.query_position(gst.FORMAT_TIME)[0]
- pos += 10 * gst.SECOND
- self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, \
- pos)
- return True
- if event.keyval == 65361:
- pos = self.player.query_position(gst.FORMAT_TIME)[0]
- if pos > ( 10 * gst.SECOND):
- pos -= 10 * gst.SECOND
- else:
- pos = 0
- self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, \
- pos)
- return True
- if event.keyval == 65307 or chr(event.keyval) == 'q':
- clutter.main_quit()
- elif chr(event.keyval) == 'f':
- self.toggle_fullscreen()
- return True
- elif chr(event.keyval) == ' ':
- self.toggle_playing_state()
- return True
- elif chr(event.keyval) == '9':
- volume = self.player.get_property("volume")
- if volume < 1.0:
- volume += 0.1
- self.player.set_property("volume", volume)
- return True
- elif chr(event.keyval) == '0':
- volume = self.player.get_property("volume")
- if volume > 0.0:
- volume -= 0.1
- self.player.set_property("volume", volume)
- return True
- elif chr(event.keyval) == '8':
- mute = self.player.get_property("mute")
- self.player.set_property("mute", not mute)
- return True
-
- # Mouse click event.
- elif event.type == clutter.BUTTON_PRESS:
- actor = self.ui.stage.get_actor_at_pos (clutter.PICK_ALL, \
- int(event.x), int(event.y))
- if (actor == self.ui.control_pause or actor == self.ui.control_play):
- self.toggle_playing_state()
- elif (actor == self.ui.control_seek1 or \
- actor == self.ui.control_seek2 or \
- actor == self.ui.control_seekbar):
- x, y = self.ui.control_seekbar.get_transformed_position()
- dist = event.x - x
- if self.duration == 0:
- self.duration = self.player.query_duration(gst.FORMAT_TIME)[0]
- progress = self.duration * (dist / self.ui.seek_W)
- self.player.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, \
- progress)
- self.ui.control_seekbar.set_size (dist, self.ui.seek_H)
- return True
-
- # Cursor motion event
- elif event.type == clutter.MOTION:
- self.ui.show_controls (True)
- return True
-
- def progress_update (self, data):
- pos = self.player.query_position(gst.FORMAT_TIME)[0]
- if self.duration == 0:
- self.duration = self.player.query_duration(gst.FORMAT_TIME)[0]
- pos = float(pos) / self.duration
- self.ui.control_seekbar.set_size (pos * self.ui.seek_W, self.ui.seek_H)
-
- return True
-
- def load_ui (self):
- if not self.ui_loaded:
- self.ui = UI (self.texture, self.mediafile, self.width, self.height)
- self.ui.stage.connect('destroy', clutter.main_quit)
- self.ui.stage.connect('event', self.event)
- self.ui.stage.connect('fullscreen', self.ui.change_on_size)
- self.ui.stage.connect('unfullscreen', self.ui.change_on_size)
-
- self.playing = True
- self.player.set_state(gst.STATE_PLAYING)
-
- self.ui.stage.show_all()
-
- if self.fullscreen:
- self.ui.stage.set_fullscreen(True)
- self.ui_loaded = True
-
- def __init__ (self, args, options):
- self.mediafile = args[0]
- self.fullscreen = options.fullscreen
- self.duration = 0
- self.width = 0
- self.height = 0
- self.ui_loaded = False
-
- def bus_handler(unused_bus, message):
- if message.type == gst.MESSAGE_STATE_CHANGED:
- old, new, pending = message.parse_state_changed()
- if new == gst.STATE_PAUSED:
- if self.width == 0:
- pad = self.sink.get_pad("sink")
- caps = pad.get_negotiated_caps()
- if caps:
- s = caps.get_structure(0)
- if s.has_key('width'):
- self.width = s['width']
- if s.has_key('height'):
- self.height = s['height']
- self.load_ui()
- if message.type == gst.MESSAGE_ERROR:
- pass
- return gst.BUS_PASS
-
- self.player = gst.element_factory_make("playbin2", "player")
- self.texture = clutter.Texture()
- self.sink = cluttergst.VideoSink(self.texture)
- self.player.set_property("video-sink", self.sink)
- self.bus = self.player.get_bus()
- self.bus.add_signal_watch()
- self.bus.connect("message", bus_handler)
-
- self.player.set_property("uri", "file://" + self.mediafile)
- self.player.set_state(gst.STATE_PAUSED)
-
- gobject.timeout_add(2000, self.progress_update, self)
-
- clutter.main()
-
-
-if __name__ == "__main__":
- parser = optparse.OptionParser("usage: %prog [options] arg")
- parser.add_option("-f", "--fullscreen", action="store_true", dest="fullscreen", help="Start in FullScreen mode", default=False)
- (options, args) = parser.parse_args()
- if len(args) < 1:
- parser.error("You need to specify a media file.")
-
- print "Playing... %r" % args[0]
-
- dogme = Dogme (args, options)
diff --git a/src/ui.py b/src/ui.py
deleted file mode 100644
index e8b30bf..0000000
--- a/src/ui.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# Copyright (C) 2011 Collabora Multimedia Ltd.
-# <luis.debethencourt@collabora.co.uk>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
-# USA
-
-import clutter
-import os
-import gobject
-
-
-class UI:
- def __init__ (self, video_texture, mediafile, width, height):
- self.video_width = width
- self.video_height = height
- self.video_texture = video_texture
- self.color1 = clutter.Color(73, 74, 77, 0xee);
- self.color2 = clutter.Color(0xcc, 0xcc, 0xcc, 0xff);
- self.black = clutter.Color(0, 0, 0, 0)
- self.controls_showing = False
- self.controls_timeout = 0
- self.seek_H = 20
- self.seek_W = 640
-
- self.stage = clutter.stage_get_default()
- self.stage.set_color(self.black)
- self.stage.set_size(self.video_width, self.video_height)
- filename = os.path.split(mediafile)[1]
- self.stage.set_title('dogme~ ' + filename)
-
- self.controls = clutter.Group()
- self.control_bg = clutter.Texture(filename="img/vid-panel.png")
- self.control_play = clutter.Texture(filename="img/media-actions-start.png")
- self.control_pause = clutter.Texture(filename="img/media-actions-pause.png")
- self.control_seek1 = clutter.Rectangle(color=self.color1)
- self.control_seek2 = clutter.Rectangle(color=self.color2)
- self.control_seekbar = clutter.Rectangle(color=self.color1)
- self.control_seekbar.set_opacity(0x99)
-
- self.control_label = clutter.Text()
- self.control_label.set_font_name("Sans Bold 24")
- if len(filename) > 30:
- filename = filename[:29]
- self.control_label.set_text(filename)
-
- self.controls.hide()
- self.controls.add(self.control_bg, \
- self.control_play, \
- self.control_pause, \
- self.control_seek1, \
- self.control_seek2, \
- self.control_seekbar, \
- self.control_label)
- self.controls.set_opacity (0xee)
-
- self.control_play.hide()
- self.control_play.set_position (30, 30)
- self.control_pause.set_position (30, 30)
-
- self.control_seek1.set_size(self.seek_W + 10, self.seek_H + 10)
- self.control_seek1.set_position (200, 100)
- self.control_seek2.set_size(self.seek_W, self.seek_H)
- self.control_seek2.set_position (205, 105)
- self.control_seekbar.set_size(0, self.seek_H / 5)
- self.control_seekbar.set_position (205, 105)
- self.control_label.set_position (200, 40)
-
- self.stage.add(self.video_texture, self.controls)
- width, height = self.stage.get_size()
- self.center_controls(width, height)
-
- self.stage.hide_cursor()
- self.controls.animate(clutter.AnimationMode(clutter.EASE_OUT_QUINT), \
- 1000, "opacity", 0)
-
- def controls_timeout_cb (self, data):
- self.controls_timeout = 0
- self.show_controls (False)
-
- return False
-
- def show_controls (self, visibility):
- if (visibility == True and self.controls_showing == True):
- if self.controls_timeout == 0:
- self.controls_timeout = gobject.timeout_add (3000, \
- self.controls_timeout_cb, self)
- return
- if (visibility == True and self.controls_showing == False):
- self.controls_showing = True
- self.stage.show_cursor()
- self.controls.animate(
- clutter.AnimationMode(clutter.EASE_OUT_QUINT), \
- 250, "opacity", 224)
- return
- if (visibility == False and self.controls_showing == True):
- self.controls_showing = False
- self.stage.hide_cursor()
- self.controls.animate(
- clutter.AnimationMode(clutter.EASE_OUT_QUINT), \
- 250, "opacity", 0)
- return
-
- def center_controls (self, width, height):
- x = (width - self.controls.get_width()) / 2
- y = height - (height / 3)
-
- self.controls.set_position (x, y)
-
- def toggle_fullscreen (self, mode):
- if mode:
- self.stage.set_fullscreen(True)
- else:
- self.stage.set_fullscreen(False)
-
- def change_on_size (self, data):
- stage_width, stage_height = self.stage.get_size()
- new_width, new_height = stage_width, stage_height
-
- if self.video_height <= self.video_width:
- aratio = self.video_height / float(self.video_width)
- new_height = new_width * aratio
- center = (stage_height - new_height) / 2
- self.video_texture.set_position (0, center)
- else:
- aratio = self.video_width / float(self.video_height)
- new_width = new_height * aratio
- center = (stage_width - new_width) / 2
- self.video_texture.set_position (center, 0)
-
- self.video_texture.set_size (new_width, new_height)
- self.center_controls(stage_width, stage_height)