Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segmentation fault accessing attributes after closing container #1137

Closed
4 of 6 tasks
heyoeyo opened this issue Jun 26, 2023 · 3 comments · Fixed by #1205
Closed
4 of 6 tasks

Segmentation fault accessing attributes after closing container #1137

heyoeyo opened this issue Jun 26, 2023 · 3 comments · Fixed by #1205
Labels

Comments

@heyoeyo
Copy link

heyoeyo commented Jun 26, 2023

Overview

I consistently get segmentation faults when trying to access certain attributes on an InputContainer after calling the .close() function.

Most of the time I'm running in a REPL, so this behavior has been a bit of a landmine for me.

Expected behavior

Ideally the attribute would either return a value as expected for attributes that make sense to ask about 'after-the-fact' (i.e. getting the size or duration after closing the video) or otherwise raise an error.

Actual behavior

Simple example:

import av

container = av.open("any_video.mp4", mode="r")
container.close()
print(container.duration) # -> Segmentation fault (core dumped)
print("Finished!")

On Linux: I get the message: Segmentation fault (core dumped) and the script ends (it doesn't print out 'Finished!')

On a Windows 10 VM (using cmd.exe): The script prints a single blank line, it doesn't say segmentation fault, but also doesn't print 'Finished!'

It doesn't seem to depend on the video (at least from the ones I've tested).

Investigation

I've checked each of the (non underscored) attributes to see which ones do/don't cause a crash (using the simple example above where the attribute is printed immediately after the call to close).

In the table below, (InputContainer) attributes on the left are fine (no segfault) attributes on the right always segfault:

OK Crashes
.close .auto_csf
.container_options .bit_rate
.file .bit_exact
.decode .custom_io
.demux .discard_corrupt
.dumps_format .duration
.format .fast_seek
.io_open .flags
.metadata .flush_packets
.metadata_encoding .gen_pts
.metadata_errors .ign_dts
.name .ign_idx
.open_files .no_buffer
.open_timeout .no_fill_in
.options .no_parse
.read_timeout .non_block
.seek .priv_opt
.stream_options .shortest
.streams .size
.writeable .sort_dts
.start_time

Hopefully there's some meaningful pattern here as to what leads to crashes, but I'm new to PyAV, so I have no idea...

I found a similar issue here: #1070

Though it seems to have been closed due to inactivity, and in fairness, may be tied to debugpy (not familiar with it).

Reproduction

You can try the simple example above with an existing video, or otherwise here's a version that creates a video first (needs pip install av numpy to run):

import os.path as osp
import av
import numpy as np

def make_sample_video(save_path):

    """
    Modified example from:
    https://pyav.org/docs/stable/cookbook/numpy.html#generating-video
    """
    
    # Avoid repeat video creation
    if osp.exists(save_path):
        return
    
    # Output settings (not important?)
    duration = 4
    fps = 24
    total_frames = duration * fps
    vid_w = 256
    vid_h = 128
    
    container = av.open(save_path, mode="w")    
    stream = container.add_stream("mpeg4", rate=fps)
    stream.width = vid_w
    stream.height = vid_h
    stream.pix_fmt = "yuv420p"
    
    # Write random frames
    for frame_i in range(total_frames):        
        img = np.random.randint(0, 255, (vid_h, vid_w, 3), dtype=np.uint8)
        frame = av.VideoFrame.from_ndarray(img, format="rgb24")
        for packet in stream.encode(frame):
            container.mux(packet)
    
    # Flush stream
    for packet in stream.encode():
        container.mux(packet)
    
    # Close the file
    container.close()

sample_file_path = "rainbow_noise.mp4"
make_sample_video(sample_file_path)

container = av.open(sample_file_path, mode = "r")
container.close()

print("Check duration:")
print(container.duration)
print("Finished!")

For me, this will always segfault before reaching the 'Finished!' print out.

Versions

  • OS: Linux (pop os 22.04 or Window 10 VM)
  • PyAV runtime:
PyAV v10.0.0
library configuration: --disable-static --enable-shared --libdir=/tmp/vendor/lib --prefix=/tmp/vendor --disable-alsa --disable-doc --disable-mediafoundation --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-lzma --enable-version3 --enable-zlib
library license: GPL version 3 or later
libavcodec     59. 37.100
libavdevice    59.  7.100
libavfilter     8. 44.100
libavformat    59. 27.100
libavutil      57. 28.100
libswresample   4.  7.100
libswscale      6.  7.100

  • PyAV build:
pip installed av
  • FFmpeg:
ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers
  built with gcc 11 (Ubuntu 11.2.0-19ubuntu1)
  configuration: --prefix=/usr --extra-version=0ubuntu0.22.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-librsvg --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
  libpostproc    55.  9.100 / 55.  9.100


Research

I have done the following:

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Oct 25, 2023
@jlaine
Copy link
Member

jlaine commented Nov 1, 2023

This is a valid bug! The problem is that self.ptr gets set to null when the container is closed. I'll try to put together a PR

jlaine added a commit that referenced this issue Nov 1, 2023
Calling `close()` on an input container calls `avformat_close_input`
which will set the `self.ptr` AVFormatContext pointer to NULL. We need
to ensure `self.ptr` is not NULL before using it.

Fixes: #1137
@github-actions github-actions bot removed the stale label Nov 2, 2023
jlaine added a commit that referenced this issue Nov 2, 2023
Calling `close()` on an input container calls `avformat_close_input`
which will set the `self.ptr` AVFormatContext pointer to NULL. We need
to ensure `self.ptr` is not NULL before using it.

Fixes: #1137
WyattBlue pushed a commit to rawler/PyAV that referenced this issue Nov 25, 2023
Calling `close()` on an input container calls `avformat_close_input`
which will set the `self.ptr` AVFormatContext pointer to NULL. We need
to ensure `self.ptr` is not NULL before using it.

Fixes: PyAV-Org#1137
@ncheng89
Copy link

I found that this problem still exists. If the input container is closed and the input container is used as the output template, a Segmentation fault (core dumped) will be reported.

import os.path as osp
import time

import av
import numpy as np

def make_sample_video(save_path,in_video_stream):
"""
Modified example from:
https://pyav.org/docs/stable/cookbook/numpy.html#generating-video
"""

# Avoid repeat video creation
if osp.exists(save_path):
    return

# Output settings (not important?)
duration = 4
fps = 24
total_frames = duration * fps
vid_w = 256
vid_h = 128

container = av.open(save_path, mode="w")
stream = container.add_stream(template=in_video_stream)
stream.width = vid_w
stream.height = vid_h
stream.pix_fmt = "yuv420p"

# Write random frames
for frame_i in range(total_frames):
    img = np.random.randint(0, 255, (vid_h, vid_w, 3), dtype=np.uint8)
    frame = av.VideoFrame.from_ndarray(img, format="rgb24")
    for packet in stream.encode(frame):
        container.mux(packet)

# Flush stream
for packet in stream.encode():
    container.mux(packet)

# Close the file
container.close()

sample_file_path = "rainbow_noise.mp4"
container = av.open(sample_file_path, mode="r")
in_video_stream = container.streams.video[0]

make_sample_video(sample_file_path,in_video_stream)

time.sleep(3)
container.close()

print("Check duration:")
print(in_video_stream)
print(container.duration)
print("Finished!")

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants