157 lines
3.9 KiB
Python
157 lines
3.9 KiB
Python
#!/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()
|