diff --git a/bin/picamserve b/bin/picamserve --- a/bin/picamserve +++ b/bin/picamserve @@ -20,7 +20,7 @@ except ImportError: def capture_continuous(self, *a, **kw): for i in range(1000): time.sleep(1) - yield i + yield str(i) def setCameraParams(c, arg): res = int(arg('res', 480)) diff --git a/bin/vidref b/bin/vidref --- a/bin/vidref +++ b/bin/vidref @@ -25,11 +25,7 @@ parser.add_option("-v", "--verbose", act (options, args) = parser.parse_args() -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) - +log.setLevel(logging.DEBUG if options.verbose else logging.INFO) class Snapshot(cyclone.web.RequestHandler): @defer.inlineCallbacks diff --git a/light9/vidref/main.py b/light9/vidref/main.py --- a/light9/vidref/main.py +++ b/light9/vidref/main.py @@ -12,6 +12,7 @@ from twisted.python.util import sibpath from light9.vidref.replay import ReplayViews, framerate from light9.vidref.musictime import MusicTime from light9.vidref.videorecorder import Pipeline +from light9.vidref import remotepivideo log = logging.getLogger() class Gui(object): @@ -39,8 +40,17 @@ class Gui(object): mainwin.show_all() vid3 = wtree.get_object("vid3") - self.pipeline = Pipeline(vid3.window.xid, self.musicTime, - self.recordingTo) + if 0: + self.pipeline = Pipeline( + liveVideoXid=vid3.window.xid, + musicTime=self.musicTime, + recordingTo=self.recordingTo) + else: + self.pipeline = remotepivideo.Pipeline( + liveVideo=vid3, + musicTime=self.musicTime, + recordingTo=self.recordingTo, + picsUrl='http://10.1.0.125:8001/pics?res=1080&resize=450&x=0&y=.3&w=1&h=.5&awb_mode=auto&exposure_mode=auto') vid3.props.width_request = 360 vid3.props.height_request = 220 diff --git a/light9/vidref/musictime.py b/light9/vidref/musictime.py --- a/light9/vidref/musictime.py +++ b/light9/vidref/musictime.py @@ -40,7 +40,7 @@ class MusicTime(object): if not hasattr(self, 'position'): return {'t' : 0, 'song' : None} pos = self.position.copy() - if pos['playing']: + if pos.get('playing'): pos['t'] = pos['t'] + (time.time() - self.positionFetchTime) else: if self.lastHoverTime is not None: diff --git a/light9/vidref/remotepivideo.py b/light9/vidref/remotepivideo.py new file mode 100644 --- /dev/null +++ b/light9/vidref/remotepivideo.py @@ -0,0 +1,115 @@ +""" +like videorecorder.py, but talks to a bin/picamserve instance +""" +import os, time, logging +import gtk +import numpy +import treq +from light9.vidref.replay import framerate, songDir, takeDir, snapshotDir +from light9 import prof +from PIL import Image +from StringIO import StringIO +log = logging.getLogger('remotepi') + +class Pipeline(object): + def __init__(self, liveVideo, musicTime, recordingTo, picsUrl): + self.musicTime = musicTime + self.recordingTo = recordingTo + + self.liveVideo = self._replaceLiveVideoWidget(liveVideo) + + self._startRequest(picsUrl) + self._buffer = '' + + def _replaceLiveVideoWidget(self, liveVideo): + aspectFrame = liveVideo.get_parent() + liveVideo.destroy() + img = gtk.Image() + img.set_visible(True) + #img.set_size_request(320, 240) + aspectFrame.add(img) + return img + + def _startRequest(self, url): + d = treq.get(url) + d.addCallback(treq.collect, self._dataReceived) + # not sure how to stop this + return d + + def _dataReceived(self, chunk): + self._buffer += chunk + if len(self._buffer) < 100: + return + i = self._buffer.index('\n') + size, frameTime = self._buffer[:i].split() + size = int(size) + if len(self._buffer) - i - 1 < size: + return + jpg = self._buffer[i+1:i+1+size] + self.onFrame(jpg, float(frameTime)) + self._buffer = self._buffer[i+1+size:] + + def snapshot(self): + """ + returns deferred to the path (which is under snapshotDir()) where + we saved the image. + """ + filename = "%s/%s.jpg" % (snapshotDir(), time.time()) + return + + def setInput(self, name): + pass + + def setLiveVideo(self, on): + print "setLiveVideo", on + + def onFrame(self, jpg, frameTime): + position = self.musicTime.getLatest() + if not position['song']: + self.updateLiveFromTemp(jpg) + return + 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 + self.updateLiveFromTemp(jpg) + return + try: + os.makedirs(outDir) + except OSError: + pass + with open(outFilename, 'w') as out: + out.write(jpg) + + self.updateLiveFromFile(outFilename) + + # if you're selecting the text while gtk is updating it, + # you can get a crash in xcb_io + if getattr(self, '_lastRecText', None) != outDir: + with gtk.gdk.lock: + self.recordingTo.set_text(outDir) + self._lastRecText = outDir + + def updateLiveFromFile(self, outFilename): + self.liveVideo.set_from_file(outFilename) + + def updateLiveFromTemp(self, jpg): + try: + img = Image.open(StringIO(jpg)) + if not hasattr(self, 'livePixBuf'): + self.livePixBuf = gtk.gdk.pixbuf_new_from_data( + img.tostring(), + gtk.gdk.COLORSPACE_RGB, + False, 8, + img.size[0], img.size[1], + img.size[0]*3) + log.info("live images are %r", img.size) + else: + # don't leak pixbufs; update the one we have + a = self.livePixBuf.pixel_array + newImg = numpy.fromstring(img.tostring(), dtype=numpy.uint8) + a[:,:,:] = newImg.reshape(a.shape) + self.liveVideo.set_from_pixbuf(self.livePixBuf) + + except Exception: + import traceback + traceback.print_exc() diff --git a/light9/vidref/replay.py b/light9/vidref/replay.py --- a/light9/vidref/replay.py +++ b/light9/vidref/replay.py @@ -117,14 +117,11 @@ class ReplayView(object): if True: af = gtk.AspectFrame() af.set_visible(True) - w,h = (640-140-110), (480-130-70) - af.set_size_request(w, h) af.set_shadow_type(gtk.SHADOW_OUT) af.props.obey_child = True img = gtk.Image() img.set_visible(True) - img.set_size_request(w, h) self.picWidget = img af.add(img) diff --git a/light9/vidref/vidref.glade b/light9/vidref/vidref.glade --- a/light9/vidref/vidref.glade +++ b/light9/vidref/vidref.glade @@ -3,28 +3,34 @@ + False vidref 690 500 True + False True + False True + False - 336 + 450 277 True + False 0 out True + False 0 none 1.3300000429153442 @@ -33,6 +39,7 @@ 320 240 True + False @@ -40,6 +47,7 @@ True + False <b>Live view</b> True @@ -47,12 +55,14 @@ False + True 0 True + False Enabled @@ -61,26 +71,31 @@ True True True + False True - + False + True 0 True + False 75 20 True + False Frame rate: False + True 0 @@ -91,30 +106,40 @@ True True + False + False + True + True True + True + True 1 False + True 1 True + False 85 20 True + False Input source: False + True 0 @@ -122,27 +147,35 @@ 100 True + False + True + True 1 False + True 2 True + False True + False Recording to: + True + True 0 @@ -155,17 +188,22 @@ to: recordingTo + True + True 1 + True + True 3 True + False 0 none @@ -179,24 +217,29 @@ to: True + False <b>Last log</b> True + True + True 4 False + True 1 False + True 0 @@ -205,6 +248,7 @@ to: 336 259 True + False 0 out @@ -219,10 +263,12 @@ to: True + False queue True + False @@ -241,28 +287,35 @@ to: True + False <b>Playback 1</b> True + True + True 1 + True + True 0 True + False 0 none True + False 12 @@ -276,6 +329,7 @@ to: True + False <b>Music position</b> True @@ -283,21 +337,34 @@ to: False + True 1 + + True + False + gtk-delete + + + + /home/drewp/light9-vidref/play-light9.bigasterisk.com_show_dance2010_song6/1276582699 + + False True + False 320 240 True + False 0 out 1.3300000429153442 @@ -306,24 +373,29 @@ to: 320 240 True + False gtk-missing-image False + True 0 True + False True + False True + False Started: @@ -340,6 +412,10 @@ to: 12 Sat 14:22:25 + False + False + True + True False @@ -350,6 +426,7 @@ to: False + True 0 @@ -359,9 +436,11 @@ to: True True True + False False + True 1 @@ -371,10 +450,12 @@ to: True True True + False image2 False + True 2 @@ -384,28 +465,23 @@ to: True True False + False True False + True 3 False + True 1 - - True - gtk-delete - - - /home/drewp/light9-vidref/play-light9.bigasterisk.com_show_dance2010_song6/1276582699 - - diff --git a/pydeps b/pydeps --- a/pydeps +++ b/pydeps @@ -17,4 +17,5 @@ python-dateutil==2.2 txosc==0.2.0 service_identity==0.2 Pillow==2.4.0 -faulthandler==2.3 \ No newline at end of file +faulthandler==2.3 +treq==0.2.1 \ No newline at end of file