Skip to content

Instantly share code, notes, and snippets.

@arsfeld
Created July 7, 2011 21:49
Show Gist options
  • Save arsfeld/1070632 to your computer and use it in GitHub Desktop.
Save arsfeld/1070632 to your computer and use it in GitHub Desktop.
Slideshow demo to show Clutter + GStreamer exporting (compile with valac -g --save-temps --pkg gdk-pixbuf-2.0 --pkg gstreamer-0.10 --pkg gstreamer-app-0.10 --pkg gio-2.0 --pkg clutter-1.0 slideshow.vala)
using Clutter;
using Mx;
class ClutterDemo {
private Stage stage;
private Rectangle[] rectangles;
private List<string> filenames;
private int current;
private Queue<string> queue;
private uint timeout_id;
private Texture image;
private DeformTexture widget;
const int TRANSITION = 2000;
const int TIME = 3;
public void add_filename(string filename) {
var file = File.new_for_path(filename);
if (file.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) {
var enumerator = file.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME, 0);
var file_info = enumerator.next_file();
while (file_info != null) {
this.add_filename(GLib.Path.build_filename(filename, file_info.get_name()));
file_info = enumerator.next_file();
}
} else {
stdout.printf ("Adding: %s\n", filename);
this.filenames.prepend(filename);
}
}
public ClutterDemo (string[] filenames) {
this.filenames = new List<string>();
foreach (string filename in filenames) {
this.add_filename(filename);
}
this.filenames.reverse();
this.queue = new Queue<string>();
this.fill_queue();
this.current = -1;
this.image = null;
stage = (Stage) Stage.get_default ();
stage.hide.connect (Clutter.main_quit);
this.load_next();
this.timeout_id = Timeout.add_seconds(TIME, this.load_next_timeout);
stage.key_press_event.connect((actor, event) => {
Source.remove(timeout_id);
if (event.keyval == 65363)
this.load_next(false);
if (event.unicode_value.tolower() == 'q')
Clutter.main_quit();
this.timeout_id = Timeout.add_seconds(TIME, this.load_next_timeout);
return false;
});
stage.color = Color () { alpha = 255 };
stage.show_all ();
Idle.add(() => {
setup_video();
return false;
});
}
public void setup_video() {
var timer = new Timer();
int width = (int)this.stage.width;
int height = (int)this.stage.height;
var pipeline_desc = @"appsrc name=mysource ! videoparse width=$width height=$height format=rgb ! ffmpegcolorspace ! videoscale method=1 ! videorate ! theoraenc ! oggmux ! filesink location=video.ogv";
var pipeline = (Gst.Pipeline) Gst.parse_launch(pipeline_desc);
var bus = pipeline.get_bus();
var app_src = (Gst.AppSrc) pipeline.get_by_name("mysource");
ulong need_data_id = 0;
app_src.need_data.connect(() => {
//need_data_id = Idle.add(() => {
var ms = timer.elapsed();
debug("Giving data! elapsed %f, %dx%d", ms, width, height);
var buffer = new Gst.Buffer();
uint8[] data = this.stage.read_pixels(0, 0, (int) this.stage.width, (int) this.stage.height);
debug("Has pixels!");
data.length = (width * height * 4);
var pixbuf = new Gdk.Pixbuf.from_data(data, Gdk.Colorspace.RGB, true, 8, width, height, width * 4, null);
pixbuf.save("video%u.png".printf(Random.next_int()), "png");
buffer.data = data;
app_src.push_buffer(buffer);
timer.start();
});
/* set the caps on the source */
var caps = new Gst.Caps.simple("video/x-raw-rgb",
"bpp", typeof(int), 32,
"depth", typeof(int), 24,
"width", typeof(int), width,
"height", typeof(int), height
);
app_src.caps = caps;
debug("Playing");
pipeline.set_state(Gst.State.PLAYING);
}
public bool fill_queue() {
this.queue.clear();
foreach (string filename in this.filenames) {
this.queue.push_head(filename);
}
return (queue.get_length() > 0);
}
public bool load_next_timeout() {
return this.load_next(true);
}
public bool load_next(bool transition = false) {
var previous = this.image;
var previous_widget = this.widget;
if (previous != null) {
if (transition)
previous.animate(AnimationMode.LINEAR, TRANSITION, opacity: 0).completed.connect(() => {
stage.remove_actor(previous);
});
else
stage.remove_actor(previous);
}
Texture? image = null;
while (image == null) {
if (this.queue.is_empty() && !this.fill_queue())
break;
var filename = this.queue.pop_nth(Random.int_range(0, (int32)this.queue.get_length()));
stdout.printf("Queue: %u\n", this.queue.get_length());
try {
image = new Texture();
image.set_from_file(filename);
} catch {
image = null;
stdout.printf("File %s is not a valid image\n", filename);
}
}
if (image == null) {
var text = new Text.full ("Bitstream Vera Sans 18",
"No valid images found!",
Color.from_string ("white"));
text.anchor_gravity = Gravity.CENTER;
text.x = stage.width / 2;
text.y = stage.height / 2;
text.opacity = 0;
stage.add_actor (text);
text.animate (AnimationMode.LINEAR, TRANSITION,
opacity: 255);
return false;
}
image.keep_aspect_ratio = true;
image.set_request_mode(Clutter.RequestMode.WIDTH_FOR_HEIGHT);
image.height = stage.height;
if (image.width > stage.width) {
image.set_request_mode(Clutter.RequestMode.HEIGHT_FOR_WIDTH);
image.width = stage.width;
}
//image.x = this.stage.width / 2 - image.width / 2;
image.x = this.stage.width;
//image.y = -image.width;
image.y = this.stage.height / 2 - image.height / 2;
image.animate(Clutter.AnimationMode.LINEAR, TRANSITION, "x", this.stage.width / 2 - image.width / 2);
this.image = image;
stage.add_actor(image);
return true;
}
public void start () {
var text_group = new Group();
var shadow = new Text.full ("Bitstream Vera Sans 40",
"Congratulations!",
Color.from_string ("black"));
shadow.add_effect(new BlurEffect());
text_group.add_actor(shadow);
var text = new Text.full ("Bitstream Vera Sans 40",
"Congratulations!",
Color.from_string ("white"));
text_group.add_actor(text);
text_group.anchor_gravity = Gravity.CENTER;
text_group.x = stage.width / 2;
text_group.y = -text_group.height; // Off-stage
text.animate (AnimationMode.EASE_IN_OUT_BOUNCE, 3000,
y: stage.height / 2);
}
}
void main (string[] args) {
Clutter.init (ref args);
Gst.init(ref args);
var demo = new ClutterDemo (args[1:args.length]);
demo.start ();
Clutter.main ();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment