avio_alloc_context()
documentation specifies that buffer
parameter should be allocated by av_malloc()
and moreover it will be released by AVIOContext
destructor and can be reallocated at any time:
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
* It may be freed and replaced with a new buffer by libavformat.
* AVIOContext.buffer holds the buffer currently in use,
* which must be later freed with av_free().
In your code sample you omit details of allocation of audio
buffer, but I suppose that it doesn't meet these requirements, so that crash happens when FFmpeg tries to release or reallocate audio
buffer.
I guess passing an entire audio file content as an externally allocated buffer wouldn't work with AVIOContext
- this API is really meant to be used with a temporary buffer for streaming data from somewhere else (file, web or another memory buffer).
I don't have a complete sample to see if it will work as expected, but code might look like this (you will probably need to tune read()
function and consider implementing seeking procedure as well):
struct MemoryAVFormat {
MemoryAVFormat(const MemoryAVFormat &) = delete;
AVFormatContext *ctx;
AVIOContext *ioCtx;
char *audio;
size_t audio_length;
size_t audio_offset;
MemoryAVFormat(char *theAudio, size_t theAudioLength)
: ctx(avformat_alloc_context()),
ioCtx(nullptr),
audio(theAudio),
audio_length(theAudioLength),
audio_offset(0) {
ioCtx = create_audio_buffer_io_context();
if (ctx == nullptr)
throw audio_processing_exception("Failed to allocate context");
if (ioCtx == nullptr)
throw audio_processing_exception("Failed to allocate IO context for audio buffer");
ctx->pb = ioCtx;
ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
int err = avformat_open_input(&ctx, "nullptr", NULL, NULL);
if (err != 0)
throwAvError("Error configuring context from audio buffer", err);
}
int read (uint8_t* theBuf, int theBufSize) {
int aNbRead = std::min (int(audio_length - audio_offset), theBufSize);
if(aNbRead == 0) { return AVERROR_EOF; }
memcpy(theBuf, audio + audio_offset, aNbRead);
audio_offset += aNbRead;
return aNbRead;
}
int64_t seek(int64_t offset, int whence) {
if (whence == AVSEEK_SIZE) { return audio_length; }
audio_offset = offset;
if(audio == NULL || audio_length == 0) { return -1; }
if (whence == SEEK_SET) { audio_offset = offset; }
else if(whence == SEEK_CUR) { audio_offset += offset; }
else if(whence == SEEK_END) { audio_offset = audio_length + offset; }
//if(audio_offset < 0) { audio_offset = 0; } else
//if(audio_offset > audio_length) { audio_offset = audio_length; }
return offset;
}
AVIOContext *create_audio_buffer_io_context() {
const int aBufferSize = 4096;
unsigned char* aBufferIO = (unsigned char* )av_malloc(aBufferSize + AV_INPUT_BUFFER_PADDING_SIZE);
return avio_alloc_context(aBufferIO,
aBufferSize,
0,
this,
[](void* opaque, uint8_t* buf, int bufSize)
{ return ((MemoryAVFormat* )opaque)->read(buf, bufSize); },
NULL,
[](void* opaque, int64_t offset, int whence)
{ return ((MemoryAVFormat* )opaque)->seek(offset, whence); });
}
~MemoryAVFormat() {
av_free(ioCtx);
avformat_close_input(&ctx);
}
}
An alternative to implementing AVIOContext
interface and using avformat_open_input()
could be passing an audio buffer as a payload of a custom AVPacket
directly to decoder, if you know in advance in which audio format your stream is (e.g. by skipping creation of AVFormatContext
at all). I did this for decoding image pixmaps, but don't know if it could be (easily) applied to audio.