Files
@ a30550d5827f
Branch filter:
Location: light9/light9/vidref/main.py
a30550d5827f
9.1 KiB
text/x-python
vidref shows more logs in the gui
Ignore-this: 933f1edd615ecea9b9c1a4e5a6f37355
Ignore-this: 933f1edd615ecea9b9c1a4e5a6f37355
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | #!/usr/bin/python
"""
dvcam test
gst-launch dv1394src ! dvdemux name=d ! dvdec ! ffmpegcolorspace ! hqdn3d ! xvimagesink
"""
import pygst
pygst.require("0.10")
import gst, gobject, time, jsonlib, restkit, logging, os, traceback
import gtk
from twisted.python.util import sibpath
import Image
from threading import Thread
from Queue import Queue
from light9 import networking
from light9.vidref.replay import ReplayViews, songDir, takeDir, framerate
log = logging.getLogger()
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, onChange=lambda position: None):
"""period is the seconds between http time requests.
We call onChange with the time in seconds and the total time
The choice of period doesn't need to be tied to framerate,
it's more the size of the error you can tolerate (since we
make up times between the samples, and we'll just run off the
end of a song)
"""
self.period = period
self.onChange = onChange
self.musicResource = restkit.Resource(networking.musicPlayer.url)
t = Thread(target=self._timeUpdate)
t.setDaemon(True)
t.start()
def getLatest(self):
"""
dict with 't' and 'song', etc.
"""
if not hasattr(self, 'position'):
return {'t' : 0, 'song' : None}
pos = self.position.copy()
if pos['playing']:
pos['t'] = pos['t'] + (time.time() - self.positionFetchTime)
return pos
def _timeUpdate(self):
while True:
try:
position = jsonlib.loads(self.musicResource.get("time").body_string(),
use_float=True)
# this is meant to be the time when the server gave me its
# report, and I don't know if that's closer to the
# beginning of my request or the end of it (or some
# fraction of the way through)
self.positionFetchTime = time.time()
self.position = position
self.onChange(position)
except restkit.RequestError, e:
log.error(e)
time.sleep(1)
time.sleep(self.period)
def sendTime(self, t):
"""request that the player go to this time"""
self.musicResource.post("time", payload=jsonlib.dumps({"t" : t}),
headers={"content-type" : "application/json"})
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]
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):
def __init__(self):
self.musicTime = MusicTime(onChange=self.onMusicTimeChange)
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)
self.recordingTo = wtree.get_object('recordingTo')
self.musicScale = wtree.get_object("musicScale")
self.musicScale.connect("value-changed", self.onMusicScaleValue)
self.ignoreScaleChanges = False
self.attachLog(wtree.get_object("lastLog"))
# wtree.get_object("replayPanel").show() # demo only
rp = wtree.get_object("replayVbox")
self.replayViews = ReplayViews(rp)
mainwin.show_all()
self.liveVideoXid = wtree.get_object("vid3").window.xid
self.setInput('dv') # 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):
def emit(self, record):
textBuffer.set_text(record.getMessage())
h = ToBuffer()
h.setLevel(logging.INFO)
log.addHandler(h)
def updateLoop(self):
position = self.musicTime.getLatest()
try:
with gtk.gdk.lock:
self.replayViews.update(position)
except:
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 ! hqdn3d" ,
}[name]
cam = (sourcePipe + " ! "
"videorate ! video/x-raw-yuv,framerate=%s/1 ! "
"videoscale ! video/x-raw-yuv,width=320,height=240;video/x-raw-rgb,width=320,height=240 ! "
"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)
def on_liveFrameRate_value_changed(self, widget):
print widget.get_value()
def onMusicTimeChange(self, position):
self.ignoreScaleChanges = True
try:
self.musicScale.set_range(0, position['duration'])
self.musicScale.set_value(position['t'])
finally:
self.ignoreScaleChanges = False
def onMusicScaleValue(self, scaleRange):
"""the scale position has changed. if it was by the user, send
it back to music player"""
if not self.ignoreScaleChanges:
self.musicTime.sendTime(scaleRange.get_value())
|