[Libav-user] How to demux an audio file received in chunks?

Anna Mace anna.louise.mace at gmail.com
Tue Jan 30 16:35:17 EET 2024


I'm new to audio things so I may make some incorrect or silly assumptions,
but I'm hoping someone can help!

I'm making a C++ project that receives the audio data of a file that is
streamed in chunks. It will demux it, then decode and further process it.
The audio can be any audio file format, so I expect the beginning of the
data to be the header bytes and then encoded audio data to follow. But I do
not have all the audio data at the start, and I want to start processing it
while I am still receiving more!

[Code below] I'm making a custom AVIOContext and using it to initialise an
AVFormatContext. I open the context using avformat_open_input() and make
sure the first chunk of data is available to my custom read function in the
AVIOContext. Then I repeatedly call av_read_frame() to extract the encoded
audio packets.

However, because the audio is coming as a stream, I don't have all the
audio available at once. So when I call av_read_frame() ffmpeg will read
through my data with my read function until it gets to the end of available
data, and then it treats it like an end of file (even though I return
AVERROR(EAGAIN)). Then I receive more audio data to put at the end of my
previous data, and I call av_read_frame() again to process the new data.
But this time when I call it, it immediately fails with EOF because the
AVFormatContext thinks it reached the end of the file the last time (even
though I returned EAGAIN). The only workaround I have is to stop calling
av_read_frame() when ffmpeg reads the last bit of my buffer, so it
hopefully doesn't try to read more, until I receive more. But this means
there will be a few packets left in the demuxer that I don't process until
I get the next chunk, and I really want to process all available data as
soon as possible (it should arrive in framed-aligned chunks).

Is this actually possible with an AVFormatContext? Should my read function
return something special? Do I need to set a special value in the
AVFormatContext to make it work when data isn't immediately available? Or
can I reset the AVFormatContext back to a good non-EOF state somehow? Or
should I use my workaround and try to never let it read to the end of the
available data until the stream has finished?

All help is appreciated. Thank you

Here is my code (error handling removed):

-------------------------------------------
int read_packet(void* opaque, uint8_t* buffer, int bufferSize) {
StreamedDataAccessor* dataAccessor = static_cast<StreamedDataAccessor*>
(opaque);
size_t bytesToRead = std::min((size_t)bufferSize, dataAccessor->
remainingBytes());
if (!bytesToRead) {
// Need to wait until we get the next chunk of data
return AVERROR(EAGAIN);
}

size_t bytesRead = dataAccessor->copyData(buffer, bytesToRead);
return bytesRead;
}

bool processHeaderPacket(const Data headerPacket) {
dataAccessor = StreamedDataAccessor{
.header = headerPacket,
// Set up other internal stuff
};

size_t bufferSize = 4096;
contextBuffer = allocateEmptyBuffer(bufferSize);
IOContext = avio_alloc_context(contextBuffer.ptr,
bufferSize,
0,
&(dataAccessor),
read_packet,
0,
0);
// no seek_function provided

AVFormatContext* inFormatContext = avformat_alloc_context();
inFormatContext->pb = IOContext;
inFormatContext->flags = AVFMT_FLAG_CUSTOM_IO;
int ret = avformat_open_input(&inFormatContext, "", nullptr, nullptr);

// Error handling
if (ret == ...) {}

// Probably only necessary for mpgs
if (avformat_find_stream_info(inFormatContext, nullptr) < 0) {
// ERROR could not find stream info
// ...
}
// Now we access stream info, codec info, etc
// ...
}


inputPacket = av_packet_alloc();

void processData(AudioData data) {
dataAccessor.appendData(data);
int ret = 0;
while ((ret = av_read_frame(inFormatContext, inputPacket)) >= 0) {
// process inputPacket further
}

if (ret == AVERROR(EAGAIN)) {
// Hits here the first time, once ffmpeg has read all available data
} else if (ret == AVERROR_EOF) {
// Hits here the second time, after we receive more data. av_read_frame
immediately returns
} else {
// other error handling here
}
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20240130/97702d87/attachment.htm>


More information about the Libav-user mailing list