PyAV-Org / PyAV

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

Error calling subtitle_stream.decode() #1454

Closed WyattBlue closed 1 month ago

WyattBlue commented 2 months ago

Minimum repro:

# Any input that has a subtitle stream, codec does not matter
src = "/Users/wyattblue/projects/auto-editor/resources/subtitle.mp4"

c = av.open(src)
s = c.streams.subtitles[0]

for subset in s.decode():
    pass
zsh: segmentation fault  python3 test.py

Calling decode like this sidesteps the segfault and works as expected. This confirms this is our fault and not a downstream lib.

for subset in c.decode(subtitles=0):
    for sub in subset:
        print(sub)

Versions

OS: All (confirmed macos-arm64 macos-x86_64) Python: All (confirmed 3.12, 3.11) PyAVs: from at least 10.0.0, til 12.3.0rc1

moonsikpark commented 1 month ago

test.py is the min repro, and test2.py is the sidestepping version

(lldb) run test.py
Process 21360 launched: '/opt/homebrew/Cellar/python@3.12/3.12.4/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python' (arm64)
Process 21360 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000102804c2c libavcodec.60.31.102.dylib`avcodec_decode_subtitle2(avctx=0x000000014862f800, sub=0x0000000104282710, got_sub_ptr=0x000000016fdfdf54, avpkt=0x0000000000000000) at decode.c:972:9
   969  int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
   970                               int *got_sub_ptr, const AVPacket *avpkt)
   971  {
-> 972      int ret = 0;
   973 
   974      if (!avpkt->data && avpkt->size) {
   975          av_log(avctx, AV_LOG_ERROR, "invalid packet: NULL data, size != 0\n");
Target 0: (Python) stopped.
(lldb) frame variable avpkt
(const AVPacket *) avpkt = NULL
(lldb) kill
Process 21360 exited with status = 9 (0x00000009) killed
(lldb) run test2.py
Process 21617 launched: '/opt/homebrew/Cellar/python@3.12/3.12.4/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python' (arm64)
Process 21617 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000102804c2c libavcodec.60.31.102.dylib`avcodec_decode_subtitle2(avctx=0x000000015b0a8450, sub=0x000000010423ea70, got_sub_ptr=0x000000016fdfddb4, avpkt=0x0000000159e28050) at decode.c:972:9
   969  int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
   970                               int *got_sub_ptr, const AVPacket *avpkt)
   971  {
-> 972      int ret = 0;
   973 
   974      if (!avpkt->data && avpkt->size) {
   975          av_log(avctx, AV_LOG_ERROR, "invalid packet: NULL data, size != 0\n");
Target 0: (Python) stopped.
(lldb) frame variable avpkt
(const AVPacket *) avpkt = 0x0000000159e28050

seems to be related to this part

https://github.com/PyAV-Org/PyAV/blob/4bd4984daaf1c81731185e3bcada1e017ca1720c/av/subtitles/codeccontext.pyx#L14-L16

It seems to perfectly work as expected, because ffmpeg will sure not expect a null pointer of AVPacket incoming in avcodec_decode_subtitle2, unlike its media counterpart avcodec_send_packet which expects one. we should definitely change avcodec_decode_subtitle2 wrapper to raise an exception when avpkt is None.

From the looks of it, I think we are unable to provide the member function decode() of av.SubtitleStream because we can't decode the subtitles if we don't have access to the AVPackets, thus having dependency to av.InputContainer.

edit: Working on it now.

WyattBlue commented 1 month ago

My patch stops the segfault but the functionality still does not work.

moonsikpark commented 1 month ago

Flushing is done by calling this function with packets with avpkt->data set to NULL and avpkt->size set to 0 until it stops returning subtitles.

It seems we just need to send a new Packet() when user wants to flush so they supplied no packet.