c++ - FFMPEG 从内存中读取音频不起作用
问题描述
当我尝试实例化这个结构时,我的程序崩溃了:
struct MemoryAVFormat {
MemoryAVFormat(const MemoryAVFormat &) = delete;
AVFormatContext *ctx;
AVIOContext *ioCtx;
MemoryAVFormat(char *audio, size_t audio_length) :
ctx(avformat_alloc_context()),
ioCtx(create_audio_buffer_io_context(audio, audio_length)) {
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);
}
AVIOContext *create_audio_buffer_io_context(char *audio, size_t audio_length) const {
return avio_alloc_context(reinterpret_cast<unsigned char *>(audio),
audio_length,
0,
audio,
[](void *, uint8_t *, int buf_size) { return buf_size; },
NULL,
NULL);
}
~MemoryAVFormat() {
av_free(ioCtx);
avformat_close_input(&ctx);
}
}
我已经阅读并尝试了每一个关于这样做的教程,但它们都不起作用
有没有人以前做过这个工作?
在线崩溃:int err = avformat_open_input(&ctx, "nullptr", NULL, NULL);
解决方案
avio_alloc_context()
文档指定buffer
参数应该由析构函数分配av_malloc()
,而且它将由AVIOContext
析构函数释放,并且可以随时重新分配:
* @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().
在您的代码示例中,您省略了audio
缓冲区分配的详细信息,但我认为它不符合这些要求,因此当 FFmpeg 尝试释放或重新分配audio
缓冲区时会发生崩溃。
我猜想将整个音频文件内容作为外部分配的缓冲区传递是行不通的AVIOContext
——这个 API 实际上是要与一个临时缓冲区一起使用,以便从其他地方(文件、Web 或其他内存缓冲区)流式传输数据。
我没有完整的示例来查看它是否会按预期工作,但代码可能看起来像这样(您可能需要调整read()
函数并考虑实现搜索过程):
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);
}
}
如果您事先知道您的流是哪种音频格式(例如完全跳过创建),则实现AVIOContext
接口和使用的替代方法avformat_open_input()
是将音频缓冲区作为自定义的有效负载直接传递给解码器。我这样做是为了解码图像像素图,但不知道它是否可以(轻松)应用于音频。AVPacket
AVFormatContext
推荐阅读
- html - 容器 div 中 div 的水平对齐不稳定
- python - 如何根据另一个二维数组中给出的索引对二维数组进行切片
- c# - 无法创建“员工”类型的常量值。此上下文仅支持原始类型或枚举类型
- angular - 在Angular中为具有未知参数的孩子传递函数
- c# - 如何将 MSIL 或 CIL 编写并编译成可执行文件?
- sql - 根据同一列的值更新列中的值
- javascript - 如何隐藏nodejs的控制台窗口(用nexe编译)
- c# - C# 单选按钮:如何将两个值分配给单个单选按钮。一个文本值用于前端用户,另一个 ID 值用于程序
- java - 我无法在我的数据库中插入值(java 不显示错误)
- javascript - 如何打开没有 URL 的新浏览器选项卡(通过网页上的 onClick),就像单击“+”按钮添加新选项卡一样