c++ - FFMPEG 从内存中读取音频不起作用

标签 c++ c ffmpeg libav libavformat

当我尝试实例化这个结构时,我的程序崩溃了:

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() 函数并考虑实现 seeking procedure):
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 接口(interface)和使用 avformat_open_input() 的替代方法可以是将音频缓冲区作为自定义 AVPacket 的有效负载直接传递给解码器,如果您事先知道您的流是哪种音频格式(例如,完全跳过 AVFormatContext 的创建)。我这样做是为了解码图像像素图,但不知道它是否可以(轻松)应用于音频。

关于c++ - FFMPEG 从内存中读取音频不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66003356/

相关文章:

c++ - 不推荐包含来自 include/Qt 的头文件

c++ - cpp中数组的负索引

c++ - 对常规文件进行 Epoll

php - 如何确定 PHP/FFMPEG/PhoneGap 中的视频旋转/方向?

c++ - 尾随返回类型中 SFINAE 的 GCC 错误

将 double 的 2 列 .txt 转换为 2 个数组并读取它们

c++ - lib\app_error.c|49|error : expected '=' , ',' , ';' , 'asm' or '__attribute__' before 'void' Nortic Semi 例子

c - ftp 客户端从服务器读取不完整的消息

node.js - FFMPEG 仅执行一次 HTTP 范围请求 (HTML5 MediaStream/WebM)

c++ - 如何从多个线程使用 ffmpeg 的 sws_scale?