changeset 534:6f1eb6437c96

vidref refactor, start to add more widgets Ignore-this: b81bc966dffb486cb8b20f975a4b4b2a
author drewp@bigasterisk.com
date Sat, 12 Jun 2010 09:32:09 +0000
parents 846bc4712cf7
children 481aa9a970b3
files bin/vidref light9/vidref/main.py light9/vidref/vidref.glade
diffstat 3 files changed, 185 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/bin/vidref	Sat Jun 12 08:03:00 2010 +0000
+++ b/bin/vidref	Sat Jun 12 09:32:09 2010 +0000
@@ -2,11 +2,17 @@
 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()
 
--- a/light9/vidref/main.py	Sat Jun 12 08:03:00 2010 +0000
+++ b/light9/vidref/main.py	Sat Jun 12 09:32:09 2010 +0000
@@ -18,34 +18,84 @@
 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():
@@ -57,18 +107,19 @@
         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)
@@ -77,10 +128,10 @@
             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):
@@ -99,28 +150,12 @@
         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):
@@ -131,9 +166,7 @@
         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:
@@ -142,43 +175,49 @@
         ##     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()
--- a/light9/vidref/vidref.glade	Sat Jun 12 08:03:00 2010 +0000
+++ b/light9/vidref/vidref.glade	Sat Jun 12 09:32:09 2010 +0000
@@ -77,6 +77,87 @@
             <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>