FFmpeg.AutoGen icon indicating copy to clipboard operation
FFmpeg.AutoGen copied to clipboard

Custom `AVIOContext` fails to open input file

Open adamhewitt627 opened this issue 2 years ago • 9 comments

I'm submitting a bug report.

  • Do you want to request a feature or report a bug? This could be a bug with ffmpeg, but seems to be a failure with the managed/native callbacks. Our code worked under ffmpeg 4.3, and broke in 5.0. I checked again and it's still broken in ffmpeg6. I will work on a repro example, but wanted to get this filed.

  • What is the current behavior?

  1. Custom AVIOContext fails to open for reading a file.
  2. The Read callback executes once, but then an AccessViolationException is thrown.
  • *If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem:

  • What is the expected behavior? Reading a file with a custom IO context should work. (Interestingly, it does work for writing a file)

  • Please tell us about your environment:

  • version: ffmpeg6, Windows 11, .NET6.0 WPF application.

adamhewitt627 avatar Mar 22 '23 18:03 adamhewitt627

Here is a sample project, just a small console app that tries to open the file. FfmegCustomIO.zip

  1. Fill in the ffmpeg.RootPath with your environment.
  2. Fill in the file path to some mp4, avi, etc.
  3. Run the app - should blow up on avformat_open_input. Breakpoints will show that the Read callback is executed once.

adamhewitt627 avatar Mar 22 '23 22:03 adamhewitt627

Interesting, removing this line appears to resolve it but also calls Seek (which I wouldn't expect)

Reference->seekable = stream.CanSeek ? 1 : 0;

adamhewitt627 avatar Mar 22 '23 22:03 adamhewitt627

I haven't tried your code, but it looks very suspicious that write_flag: stream.CanWrite ? 1 : 0, and write_packet: null,

I think you should set the write flag to 0 if the write delegate is null.

zgabi avatar Mar 22 '23 23:03 zgabi

True, I didn't get that line entirely pulled out of the minimum repro. But that will also always be 0 because the file is being opened readonly.

adamhewitt627 avatar Mar 22 '23 23:03 adamhewitt627

I think it could be related issue #217

Ruslan-B avatar Mar 23 '23 07:03 Ruslan-B

I'll try to check integrity of AVIOContext structure. As indeed when commenting out Reference->seekable = stream.CanSeek ? 1 : 0; it seems to be working fine.

Ruslan-B avatar Mar 23 '23 19:03 Ruslan-B

The crash in avformat_open_input() seems to be happening somewhere around here: https://github.com/FFmpeg/FFmpeg/blob/5bad4856035ca5ed571e9d7d9b1d503a5c9ef0a5/libavformat/demux.c#L258-L272

The C# struct layout for AVIOContext seem to mismatch the one in the Win32 build. In particular, the field pb->seekable (at offset 0x90 in the C# struct) is swapped with pb->xxx_whitelist.

I didn't want to build ffmpeg myself, so I confirmed this by setting a dummy value like pb->seekable = 0x1234567, which ends up being passed to av_strdup() via rcx:

image

Also I don't think that the C# layout is wrong as it matches the clang output, so the maybe the problem is specific to Windows builds?

Edit: note that it's not necessary to set seekable manually because avio_alloc_context() already does so when you pass a non-null seek callback (ref). There seems to be no other way to check for the AVIO_SEEKABLE_NORMAL flag, so I'm just checking for pb->seek.Pointer != IntPtr.Zero; instead.

dubiousconst282 avatar Apr 03 '23 23:04 dubiousconst282

I updated state of research and passible actions in #217

Ruslan-B avatar Nov 15 '23 08:11 Ruslan-B

btw i encountered this with ffmpeg 5 feeding it a buffer of FLAC and consequently getting free() invalid pointer every time. i have been hitting my head against GDB for the past day trying to do something like this:

/// buffer comes from network, 
uint8_t buffer[4096] = // assume mock read 
AVFormatContext* pFormat = avformat_alloc_context()
AVIOContext* pAvioCtx = avio_alloc_context(buffer, 4096, 0, nullptr, nullptr, nullptr, nullptr);
pFormat->pb = pAvioCtx;
// SIGABRT free() invalid pointer occurs every time inside this ->
if (avformat_open_input(&pFormat, nullptr, nullptr, nullptr)) {
     log("couldn't open input");
}

AnthonyParkssonos avatar Nov 30 '23 21:11 AnthonyParkssonos