[Libav-user] How to stream incoming chunked data to libavformat reliably?

Jack Bruienne jackbruienne at gmail.com
Sat Jun 17 06:25:14 EEST 2023


I'm developing a library in which you can push in a stream of bytes to a 
decoder for a specific codec (right now I'm testing with OGG), and it 
will send back partial audio packets as it becomes available, plus a 
final stream of decoded audio once the user indicates the stream is 
finished. This data could come in at any time with any size, and I keep 
a buffer of the unprocessed data until it's consumed. I want to be able 
to support partial frames/streaming as well, so I need to have the data 
processing in the background before the finish method is called. The 
library is written in Java and is interfaced with in Lua, but it uses a 
native wrapper to FFmpeg in C (https://github.com/Manevolent/ffmpeg4j). 
For example, here's an example of how a user could pipe data from a file 
and then wait for the result:

local file = io.open("audio.ogg", "rb") local bus = 
peripheral.wrap("back") -- gets the decoder object repeat local data = 
file:read(16384) if data == nil then break end bus.input(1, data) -- 
send 16384 bytes to the decoder in slot 1 and process it in the 
background until #data < 16384 file:close() bus.finish(1) -- tell the 
decoder that the data is finished local event, side, slot, ok, data = 
coroutine.yield("asicraft.result") -- wait for processing to finish & 
get the result -- do things with data

I'm currently using the Java library's wrapper around an `InputStream` 
to send the data. I have my own `InputStream` which holds the buffered 
input, and the library will call its `read` method through an 
implementation of libavformat `AVIOContext`'s `read_packet` function. If 
the buffer runs out, the read method returns -1, and I used some 
mixins/reflection on the Java library to make it return 
`AVERROR(EAGAIN)` to libavformat. This worked for the first packet sent 
to the stream, but despite always sending `EAGAIN` I was still getting 
EOFs simply from returning less data than the demuxer expected. To solve 
this, I forced the `eof_reached` flag to be reset to 0 each time data is 
added to the stream, but because a partial amount of data was read 
during the last decoding period, it ends up skipping that frame.

I've considered putting the audio processing on a separate thread, and 
having the `read_packet` function wait for more input before returning 
(which luckily Java makes pretty easy). But this would mean that I would 
need a new thread for every single input process, which theoretically 
could be opened and then left running forever, and I want to avoid 
ending up with thread exhaustion (this needs to be able to run on public 
servers where users can trigger the decoder at any time). I also tried 
throwing an exception to attempt to unwind the reader back across 
`av_frame_read`, but this failed spectacularly as I expected.

What's the most optimal way to decode/demux a data stream in chunks that 
may arrive at any time, without losing any frames in the process? And, 
more specifically for my current method, is there any way to figure out 
where in the data stream the last frame was read by the demuxer? If I 
can know that, I can just rewind the data stream to that point and 
continue the decoder.

If anyone wants to look at my current code, it's available at 
https://github.com/MCJack123/ASICraft/tree/ffmpeg-decode/common/src/main/java/cc/craftospc/ASICraft/algorithms/OGGDecodeAlgorithm.java. 
I don't expect anyone to want to poke through it, but it's there if 
someone does. There's also some related code in 
`util/FFmpegInputFixed.java` and `mixin/FFmpegSourceStreamInject.java` 
for patching a few things - mainly, switching it to use `EAGAIN` instead 
of EOF.

I would appreciate any help in getting this working, as it drives a 
major part of my project, and I don't want to have to drop it or limit 
the features because I can't get libav to cooperate with my input scheme.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20230616/18f80625/attachment.htm>


More information about the Libav-user mailing list