mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 17:51:16 +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('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,8 +54,8 @@ 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",
|
||||||
Gst.PadDirection.SRC,
|
Gst.PadDirection.SRC,
|
||||||
|
@ -68,13 +67,13 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
||||||
ICAPS))
|
ICAPS))
|
||||||
__gproperties__ = {
|
__gproperties__ = {
|
||||||
"window-duration": (float,
|
"window-duration": (float,
|
||||||
"Window Duration",
|
"Window Duration",
|
||||||
"Duration of the sliding window, in seconds",
|
"Duration of the sliding window, in seconds",
|
||||||
0.01,
|
0.01,
|
||||||
100.0,
|
100.0,
|
||||||
DEFAULT_WINDOW_DURATION,
|
DEFAULT_WINDOW_DURATION,
|
||||||
GObject.ParamFlags.READWRITE
|
GObject.ParamFlags.READWRITE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -128,7 +127,7 @@ class AudioPlotFilter(GstBase.BaseTransform):
|
||||||
inbuf = self.queued_buf
|
inbuf = self.queued_buf
|
||||||
_, info = inbuf.map(Gst.MapFlags.READ)
|
_, info = inbuf.map(Gst.MapFlags.READ)
|
||||||
res, data = self.converter.convert(GstAudio.AudioConverterFlags.NONE,
|
res, data = self.converter.convert(GstAudio.AudioConverterFlags.NONE,
|
||||||
info.data)
|
info.data)
|
||||||
data = memoryview(data).cast('i')
|
data = memoryview(data).cast('i')
|
||||||
|
|
||||||
nsamples = len(data) - self.buf_offset
|
nsamples = len(data) - self.buf_offset
|
||||||
|
@ -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)
|
||||||
|
|
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('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,27 +40,29 @@ 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__ = (
|
||||||
Gst.PadTemplate.new_with_gtype("sink_%u",
|
Gst.PadTemplate.new_with_gtype("sink_%u",
|
||||||
Gst.PadDirection.SINK,
|
Gst.PadDirection.SINK,
|
||||||
Gst.PadPresence.REQUEST,
|
Gst.PadPresence.REQUEST,
|
||||||
ICAPS,
|
ICAPS,
|
||||||
GstBase.AggregatorPad.__gtype__),
|
GstBase.AggregatorPad.__gtype__),
|
||||||
Gst.PadTemplate.new_with_gtype("src",
|
Gst.PadTemplate.new_with_gtype("src",
|
||||||
Gst.PadDirection.SRC,
|
Gst.PadDirection.SRC,
|
||||||
Gst.PadPresence.ALWAYS,
|
Gst.PadPresence.ALWAYS,
|
||||||
OCAPS,
|
OCAPS,
|
||||||
GstBase.AggregatorPad.__gtype__)
|
GstBase.AggregatorPad.__gtype__)
|
||||||
)
|
)
|
||||||
|
|
||||||
def mix_buffers(self, agg, pad, bdata):
|
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.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)
|
||||||
|
|
Loading…
Reference in a new issue