python: Update plugin examples

Fix warnings from bindings changes in various plugin
examples

Fix the python mixer plugin by ensuring that PIL
is not holding a reference to mapped GstBuffer memory.

Port the filesrc example from old_examples

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5187>
This commit is contained in:
Jan Schmidt 2023-08-15 14:22:12 +10:00 committed by GStreamer Marge Bot
parent 627a956768
commit f48442d62a
3 changed files with 151 additions and 31 deletions

View file

@ -15,7 +15,6 @@ gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0') gi.require_version('GstBase', '1.0')
gi.require_version('GstAudio', '1.0') gi.require_version('GstAudio', '1.0')
gi.require_version('GstVideo', '1.0') gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, GLib, GObject, GstBase, GstAudio, GstVideo from gi.repository import Gst, GLib, GObject, GstBase, GstAudio, GstVideo
try: try:
@ -37,8 +36,8 @@ AUDIO_FORMATS = [f.strip() for f in
ICAPS = Gst.Caps(Gst.Structure('audio/x-raw', ICAPS = Gst.Caps(Gst.Structure('audio/x-raw',
format=Gst.ValueList(AUDIO_FORMATS), format=Gst.ValueList(AUDIO_FORMATS),
layout='interleaved', layout='interleaved',
rate = Gst.IntRange(range(1, GLib.MAXINT)), rate=Gst.IntRange(range(1, GLib.MAXINT)),
channels = Gst.IntRange(range(1, GLib.MAXINT)))) channels=Gst.IntRange(range(1, GLib.MAXINT))))
OCAPS = Gst.Caps(Gst.Structure('video/x-raw', OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
format='ARGB', format='ARGB',
@ -55,7 +54,7 @@ DEFAULT_FRAMERATE_DENOM = 1
class AudioPlotFilter(GstBase.BaseTransform): class AudioPlotFilter(GstBase.BaseTransform):
__gstmetadata__ = ('AudioPlotFilter','Filter', \ __gstmetadata__ = ('AudioPlotFilter', 'Filter',
'Plot audio waveforms', 'Mathieu Duponchelle') 'Plot audio waveforms', 'Mathieu Duponchelle')
__gsttemplates__ = (Gst.PadTemplate.new("src", __gsttemplates__ = (Gst.PadTemplate.new("src",
@ -187,10 +186,8 @@ class AudioPlotFilter(GstBase.BaseTransform):
return ret.fixate() return ret.fixate()
def do_set_caps(self, icaps, ocaps): def do_set_caps(self, icaps, ocaps):
in_info = GstAudio.AudioInfo() in_info = GstAudio.AudioInfo.new_from_caps(icaps)
in_info.from_caps(icaps) out_info = GstVideo.VideoInfo().new_from_caps(ocaps)
out_info = GstVideo.VideoInfo()
out_info.from_caps(ocaps)
self.convert_info = GstAudio.AudioInfo() self.convert_info = GstAudio.AudioInfo()
self.convert_info.set_format(GstAudio.AudioFormat.S32, self.convert_info.set_format(GstAudio.AudioFormat.S32,
@ -238,5 +235,6 @@ class AudioPlotFilter(GstBase.BaseTransform):
return True return True
GObject.type_register(AudioPlotFilter) GObject.type_register(AudioPlotFilter)
__gstelementfactory__ = ("audioplot", Gst.Rank.NONE, AudioPlotFilter) __gstelementfactory__ = ("audioplot", Gst.Rank.NONE, AudioPlotFilter)

View file

@ -0,0 +1,116 @@
#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
# GStreamer python bindings
# Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.net>
# 2004 Johan Dahlin <johan@gnome.org>
#
# filesrc.py: implements a file source element completely in python
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
import gi
import sys
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
from gi.repository import GLib, Gst, GObject, GstBase
if __name__ == '__main__':
Gst.init(None)
Gst.init_python()
class FileSource(GstBase.BaseSrc):
__gstmetadata__ = ('PyFileSource', 'Source',
'Custom Python filesrc example', 'Jan Schmidt')
__gsttemplates__ = (
Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()),
)
blocksize = 4096
fd = None
def __init__(self, name):
super().__init__()
self.curoffset = 0
self.set_name(name)
def set_property(self, name, value):
if name == 'location':
self.fd = open(value, 'rb')
def do_create(self, offset, size, wot):
if offset != self.curoffset:
self.fd.seek(offset, 0)
data = self.fd.read(self.blocksize)
if data:
self.curoffset += len(data)
return Gst.FlowReturn.OK, Gst.Buffer.new_wrapped(data)
else:
return Gst.FlowReturn.EOS, None
GObject.type_register(FileSource)
__gstelementfactory__ = ("filesrc_py", Gst.Rank.NONE, FileSource)
def main(args):
Gst.init()
if len(args) != 3:
print(f'Usage: {args[0]} input output')
return -1
bin = Gst.Pipeline('pipeline')
filesrc = FileSource('filesource')
assert filesrc
filesrc.set_property('location', args[1])
filesink = Gst.ElementFactory.make('filesink', 'sink')
filesink.set_property('location', args[2])
bin.add(filesrc, filesink)
Gst.Element.link_many(filesrc, filesink)
bin.set_state(Gst.State.PLAYING)
mainloop = GLib.MainLoop()
def bus_event(bus, message):
t = message.type
if t == Gst.MessageType.EOS:
mainloop.quit()
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
print(f"Error: {err}, {debug}")
mainloop.quit()
return True
bus = bin.get_bus()
bus.add_signal_watch()
bus.connect('message', bus_event)
mainloop.run()
bin.set_state(Gst.State.NULL)
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -17,7 +17,6 @@ import gi
gi.require_version('Gst', '1.0') gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0') gi.require_version('GstBase', '1.0')
gi.require_version('GObject', '2.0') gi.require_version('GObject', '2.0')
from gi.repository import Gst, GObject, GstBase from gi.repository import Gst, GObject, GstBase
Gst.init_python() Gst.init_python()
@ -41,14 +40,16 @@ OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
height=240, height=240,
framerate=Gst.Fraction(30, 1))) framerate=Gst.Fraction(30, 1)))
class BlendData: class BlendData:
def __init__(self, outimg): def __init__(self, outimg):
self.outimg = outimg self.outimg = outimg
self.pts = 0 self.pts = 0
self.eos = True self.eos = True
class Videomixer(GstBase.Aggregator): class Videomixer(GstBase.Aggregator):
__gstmetadata__ = ('Videomixer','Video/Mixer', \ __gstmetadata__ = ('Videomixer', 'Video/Mixer',
'Python video mixer', 'Mathieu Duponchelle') 'Python video mixer', 'Mathieu Duponchelle')
__gsttemplates__ = ( __gsttemplates__ = (
@ -73,6 +74,10 @@ class Videomixer(GstBase.Aggregator):
bdata.outimg = Image.blend(bdata.outimg, img, alpha=0.5) bdata.outimg = Image.blend(bdata.outimg, img, alpha=0.5)
bdata.pts = buf.pts bdata.pts = buf.pts
# Need to ensure the PIL image has been released, or unmap will fail
# with an outstanding memoryview buffer error
del img
buf.unmap(info) buf.unmap(info)
bdata.eos = False bdata.eos = False
@ -91,7 +96,7 @@ class Videomixer(GstBase.Aggregator):
outbuf = Gst.Buffer.new_allocate(None, len(data), None) outbuf = Gst.Buffer.new_allocate(None, len(data), None)
outbuf.fill(0, data) outbuf.fill(0, data)
outbuf.pts = bdata.pts outbuf.pts = bdata.pts
self.finish_buffer (outbuf) self.finish_buffer(outbuf)
# We are EOS when no pad was ready to be aggregated, # We are EOS when no pad was ready to be aggregated,
# this would obviously not work for live # this would obviously not work for live
@ -100,5 +105,6 @@ class Videomixer(GstBase.Aggregator):
return Gst.FlowReturn.OK return Gst.FlowReturn.OK
GObject.type_register(Videomixer) GObject.type_register(Videomixer)
__gstelementfactory__ = ("py_videomixer", Gst.Rank.NONE, Videomixer) __gstelementfactory__ = ("py_videomixer", Gst.Rank.NONE, Videomixer)