changeset 937:b0337e6f68f1

refactor to videorecorder.py Ignore-this: 3b3eeaa3e817b8ebea688e239624683b
author drewp@bigasterisk.com
date Wed, 12 Jun 2013 19:46:26 +0000
parents 11aa214cb9a1
children c2faa69099e6
files bin/vidref light9/vidref/main.py light9/vidref/videorecorder.py
diffstat 3 files changed, 159 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/bin/vidref	Wed Jun 12 19:27:14 2013 +0000
+++ b/bin/vidref	Wed Jun 12 19:46:26 2013 +0000
@@ -8,7 +8,8 @@
 import gtk
 import sys, logging, optparse
 sys.path.append(".")
-from light9.vidref.main import Main
+from light9.vidref.main import Gui
+
 
  # find replay dirs correctly. show multiple
  # replays. trash. reorder/pin. dump takes that are too short; they're
@@ -26,6 +27,8 @@
 logging.getLogger("restkit.client").setLevel(logging.WARN)
 
 
-start=Main()
+        
+start=Gui()
+
 reactor.run()
 
--- a/light9/vidref/main.py	Wed Jun 12 19:27:14 2013 +0000
+++ b/light9/vidref/main.py	Wed Jun 12 19:46:26 2013 +0000
@@ -6,93 +6,15 @@
 gst-launch dv1394src ! dvdemux name=d ! dvdec ! ffmpegcolorspace ! hqdn3d ! xvimagesink
 
 """
-import pygst
-pygst.require("0.10")
-import gst, gobject, time, logging, os, traceback
+import gobject, logging, traceback
 import gtk
 from twisted.python.util import sibpath
-import Image
-from threading import Thread
-from Queue import Queue
-from light9.vidref.replay import ReplayViews, songDir, takeDir, framerate
+from light9.vidref.replay import ReplayViews, framerate
 from light9.vidref.musictime import MusicTime
-
+from light9.vidref.videorecorder import Pipeline
 log = logging.getLogger()
-        
-class VideoRecordSink(gst.Element):
-    _sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
-                                        gst.PAD_SINK,
-                                        gst.PAD_ALWAYS,
-                                        gst.caps_new_any())
-
-    def __init__(self, musicTime, updateRecordingTo):
-        gst.Element.__init__(self)
-        self.updateRecordingTo = updateRecordingTo
-        self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
-        self.add_pad(self.sinkpad)
-        self.sinkpad.set_chain_function(self.chainfunc)
-        self.lastTime = 0
-        
-        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):
-        position = self.musicTime.getLatest()
-
-        if not position['song']:
-            print "no song" # todo: this prints too much when the player has no song
-            return gst.FLOW_OK
-
-        try:
-            cap = buffer.caps[0]
-            #print "cap", (cap['width'], cap['height'])
-            img = Image.fromstring('RGB', (cap['width'], cap['height']),
-                                   buffer.data)
-            self.imagesToSave.put((position, img, buffer.timestamp))
-        except:
-            traceback.print_exc()
-
-        return gst.FLOW_OK
-
-    def saveImg(self, position, img, bufferTimestamp):
-        t1 = time.time()
-        outDir = takeDir(songDir(position['song']), position['started'])
-        outFilename = "%s/%08.03f.jpg" % (outDir, position['t'])
-        if os.path.exists(outFilename): # we're paused on one time
-            return
-        
-        try:
-            os.makedirs(outDir)
-        except OSError:
-            pass
-
-        img.save(outFilename)
-
-        now = time.time()
-        log.info("wrote %s delay of %.2fms, took %.2fms",
-                  outFilename,
-                  (now - self.lastTime) * 1000,
-                  (now - t1) * 1000)
-        self.updateRecordingTo(outDir)
-        self.lastTime = now
-
-gobject.type_register(VideoRecordSink)
-
-class Main(object):
+class Gui(object):
     def __init__(self):
         wtree = gtk.Builder()
         wtree.add_from_file(sibpath(__file__, "vidref.glade"))
@@ -116,13 +38,16 @@
 
         mainwin.show_all()
         vid3 = wtree.get_object("vid3")
-        self.liveVideoXid = vid3.window.xid
+
+        self.pipeline = Pipeline(vid3.window.xid, self.musicTime,
+                                 self.recordingTo)
+
         vid3.props.width_request = 360
         vid3.props.height_request = 220
         wtree.get_object("frame1").props.height_request = 220
         
 
-        self.setInput('v4l') # auto seems to not search for dv
+        self.pipeline.setInput('v4l') # auto seems to not search for dv
 
         gobject.timeout_add(1000 // framerate, self.updateLoop)
 
@@ -148,58 +73,9 @@
     def getInputs(self):
         return ['auto', 'dv', 'video0']
 
-    def setInput(self, name):
-        sourcePipe = {
-            "auto": "autovideosrc name=src1",
-            "testpattern" : "videotestsrc name=src1",
-            "dv": "dv1394src name=src1 ! dvdemux ! dvdec",
-            "v4l": "v4l2src device=/dev/video0 name=src1" ,
-            }[name]
-
-        cam = (sourcePipe + " ! "
-              "videorate ! video/x-raw-yuv,framerate=%s/1 ! "
-              "videoscale ! video/x-raw-yuv,width=640,height=480;video/x-raw-rgb,width=320,height=240 ! "
-              "videocrop left=160 top=180 right=120 bottom=80 ! "
-              "queue name=vid" % framerate)
-
-        self.pipeline = gst.parse_launch(cam)
-
-        def makeElem(t, n=None):
-            e = gst.element_factory_make(t, n)
-            self.pipeline.add(e)
-            return e
-        
-        sink = makeElem("xvimagesink")
-        def setRec(t):
-            # if you're selecting the text while gtk is updating it,
-            # you can get a crash in xcb_io
-            if getattr(self, '_lastRecText', None) == t:
-                return
-            with gtk.gdk.lock:
-                self.recordingTo.set_text(t)
-            self._lastRecText = t
-        recSink = VideoRecordSink(self.musicTime, setRec)
-        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(self.pipeline.get_by_name("vid"), tee, sink)
-        gst.element_link_many(tee, makeElem("ffmpegcolorspace"), caps, recSink)
-        sink.set_xwindow_id(self.liveVideoXid)
-        self.pipeline.set_state(gst.STATE_PLAYING)        
 
     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)
+        self.pipeline.setLiveVideo(widget.get_active())
                                                    
     def on_liveFrameRate_value_changed(self, widget):
         print widget.get_value()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/vidref/videorecorder.py	Wed Jun 12 19:46:26 2013 +0000
@@ -0,0 +1,144 @@
+import pygst
+pygst.require("0.10")
+import gst, gobject, time, logging, os, traceback
+import gtk
+import Image
+from threading import Thread
+from Queue import Queue
+from light9.vidref.replay import framerate, songDir, takeDir
+log = logging.getLogger()
+
+class Pipeline(object):
+    def __init__(self, liveVideoXid, musicTime, recordingTo):
+        self.musicTime = musicTime
+        self.liveVideoXid = liveVideoXid
+        self.recordingTo = recordingTo
+    
+    def setInput(self, name):
+        sourcePipe = {
+            "auto": "autovideosrc name=src1",
+            "testpattern" : "videotestsrc name=src1",
+            "dv": "dv1394src name=src1 ! dvdemux ! dvdec",
+            "v4l": "v4l2src device=/dev/video0 name=src1" ,
+            }[name]
+
+        cam = (sourcePipe + " ! "
+              "videorate ! video/x-raw-yuv,framerate=%s/1 ! "
+              "videoscale ! video/x-raw-yuv,width=640,height=480;video/x-raw-rgb,width=320,height=240 ! "
+              "videocrop left=160 top=180 right=120 bottom=80 ! "
+              "queue name=vid" % framerate)
+
+        print cam
+        self.pipeline = gst.parse_launch(cam)
+
+        def makeElem(t, n=None):
+            e = gst.element_factory_make(t, n)
+            self.pipeline.add(e)
+            return e
+        
+        sink = makeElem("xvimagesink")
+        def setRec(t):
+            # if you're selecting the text while gtk is updating it,
+            # you can get a crash in xcb_io
+            if getattr(self, '_lastRecText', None) == t:
+                return
+            with gtk.gdk.lock:
+                self.recordingTo.set_text(t)
+            self._lastRecText = t
+        recSink = VideoRecordSink(self.musicTime, setRec)
+        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(self.pipeline.get_by_name("vid"), tee, sink)
+        gst.element_link_many(tee, makeElem("ffmpegcolorspace"), caps, recSink)
+        sink.set_xwindow_id(self.liveVideoXid)
+        self.pipeline.set_state(gst.STATE_PLAYING)        
+
+    def setLiveVideo(self, on):
+        
+        if on:
+            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)
+                                                   
+
+class VideoRecordSink(gst.Element):
+    _sinkpadtemplate = gst.PadTemplate ("sinkpadtemplate",
+                                        gst.PAD_SINK,
+                                        gst.PAD_ALWAYS,
+                                        gst.caps_new_any())
+
+    def __init__(self, musicTime, updateRecordingTo):
+        gst.Element.__init__(self)
+        self.updateRecordingTo = updateRecordingTo
+        self.sinkpad = gst.Pad(self._sinkpadtemplate, "sink")
+        self.add_pad(self.sinkpad)
+        self.sinkpad.set_chain_function(self.chainfunc)
+        self.lastTime = 0
+        
+        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):
+        position = self.musicTime.getLatest()
+
+        if not position['song']:
+            print "no song" # todo: this prints too much when the player has no song
+            return gst.FLOW_OK
+
+        try:
+            cap = buffer.caps[0]
+            #print "cap", (cap['width'], cap['height'])
+            img = Image.fromstring('RGB', (cap['width'], cap['height']),
+                                   buffer.data)
+            self.imagesToSave.put((position, img, buffer.timestamp))
+        except:
+            traceback.print_exc()
+
+        return gst.FLOW_OK
+
+    def saveImg(self, position, img, bufferTimestamp):
+        t1 = time.time()
+        outDir = takeDir(songDir(position['song']), position['started'])
+        outFilename = "%s/%08.03f.jpg" % (outDir, position['t'])
+        if os.path.exists(outFilename): # we're paused on one time
+            return
+        
+        try:
+            os.makedirs(outDir)
+        except OSError:
+            pass
+
+        img.save(outFilename)
+
+        now = time.time()
+        log.info("wrote %s delay of %.2fms, took %.2fms",
+                  outFilename,
+                  (now - self.lastTime) * 1000,
+                  (now - t1) * 1000)
+        self.updateRecordingTo(outDir)
+        self.lastTime = now
+
+gobject.type_register(VideoRecordSink)