简体   繁体   English

FFMPEG 从 memory 读取音频不起作用

[英]FFMPEG Reading audio from memory doesn't work

When I try to instantiate this struct, my program crashes:当我尝试实例化这个结构时,我的程序崩溃了:

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);
    }
}

I've read and tried every single tutorial on doing this and none of them work我已经阅读并尝试了每一个关于这样做的教程,但它们都不起作用

Has anyone got this working before?有没有人以前做过这个工作?

crashes on the line: int err = avformat_open_input(&ctx, "nullptr", NULL, NULL);崩溃上线: int err = avformat_open_input(&ctx, "nullptr", NULL, NULL);

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: 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().

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.在您的代码示例中,您省略了audio缓冲区分配的详细信息,但我认为它不符合这些要求,因此当 FFmpeg 尝试释放或重新分配audio缓冲区时会发生崩溃。

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).我猜想将整个音频文件内容作为外部分配的缓冲区传递不适用于AVIOContext - 这个 API 真的是要与临时缓冲区一起用于从其他地方流式传输数据(文件,web 或另一个 ZCD69B4957F06CD8291Z73 缓冲区)。

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):我没有完整的示例来查看它是否会按预期工作,但代码可能看起来像这样(您可能需要调整read() function 并考虑实施搜索过程):

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 (eg by skipping creation of AVFormatContext at all).如果您事先知道 stream 是哪种音频格式(例如完全跳过创建AVFormatContext ),则实现AVIOContext接口和使用avformat_open_input()的替代方法可以将音频缓冲区作为自定义AVPacket的有效负载直接传递给解码器. I did this for decoding image pixmaps, but don't know if it could be (easily) applied to audio.我这样做是为了解码图像像素图,但不知道它是否可以(轻松)应用于音频。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM