Потоковое аудио с OpenAL и FFMPEG

Хорошо, в основном я работаю над простым видеоплеером и, вероятно, позже задам еще один вопрос о задержке видео \ синхронизации со звуком, но сейчас у меня проблема со звуком. Что мне удалось сделать, так это просмотреть все звуковые кадры видео и добавить их в векторный буфер, а затем воспроизвести звук из этого буфера с помощью OpenAL.

Это неэффективно и требует много памяти, поэтому мне нужно иметь возможность передавать его, используя то, что я думаю, называется вращающимся буфером. Я столкнулся с проблемами, одна из которых заключалась в том, что не так много информации о потоковой передаче с помощью OpenAL, не говоря уже о правильном способе декодирования звука с помощью FFMPEG и передачи его в OpenAL. Мне еще менее комфортно использовать вектор в качестве буфера, потому что я, честно говоря, понятия не имею, как векторы работают в C ++, но мне каким-то образом удалось вытащить кое-что из головы, чтобы заставить его работать.

В настоящее время у меня есть класс Video, который выглядит так:

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

Итак, я думаю, главный вопрос в том, как мне это сделать? Понятия не имею. Любая помощь приветствуется, спасибо!


person Michael Barth    schedule 28.01.2013    source источник


Ответы (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;
}

Затем вам нужно получить пустой буфер из пула и вызвать свой код bufferData с его идентификатором буфера.

в bufferData создавать источник не нужно - передайте его как параметр. Также вам не нужно настраивать параметры источника. Просто поставьте существующий буфер в очередь к существующему источнику. вместо alSourcei (sourceID, AL_BUFFER, bufferID) вам нужно использовать alSourceQueueBuffers (sourceID, 1 и bufferID)

person PSyton    schedule 28.01.2013