Changeset - 82e98aa4d159
[Not reviewed]
default
0 5 0
drewp@bigasterisk.com - 6 years ago 2019-06-04 17:17:55
drewp@bigasterisk.com
reformatting and typing
Ignore-this: 208dd84c52c544998142de6622fced2c
5 files changed with 39 insertions and 30 deletions:
0 comments (0 inline, 0 general)
bin/musictime
Show inline comments
 
#!bin/python
 
import run_local  # noqa
 
import light9.networking
 

	
 
import tkinter as tk
 
import time
 
import json
 
from twisted.internet import reactor, tksupport, task, defer
 
from twisted.internet import reactor, tksupport, task
 

	
 
from light9.ascoltami.musictime_client import MusicTime
 

	
 
mt = MusicTime()
 

	
 

	
 
class MusicTimeTk(tk.Frame, MusicTime):
 

	
 
    def __init__(self, master, url):
 
        tk.Frame.__init__(self)
 
        MusicTime.__init__(self, url)
 
        self.timevar = tk.DoubleVar()
 
@@ -32,23 +31,23 @@ class MusicTimeTk(tk.Frame, MusicTime):
 
            print(self.timevar.get(), evt.keysym)
 

	
 
        self.timelabel.bind('<KeyPress>', print_time)
 
        self.timelabel.bind('<1>', print_time)
 
        self.timelabel.focus()
 
        task.LoopingCall(self.update_time).start(.1)
 
        
 

	
 
    def update_time(self):
 
        t = self.getLatest().get('t', 0)
 
        self.timevar.set(t)
 

	
 

	
 
if __name__ == "__main__":
 
    from optparse import OptionParser
 
    parser = OptionParser()
 
    parser.add_option("-u", "--url", default=light9.networking.musicPlayer.url)
 
    options, args = parser.parse_args()
 

	
 
    root = tk.Tk()
 
    root.title("Time")
 
    MusicTimeTk(root, options.url).pack(expand=1, fill='both')
 
    tksupport.install(root, ms=20)
 
    reactor.run()
 

	
bin/vidref
Show inline comments
 
@@ -54,13 +54,13 @@ class Snapshot(cyclone.web.RequestHandle
 
            traceback.print_exc()
 
            raise
 

	
 

	
 
pipeline = videorecorder.GstSource(
 
    '/dev/v4l/by-id/usb-Bison_HD_Webcam_200901010001-video-index0'
 
#    '/dev/v4l/by-id/usb-Generic_FULL_HD_1080P_Webcam_200901010001-video-index0'
 
    #    '/dev/v4l/by-id/usb-Generic_FULL_HD_1080P_Webcam_200901010001-video-index0'
 
)
 

	
 

	
 
class Live(cyclone.websocket.WebSocketHandler):
 

	
 
    def connectionMade(self, *args, **kwargs):
light9/ascoltami/musictime_client.py
Show inline comments
 
@@ -17,13 +17,14 @@ class MusicTime(object):
 
    """
 

	
 
    def __init__(self,
 
                 period=.2,
 
                 onChange=lambda position: None,
 
                 pollCurvecalc='ignored'):
 
        """period is the seconds between http time requests.
 
        """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
light9/vidref/videorecorder.py
Show inline comments
 
from dataclasses import dataclass
 
from queue import Queue
 
from typing import Optional
 
import time, logging, os, traceback, sys
 

	
 
import gi
 
gi.require_version('Gst', '1.0')
 
gi.require_version('GstBase', '1.0')
 

	
 
from PIL import Image
 
from gi.repository import Gst
 
from rx.subjects import BehaviorSubject
 
from dataclasses import dataclass
 
from PIL import Image
 
from twisted.internet import defer, threads
 
from queue import Queue
 
from light9.vidref.replay import framerate, songDir, takeDir, snapshotDir
 
from typing import Set, Optional
 
import moviepy.editor
 
import numpy
 

	
 
from light9.ascoltami.musictime_client import MusicTime
 
from light9.newtypes import Song
 

	
 
from IPython.core import ultratb
 
sys.excepthook = ultratb.FormattedTB(mode='Verbose',
 
                                     color_scheme='Linux',
 
                                     call_pdb=1)
 

	
 
log = logging.getLogger()
 

	
 

	
 
@dataclass(frozen=True)
 
class CaptureFrame:
 
    img: Image
 
    song: Song
 
    t: float
 
    isPlaying: bool
 

	
 

	
 
class FramesToVideoFiles:
 
    """
 

	
 
    nextWriteAction: 'ignore'
 
    currentOutputClip: None
 

	
 
@@ -43,18 +47,19 @@ class FramesToVideoFiles:
 
    (music stops or song changes)
 
    nextWriteAction: 'close'
 
    currentOutputClip: None
 
    nextWriteAction: 'ignore'
 
    
 
    """
 

	
 
    def __init__(self, frames: BehaviorSubject):
 
        self.frames = frames
 
        self.nextImg = None
 
        self.nextImg: Optional[CaptureFrame] = None
 

	
 
        self.currentOutputClip = None
 
        self.currentOutputSong = None
 
        self.currentOutputSong: Optional[Song] = None
 
        self.nextWriteAction = 'ignore'
 
        self.frames.subscribe(on_next=self.onFrame)
 

	
 
    def onFrame(self, cf: Optional[CaptureFrame]):
 
        if cf is None:
 
            return
 
@@ -66,19 +71,19 @@ class FramesToVideoFiles:
 
            self.currentOutputSong = cf.song
 
            self.save('/tmp/out%s' % time.time())
 
        elif self.currentOutputClip and cf.isPlaying:
 
            self.nextWriteAction = 'saveFrames'
 
            # continue recording this
 
        elif self.currentOutputClip is None and not cf.isPlaying:
 
            self.nextWriteAction  = 'notWritingClip'
 
            pass # continue waiting
 
            self.nextWriteAction = 'notWritingClip'
 
            pass  # continue waiting
 
        elif self.currentOutputClip and not cf.isPlaying or self.currentOutputSong != cf.song:
 
            # stop
 
            self.nextWriteAction = 'close'
 
        else:
 
            raise NotImplementedError        
 
            raise NotImplementedError(str(vars()))
 

	
 
    def save(self, outBase):
 
        """
 
        receive frames (infinite) and wall-to-song times (stream ends with
 
        the song), and write a video file and a frame map
 
        """
 
@@ -90,56 +95,59 @@ class FramesToVideoFiles:
 
        # (immediately calls make_frame)
 
        self.currentOutputClip = moviepy.editor.VideoClip(
 
            self._bg_make_frame, duration=999.)
 
        self.currentOutputClip.fps = 5
 
        log.info(f'write_videofile {outBase} start')
 
        try:
 
            self.currentOutputClip.write_videofile(
 
                outBase + '.mp4',
 
                audio=False, preset='ultrafast', verbose=True, bitrate='150000')
 
            self.currentOutputClip.write_videofile(outBase + '.mp4',
 
                                                   audio=False,
 
                                                   preset='ultrafast',
 
                                                   logger=None,
 
                                                   bitrate='150000')
 
        except (StopIteration, RuntimeError):
 
            pass
 
            self.frameMap.close()
 

	
 
        log.info('write_videofile done')
 
        self.currentOutputClip = None
 
         
 

	
 
    def _bg_make_frame(self, video_time_secs):
 
        if self.nextWriteAction == 'close':
 
            raise StopIteration # the one in write_videofile
 

	
 
        # should be a queue to miss fewer frames
 
        while self.nextImg is None:
 
            time.sleep(.03)
 
        cf, self.nextImg = self.nextImg, None
 

	
 
        self.frameMap.write(
 
            f'video {video_time_secs:g} = song {cf.t:g}\n')
 
        self.frameMap.flush()
 
        self.frameMap.write(f'video {video_time_secs:g} = song {cf.t:g}\n')
 
        return numpy.asarray(cf.img)
 

	
 

	
 
class GstSource:
 

	
 
    def __init__(self, dev):
 
        """
 
        make new gst pipeline
 
        """
 
        Gst.init(None)
 
        self.musicTime = MusicTime(pollCurvecalc=False)
 
        self.liveImages: BehaviorSubject[Optional[CaptureFrame]] = BehaviorSubject(None)
 
        self.liveImages: BehaviorSubject[
 
            Optional[CaptureFrame]] = BehaviorSubject(None)
 

	
 
        size = [640, 480]
 

	
 
        log.info("new pipeline using device=%s" % dev)
 
        
 

	
 
        # using videocrop breaks the pipeline, may be this issue
 
        # https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/issues/732
 
        pipeStr = (
 
            #f"v4l2src device=\"{dev}\""
 
            f'autovideosrc'
 
            f" ! videoconvert"
 
            f" ! appsink emit-signals=true max-buffers=1 drop=true name=end0 caps=video/x-raw,format=RGB,width={size[0]},height={size[1]}"
 
            )
 
        )
 
        log.info("pipeline: %s" % pipeStr)
 

	
 
        self.pipe = Gst.parse_launch(pipeStr)
 

	
 
        self.setupPipelineError(self.pipe, self.onError)
 

	
 
@@ -164,14 +172,14 @@ class GstSource:
 
            finally:
 
                buf.unmap(mapinfo)
 
            # could get gst's frame time and pass it to getLatest
 
            latest = self.musicTime.getLatest()
 
            if 'song' in latest:
 
                self.liveImages.on_next(
 
                    CaptureFrame(img, Song(latest['song']),
 
                                 latest['t'], latest['playing']))
 
                    CaptureFrame(img, Song(latest['song']), latest['t'],
 
                                 latest['playing']))
 
        except Exception:
 
            traceback.print_exc()
 
        return Gst.FlowReturn.OK
 

	
 
    def setupPipelineError(self, pipe, cb):
 
        bus = pipe.get_bus()
tasks.py
Show inline comments
 
@@ -42,12 +42,13 @@ def mypy(ctx):
 
    sources = ' '.join(pkg_sources())
 
    run(['bin/collector'])
 
    run(['bin/rdfdb'])
 
    run(['bin/keyboardcomposer'])
 
    run(['bin/effectsequencer'])
 
    run(['bin/ascoltami2'])
 
    run(['bin/vidref'])
 
    #for src in bin_sources:
 
    #    print(f"mypy {src}")
 
    #    run([src])# + pkg_sources())
 
@task
 
def reformat(ctx):
 
    ctx.run("env/bin/yapf --verbose --parallel --in-place --style google light9/*.py light9/*/*.py `file --no-pad  bin/* | grep 'Python script' | perl -lpe 's/:.*//'`")
0 comments (0 inline, 0 general)