Replies: 14 comments 2 replies
-
hi @hmaarrfk Have you found any other solution besides explicitly closing containers and streams? This is my current code that encodes and decodes from the list of numpy array images ( with io.BytesIO() as buf:
with av.open(buf, "w", format=container_string) as container:
stream = container.add_stream(codec, rate=rate, options=options)
stream.height = imgs[0].shape[0]
stream.width = imgs[0].shape[1]
stream.pix_fmt = pixel_fmt
for img in imgs_padded:
frame = av.VideoFrame.from_ndarray(img, format="rgb24")
frame.pict_type = "NONE"
for packet in stream.encode(frame):
container.mux(packet)
# Flush stream
for packet in stream.encode():
container.mux(packet)
stream.close()
outputs = []
with av.open(buf, "r", format=container_string) as video:
for i, frame in enumerate(video.decode(video=0)):
if sequence_length <= i < sequence_length * 2:
outputs.append(frame.to_rgb().to_ndarray().astype(np.uint8))
if i >= sequence_length * 2:
break
video.streams.video[0].close() |
Beta Was this translation helpful? Give feedback.
-
no, i just explicitly call close. |
Beta Was this translation helpful? Give feedback.
-
Ok, thanks. I later found out the memory leak only occurs if I use the |
Beta Was this translation helpful? Give feedback.
-
I can also reproduce this, without It seems to happen when I process the frames in a different process, and stream.close causes everything to hang if I use more than 1 processes. Multiple bugs bundled into one 😅 |
Beta Was this translation helpful? Give feedback.
-
hi, @meakbiyik |
Beta Was this translation helpful? Give feedback.
-
Hey @RoyaltyLJW, I still use |
Beta Was this translation helpful? Give feedback.
-
@meakbiyik Thanks a lot. It fix my deadlock issue |
Beta Was this translation helpful? Give feedback.
-
I also experienced the same issue - when deployed in Docker, not closing the stream causes memory instability. @hmaarrfk you saved my life with this extremely niche bug report. |
Beta Was this translation helpful? Give feedback.
-
Hi, I encountered a similar problem on repeated video decoding. It seems that adding The simplified code I'm using: from contextlib import closing
import av # v12.3.0 in my case
def iterate_frames(path: str, *, with_threads: bool = False):
with av.open(path) as container:
stream = container.streams.video[0]
if with_threads:
stream.thread_type = 'AUTO'
for packet in container.demux(stream):
for frame in packet.decode():
yield frame
# stream.codec_context.close() # seems to be the fix
while True:
for frame in iterate_frames("myvideo.mp4", with_threads=True):
pass I still can see slight memory increase after many iterations, but it's fluctuating in the range of several MB, while without the fixing line the memory consumption rockets up into gigabytes. This happens both in singlethreaded and multithreaded modes. Full example: import io
import os
from contextlib import closing, contextmanager
from typing import Union
import av
import av.video
import matplotlib.pyplot as plt
import numpy as np
import psutil
from PIL import Image
class _AvVideoReading:
@contextmanager
def read_av_container(
self, source: Union[str, io.BytesIO], *, with_context_fix: bool = True
) -> av.container.InputContainer:
if isinstance(source, io.BytesIO):
source.seek(0) # required for re-reading
container = av.open(source)
try:
yield container
finally:
if with_context_fix:
# fixes a memory leak in input container closing
# https://github.com/PyAV-Org/PyAV/issues/1117
for stream in container.streams:
context = stream.codec_context
if context and context.is_open:
context.close()
if container.open_files:
container.close()
def iterate_frames(
source: Union[str, io.BytesIO], *, with_threads: bool = False, with_context_fix: bool = True
):
_av = _AvVideoReading()
with _av.read_av_container(source, with_context_fix=with_context_fix) as container:
stream = container.streams.video[0]
if with_threads:
stream.thread_type = "AUTO"
else:
stream.thread_type = "NONE"
with closing(container.demux(stream)) as demux_iter:
for packet in demux_iter:
for frame in packet.decode():
yield frame
def get_current_process_memory() -> tuple[int, int]:
process = psutil.Process(os.getpid())
memory_info = process.memory_info()
return memory_info.rss, memory_info.vms
def generate_video_file(num_frames: int, size: tuple[int, int] = (100, 50)) -> io.BytesIO:
f = io.BytesIO()
f.name = "video.avi"
with av.open(f, "w") as container:
stream = container.add_stream("mjpeg", rate=60)
stream.width = size[0]
stream.height = size[1]
stream.color_range = av.video.reformatter.ColorRange.JPEG
for i in range(num_frames):
frame = av.VideoFrame.from_image(Image.new("RGB", size=size, color=(i, i, i)))
for packet in stream.encode(frame):
container.mux(packet)
f.seek(0)
return f
def test_memory_leaks(*args, repeats: int = 10000, **kwargs):
mem_virt = np.zeros(repeats, dtype=int)
mem_used = np.zeros(repeats, dtype=int)
i = 0
while not repeats or i < repeats:
print(i)
try:
test_full_decoding(*args, **kwargs)
rss, virt = get_current_process_memory()
print(rss, virt)
mem_used[i] = rss
mem_virt[i] = virt
except Exception as e:
print(e)
i += 1
plt.loglog(np.arange(1, len(mem_used)), mem_used[1:] - mem_used[0], label="Memory Used")
plt.ylabel("Memory Usage (Bytes)")
plt.xlabel("Iteration (count)")
plt.legend()
plt.show()
def test_full_decoding(*args, **kwargs):
print("reading started")
for frame in iterate_frames(*args, **kwargs):
pass
print("reading finished")
sample_video = generate_video_file(num_frames=300, size=(1024, 768))
test_memory_leaks(sample_video, with_threads=True, with_context_fix=False, repeats=1000) Multithreaded mode, no fix: Multithreaded mode, with the fix:
Upd: still actual in 12.3.0 |
Beta Was this translation helpful? Give feedback.
-
@WyattBlue do you not this this deserves to be a bug? |
Beta Was this translation helpful? Give feedback.
-
@hmaarrfk Can you review #1602. In your opinion, does it fully or partially fixes the issue? |
Beta Was this translation helpful? Give feedback.
-
@WyattBlue, I added more details to the other similar problem mentioned above. |
Beta Was this translation helpful? Give feedback.
-
The core issue has been fixed. It's time to give this thread a rest. |
Beta Was this translation helpful? Give feedback.
-
Thank you in fact with Python 3.10 + PyAV 14 it does in fact show little memory growth. However, I get the rror:
I had to remove the |
Beta Was this translation helpful? Give feedback.
-
Overview
I think there is a memory leak that occurs if you don't explicitly close a container or stream.
I'm still trying to drill the problem down, but I think I have a minimum reproducing example that I think is worthwhile to share at this stage.
It seems that
__dealloc__
isn't called as expected maybe??? https://github.com/PyAV-Org/PyAV/blob/main/av/container/input.pyx#L88Expected behavior
That the memory be cleared. If I add the
close
calls to the loop I get.Versions
Beta Was this translation helpful? Give feedback.
All reactions