Public feed #604099

python loading pictures to creat

public text v1 · immutable
Raw Edit
·published 2007-07-05 09:17 UTC
rendered paste body
#!/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))