diff options
author | Mathieu Duponchelle <mathieu@centricular.com> | 2018-07-29 19:51:34 +0200 |
---|---|---|
committer | Mathieu Duponchelle <mathieu@centricular.com> | 2018-07-29 19:51:34 +0200 |
commit | 6cf081fa115d016f18904829039fbb7c24804ae8 (patch) | |
tree | fb579616b2341e9275f853022d667289f2293b59 /examples | |
parent | 6fcc1433d731a76fe90efc0a146256b07318f036 (diff) |
Examples: add mixer plugin example
Diffstat (limited to 'examples')
-rw-r--r-- | examples/README.md | 11 | ||||
-rw-r--r-- | examples/plugins/python/mixer.py | 104 | ||||
-rw-r--r-- | examples/requirements.txt | 2 |
3 files changed, 117 insertions, 0 deletions
diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..9a88898 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ +# Dependencies + +Some of the examples require external python dependencies, for this purpose +an illustrative requirements.txt is provided, with annotations documenting +which example requires a dependency. + +You can install all the dependencies with: + +``` +python3 -m pip install -r requirements.txt --user +``` diff --git a/examples/plugins/python/mixer.py b/examples/plugins/python/mixer.py new file mode 100644 index 0000000..5d9aafe --- /dev/null +++ b/examples/plugins/python/mixer.py @@ -0,0 +1,104 @@ +''' +Simple mixer element, accepts 320 x 240 RGBA at 30 fps +on any number of sinkpads. + +Requires PIL (Python Imaging Library) + +Example pipeline: + +gst-launch-1.0 py_videomixer name=mixer ! videoconvert ! autovideosink \ + videotestsrc ! mixer. \ + videotestsrc pattern=ball ! mixer. \ + videotestsrc pattern=snow ! mixer. +''' + +import gi + +gi.require_version('Gst', '1.0') +gi.require_version('GstBase', '1.0') +gi.require_version('GObject', '2.0') + +from gi.repository import Gst, GObject, GstBase + +Gst.init(None) + +try: + from PIL import Image +except ImportError: + Gst.error('py_videomixer requires PIL') + raise + +# Completely fixed input / output +ICAPS = Gst.Caps(Gst.Structure('video/x-raw', + format='RGBA', + width=320, + height=240, + framerate=Gst.Fraction(30, 1))) + +OCAPS = Gst.Caps(Gst.Structure('video/x-raw', + format='RGBA', + width=320, + height=240, + framerate=Gst.Fraction(30, 1))) + +class BlendData: + def __init__(self, outimg): + self.outimg = outimg + self.pts = 0 + self.eos = True + +class Videomixer(GstBase.Aggregator): + __gstmetadata__ = ('Videomixer','Video/Mixer', \ + 'Python video mixer', 'Mathieu Duponchelle') + + __gsttemplates__ = ( + Gst.PadTemplate.new_with_gtype("sink_%u", + Gst.PadDirection.SINK, + Gst.PadPresence.REQUEST, + ICAPS, + GstBase.AggregatorPad.__gtype__), + Gst.PadTemplate.new_with_gtype("src", + Gst.PadDirection.SRC, + Gst.PadPresence.ALWAYS, + OCAPS, + GstBase.AggregatorPad.__gtype__) + ) + + def mix_buffers(self, agg, pad, bdata): + buf = pad.pop_buffer() + _, info = buf.map(Gst.MapFlags.READ) + + img = Image.frombuffer('RGBA', (320, 240), info.data, "raw", 'RGBA', 0, 1) + + bdata.outimg = Image.blend(bdata.outimg, img, alpha=0.5) + bdata.pts = buf.pts + + buf.unmap(info) + + bdata.eos = False + + return True + + def do_aggregate(self, timeout): + outimg = Image.new('RGBA', (320, 240), 0x00000000) + + bdata = BlendData(outimg) + + self.foreach_sink_pad(self.mix_buffers, bdata) + + data = bdata.outimg.tobytes() + + outbuf = Gst.Buffer.new_allocate(None, len(data), None) + outbuf.fill(0, data) + outbuf.pts = bdata.pts + self.finish_buffer (outbuf) + + # We are EOS when no pad was ready to be aggregated, + # this would obviously not work for live + if bdata.eos: + return Gst.FlowReturn.EOS + + return Gst.FlowReturn.OK + +GObject.type_register(Videomixer) +__gstelementfactory__ = ("py_videomixer", Gst.Rank.NONE, Videomixer) diff --git a/examples/requirements.txt b/examples/requirements.txt new file mode 100644 index 0000000..84fa703 --- /dev/null +++ b/examples/requirements.txt @@ -0,0 +1,2 @@ +# py_videomixer plugin +Pillow >= 5.1.0 |