#!/usr/bin/env python import sys import gi from gi.repository import GLib gi.require_version('Gst', '1.0') from gi.repository import Gst import fcntl import os import ast import re # for the moment... gst_eval = ast.literal_eval class Parser(object): def __init__(self, pipeline): self.pipeline = pipeline self.expressions = [ ('^(\w+)\.(\w+) = (.+)$', self.set_property), ('^(\w+)(?:\.(\w+))? ([-x])> (\w+)(?:\.(\w+))?$', self.link_pads), ('^\+ (\w+)(?: (.*))?$', self.add_element), ('^- (\w+)$', self.remove_element), ('^(stop|play|pause)$', self.set_state), ('^seekto ([0-9.]+)$', self.seek), ] self.expressions = [ (re.compile(regex), fn) for regex, fn in self.expressions ] def parse_line(self, line): for regex, fn in self.expressions: m = regex.match(line) if m: try: fn(*m.groups()) except Exception: import traceback traceback.print_exc() break else: print 'Error: could not parse line.' def set_property(self, target, attr, value): el = self.pipeline.get_by_name(target) value = gst_eval(value) el.set_property(attr, value) def link_pads(self, src, src_pad, char, dst, dst_pad): src_el = self.pipeline.get_by_name(src) dst_el = self.pipeline.get_by_name(dst) print src_el, src_pad, char, dst_el, dst_pad if char == '-': success = src_el.link_pads(src_pad, dst_el, dst_pad) if not success: print 'Could not link pads.' elif char == 'x': success = src_el.unlink_pads(src_pad, dst_el, dst_pad) if not success: print 'Could not unlink pads.' def set_state(self, state): state = { 'stop': Gst.State.READY, 'play': Gst.State.PLAYING, 'pause': Gst.State.PAUSED, }[state] self.pipeline.set_state(state) def seek(self, to): to=int(float(to)*1000000000) print to pipeline.seek_simple( Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, to ) def add_element(self, kind, properties): # TODO: write a proper parser, and don't have this here properties = dict( pair.split('=', 1) for pair in properties.split(' ') ) # create the element name = properties.pop('name', None) element = Gst.ElementFactory.make(kind, name) # set the properties for key, value in properties: value = gst_eval(value) element.set_property(key, value) self.pipeline.add(element) def remove_element(self, name): element = self.pipeline.get_by_name(name) self.pipeline.remove(element) def setup_non_blocking_read(f, on_line): fd = f.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) buf = [''] def on_data_available(fd, condition): data = f.read() buf[0] += data lines = buf[0].split('\n') buf[0] = lines.pop() for line in lines: on_line(line) return True GLib.io_add_watch( fd, GLib.IOCondition.IN, on_data_available, ) # init gstreamer, parse args args = sys.argv[:] Gst.init(args) desc = ' '.join(args[1:]) # load gstreamer pipeline, press play pipeline = Gst.parse_launch(desc) pipeline.set_state(Gst.State.PLAYING) # make stdin non-blocking # when a line of input is recieved, parse it # the parser has most of the logic parser = Parser(pipeline) setup_non_blocking_read(sys.stdin, parser.parse_line) # run glib main loop loop = GLib.MainLoop() loop.run()