Skip to content

Instantly share code, notes, and snippets.

@eagsalazar
Created July 21, 2010 19:50
Show Gist options
  • Save eagsalazar/485014 to your computer and use it in GitHub Desktop.
Save eagsalazar/485014 to your computer and use it in GitHub Desktop.
using Clutter;
using Gst;
using ClutterGst;
using Mx;
public class VideoPlayer {
private string fileName;
private Pipeline pipeline;
private Element src;
private Element sink;
private Element decode;
private Element colorspace;
public VideoPlayer(string? _fileName, Element _sink) {
fileName = _fileName;
sink = _sink;
setup_pipeline();
}
public void play() {
if(pipeline.current_state == State.PLAYING)
pipeline.set_state(State.PAUSED);
else
pipeline.set_state(State.PLAYING);
}
public void stop() {
pipeline.set_state(State.READY);
}
private void setup_pipeline() {
pipeline = new Pipeline("pipeline");
// To play a video file you need these elements:
// - "filesrc" to read file or "v4l2src" (src)
// - "decodebin" to decode file (filter)
// - "ffmpegcolorspace" to do colorspace conversion (filter)
// - Some sink that we have passed in.
// If we don't have a fileName, then use the video camera, which is fun.
if(fileName == null) {
src = ElementFactory.make("v4l2src", null);
} else {
src = ElementFactory.make("filesrc", null);
// Unlike in GOBject where we use .set_attribute, here we use .set as this is
// built into vala, I guess.
src.set("location", fileName);
}
decode = ElementFactory.make("decodebin", null);
colorspace = ElementFactory.make("ffmpegcolorspace", null);
// decodebins output (src) isnt created until after the state != State.READY. So we wait
// for the new-decoded-pad event and hook up those pads at that time.
//
// The last argument is because on_new_decoded_pad is static so we need to
// provide a reference to the instance variables it will need.
//
// We should be able to simply do:
//
// decode.new_decoded_pad.connect(on_new_decoded_pad);
//
// Where on_new_decoded_pad would be an instance method. However
// There is a bug that requires we do this crap. See on_new_decoded_pad comments.
Signal.connect(decode, "new-decoded-pad", (GLib.Callback) on_new_decoded_pad, colorspace);
// Add all our elements to the pipeline
pipeline.add_many(src, decode, colorspace, sink);
// Hook up the elements that we can hook up. Which pads to hook up is
// resolved automatically by direction and type when not ambiguous.
src.link(decode);
colorspace.link(sink);
}
// The last argument inherits from GPointer in C which in the context means any reference type.
// We declare this method to be static and use the last arg to get a ref back to the instance,
// instead of just making this an instance method because of bug: (which I don't totally understand)
// https://bugzilla.gnome.org/show_bug.cgi?id=615979
private static void on_new_decoded_pad(Element decodebin, Pad pad, bool last, Element colorspace) {
pad.link(colorspace.get_pad("sink"));
}
}
// Clutter.Texture is an actor, for drawing images on. We are going
// to draw video frames on this texture. It exposes a public attribute
// player which is an instance of VideoPlayer.
class VideoTexture : Clutter.Texture {
public VideoPlayer player;
private ClutterGst.VideoSink sink;
// Input args
private string videoFile;
public VideoTexture(string? _videoFile) {
videoFile = _videoFile;
// ClutterGst.VideoSink is a Gst sink element. We pass in this
// actor so that this sink knows to copy frames to that texture.
sink = new ClutterGst.VideoSink(this);
// Create the player
player = new VideoPlayer(videoFile, sink);
}
}
class ClutterGstTest {
private Timeline spin_timeline;
private Timeline fade_out_timeline;
private Timeline fade_in_timeline;
private Timeline scale_up_timeline;
private Timeline scale_down_timeline;
private Alpha spin_alpha;
private BehaviourRotate behaviour_rotate;
private Alpha zoom_alpha_down;
private Alpha zoom_alpha_up;
private BehaviourScale behaviour_scale_down;
private BehaviourScale behaviour_scale_up;
private Alpha fade_alpha_out;
private Alpha fade_alpha_in;
private BehaviourOpacity behaviour_fade_out;
private BehaviourOpacity behaviour_fade_in;
public VideoTexture videoTexture;
private bool on_button_click(ButtonEvent event) {
spin();
return true;
}
public void spin() {
spin_timeline.start();
}
public void zoom() {
scale_down_timeline.start();
}
public void fade() {
fade_out_timeline.start();
}
public void play() {
// Play must be called after the stage has been shown or else segfault!!
// Calling play sets the gst pipelines state to State.PLAYING
videoTexture.player.play();
}
public ClutterGstTest(string? vid, Stage stage, float scale, int loopTime, RotateAxis axis = RotateAxis.X_AXIS) {
videoTexture = new VideoTexture(vid);
videoTexture.height = scale*stage.height;
videoTexture.width = scale*stage.width;
videoTexture.anchor_gravity = Gravity.CENTER;
videoTexture.y = stage.height/2;
videoTexture.x = stage.width/2;
videoTexture.button_press_event.connect(on_button_click);
videoTexture.reactive = true;
// spin
spin_timeline = new Timeline(loopTime);
spin_alpha = new Alpha.full(spin_timeline, AnimationMode.EASE_IN_OUT_QUAD);
behaviour_rotate = new BehaviourRotate(spin_alpha, axis, RotateDirection.CW, 0, 360);
behaviour_rotate.apply(videoTexture);
// zoom
scale_down_timeline = new Timeline(1000);
zoom_alpha_down = new Alpha.full(scale_down_timeline, AnimationMode.EASE_OUT_BOUNCE);
behaviour_scale_down = new BehaviourScale(zoom_alpha_down, 1, 1, 0.2, 0.2);
behaviour_scale_down.apply(videoTexture);
scale_up_timeline = new Timeline(1000);
zoom_alpha_up = new Alpha.full(scale_up_timeline, AnimationMode.EASE_OUT_BOUNCE);
behaviour_scale_up = new BehaviourScale(zoom_alpha_up, 0.2, 0.2, 1, 1);
behaviour_scale_up.apply(videoTexture);
scale_down_timeline.completed.connect(() => {
scale_up_timeline.start();
});
// fade
fade_out_timeline = new Timeline(1000);
fade_alpha_out = new Alpha.full(fade_out_timeline, AnimationMode.LINEAR);
behaviour_fade_out = new BehaviourOpacity(fade_alpha_out, 255, 50);
behaviour_fade_out.apply(videoTexture);
fade_in_timeline = new Timeline(1000);
fade_alpha_in = new Alpha.full(fade_in_timeline, AnimationMode.LINEAR);
behaviour_fade_in = new BehaviourOpacity(fade_alpha_in, 50, 255);
behaviour_fade_in.apply(videoTexture);
fade_out_timeline.completed.connect(() => {
stdout.printf("starting fade in.\n");
fade_in_timeline.start();
});
stage.add_actor(videoTexture);
}
}
class ButtonBar : Mx.BoxLayout {
public signal void play();
public signal void spin1();
public signal void spin2();
public signal void zoom1();
public signal void zoom2();
public signal void fade1();
public signal void fade2();
public ButtonBar() {
reactive = true;
Mx.Button button;
button = new Mx.Button.with_label("Play/Pause");
button.clicked.connect(() => { play(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Spin 1");
button.clicked.connect(() => { spin1(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Spin 2");
button.clicked.connect(() => { spin2(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Zoom 1");
button.clicked.connect(() => { zoom1(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Zoom 2");
button.clicked.connect(() => { zoom2(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Fade 1");
button.clicked.connect(() => { fade1(); });
add((Clutter.Actor)button);
button = new Mx.Button.with_label("Fade 2");
button.clicked.connect(() => { fade2(); });
add((Clutter.Actor)button);
}
// This code is broken until this bug is fixed:
// https://bugzilla.gnome.org/show_bug.cgi?id=624902
/*
delegate void ButtonAction(Button button);
private void create_button(string tooltip, string label, owned ButtonAction callback) {
var button = new Mx.Button.with_label(label);
button.tooltip_text = tooltip;
button.clicked.connect(callback);
add((Clutter.Actor)button);
}
public ButtonBar() {
create_button("Click to play/pause", "Play", (() => { play(); }));
create_button("Click to spin 1", "Spin 1", (() => { spin1(); }));
create_button("Click to spin 2", "Spin 2", (() => { spin2(); }));
}
*/
}
bool on_keypress_event(KeyEvent e) {
Clutter.main_quit();
return true;
}
void main(string[] args) {
var video_file1 = null;
var d = ""; // FIXME - get cwd
var video_file2 = @"$(d)../data/Rendezvous.avi";
// Initialize both Clutter and ClutterGst
Clutter.init(ref args);
Gst.init(ref args);
ClutterGst.init(ref args);
// Each window by default has a stage associated with it
var stage = Stage.get_default();
stage.color = Color.from_string("black");
stage.title = "ClutterGstTest";
stage.width = 800;
stage.height = 600;
var test1 = new ClutterGstTest(video_file1, stage, 0.9f, 1400, RotateAxis.Y_AXIS);
var test2 = new ClutterGstTest(video_file2, stage, 0.2f, 400, RotateAxis.X_AXIS);
test2.videoTexture.x = stage.width - (test2.videoTexture.width/2 + 50f);
test2.videoTexture.y = stage.height - (test2.videoTexture.height/2 + 50f);
test1.play();
test2.play();
var buttonBar = new ButtonBar();
stage.add_actor(buttonBar);
buttonBar.anchor_gravity = Gravity.SOUTH;
float barIdlePos = stage.height + 50f;
buttonBar.y = barIdlePos;
buttonBar.x = stage.width/2;
buttonBar.play.connect(() => {
test1.play();
test2.play();
});
buttonBar.spin1.connect(() => { test1.spin(); });
buttonBar.spin2.connect(() => { test2.spin(); });
buttonBar.zoom1.connect(() => { test1.zoom(); });
buttonBar.zoom2.connect(() => { test2.zoom(); });
buttonBar.fade1.connect(() => { test1.fade(); });
buttonBar.fade2.connect(() => { test2.fade(); });
buttonBar.enter_event.connect(() => {
buttonBar.animate(AnimationMode.LINEAR, 200, "y", stage.height);
return true;
});
buttonBar.leave_event.connect(() => {
buttonBar.animate(AnimationMode.LINEAR, 200, "y", barIdlePos);
return true;
});
unowned Style style = Style.get_default();
try {
style.load_from_file("clutter-gst-test.css");
} catch (GLib.Error e) {
stdout.printf("Error in style.load_from_file: %s\n", e.message);
}
buttonBar.set_style(style);
buttonBar.spacing = 10;
stage.show_all();
Clutter.main();
stage.hide.connect(Clutter.main_quit);
stage.key_press_event.connect(on_keypress_event);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment