Changeset - 6f1eb6437c96
[Not reviewed]
default
0 3 0
drewp@bigasterisk.com - 15 years ago 2010-06-12 09:32:09
drewp@bigasterisk.com
vidref refactor, start to add more widgets
Ignore-this: b81bc966dffb486cb8b20f975a4b4b2a
3 files changed with 185 insertions and 59 deletions:
0 comments (0 inline, 0 general)
bin/vidref
Show inline comments
 
#!/usr/bin/python
 
import gobject
 
gobject.threads_init()
 
import gtk
 
import sys
 
import sys, logging
 
sys.path.append(".")
 
from light9.vidref.main import Main
 

	
 

	
 
logging.basicConfig()
 
log = logging.getLogger()
 
log.setLevel(logging.DEBUG)
 
logging.getLogger("restkit.client").setLevel(logging.WARN)
 

	
 

	
 
start=Main()
 
gtk.main()
 

	
light9/vidref/main.py
Show inline comments
 
@@ -15,75 +15,126 @@ import pygtk
 
import gtk
 
from twisted.python.util import sibpath
 
import Image
 
from threading import Thread
 
from Queue import Queue
 
from light9 import networking
 
logging.basicConfig(level=logging.WARN)
 
logging.getLogger().setLevel(logging.WARN)
 
#restkit.set_logging(logging.WARN)
 

	
 
existingDir = "/tmp/vidref/play-light9.bigasterisk.com_show_dance2010_song7-1276065914"
 
log = logging.getLogger()
 

	
 
existingDir = "/tmp/vidref/play-_my_proj_light9_show_dance2010_music_07-jacksonmix-complete.wav-1276331148"
 
existingFrames = sorted([Decimal(f.split('.jpg')[0])
 
                         for f in os.listdir(existingDir)])
 

	
 
otherPic = None
 

	
 
class MusicTime(object):
 
    """
 
    fetch times from ascoltami in a background thread; return times
 
    upon request, adjusted to be more precise with the system clock
 
    """
 
    def __init__(self, period=.2):
 
        """period is the seconds between http time requests."""
 
        self.period = period
 
        self.musicResource = restkit.Resource(networking.musicUrl())
 
        t = Thread(target=self._timeUpdate)
 
        t.setDaemon(True)
 
        t.start()
 

	
 
    def getLatest(self):
 
        """
 
        dict with 't' and 'song', etc.
 
        """
 
        pos = self.position.copy()
 
        if pos['playing']:
 
            pos['t'] = pos['t'] + (time.time() - self.positionFetchTime)
 
        return pos
 

	
 
    def _timeUpdate(self):
 
        while True:
 
            position = jsonlib.loads(self.musicResource.get("time").body,
 
                                     use_float=True)
 
            self.positionFetchTime = time.time()
 
            self.position = position
 
            time.sleep(self.period)
 

	
 
class ReplayFrames(object):
 
    """
 
    supplies all the frames from disk for the replay videos
 
    """
 
    def update(self, position):
 
        inPic = self.findClosestFrame(position['t']+.2)
 
        with gtk.gdk.lock:
 
            otherPic.set_from_file(inPic)
 
            if 0:
 
                # force redraw of that widget
 
                otherPic.queue_draw_area(0,0,320,240)
 
                otherPic.get_window().process_updates(True)
 

	
 
    def findClosestFrame(self, t):
 
        i = bisect_left(existingFrames, Decimal(str(t)))
 
        if i >= len(existingFrames):
 
            i = len(existingFrames) - 1
 
        return os.path.join(existingDir, "%08.03f.jpg" % existingFrames[i])
 

	
 
        
 
class VideoRecordSink(gst.Element):
 
    _sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
 
                                        gst.PAD_SINK,
 
                                        gst.PAD_ALWAYS,
 
                                        gst.caps_new_any())
 

	
 
    def __init__(self):
 
    def __init__(self, replay):
 
        gst.Element.__init__(self)
 
        self.replay = replay
 
        self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
 
        self.add_pad(self.sinkpad)
 
        self.sinkpad.set_chain_function(self.chainfunc)
 
        self.lastTime = 0
 
        
 
        self.musicResource = restkit.Resource(networking.musicUrl(api='rest'))
 
        self.musicTime = MusicTime()
 

	
 
        self.imagesToSave = Queue()
 
        self.startBackgroundImageSaver(self.imagesToSave)
 

	
 
        
 
    def startBackgroundImageSaver(self, imagesToSave):
 
        """do image saves in another thread to not block gst"""
 
        def imageSaver():
 
            while True:
 
                args = imagesToSave.get()
 
                self.saveImg(*args)
 
                imagesToSave.task_done()
 
        
 
        t = Thread(target=imageSaver)
 
        t.setDaemon(True)
 
        t.start()
 
        
 

	
 
    def chainfunc(self, pad, buffer):
 
        return gst.FLOW_OK
 
        global nextImageCb
 
        self.info("%s timestamp(buffer):%d" % (pad, buffer.timestamp))
 

	
 
        position = self.musicTime.getLatest()
 

	
 
        if not position['song']:
 
            print "no song"
 
            return gst.FLOW_OK
 

	
 
        try:
 
            position = jsonlib.loads(self.musicResource.get("position").body,
 
                                     use_float=True)
 
            if not position['song']:
 
                return gst.FLOW_OK
 
            
 

	
 
            cap = buffer.caps[0]
 
            img = Image.fromstring('RGB', (cap['width'], cap['height']),
 
                                   buffer.data)
 
            self.imagesToSave.put((position, img, buffer.timestamp))
 
        except:
 
            traceback.print_exc()
 

	
 
        try:
 
            self.updateOtherFrames(position)
 
            self.replay.update(position)
 
        except:
 
            traceback.print_exc()
 
        
 

	
 
        return gst.FLOW_OK
 

	
 
    def saveImg(self, position, img, bufferTimestamp):
 
        outDir = "/tmp/vidref/play-%s-%d" % (
 
            position['song'].split('://')[-1].replace('/','_'),
 
            position['started'])
 
@@ -96,89 +147,77 @@ class VideoRecordSink(gst.Element):
 
        except OSError:
 
            pass
 

	
 
        img.save(outFilename)
 

	
 
        now = time.time()
 
        print "wrote %s delay of %.2fms %s" % (outFilename,
 
                                        (now - self.lastTime) * 1000,
 
                                               bufferTimestamp)
 
        log.info("wrote %s delay of %.2fms %s",
 
                 outFilename,
 
                 (now - self.lastTime) * 1000,
 
                 bufferTimestamp)
 
        self.lastTime = now
 

	
 
    def updateOtherFrames(self, position):
 
        inPic = self.findClosestFrame(position['t']+.2)
 
        #print "load", inPic
 
        with gtk.gdk.lock:
 
            otherPic.set_from_file(inPic)
 
            if 0:
 
                otherPic.queue_draw_area(0,0,320,240)
 
                otherPic.get_window().process_updates(True)
 

	
 
    def findClosestFrame(self, t):
 
        i = bisect_left(existingFrames, Decimal(str(t)))
 
        if i >= len(existingFrames):
 
            i = len(existingFrames) - 1
 
        return os.path.join(existingDir, "%08.03f.jpg" % existingFrames[i])
 
    
 

	
 

	
 
gobject.type_register(VideoRecordSink)
 

	
 
class Main(object):
 
    def __init__(self):
 
        global otherPic
 
        wtree = gtk.Builder()
 
        wtree.add_from_file(sibpath(__file__, "vidref.glade"))
 
        mainwin = wtree.get_object("MainWindow")
 
        otherPic = wtree.get_object("liveVideo")
 
        mainwin.connect("destroy", gtk.main_quit)
 
        wtree.connect_signals({
 
            "foo" : self.OnPlay,
 
            })
 
        wtree.connect_signals(self)
 

	
 
        # other sources: videotestsrc, v4l2src device=/dev/video0
 
        ## if 0:
 
        ##     source = makeElem("videotestsrc", "video")
 
        ## else:
 
        ##     source = makeElem("v4l2src", "vsource")
 
        ##     source.set_property("device", "/dev/video0")
 

	
 
        dv = "dv1394src ! dvdemux ! dvdec ! videoscale ! video/x-raw-yuv,width=320,height=240;video/x-raw-rgb,width=320,height=240 ! queue name=vid"
 
        v4l = "v4l2src device=/dev/video0 ! hqdn3d name=vid" 
 
        dv = "dv1394src name=src1 ! dvdemux ! dvdec"
 
        v4l = "v4l2src device=/dev/video0 ! hqdn3d" 
 

	
 
        pipeline = gst.parse_launch(dv)
 
        cam = (dv + " ! "
 
              "videorate ! video/x-raw-yuv,framerate=15/1 ! "
 
              "videoscale ! video/x-raw-yuv,width=320,height=240;video/x-raw-rgb,width=320,height=240 ! "
 
              "queue name=vid")
 

	
 
        self.pipeline = gst.parse_launch(cam)
 

	
 
        def makeElem(t, n=None):
 
            e = gst.element_factory_make(t, n)
 
            pipeline.add(e)
 
            self.pipeline.add(e)
 
            return e
 
        
 
        sink = makeElem("xvimagesink")
 
        recSink = VideoRecordSink()
 
        pipeline.add(recSink)
 
        replay = ReplayFrames()
 
        recSink = VideoRecordSink(replay)
 
        self.pipeline.add(recSink)
 

	
 
        tee = makeElem("tee")
 
        
 
        caps = makeElem("capsfilter")
 
        caps.set_property('caps', gst.caps_from_string('video/x-raw-rgb'))
 

	
 
        gst.element_link_many(pipeline.get_by_name("vid"), tee, sink)
 
        gst.element_link_many(self.pipeline.get_by_name("vid"), tee, sink)
 
        gst.element_link_many(tee, makeElem("ffmpegcolorspace"), caps, recSink)
 

	
 
        mainwin.show_all()
 

	
 
        sink.set_xwindow_id(wtree.get_object("vid3").window.xid)
 

	
 
        pipeline.set_state(gst.STATE_PLAYING)
 

	
 
    def OnPlay(self, widget):
 
        print "play"
 
       # Tell the video sink to display the output in our DrawingArea
 
        self.sinkx.set_xwindow_id(self.da.window.xid)
 
        self.pipeline.set_state(gst.STATE_PLAYING)
 

	
 
    def OnStop(self, widget):
 
        print "stop"
 
        self.pipeline.set_state(gst.STATE_READY)
 

	
 
    def OnQuit(self, widget):
 
        gtk.main_quit()
 
    def on_liveVideoEnabled_toggled(self, widget):
 
        if widget.get_active():
 
            self.pipeline.set_state(gst.STATE_PLAYING)
 
            # this is an attempt to bring the dv1394 source back, but
 
            # it doesn't work right.
 
            self.pipeline.get_by_name("src1").seek_simple(
 
                gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, 0 * gst.SECOND)
 
        else:
 
            self.pipeline.set_state(gst.STATE_READY)
 
                                                   
 
    def on_liveFrameRate_value_changed(self, widget):
 
        print widget.get_value()
light9/vidref/vidref.glade
Show inline comments
 
@@ -74,10 +74,91 @@
 
          </object>
 
          <packing>
 
            <property name="x">373</property>
 
            <property name="y">23</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkToggleButton" id="liveVideoEnabled">
 
            <property name="label" translatable="yes">Enabled</property>
 
            <property name="width_request">110</property>
 
            <property name="height_request">36</property>
 
            <property name="visible">True</property>
 
            <property name="can_focus">True</property>
 
            <property name="receives_default">True</property>
 
            <property name="active">True</property>
 
            <signal name="toggled" handler="on_liveVideoEnabled_toggled"/>
 
          </object>
 
          <packing>
 
            <property name="x">20</property>
 
            <property name="y">307</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkSpinButton" id="liveFrameRate">
 
            <property name="width_request">52</property>
 
            <property name="height_request">25</property>
 
            <property name="visible">True</property>
 
            <property name="can_focus">True</property>
 
            <property name="invisible_char">&#x25CF;</property>
 
            <property name="numeric">True</property>
 
          </object>
 
          <packing>
 
            <property name="x">96</property>
 
            <property name="y">360</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkLabel" id="label1">
 
            <property name="width_request">75</property>
 
            <property name="height_request">20</property>
 
            <property name="visible">True</property>
 
            <property name="label" translatable="yes">Frame rate</property>
 
          </object>
 
          <packing>
 
            <property name="x">16</property>
 
            <property name="y">363</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkComboBox" id="videoSource">
 
            <property name="width_request">100</property>
 
            <property name="visible">True</property>
 
          </object>
 
          <packing>
 
            <property name="x">114</property>
 
            <property name="y">405</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkLabel" id="label4">
 
            <property name="width_request">85</property>
 
            <property name="height_request">20</property>
 
            <property name="visible">True</property>
 
            <property name="label" translatable="yes">Input source</property>
 
          </object>
 
          <packing>
 
            <property name="x">20</property>
 
            <property name="y">407</property>
 
          </packing>
 
        </child>
 
        <child>
 
          <object class="GtkScrolledWindow" id="scrolledwindow1">
 
            <property name="width_request">336</property>
 
            <property name="height_request">310</property>
 
            <property name="visible">True</property>
 
            <property name="can_focus">True</property>
 
            <property name="hscrollbar_policy">automatic</property>
 
            <property name="vscrollbar_policy">automatic</property>
 
            <child>
 
              <placeholder/>
 
            </child>
 
          </object>
 
          <packing>
 
            <property name="x">383</property>
 
            <property name="y">323</property>
 
          </packing>
 
        </child>
 
      </object>
 
    </child>
 
  </object>
 
</interface>
0 comments (0 inline, 0 general)