4

好吧,基本上我正在开发一个简单的视频播放器,稍后我可能会问另一个关于滞后视频\同步到音频的问题,但现在我遇到了音频问题。我设法做的是浏览视频的所有音频帧并将它们添加到矢量缓冲区,然后使用 OpenAL 从该缓冲区播放音频。

这是低效且占用内存的,因此我需要能够使用我猜想的所谓旋转缓冲区来流式传输它。我遇到了一些问题,其中一个是关于使用 OpenAL 进行流式传输的信息并不多,更不用说使用 FFMPEG 解码音频并将其通过管道传输到 OpenAL 的正确方法了。我什至不太习惯使用向量作为缓冲区,因为老实说,我不知道向量在 C++ 中是如何工作的,但我知道如何设法从我的脑海中抽出一些东西来使它工作。

目前我有一个看起来像这样的视频类:

class Video
{
    public:
        Video(string MOV);
        ~Video();
        bool HasError();
        string GetError();
        void UpdateVideo();
        void RenderToQuad(float Width, float Height);
        void CleanTexture();
    private:
        string FileName;
        bool Error;
        int videoStream, audioStream, FrameFinished, ErrorLevel;
        AVPacket packet;
        AVFormatContext *pFormatCtx;
        AVCodecContext *pCodecCtx, *aCodecCtx;
        AVCodec *pCodec, *aCodec;
        AVFrame *pFrame, *pFrameRGB, *aFrame;

        GLuint VideoTexture;
        struct SwsContext* swsContext;

        ALint state;
        ALuint bufferID, sourceID;
        ALenum format;
        ALsizei freq;

        vector <uint8_t> bufferData;
};

底部的私有变量是相关的。目前我正在将类构造函数中的音频解码为 AVFrame 并将数据添加到 bufferData,如下所示:

    av_init_packet(&packet);

    alGenBuffers(1, &bufferID);
    alGenSources(1, &sourceID);

    alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);

    int GotFrame = 0;

    freq = aCodecCtx->sample_rate;
    if (aCodecCtx->channels == 1)
        format = AL_FORMAT_MONO16;
    else
        format = AL_FORMAT_STEREO16;

    while (av_read_frame(pFormatCtx, &packet) >= 0)
    {
        if (packet.stream_index == audioStream)
        {
            avcodec_decode_audio4(aCodecCtx, aFrame, &GotFrame, &packet);
            bufferData.insert(bufferData.end(), aFrame->data[0], aFrame->data[0] + aFrame->linesize[0]);
            av_free_packet(&packet);
        }
    }
    av_seek_frame(pFormatCtx, audioStream, 0, AVSEEK_FLAG_BACKWARD);

    alBufferData(bufferID, format, &bufferData[0], static_cast<ALsizei>(bufferData.size()), freq);

    alSourcei(sourceID, AL_BUFFER, bufferID);

在我的 UpdateVideo() 中,我通过视频流将视频解码为 OpenGL 纹理,因此我在那里解码音频并将其流式传输是有意义的:

void Video::UpdateVideo()
{
    alGetSourcei(sourceID, AL_SOURCE_STATE, &state);
    if (state != AL_PLAYING)
        alSourcePlay(sourceID);
    if (av_read_frame(pFormatCtx, &packet) >= 0)
    {
        if (packet.stream_index == videoStream)
        {
            avcodec_decode_video2(pCodecCtx, pFrame, &FrameFinished, &packet);
            if (FrameFinished)
            {
                sws_scale(swsContext, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                av_free_packet(&packet);
            }
        }
        else if (packet.stream_index == audioStream)
        {
            /*
            avcodec_decode_audio4(aCodecCtx, aFrame, &FrameFinishd, &packet);
            if (FrameFinished)
            {
                //Update Audio and rotate buffers here!
            }
            */
        }
        glGenTextures(1, &VideoTexture);
        glBindTexture(GL_TEXTURE_2D, VideoTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, pCodecCtx->width, pCodecCtx->height, 0, GL_RGB, GL_UNSIGNED_BYTE, pFrameRGB->data[0]);
    }
    else
    {
        av_seek_frame(pFormatCtx, videoStream, 0, AVSEEK_FLAG_BACKWARD);
    }
}

所以我想最大的问题是我该怎么做?我一点头绪都没有。任何帮助表示赞赏,谢谢!

4

1 回答 1

1

首先,您需要创建 OpenAL 流式声源。此外,您还需要缓冲区池来读取流数据并排队等待 OpenAL 播放。

在打开时,您需要从 pull 中填充一些缓冲区并将它们添加到源队列 (alSourceQueueBuffers)。在更新过程中,您需要取消已播放缓冲区的队列

int processed = 0;
ALuint bufID = 0;
// ...
alGetSourcei(source_handle, AL_BUFFERS_PROCESSED, &processed);
while (processed--) {
    alGetError();
    alSourceUnqueueBuffers(source_handle, 1, &bufID);
    // return buffId to buffers pool;
}

然后您需要从池中获取空缓冲区并使用它的缓冲区 ID 调用您的 bufferData 代码。

在 bufferData 中,您不需要创建源 - 将其作为参数传递。此外,您不需要设置源参数。只需将现有缓冲区排队到现有源。而不是 alSourcei(sourceID, AL_BUFFER, bufferID) 你需要使用 alSourceQueueBuffers(sourceID, 1, &bufferID)

于 2013-01-28T06:23:31.990 回答