#!/usr/bin/python # 2007-07 # test-dr // based on my c-coding tests for picture injections into video # first try as pyhton-src - based on the gst-examples like gst-python-0.10.7/examples/filesrc.py import inspect import sys import random import string import gc import pygst pygst.require("0.10") import gobject import gst import pygtk import gtk import copy def get_attributes(obj, public_only=True): attrs = inspect.getmembers(obj, lambda o: not inspect.isroutine(o)) if public_only: return [x for x in attrs if x[0][0] != "_"] else: return attrs class PictureSource(gst.BaseSrc): __gsttemplates__ = ( gst.PadTemplate("src", gst.PAD_SRC, gst.PAD_ALWAYS, gst.caps_new_any()), ) filename = "" width = 0 height = 0 framerate = 10 pixbuf = None last_pixbuf = None out_pixbuf = None pic_duration = 100 pic_number = 0 frame = 0 step_scale = 0 transition_duration = 2 transition_type = 0 random_value = range(600) datarate = 0 def __init__(self, name): self.__gobject_init__() self.curoffset = 0 self.set_name(name) def set_property(self, name, value): if name == 'location': self.filename = value pixbuf = gtk.gdk.pixbuf_new_from_file(self.filename) if self.width == 0: self.width = pixbuf.get_width() if self.height == 0: self.height = pixbuf.get_height() # scale picture, if not the right size if self.height != pixbuf.get_height() or self.width != pixbuf.get_width: self.pixbuf = pixbuf.scale_simple(self.width, self.height, gtk.gdk.INTERP_BILINEAR) print "new picture ", value if name == "width": self.width = value if name == "height": self.height = value if name == "pic_duration": self.pic_duration = value if name == "framerate": self.framerate = value if name == "transition_type": self.transition_type = value if name == "transition_duration": self.transition_duration = value def get_property(self, name): if name == "width": return self.width if name == "height": return self.height if name == "framerate": return self.framerate def do_create(self, offset, size): if self.datarate == 0: self.datarate = self.width * self.height * 3 * self.framerate if self.frame % self.pic_duration == 0: if self.pic_number >= len(pic_names): return gst.FLOW_UNEXPECTED, None if self.transition_type > 0 and self.pic_number > 0: self.step_scale = self.transition_duration * self.framerate if self.last_pixbuf: del self.last_pixbuf self.last_pixbuf = self.pixbuf.copy() del self.pixbuf self.set_property("location", pic_names[self.pic_number]) self.pic_number += 1 # need this gc-stuff or memory usage grows on and on ... gc.collect() if not self.pixbuf: return gst.FLOW_UNEXPECTED, None # !! at the moment this should always be the same, because the pic is scaled when loaded # or should i do some more checks with self.width/height width = self.pixbuf.get_width() height = self.pixbuf.get_height() #print "settings pic pixbuf: %s (%d x %d)" % (self.filename, width, height) if self.step_scale > 0: step_scale = self.step_scale transition_type = self.transition_type steps = self.transition_duration * self.framerate print "step_scale:", step_scale self.out_pixbuf = self.last_pixbuf.copy() if transition_type == 1: self.pixbuf.scale(self.out_pixbuf, 0, 0, width/step_scale, height/step_scale, 0, 0, 1.0/step_scale, 1.0/step_scale, gtk.gdk.INTERP_BILINEAR) if transition_type == 2: self.pixbuf.composite(self.out_pixbuf, 0, 0, width/step_scale, height/step_scale, 0, 0, 1.0/step_scale, 1.0/step_scale, gtk.gdk.INTERP_BILINEAR, 100+(150/step_scale)) if transition_type == 3: self.pixbuf.scale(self.out_pixbuf, width*(step_scale-1)/(2*step_scale), height*(step_scale-1)/(2*step_scale), width/step_scale, height/step_scale, width*(step_scale-1)/(2*step_scale), height*(step_scale-1)/(2*step_scale), 1.0/step_scale, 1.0/step_scale, gtk.gdk.INTERP_BILINEAR) if transition_type == 4: self.pixbuf.composite(self.out_pixbuf, width*(step_scale-1)/(2*step_scale), height*(step_scale-1)/(2*step_scale), width/step_scale, height/step_scale, width*(step_scale-1)/(2*step_scale), height*(step_scale-1)/(2*step_scale), 1.0/step_scale, 1.0/step_scale, gtk.gdk.INTERP_BILINEAR, 100+(150/step_scale)) if transition_type == 5: self.pixbuf.composite(self.out_pixbuf, 0, 0, width, height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 50+(200/step_scale)) if transition_type == 6: del self.out_pixbuf self.out_pixbuf = self.pixbuf.copy() self.last_pixbuf.composite(self.out_pixbuf, 0, height - ((height/steps)*step_scale), width, ((height/steps)*step_scale), 0, height - ((height/steps)*step_scale), 1.0, 1.0 - (1.0/step_scale), gtk.gdk.INTERP_BILINEAR, 150+step_scale*100/steps) # scale the old picture down in the height, like melte down to the bottom if transition_type == 7: del self.out_pixbuf self.out_pixbuf = self.pixbuf.copy() self.last_pixbuf.composite(self.out_pixbuf, 0, height - ((height/steps)*step_scale), width, ((height/steps)*step_scale), 0, height - ((height/steps)*step_scale), 1.0, step_scale*(1.0/steps), gtk.gdk.INTERP_BILINEAR, 150+step_scale*100/steps) # move old picture out to the right with a bit down-scaling in the width if transition_type == 8: del self.out_pixbuf self.out_pixbuf = self.pixbuf.copy() self.last_pixbuf.composite(self.out_pixbuf, width - ((width/steps)*step_scale), 0, ((width/steps)*step_scale), height, width - ((width/steps)*step_scale), 0, 1.0, 1.0 - (1.0/step_scale), gtk.gdk.INTERP_BILINEAR, 150+step_scale*100/steps) # move old picture out to the left without scaling if transition_type == 9: del self.out_pixbuf self.out_pixbuf = self.pixbuf.copy() self.last_pixbuf.composite(self.out_pixbuf, 0, 0, ((width/steps)*step_scale), height, ((width/steps)*step_scale)-width, 0, 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 150+step_scale*100/steps) # try of some good old doom-kind melting if transition_type == 10: stripsize = width/500 if stripsize < 2: stripsize = 1 del self.out_pixbuf self.out_pixbuf = self.pixbuf.copy() for i in range(0, width/stripsize): if steps == step_scale: self.random_value[i] = int(random.random() * 40 + 100) #print stripsize, " val: ", i, ":", self.random_value[i], " ", i*stripsize, " ", (i+1)*stripsize if self.random_value[i] < 100: self.last_pixbuf.composite(self.out_pixbuf, i*stripsize, height-1-((height/100)*self.random_value[i]), stripsize, ((height/100)*self.random_value[i]), 0, height -1- ((height/100)*self.random_value[i]), 1.0, self.random_value[i]/100.0, gtk.gdk.INTERP_BILINEAR, 200+step_scale*50/steps) else: self.last_pixbuf.composite(self.out_pixbuf, i*stripsize, 0, stripsize, height, 0, 0, 1.0, 1.0, gtk.gdk.INTERP_BILINEAR, 200+step_scale*50/steps) self.random_value[i] -= int(80/(steps))+(steps-step_scale) if self.random_value[i] < 0: self.random_value[i] = 0 self.step_scale -= 1 data3 = self.out_pixbuf.get_pixels_array().flat.tostring() #data1 = self.out_pixbuf.get_pixels_array() else: data3 = self.pixbuf.get_pixels_array().flat.tostring() #data1 = self.pixbuf.get_pixels_array() # get the array, make it flat and to a string=byte-sequence #data2 = data1.flat #data3 = data2.tostring() #print data1[0] #print len(data1[0]) #print len(data3) #print len(data1[:,0]) #print data1[:, 0] if data3: self.frame += 1 print "frame ", self.frame #buffer = gst.Buffer(data3) return gst.FLOW_OK, gst.Buffer(data3) #return gst.FLOW_OK, buffer else: return gst.FLOW_UNEXPECTED, None gobject.type_register(PictureSource) pic_names=[] def main(args): width = height = framerate = pic_duration = transition_type = pic_first = pic_last = transition_duration = 0 outputfile = "" pic_pattern = "" if len(args) < 2: print 'Usage: %s -width scale2## -height scale2## -framerate ## -pic-duration #frames -pic-names {picture1}{picture2}......' % (args[0]) print ' example: %s -framerate 20 -pic-duration 40 -pic-names ~/mypics/*.png\n\tchanges picture after 40 frames(=2 seconds for 20fps) to next picture' %(args[0]) print ' write to theora-ogg: %s -outputfile sheets.ogg -pic-names ~/mypics/hello.png ~/mypics/way*.png\n\t uses size of first picture and default framerate of 10fps with 10 seconds displaytime for each picture' %(args[0]) print ' -transition 1-10 = a optional transition-effect with a duration of 2 seconds' print ' optional instead of single picture-names, use a pattern with startnumber and endnumber like\n\t %s -pic-pattern "out%%05d.png" -pic-first 100 -pic-last 10\n\t will create from out00100.png down to out00010.png' %(args[0]) return -1 for i in range(len(args)): #print i, ":", args[i] if args[i] == "-outputfile": outputfile = args[i+1] if args[i] == "-framerate": framerate = string.atoi(args[i+1]) if args[i] == "-width": width = string.atoi(args[i+1]) if args[i] == "-height": height = string.atoi(args[i+1]) if args[i] == "-pic-duration": pic_duration = string.atoi(args[i+1]) if args[i] == "-transition": transition_type = string.atoi(args[i+1]) if args[i] == "-transition-duration": transition_duration = string.atoi(args[i+1]) if args[i] == "-pic-pattern": pic_pattern = args[i+1] if args[i] == "-pic-first": pic_first = string.atoi(args[i+1]) if args[i] == "-pic-last": pic_last = string.atoi(args[i+1]) if args[i] == "-pic-names": for j in range(i+1, len(args)): print args[j] pic_names.append(args[j]) i += 1 break if not pic_pattern == "": if pic_first <= pic_last: for i in range(pic_first, pic_last+1): str = pic_pattern % (i) pic_names.append( str ) else: for i in range(pic_first, pic_last-1, -1): str = pic_pattern % (i) pic_names.append( str ) for i in (pic_names): print i pipeline = gst.Pipeline('pipeline') if outputfile == "": bin1 = gst.gst_parse_bin_from_description("capsfilter name=caps ! identity single-segment=True ! ffmpegcolorspace ! xvimagesink", 1) else: str = "capsfilter name=caps ! identity single-segment=True ! ffmpegcolorspace ! theoraenc quality=60 ! oggmux ! filesink location=%s" % (outputfile) bin1 = gst.gst_parse_bin_from_description(str, 1) assert bin1 picture = PictureSource('picture') assert picture if width > 0: picture.set_property('width', width) if height > 0: picture.set_property('height', height) if pic_duration > 0: picture.set_property('pic_duration', pic_duration) if framerate > 0: picture.set_property('framerate', framerate) if transition_type > 0: picture.set_property('transition_type', transition_type) if transition_duration > 0: picture.set_property('transition_duration', transition_duration) # need this to setup sizes from first picture if not specified for scaling picture.set_property('location', pic_names[0]) capsfilter = bin1.get_by_name('caps') pipeline.add(picture, bin1) str = "video/x-raw-rgb, width=(int)%d, height=(int)%d, framerate=(fraction)%d/1, bpp=(int)24, depth=(int)24, red_mask=(int)16711680, green_mask=(int)65280, blue_mask=(int)255, endianness=(int)4321" % ( picture.get_property('width'), picture.get_property('height'), picture.get_property('framerate')) print str caps = gst.caps_from_string(str) capsfilter.set_property("caps", caps) gst.element_link_many(picture, bin1) pipeline.set_state(gst.STATE_PLAYING) mainloop = gobject.MainLoop() def bus_event(bus, message): t = message.type if t == gst.MESSAGE_EOS: mainloop.quit() elif t == gst.MESSAGE_ERROR: err, debug = message.parse_error() print "Error: %s" % err, debug mainloop.quit() return True pipeline.get_bus().add_watch(bus_event) mainloop.run() pipeline.set_state(gst.STATE_NULL) if __name__ == '__main__': sys.exit(main(sys.argv))