<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<p><font size="2">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. </font><font size="2"><font
size="2">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. </font>The
library is written in Java and is interfaced with in Lua, but it
uses a native wrapper to FFmpeg in C
(<a class="moz-txt-link-freetext" href="https://github.com/Manevolent/ffmpeg4j">https://github.com/Manevolent/ffmpeg4j</a>). For example, here's an
example of how a user could pipe data from a file and then wait
for the result:</font></p>
<pre><font size="2">local file = io.open("audio.ogg", "rb")
local bus = peripheral.wrap("back") -- gets the decoder object
repeat
local data = <a class="moz-txt-link-freetext" href="file:read(16384)">file:read(16384)</a>
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
<a class="moz-txt-link-freetext" href="file:close()">file:close()</a>
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
</font></pre>
<p><font size="2">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.</font></p>
<p><font size="2">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.</font></p>
<p><font size="2">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.</font></p>
<p><font size="2">If anyone wants to look at my current code, it's
available at
<a class="moz-txt-link-freetext" href="https://github.com/MCJack123/ASICraft/tree/ffmpeg-decode/common/src/main/java/cc/craftospc/ASICraft/algorithms/OGGDecodeAlgorithm.java">https://github.com/MCJack123/ASICraft/tree/ffmpeg-decode/common/src/main/java/cc/craftospc/ASICraft/algorithms/OGGDecodeAlgorithm.java</a>.
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.</font></p>
<p><font size="2">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.</font></p>
</body>
</html>