PyAV-Org / PyAV

Pythonic bindings for FFmpeg's libraries.
https://pyav.basswood-io.com/
BSD 3-Clause "New" or "Revised" License
2.45k stars 359 forks source link

Setter for VideoCodecContext.pix_fmt is not validated, application segfaults #815

Closed moi90 closed 2 years ago

moi90 commented 2 years ago

Overview

VideoCodecContext.pix_fmt can be set to an illegal value. Later, the application segfaults.

Expected behavior

If VideoCodecContext.pix_fmt is set to an illegal value, a ValueError should be raised.

Actual behavior

If VideoCodecContext.pix_fmt is set to an illegal value, it is silently unset.

Traceback:

Specified pixel format -1 is invalid or not supported
Speicherzugriffsfehler (Speicherabzug geschrieben)

Reproduction

import numpy as np

import av

duration = 4
fps = 24
total_frames = duration * fps

with av.open('test.mp4', mode='w') as container:

    stream = container.add_stream('mpeg4', rate=fps)
    stream.width = 480
    stream.height = 320
    # stream.pix_fmt = 'yuv420p' # This works
    stream.pix_fmt = 'yuv240p' # Illegal value

    print("stream", stream)

    for frame_i in range(total_frames):

        img = np.empty((320, 480, 3))
        img[:, :, 0] = 0.5 + 0.5 * np.sin(2 * np.pi * (0 / 3 + frame_i / total_frames))
        img[:, :, 1] = 0.5 + 0.5 * np.sin(2 * np.pi * (1 / 3 + frame_i / total_frames))
        img[:, :, 2] = 0.5 + 0.5 * np.sin(2 * np.pi * (2 / 3 + frame_i / total_frames))

        img = np.round(255 * img).astype(np.uint8)
        img = np.clip(img, 0, 255)

        print("img", img.shape, img.dtype)

        frame = av.VideoFrame.from_ndarray(img, format='rgb24')

        print("frame", frame)

        # Here, the Segfault happens:
        for packet in stream.encode(frame):
            container.mux(packet)
        break

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

Versions

Research

I have done the following:

moi90 commented 2 years ago

This can be fixed by implementing pix_fmt with format, like hinted in the source: https://github.com/PyAV-Org/PyAV/blob/9ac05d9ac902d71ecb2fe80f04dcae454008378c/av/video/codeccontext.pyx#L96

i3587616 commented 2 years ago

pix_fmt = 'yuv420p' like this ,you should see source code.it will help you

moi90 commented 2 years ago

@i3587616 Your comment is not helpful. It is absolutely unacceptable that a user of the library is able to set an illegal value and that the application crashes with a SEGFAULT at a later point.

jlaine commented 2 years ago

This is a good catch but I seem to have missed the corresponding PR?

moi90 commented 2 years ago

I'm not able to provide a PR in my limited time, but the fix mentioned above should be easy. Everyone is welcome to take credit.

jlaine commented 2 years ago

Ah right, but maintainers magically have unlimited time.

moi90 commented 2 years ago

Of cause! ;)

(I'd be happy to contribute, but I have zero experience with this library. Regular contributors, on the other hand, don't have the overhead of setting up a building and testing environment and digging into an unknown source. Eventually, someone will find the time. It might even be me, but not now.)

jlaine commented 2 years ago

An alternative would be to remove the pix_fmt setter, AFAICT it doesn't add anything over format?

moi90 commented 2 years ago

I wouldn't remove fix_fmt because with only format, a user would need to specify all three properties (pix_fmt, width, height) when setting.