Changeset - b0337e6f68f1
[Not reviewed]
default
0 2 1
drewp@bigasterisk.com - 12 years ago 2013-06-12 19:46:26
drewp@bigasterisk.com
refactor to videorecorder.py
Ignore-this: 3b3eeaa3e817b8ebea688e239624683b
3 files changed with 159 insertions and 136 deletions:
0 comments (0 inline, 0 general)
bin/vidref
Show inline comments
 
@@ -5,13 +5,14 @@ gtk2reactor.install()
 
from twisted.internet import reactor
 
import gobject
 
gobject.threads_init()
 
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
 
 # just from seeking
 

	
 
parser = optparse.OptionParser()
 
@@ -23,9 +24,11 @@ parser.add_option("-v", "--verbose", act
 
log.setLevel(logging.DEBUG)
 
# limit the stdout one, but leave debug messages for the gtk logger
 
log.handlers[0].setLevel(logging.DEBUG if options.verbose else logging.WARN)
 
logging.getLogger("restkit.client").setLevel(logging.WARN)
 

	
 

	
 
start=Main()
 
        
 
start=Gui()
 

	
 
reactor.run()
 

	
light9/vidref/main.py
Show inline comments
 
@@ -3,99 +3,21 @@
 
"""
 

	
 
dvcam test
 
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"))
 
        mainwin = wtree.get_object("MainWindow")
 
        mainwin.connect("destroy", gtk.main_quit)
 
        wtree.connect_signals(self)
 
@@ -113,19 +35,22 @@ class Main(object):
 
        # wtree.get_object("replayPanel").show() # demo only
 
        rp = wtree.get_object("replayVbox")
 
        self.replayViews = ReplayViews(rp)
 

	
 
        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)
 

	
 
    def attachLog(self, textBuffer):
 
        """write log lines to this gtk buffer"""
 
        class ToBuffer(logging.Handler):
 
@@ -145,64 +70,15 @@ class Main(object):
 
            traceback.print_exc()
 
        return True
 

	
 
    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()
 

	
 
    def onMusicTimeChange(self, position):
 
        self.ignoreScaleChanges = True
light9/vidref/videorecorder.py
Show inline comments
 
new file 100644
 
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)
0 comments (0 inline, 0 general)