#!/usr/bin/python2.4
## -*- coding: utf-8 -*-
# Copyright (c) 2006 INdT - Instituto Nokia de Tecnologia, Elvis Pfutzenreuter.
# All rights reserved. This software is released under the LGPL licence.
import gobject
gobject.threads_init()
import gtk, gtk.glade, gtk.gdk
gtk.gdk.threads_init()
import pygst
import gnome, gnomevfs
import gst
import time
import Image
import os, glob
from os.path import join, dirname, exists, abspath
GLADE = gtk.glade.XML(join(dirname(__file__), "camera.glade"))
PHOTOBOOTH_DIR = join(dirname(__file__), "photobooth")
try:
os.mkdir(PHOTOBOOTH_DIR)
except:
pass
WIDTH, HEIGHT = 640, 480
THUMB_WIDTH, THUMB_HEIGHT = WIDTH/5, HEIGHT/5
EFFECTS = {
"Quark": ("quarktv",),
"Rev": ("revtv",),
"Vertigo": ("vertigotv",),
"Shagadelic": ("shagadelictv",),
"Warp": ("warptv",),
"Dice": ("dicetv",),
"Aging": ("agingtv",),
"Edge": ("edgetv",),
"Color1": ("videobalance", {"saturation": 0.5, "hue": -0.5}),
"Color2": ("videobalance", {"saturation": 0.5, "hue": 0}),
"Color3": ("videobalance", {"saturation": 0.5, "hue": +0.5}),
"Color4": ("videobalance", {"saturation": 1.5, "hue": -0.5}),
"Color5": ("videobalance", {"saturation": 1.5, "hue": 0}),
"Color6": ("videobalance", {"saturation": 1.5, "hue": +0.5}),
"Color7": ("videobalance", {"saturation": 0, "contrast": 0.5}),
"Color8": ("videobalance", {"saturation": 0, "contrast": 2}),
"Color9": ("videobalance", {"saturation": 0.5, "contrast": 2}),
"Color10": ("videobalance", {"saturation": 1, "contrast": 2}),
"Color11": ("videobalance", {"saturation": 2, "contrast": 2}),
"Color12": ("videobalance", {"saturation": 0}),
}
class PhotoboothEngine(gobject.GObject):
__gsignals__ = {
"photo-taken" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_STRING]),
}
def __init__(self):
gobject.GObject.__init__(self)
self.pipeline = gst.Pipeline("photobooth")
self.do_grab_frame = False
self.xwindow = 0
# Alternate sources to run outside of Internet Tablet
#source = gst.element_factory_make("videotestsrc", "src")
#source.set_property("is-live", True)
source = gst.element_factory_make("gconfvideosrc", "src")
self.pipeline.add(source)
self.csp1 = gst.element_factory_make("ffmpegcolorspace", "csp1")
self.pipeline.add(self.csp1)
self.effect = gst.element_factory_make(EFFECTS[EFFECTS.keys()[0]][0], "effect")
self.pipeline.add(self.effect)
self.csp2 = gst.element_factory_make("ffmpegcolorspace", "csp2")
self.pipeline.add(self.csp2)
caps1 = gst.element_factory_make("capsfilter", "caps1")
self.pipeline.add(caps1)
tee = gst.element_factory_make("tee", "tee")
self.pipeline.add(tee)
queuevid = gst.element_factory_make("queue", "queuevid")
self.pipeline.add(queuevid)
csp3 = gst.element_factory_make("ffmpegcolorspace", "csp3")
self.pipeline.add(csp3)
self.sink = gst.element_factory_make("xvimagesink", "sink")
self.pipeline.add(self.sink)
queueimg = gst.element_factory_make("queue", "queueimg")
self.pipeline.add(queueimg)
fakesink = gst.element_factory_make("fakesink", "fakesink")
self.pipeline.add(fakesink)
fakesink.set_property("signal-handoffs", True)
fakesink.connect("handoff", self.on_buffer)
caps1.set_property('caps', gst.caps_from_string("video/x-raw-rgb,width=%d,height=%d,bpp=24,depth=24,framerate=30/1" % (WIDTH, HEIGHT)))
source.link(self.csp1)
self.csp1.link(self.effect)
self.effect.link(self.csp2)
self.csp2.link(caps1)
caps1.link(tee)
tee.link(queuevid)
queuevid.link(csp3)
csp3.link(self.sink)
tee.link(queueimg)
queueimg.link(fakesink)
def on_buffer(self, element, buffer, pad):
if self.do_grab_frame:
self.save_image(buffer)
self.do_grab_frame = False
return True
def save_image(self, buffer):
img = Image.frombuffer("RGB", (WIDTH, HEIGHT), buffer, 'raw', 'RGB', 0, 1)
i = 0
f = join(PHOTOBOOTH_DIR, "img-%02d.jpg" % i)
while exists(f):
i += 1
f = join(PHOTOBOOTH_DIR, "img-%02d.jpg" % i)
img.save(f)
self.emit("photo-taken", f)
print '...Saved: %s' % f
def stop(self):
self.pipeline.set_state(gst.STATE_NULL)
def pause(self):
self.pipeline.set_state(gst.STATE_PAUSED)
def play(self):
self.pipeline.set_state(gst.STATE_PLAYING)
def set_window(self, window):
self.xwindow = window
self.sink.set_xwindow_id(window)
def grab_frame(self):
self.do_grab_frame = True
def set_effect(self, effect, params={}):
self.stop()
self.csp1.unlink(self.effect)
self.effect.unlink(self.csp2)
self.pipeline.remove(self.effect)
print 'Creating effect:', effect, params
self.effect = gst.element_factory_make(effect, "effect")
for key, val in params.items():
self.effect.set_property(key, val)
self.pipeline.add(self.effect)
self.csp1.link(self.effect)
self.effect.link(self.csp2)
self.play()
self.set_window(self.xwindow)
class PhotoboothModel(gtk.ListStore):
def __init__(self, engine):
gtk.ListStore.__init__(self, gtk.gdk.Pixbuf, str)
self.engine = engine
self.engine.connect("photo-taken", self.on_photo_taken)
self.load_existing()
def append_photo(self, f):
pix = gtk.gdk.pixbuf_new_from_file(f)
pix = pix.scale_simple(THUMB_WIDTH, THUMB_HEIGHT, gtk.gdk.INTERP_BILINEAR)
self.append([pix, f])
def load_existing(self):
for f in glob.glob(join(PHOTOBOOTH_DIR, "*.jpg")):
self.append_photo(f)
def on_photo_taken(self, engine, f):
self.append_photo(f)
class PhotoboothWindow:
def __init__(self, engine):
self.engine = engine
self.window = GLADE.get_widget("photobooth")
self.timer_id = 0
self.window.connect("delete_event", self.on_delete_event)
self.window.connect("destroy", self.on_destroy)
self.screen = GLADE.get_widget("screen")
self.screen.set_size_request(500, 380)
self.screen.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.screen.connect("expose-event", self.on_expose);
self.take_picture = GLADE.get_widget("take_picture")
self.take_picture.connect("clicked", self.on_take_picture)
self.status = GLADE.get_widget("status")
self.previews = GLADE.get_widget("previews")
self.preview_model = PhotoboothModel(self.engine)
self.previews.connect("item-activated", self.on_photo_activated)
self.previews.set_model(self.preview_model)
self.previews.set_property("columns", 1000000)
self.previews.set_property("pixbuf-column", 0)
self.previews.set_size_request(WIDTH, THUMB_HEIGHT)
self.effects = GLADE.get_widget("effects")
for effect in EFFECTS.keys():
self.effects.append_text(effect)
self.effects.connect("changed", self.on_effects_changed)
self.effects.set_active(0)
self.engine.connect("photo-taken", self.on_photo_taken)
self.on_photo_taken(self.engine, None)
self.window.show_all()
self.status.hide()
def on_delete_event(self, widget, event):
return False
def on_destroy(self, widget):
engine.stop()
gtk.main_quit()
def on_expose(self, dummy1, dummy2):
self.engine.set_window(self.screen.window.xid)
def on_take_picture(self, button):
print 'Smile !'
if self.timer_id != 0:
return
def foobar():
self.status.set_markup("...Smile!")
self.engine.grab_frame()
self.take_picture.hide()
self.status.show()
self.status.set_markup("3...")
gobject.timeout_add(1000, lambda: self.status.set_markup("...2..."))
gobject.timeout_add(2000, lambda: self.status.set_markup("...1..."))
self.timer_id = gobject.timeout_add(3000, foobar)
def on_photo_taken(self, engine, f):
def foobar():
if len(self.preview_model) == 0:
return
iter = self.preview_model.get_iter_from_string(str(len(self.preview_model)-1))
path = self.preview_model.get_path(iter)
self.previews.scroll_to_path(path, True, 1.0, 0.5)
self.timer_id = 0
self.status.hide()
self.take_picture.show()
gobject.idle_add(foobar)
def on_effects_changed(self, combo):
effect_desc = EFFECTS[self.effects.get_active_text()]
self.engine.set_effect(*effect_desc)
def on_photo_activated(self, effects, path):
gnome.url_show("file://%s" % abspath(self.preview_model[path][1]))
class PhotoboothUploader:
def __init__(self, engine):
self.engine = engine
self.engine.connect("photo-taken", self.on_photo_taken)
def on_photo_taken(self, engine, f):
def foobar():
os.system("./photobooth-upload '%s' &" % abspath(f))
gobject.idle_add(foobar)
engine = PhotoboothEngine()
window = PhotoboothWindow(engine)
uploader = PhotoboothUploader(engine)
engine.play()
gtk.main()