memory leak on _remap function in gstmodule
There are a memory leak on _remap function in gstmodule. The PyObjects created don't decrement the referencies on exit.
I don't have unit test for this because because i don't know the best approach to do that, if someone can help me I appreciate. I have made a python plugin with little application that show the problem. This piece of code can be used to check the memory leak on master branch, to run this:
mkdir python
cd python
export GST_PLUGIN_PATH=..
export GST_DEBUG=python:4
python map-unmap-bug.py --help
# don't touch buffers
python map-unmap-bug.py
# map and unmap buffers (memory leak)
python map-unmap-bug.py -m
map-unmap-bug.py
#!/usr/bin/env python
import argparse
import sys
import gc
import numpy as np
import time
import datetime
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
from gi.repository import GObject, Gst, GstBase, GLib
Gst.init(None)
class ProcessMemory():
def diff(self):
start = np.asarray(self._start, dtype=np.int)
stop = np.asarray(self._stop, dtype=np.int)
d = stop - start
d = list(map(str, list(d)))
return self.labels(d)
def start(self):
self._start = self.read()
return self.labels(self._start)
def stop(self):
self._stop = self.read()
return self.labels(self._stop)
def status(self):
statm = self.read()
return self.labels(statm)
def read(self):
with open('/proc/self/statm') as f:
return list(f.read().split())
def labels(self, statm):
# https://www.kernel.org/doc/Documentation/filesystems/proc.txt
labels = ('size:', ' resident:', ' shared:', ' trs:', ' lrs:', ' drs:', ' dt:')
return "".join(i + j for i, j in zip(labels, statm))
#
# Simple MapUnmapBug element created entirely in python
#
class MapUnmapBug(GstBase.BaseTransform):
__gstmetadata__ = ('MapUnmapBug','Transform', \
'Simple MapUnmapBug element created entirely in python', 'Jose Quaresma')
__gsttemplates__ = (Gst.PadTemplate.new("src",
Gst.PadDirection.SRC,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()),
Gst.PadTemplate.new("sink",
Gst.PadDirection.SINK,
Gst.PadPresence.ALWAYS,
Gst.Caps.new_any()))
def __init__(self):
self.map_unmap = True
self.pm = ProcessMemory()
def do_transform_ip(self, buffer):
gc.collect()
if self.map_unmap:
try:
with buffer.map(Gst.MapFlags.READ | Gst.MapFlags.WRITE) as info:
pass
except Gst.MapError as e:
Gst.error("Mapping error: %s" % e)
return Gst.FlowReturn.ERROR
Gst.info(self.pm.status())
return Gst.FlowReturn.OK
def do_start(self):
self.start = self.pm.start()
return True
def do_stop(self):
self.stop = self.pm.stop()
self.diff = self.pm.diff()
return True
GObject.type_register(MapUnmapBug)
__gstelementfactory__ = ("map_unmap_bug", Gst.Rank.NONE, MapUnmapBug)
def bus_call(bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True
def main():
parser = argparse.ArgumentParser(prog='map-unmap-bug', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-m', '--map-unmap', action='store_true', help='do buffer map and unmap')
default='videotestsrc ! map_unmap_bug ! fakevideosink sync=0'
parser.add_argument('-p', '--pipeline', help=' pipeline', default=default)
args = parser.parse_args()
playbin = Gst.parse_launch(args.pipeline)
map_unmap_bug = playbin.get_by_name('map-unmap-bug+mapunmapbug0')
if map_unmap_bug:
map_unmap_bug.map_unmap = args.map_unmap
# create and event loop and feed gstreamer bus mesages to it
loop = GLib.MainLoop()
bus = playbin.get_bus()
bus.add_signal_watch()
bus.connect ("message", bus_call, loop)
seconds = time.time()
# start play back and listed to events
playbin.set_state(Gst.State.PLAYING)
try:
loop.run()
except:
pass
# cleanup
playbin.set_state(Gst.State.NULL)
playbin.get_state(Gst.CLOCK_TIME_NONE)
print('\nargv:\n\t', *sys.argv)
# memory status
if map_unmap_bug:
seconds = time.time() - seconds
print('\nduration:\n\t', str(datetime.timedelta(seconds=seconds)))
print('\nmemory:')
print('\t start:\t', map_unmap_bug.start.replace(' ','\t'))
print('\t stop:\t', map_unmap_bug.stop.replace(' ','\t'))
print('\t diff:\t', map_unmap_bug.diff.replace(' ','\t'))
if __name__ == "__main__":
main()