Simplify code that renders gstreamer pipelines.
Change-Id: Ibbd3d50fb9b7d3337155d7c3eaa12f5a50bdef18
diff --git a/edgetpuvision/gst.py b/edgetpuvision/gst.py
index e5875f1..a1fa177 100644
--- a/edgetpuvision/gst.py
+++ b/edgetpuvision/gst.py
@@ -2,7 +2,7 @@
import itertools
import re
-__all__ = ('Filter', 'Queue', 'Caps', 'Tee',
+__all__ = ('Filter', 'Source', 'Sink', 'Queue', 'Tee', 'Caps', 'Pad',
'Size', 'Fraction', 'Format',
'describe', 'max_inner_size', 'min_outer_size', 'center_inside', 'parse_format')
@@ -50,80 +50,58 @@
def join(name, sep, params, param_sep=' '):
return name if not params else name + sep + join_params(params, param_sep)
-def params_with_name(params, base, name_gens):
- if 'name' in params:
- return params
- else:
- return {**params, 'name': base + next(name_gens[base])}
-
-def suffix_gen():
- yield ''
- for i in itertools.count(1):
- yield str(i)
-
-class Element:
- def __init__(self, params):
- self.params = params
-
- def __getattr__(self, name):
- return self.params[name]
-
-class Filter(Element):
- def __init__(self, filtername, pads=None, **params):
- super().__init__(params)
- self.filtername = filtername
- self.pads = pads
+class Pad:
+ def __init__(self, name, pad=''):
+ self.name = name
+ self.pad = pad
def __str__(self):
- return join(self.filtername, ' ', self.params)
+ return '%s.%s' % (self.name, self.pad)
-class Queue(Element):
- def __init__(self, **params):
- super().__init__(params)
-
- def __str__(self):
- return join('queue', ' ', self.params)
-
-class Caps(Element):
+class Caps:
def __init__(self, mediatype, **params):
- super().__init__(params)
+ self.params = params
self.mediatype = mediatype
def __str__(self):
return join(self.mediatype, ',', self.params, ',')
-class Tee(Element):
- def __init__(self, pads=None, **params):
- super().__init__(params)
- self.pads = pads
+class Element:
+ def __init__(self, elementname, params):
+ self.elementname = elementname
self.params = params
+ def __getattr__(self, name):
+ return self.params[name]
+
def __str__(self):
- return join('tee', ' ', self.params)
+ return join(self.elementname, ' ', self.params)
-def describe0(arg, name_gens, depth):
- recur = lambda x: describe0(x, name_gens, depth + 1)
- indent = ' ' * (depth + 1)
+class Filter(Element):
+ def __init__(self, filtername, **params):
+ super().__init__(filtername, params)
+class Source(Element):
+ def __init__(self, sourcename, **params):
+ super().__init__(sourcename + 'src', params)
+
+class Sink(Element):
+ def __init__(self, sinkname, **params):
+ super().__init__(sinkname + 'sink', params)
+
+class Queue(Element):
+ def __init__(self, **params):
+ super().__init__('queue', params)
+
+class Tee(Element):
+ def __init__(self, **params):
+ super().__init__('tee', params)
+
+def describe0(arg):
if isinstance(arg, collections.Sequence):
- return ' ! '.join(recur(x) for x in arg)
- elif isinstance(arg, Tee):
- params = params_with_name(arg.params, 't', name_gens)
- return join('tee', ' ', params) + '\n' + \
- '\n'.join('%s%s. ! %s' % (indent, params['name'], recur(x)) for x in arg.pads)
- elif isinstance(arg, Filter):
- body = join(arg.filtername, ' ', arg.params)
- if arg.pads:
- params = params_with_name(arg.params, 'f', name_gens)
- return body + '\n' + \
- '\n'.join('%s%s.%s ! %s' % (indent, params['name'], pad_name, recur(x)) for pad_name, x in arg.pads.items())
- return body
- elif isinstance(arg, Queue):
- return join('queue', ' ', arg.params)
- elif isinstance(arg, Caps):
- return join(arg.mediatype, ',', arg.params, ',')
+ return ' ! '.join(describe0(x) for x in arg)
else:
- raise ValueError('Invalid element: %s' % arg)
+ return str(arg)
def describe(pipeline):
- return describe0(pipeline, collections.defaultdict(suffix_gen), 0)
+ return '\n'.join(describe0(x) for x in pipeline)
diff --git a/edgetpuvision/pipelines.py b/edgetpuvision/pipelines.py
index 2c049e2..70d1d88 100644
--- a/edgetpuvision/pipelines.py
+++ b/edgetpuvision/pipelines.py
@@ -1,136 +1,136 @@
from .gst import *
def decoded_file_src(filename):
- return (
- Filter('filesrc', location=filename),
+ return [
+ Source('file', location=filename),
Filter('decodebin'),
- )
+ ]
def v4l2_src(fmt):
- return (
- Filter('v4l2src', device=fmt.device),
+ return [
+ Source('v4l2', device=fmt.device),
Caps('video/x-raw', format=fmt.pixel, width=fmt.size.width, height=fmt.size.height,
framerate='%d/%d' % fmt.framerate),
- )
+ ]
def display_sink(fullscreen, sync=False):
- return Filter('kmssink' if fullscreen else 'waylandsink', sync=sync),
+ return Sink('kms' if fullscreen else 'wayland', sync=sync),
def h264_sink():
- return Filter('appsink', name='h264sink', emit_signals=True, max_buffers=1, drop=False, sync=False)
+ return Sink('app', name='h264sink', emit_signals=True, max_buffers=1, drop=False, sync=False)
def inference_pipeline(render_size, inference_size):
size = max_inner_size(render_size, inference_size)
- return (
+ return [
Filter('glfilterbin', filter='glcolorscale'),
Caps('video/x-raw', format='RGBA', width=size.width, height=size.height),
Filter('videoconvert'),
Caps('video/x-raw', format='RGB', width=size.width, height=size.height),
Filter('videobox', autocrop=True),
Caps('video/x-raw', width=inference_size.width, height=inference_size.height),
- Filter('appsink', name='appsink', emit_signals=True, max_buffers=1, drop=True, sync=False)
- )
+ Sink('app', name='appsink', emit_signals=True, max_buffers=1, drop=True, sync=False),
+ ]
# Display
def image_display_pipeline(filename, render_size, inference_size, fullscreen):
size = max_inner_size(render_size, inference_size)
return (
- decoded_file_src(filename),
- Tee(pads=((
- Queue(),
- Filter('imagefreeze'),
- Filter('videoconvert'),
- Filter('videoscale'),
- Caps('video/x-raw', width=render_size.width, height=render_size.height),
- Filter('rsvgoverlay', name='overlay'),
- display_sink(fullscreen),
- ),(
- Queue(),
- Filter('imagefreeze'),
- Filter('glupload'),
- inference_pipeline(render_size, inference_size),
- )))
+ [decoded_file_src(filename),
+ Tee(name='t')],
+ [Pad('t'),
+ Queue(),
+ Filter('imagefreeze'),
+ Filter('videoconvert'),
+ Filter('videoscale'),
+ Caps('video/x-raw', width=render_size.width, height=render_size.height),
+ Filter('rsvgoverlay', name='overlay'),
+ display_sink(fullscreen)],
+ [Pad('t'),
+ Queue(),
+ Filter('imagefreeze'),
+ Filter('glupload'),
+ inference_pipeline(render_size, inference_size)],
)
def video_display_pipeline(filename, render_size, inference_size, fullscreen):
return (
- decoded_file_src(filename),
- Filter('glupload'),
- Tee(pads=((
- Queue(max_size_buffers=1),
- Filter('glfilterbin', filter='glcolorscale'),
- Filter('rsvgoverlay', name='overlay'),
- Caps('video/x-raw', width=render_size.width, height=render_size.height),
- display_sink(fullscreen),
- ),(
- Queue(max_size_buffers=1, leaky='downstream'),
- inference_pipeline(render_size, inference_size),
- )))
+ [decoded_file_src(filename),
+ Filter('glupload'),
+ Tee(name='t')],
+ [Pad('t'),
+ Queue(max_size_buffers=1),
+ Filter('glfilterbin', filter='glcolorscale'),
+ Filter('rsvgoverlay', name='overlay'),
+ Caps('video/x-raw', width=render_size.width, height=render_size.height),
+ display_sink(fullscreen)],
+ [Pad('t'),
+ Queue(max_size_buffers=1, leaky='downstream'),
+ inference_pipeline(render_size, inference_size)],
)
def camera_display_pipeline(fmt, render_size, inference_size, fullscreen):
return (
- v4l2_src(fmt),
- Filter('glupload'),
- Tee(pads=((
- Queue(max_size_buffers=1, leaky='downstream'),
- Filter('glfilterbin', filter='glcolorscale'),
- Filter('rsvgoverlay', name='overlay'),
- display_sink(fullscreen),
- ),(
- Queue(max_size_buffers=1, leaky='downstream'),
- inference_pipeline(render_size, inference_size),
- )))
+ [v4l2_src(fmt),
+ Filter('glupload'),
+ Tee(name='t')],
+ [Pad(name='t'),
+ Queue(max_size_buffers=1, leaky='downstream'),
+ Filter('glfilterbin', filter='glcolorscale'),
+ Filter('rsvgoverlay', name='overlay'),
+ display_sink(fullscreen)],
+ [Pad(name='t'),
+ Queue(max_size_buffers=1, leaky='downstream'),
+ inference_pipeline(render_size, inference_size)],
)
# Headless
def image_headless_pipeline(filename, render_size, inference_size):
return (
- decoded_file_src(filename),
- Filter('imagefreeze'),
- Filter('glupload'),
- inference_pipeline(render_size, inference_size),
+ [decoded_file_src(filename),
+ Filter('imagefreeze'),
+ Filter('glupload'),
+ inference_pipeline(render_size, inference_size)],
)
def video_headless_pipeline(filename, render_size, inference_size):
return (
- decoded_file_src(filename),
- Filter('glupload'),
- inference_pipeline(render_size, inference_size),
+ [decoded_file_src(filename),
+ Filter('glupload'),
+ inference_pipeline(render_size, inference_size)],
)
def camera_headless_pipeline(fmt, render_size, inference_size):
return (
- v4l2_src(fmt),
- Filter('glupload'),
- inference_pipeline(render_size, inference_size),
+ [v4l2_src(fmt),
+ Filter('glupload'),
+ inference_pipeline(render_size, inference_size)],
)
# Streaming
def video_streaming_pipeline(filename, render_size, inference_size):
return (
- Filter('filesrc', location=filename),
- Filter('qtdemux'),
- Tee(pads=((
- Queue(max_size_buffers=1),
- Filter('h264parse'),
- Caps('video/x-h264', stream_format='byte-stream', alignment='nal'),
- h264_sink()
- ), (
- Queue(max_size_buffers=1),
- Filter('decodebin'),
- inference_pipeline(render_size, inference_size),
- )))
+ [Source('file', location=filename),
+ Filter('qtdemux'),
+ Tee(name='t')],
+ [Pad('t'),
+ Queue(max_size_buffers=1),
+ Filter('h264parse'),
+ Caps('video/x-h264', stream_format='byte-stream', alignment='nal'),
+ h264_sink()],
+ [Pad('t'),
+ Queue(max_size_buffers=1),
+ Filter('decodebin'),
+ inference_pipeline(render_size, inference_size)],
)
def camera_streaming_pipeline(fmt, profile, bitrate, render_size, inference_size):
size = max_inner_size(render_size, inference_size)
return (
- v4l2_src(fmt),
- Tee(pads=((
- Queue(max_size_buffers=1, leaky='downstream'),
- Filter('videoconvert'),
- Filter('x264enc',
+ [v4l2_src(fmt), Tee(name='t')],
+ [Pad('t'),
+ Queue(max_size_buffers=1, leaky='downstream'),
+ Filter('videoconvert'),
+ Filter('x264enc',
speed_preset='ultrafast',
tune='zerolatency',
threads=4,
@@ -140,9 +140,8 @@
Caps('video/x-h264', profile=profile),
Filter('h264parse'),
Caps('video/x-h264', stream_format='byte-stream', alignment='nal'),
- h264_sink()
- ), (
- Queue(),
- inference_pipeline(render_size, inference_size)
- )))
+ h264_sink()],
+ [Pad('t'),
+ Queue(),
+ inference_pipeline(render_size, inference_size)],
)
\ No newline at end of file