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
 
@@ -8,7 +8,8 @@ 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
 
@@ -26,6 +27,8 @@ log.handlers[0].setLevel(logging.DEBUG i
 
logging.getLogger("restkit.client").setLevel(logging.WARN)
 

	
 

	
 
start=Main()
 
        
 
start=Gui()
 

	
 
reactor.run()
 

	
light9/vidref/main.py
Show inline comments
 
@@ -6,93 +6,15 @@ 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"))
 
@@ -116,13 +38,16 @@ class Main(object):
 

	
 
        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 @@ class Main(object):
 
    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()
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)