mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 03:31:05 +00:00
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:
parent
627a956768
commit
f48442d62a
3 changed files with 151 additions and 31 deletions
|
@ -15,7 +15,6 @@ gi.require_version('Gst', '1.0')
|
|||
gi.require_version('GstBase', '1.0')
|
||||
gi.require_version('GstAudio', '1.0')
|
||||
gi.require_version('GstVideo', '1.0')
|
||||
|
||||
from gi.repository import Gst, GLib, GObject, GstBase, GstAudio, GstVideo
|
||||
|
||||
try:
|
||||
|
@ -37,8 +36,8 @@ AUDIO_FORMATS = [f.strip() for f in
|
|||
ICAPS = Gst.Caps(Gst.Structure('audio/x-raw',
|
||||
format=Gst.ValueList(AUDIO_FORMATS),
|
||||
layout='interleaved',
|
||||
rate = Gst.IntRange(range(1, GLib.MAXINT)),
|
||||
channels = Gst.IntRange(range(1, GLib.MAXINT))))
|
||||
rate=Gst.IntRange(range(1, GLib.MAXINT)),
|
||||
channels=Gst.IntRange(range(1, GLib.MAXINT))))
|
||||
|
||||
OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
|
||||
format='ARGB',
|
||||
|
@ -55,8 +54,8 @@ DEFAULT_FRAMERATE_DENOM = 1
|
|||
|
||||
|
||||
class AudioPlotFilter(GstBase.BaseTransform):
|
||||
__gstmetadata__ = ('AudioPlotFilter','Filter', \
|
||||
'Plot audio waveforms', 'Mathieu Duponchelle')
|
||||
__gstmetadata__ = ('AudioPlotFilter', 'Filter',
|
||||
'Plot audio waveforms', 'Mathieu Duponchelle')
|
||||
|
||||
__gsttemplates__ = (Gst.PadTemplate.new("src",
|
||||
Gst.PadDirection.SRC,
|
||||
|
@ -68,13 +67,13 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
|||
ICAPS))
|
||||
__gproperties__ = {
|
||||
"window-duration": (float,
|
||||
"Window Duration",
|
||||
"Duration of the sliding window, in seconds",
|
||||
0.01,
|
||||
100.0,
|
||||
DEFAULT_WINDOW_DURATION,
|
||||
GObject.ParamFlags.READWRITE
|
||||
)
|
||||
"Window Duration",
|
||||
"Duration of the sliding window, in seconds",
|
||||
0.01,
|
||||
100.0,
|
||||
DEFAULT_WINDOW_DURATION,
|
||||
GObject.ParamFlags.READWRITE
|
||||
)
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
@ -128,7 +127,7 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
|||
inbuf = self.queued_buf
|
||||
_, info = inbuf.map(Gst.MapFlags.READ)
|
||||
res, data = self.converter.convert(GstAudio.AudioConverterFlags.NONE,
|
||||
info.data)
|
||||
info.data)
|
||||
data = memoryview(data).cast('i')
|
||||
|
||||
nsamples = len(data) - self.buf_offset
|
||||
|
@ -187,10 +186,8 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
|||
return ret.fixate()
|
||||
|
||||
def do_set_caps(self, icaps, ocaps):
|
||||
in_info = GstAudio.AudioInfo()
|
||||
in_info.from_caps(icaps)
|
||||
out_info = GstVideo.VideoInfo()
|
||||
out_info.from_caps(ocaps)
|
||||
in_info = GstAudio.AudioInfo.new_from_caps(icaps)
|
||||
out_info = GstVideo.VideoInfo().new_from_caps(ocaps)
|
||||
|
||||
self.convert_info = GstAudio.AudioInfo()
|
||||
self.convert_info.set_format(GstAudio.AudioFormat.S32,
|
||||
|
@ -238,5 +235,6 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
|||
|
||||
return True
|
||||
|
||||
|
||||
GObject.type_register(AudioPlotFilter)
|
||||
__gstelementfactory__ = ("audioplot", Gst.Rank.NONE, AudioPlotFilter)
|
||||
|
|
116
subprojects/gst-python/examples/plugins/python/filesrc.py
Executable file
116
subprojects/gst-python/examples/plugins/python/filesrc.py
Executable 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))
|
|
@ -17,7 +17,6 @@ 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_python()
|
||||
|
@ -41,27 +40,29 @@ OCAPS = Gst.Caps(Gst.Structure('video/x-raw',
|
|||
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')
|
||||
__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__)
|
||||
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):
|
||||
|
@ -73,6 +74,10 @@ class Videomixer(GstBase.Aggregator):
|
|||
bdata.outimg = Image.blend(bdata.outimg, img, alpha=0.5)
|
||||
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)
|
||||
|
||||
bdata.eos = False
|
||||
|
@ -91,7 +96,7 @@ class Videomixer(GstBase.Aggregator):
|
|||
outbuf = Gst.Buffer.new_allocate(None, len(data), None)
|
||||
outbuf.fill(0, data)
|
||||
outbuf.pts = bdata.pts
|
||||
self.finish_buffer (outbuf)
|
||||
self.finish_buffer(outbuf)
|
||||
|
||||
# We are EOS when no pad was ready to be aggregated,
|
||||
# this would obviously not work for live
|
||||
|
@ -100,5 +105,6 @@ class Videomixer(GstBase.Aggregator):
|
|||
|
||||
return Gst.FlowReturn.OK
|
||||
|
||||
|
||||
GObject.type_register(Videomixer)
|
||||
__gstelementfactory__ = ("py_videomixer", Gst.Rank.NONE, Videomixer)
|
||||
|
|
Loading…
Reference in a new issue