diff --git a/bin/vidref b/bin/vidref --- a/bin/vidref +++ b/bin/vidref @@ -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() diff --git a/light9/vidref/main.py b/light9/vidref/main.py --- a/light9/vidref/main.py +++ b/light9/vidref/main.py @@ -18,34 +18,84 @@ import Image 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 @@ class VideoRecordSink(gst.Element): 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 @@ class VideoRecordSink(gst.Element): 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 @@ class VideoRecordSink(gst.Element): 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 @@ class Main(object): 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 @@ class Main(object): ## 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() diff --git a/light9/vidref/vidref.glade b/light9/vidref/vidref.glade --- a/light9/vidref/vidref.glade +++ b/light9/vidref/vidref.glade @@ -77,6 +77,87 @@ 23 + + + Enabled + 110 + 36 + True + True + True + True + + + + 20 + 307 + + + + + 52 + 25 + True + True + + True + + + 96 + 360 + + + + + 75 + 20 + True + Frame rate + + + 16 + 363 + + + + + 100 + True + + + 114 + 405 + + + + + 85 + 20 + True + Input source + + + 20 + 407 + + + + + 336 + 310 + True + True + automatic + automatic + + + + + + 383 + 323 + +