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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions