Skip to content

Encoding multiple ffv1 streams segfaults when container.mux not called together within the same timestamp #2232

@oakaigh

Description

@oakaigh

PyAV version: 16.1.0

Steps to reproduce:

  • Create script:

    Script content
    import io
    import fractions
    import logging
    logging.basicConfig(level=logging.DEBUG)
    
    import av
    import av.logging
    av.logging.set_level(av.logging.DEBUG)
    import numpy as np
    
    
    def make_rgb_frame(width: int, height: int, t: int) -> av.VideoFrame:
        y, x = np.mgrid[0:height, 0:width]
    
        r = ((x + 3 * t) % 256).astype(np.uint8)
        g = ((y + 2 * t) % 256).astype(np.uint8)
        b = (((x + y) // 2 + 5 * t) % 256).astype(np.uint8)
    
        img = np.stack([r, g, b], axis=-1)
        return av.VideoFrame.from_ndarray(img, format="rgb24")
    
    
    def run_mux1by1_segfault():
        out_path = io.BytesIO()
    
        width = 224
        height = 240
        fps = 30
        num_frames = 90
    
        container = av.open(out_path, format="mp4", mode="w")
    
        container.add_stream("ffv1", rate=fps)
        container.add_stream("ffv1", rate=fps)
    
        for i in range(num_frames):
            for vstream in container.streams.video:
                if i == 0:
                    vstream.format = av.VideoFormat("bgr0", width=width, height=height)
                frame = make_rgb_frame(width, height, i).reformat(format=vstream.format)
                frame.pts = i
                frame.time_base = fractions.Fraction(1, fps)
                print("encoding frame", frame)
                container.mux(vstream.encode(frame))
    
        for vstream in container.streams.video:
            container.mux(vstream.encode())
    
        container.close()
        print(f"Wrote {out_path}")
    
    
    def run_muxdelayed_nosegfault():
        out_path = io.BytesIO()
    
        width = 224
        height = 240
        fps = 30
        num_frames = 90
    
        container = av.open(out_path, format="mp4", mode="w")
    
        container.add_stream("ffv1", rate=fps)
        container.add_stream("ffv1", rate=fps)
    
        for i in range(num_frames):
            packets = []
            for vstream in container.streams.video:
                if i == 0:
                    vstream.format = av.VideoFormat("bgr0", width=width, height=height)
                frame = make_rgb_frame(width, height, i).reformat(format=vstream.format)
                frame.pts = i
                frame.time_base = fractions.Fraction(1, fps)
                print("encoding frame", frame)
                packets.extend(vstream.encode(frame))
            container.mux(packets)
    
        for vstream in container.streams.video:
            container.mux(vstream.encode())
    
        container.close()
        print(f"Wrote {out_path}")
    
    
    # NOTE segfault
    # run_mux1by1_segfault()
    # NOTE does not segfault
    # run_muxdelayed_nosegfault()
    
    # python3 -X faulthandler ...
    # PYTHONMALLOC=malloc valgrind --leak-check=no --track-origins=yes python3 ...
    
  • Run run_muxdelayed_nosegfault(), script finishes without segfault.

  • Run run_mux1by1_segfault(), script segfaults:

    Logs produced by `PYTHONMALLOC=malloc valgrind --leak-check=no --track-origins=yes python3 ...`
    encoding frame <av.VideoFrame, pts=0 bgr0 224x240 at 0x4e3e510>
    encoding frame <av.VideoFrame, pts=0 bgr0 224x240 at 0x5802990>
    ==1592577== Thread 26:
    ==1592577== Conditional jump or move depends on uninitialised value(s)
    ==1592577==    at 0x64FD580: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x6500921: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x699AF4A: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x79FB0E5: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x49E41F4: start_thread (pthread_create.c:442)
    ==1592577==    by 0x4A63B3F: clone (clone.S:100)
    ==1592577==  Uninitialised value was created by a heap allocation
    ==1592577==    at 0x4846990: memalign (vg_replace_malloc.c:1516)
    ==1592577==    by 0x4846AED: posix_memalign (vg_replace_malloc.c:1688)
    ==1592577==    by 0x79DD47E: av_malloc (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79BB015: av_buffer_alloc (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79C9CC4: av_frame_get_buffer (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79CA809: av_frame_ref (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x64BB422: avcodec_send_frame (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0xC9DEA13: __pyx_gb_2av_5codec_7context_12CodecContext_14generator (context.c:7537)
    ==1592577==    by 0xC9C2538: __Pyx_Coroutine_SendEx.isra.0 (dictionary.c:9973)
    ==1592577==    by 0xC9C4DD3: __Pyx_Generator_Next (dictionary.c:10239)
    ==1592577==    by 0xC9EB7C5: __pyx_f_2av_5codec_7context_12CodecContext_encode (context.c:8890)
    ==1592577==    by 0xD9C5004: __pyx_f_2av_5video_6stream_11VideoStream_encode (stream.c:3898)
    ==1592577== 
    ==1592577== Conditional jump or move depends on uninitialised value(s)
    ==1592577==    at 0x64FD5FC: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x6500921: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x699AF4A: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0x79FB0E5: ??? (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x49E41F4: start_thread (pthread_create.c:442)
    ==1592577==    by 0x4A63B3F: clone (clone.S:100)
    ==1592577==  Uninitialised value was created by a heap allocation
    ==1592577==    at 0x4846990: memalign (vg_replace_malloc.c:1516)
    ==1592577==    by 0x4846AED: posix_memalign (vg_replace_malloc.c:1688)
    ==1592577==    by 0x79DD47E: av_malloc (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79BB015: av_buffer_alloc (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79C9CC4: av_frame_get_buffer (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x79CA809: av_frame_ref (in .conda/lib/python3.11/site-packages/av.libs/libavutil-9a1620aa.so.60.8.100)
    ==1592577==    by 0x64BB422: avcodec_send_frame (in .conda/lib/python3.11/site-packages/av.libs/libavcodec-e57b519c.so.62.11.100)
    ==1592577==    by 0xC9DEA13: __pyx_gb_2av_5codec_7context_12CodecContext_14generator (context.c:7537)
    ==1592577==    by 0xC9C2538: __Pyx_Coroutine_SendEx.isra.0 (dictionary.c:9973)
    ==1592577==    by 0xC9C4DD3: __Pyx_Generator_Next (dictionary.c:10239)
    ==1592577==    by 0xC9EB7C5: __pyx_f_2av_5codec_7context_12CodecContext_encode (context.c:8890)
    ==1592577==    by 0xD9C5004: __pyx_f_2av_5video_6stream_11VideoStream_encode (stream.c:3898)
    ==1592577== ...
    

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions