C++ FFmpeg+;OpenAL-播放来自视频的流式声音赢得';行不通

C++ FFmpeg+;OpenAL-播放来自视频的流式声音赢得';行不通,c++,ffmpeg,openal,ogg,vorbis,C++,Ffmpeg,Openal,Ogg,Vorbis,我正在解码一个OGG视频(theora和vorbis作为编解码器),并希望在播放声音的同时在屏幕上显示它(使用OGRE3D)。我可以很好地解码图像流,视频以正确的帧速率完美播放,等等 但是,我无法用OpenAL播放声音 编辑:我设法使播放的声音至少在某种程度上与视频中的实际音频相似。更新的示例代码 编辑2:我现在能够听到“几乎”正确的声音。我必须将OpenAL设置为使用AL_FORMAT_stereou_FLOAT32(在初始化扩展之后),而不仅仅是STEREO16。现在声音“只是”非常高的音调

我正在解码一个OGG视频(theora和vorbis作为编解码器),并希望在播放声音的同时在屏幕上显示它(使用OGRE3D)。我可以很好地解码图像流,视频以正确的帧速率完美播放,等等

但是,我无法用OpenAL播放声音

编辑:我设法使播放的声音至少在某种程度上与视频中的实际音频相似。更新的示例代码

编辑2:我现在能够听到“几乎”正确的声音。我必须将OpenAL设置为使用AL_FORMAT_stereou_FLOAT32(在初始化扩展之后),而不仅仅是STEREO16。现在声音“只是”非常高的音调和口吃,但速度正确

下面是我解码音频包的方法(在后台线程中,等效的方法对于视频文件的图像流非常有效):

//------------------------------------------------------------------------------
int decodeAudioPacket(AVPacket&p_数据包,avcodeContext*p_audiocodeContext,AVFrame*p_帧,
FFmpegVideoPlayer*p_播放器、VideoInfo和p_VideoInfo)
{
//解码音频帧
int got_frame=0;
int decoded=avcodec_decode_audio4(p_audioCodecContext、p_帧、got_帧和p_包);
如果(解码<0)
{
p\u videoInfo.error=“解码音频帧时出错。”;
返回解码;
}
//帧已完成,请将其存储在音频帧队列中
如果(得到了框架)
{
int bufferSize=av_samples\u get_buffer_size(NULL,p_audioCodecContext->channels,p_frame->nb_samples,
p_audioCodecContext->sample_fmt,0);
int64\u t duration=p\u frame->pkt\u duration;
int64\u t dts=p\u frame->pkt\u dts;
if(staticOgreLog)
{
StaticLogRelog->logMessage(“音频帧缓冲区大小/持续时间/dts:”
+boost::词法转换(bufferSize)+“/”
+boost::词法转换(持续时间)+“/”
+boost::词汇投射(dts),Ogre::LML_正常);
}
//创建音频帧
AudioFrame*frame=新的AudioFrame();
帧->数据大小=缓冲区大小;
帧->数据=新的uint8_t[bufferSize];
如果(p_帧->通道==2)
{
memcpy(frame->data,p_frame->data[0],bufferSize>>1);
memcpy(frame->data+(bufferSize>>1),p_frame->data[1],bufferSize>>1);
}
其他的
{
memcpy(帧->数据,p_帧->数据,缓冲区大小);
}
双时基=((双)p_AudioCodeContext->time_base.num)/(双)p_AudioCodeContext->time_base.den;
帧->生存期=持续时间*时基;
p_播放器->添加音频帧(帧);
}
返回解码;
}
如你所见,我解码了这个帧,把它保存到我自己的结构,AudioFrame中。现在,当播放声音时,我使用以下音频帧:

    int numBuffers = 4;
    ALuint buffers[4];
    alGenBuffers(numBuffers, buffers);
    ALenum success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error on alGenBuffers : " + Ogre::StringConverter::toString(success) + alGetString(success));
        return;
    }

    // Fill a number of data buffers with audio from the stream
    std::vector<AudioFrame*> audioBuffers;
    std::vector<unsigned int> audioBufferSizes;
    unsigned int numReturned = FFMPEG_PLAYER->getDecodedAudioFrames(numBuffers, audioBuffers, audioBufferSizes);

    // Assign the data buffers to the OpenAL buffers
    for (unsigned int i = 0; i < numReturned; ++i)
    {
        alBufferData(buffers[i], _streamingFormat, audioBuffers[i]->data, audioBufferSizes[i], _streamingFrequency);

        success = alGetError();
        if(success != AL_NO_ERROR)
        {
            CONSOLE_LOG("Error on alBufferData : " + Ogre::StringConverter::toString(success) + alGetString(success)
                            + " size: " + Ogre::StringConverter::toString(audioBufferSizes[i]));
            return;
        }
    }

    // Queue the buffers into OpenAL
    alSourceQueueBuffers(_source, numReturned, buffers);
    success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error queuing streaming buffers: " + Ogre::StringConverter::toString(success) + alGetString(success));
        return;
    }
}

alSourcePlay(_source);
int numBuffers=4;
铝缓冲液[4];
阿尔金缓冲区(缓冲区、缓冲区);
ALenum success=algeorror();
如果(成功!=无错误)
{
控制台日志(“alGenBuffers上的错误:+Ogre::StringConverter::toString(成功)+AlgenString(成功));
返回;
}
//用流中的音频填充多个数据缓冲区
向量音频缓冲器;
std::矢量音频缓冲区大小;
unsigned int numReturned=FFMPEG_PLAYER->getdecodedataudioframes(numBuffers、audioBuffers、audiobuffersize);
//将数据缓冲区分配给OpenAL缓冲区
for(无符号整数i=0;i数据,音频缓冲区大小[i],_streamingFrequency);
成功=恐怖();
如果(成功!=无错误)
{
控制台日志(“alBufferData上的错误:+Ogre::StringConverter::toString(成功)+alGetString(成功)
+“大小:”+Ogre::StringConverter::toString(audioBufferSizes[i]);
返回;
}
}
//将缓冲区排队到OpenAL中
alSourceQueueBuffers(_source,numReturned,buffers);
成功=恐怖();
如果(成功!=无错误)
{
控制台日志(“对流缓冲区进行排队时出错:+Ogre::StringConverter::toString(成功)+alGetString(成功));
返回;
}
}
alSourcePlay(_来源);
我给OpenAL的格式和频率是AL_format_STEREO_FLOAT32(这是一个立体声声音流,我初始化了FLOAT32扩展)和48000(这是音频流的AVCodecContext的采样率)

在播放过程中,我会执行以下操作来重新填充OpenAL的缓冲区:

ALint numBuffersProcessed;

// Check if OpenAL is done with any of the queued buffers
alGetSourcei(_source, AL_BUFFERS_PROCESSED, &numBuffersProcessed);
if(numBuffersProcessed <= 0)
    return;

// Fill a number of data buffers with audio from the stream
std::vector<AudiFrame*> audioBuffers;
std::vector<unsigned int> audioBufferSizes;
unsigned int numFilled = FFMPEG_PLAYER->getDecodedAudioFrames(numBuffersProcessed, audioBuffers, audioBufferSizes);

// Assign the data buffers to the OpenAL buffers
ALuint buffer;
for (unsigned int i = 0; i < numFilled; ++i)
{
    // Pop the oldest queued buffer from the source, 
    // fill it with the new data, then re-queue it
    alSourceUnqueueBuffers(_source, 1, &buffer);

    ALenum success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error Unqueuing streaming buffers: " + Ogre::StringConverter::toString(success));
        return;
    }

    alBufferData(buffer, _streamingFormat, audioBuffers[i]->data, audioBufferSizes[i], _streamingFrequency);

    success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error on re- alBufferData: " + Ogre::StringConverter::toString(success));
        return;
    }

    alSourceQueueBuffers(_source, 1, &buffer);

    success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error re-queuing streaming buffers: " + Ogre::StringConverter::toString(success) + " "
                    + alGetString(success));
        return;
    }
}

// Make sure the source is still playing, 
// and restart it if needed.
ALint playStatus;
alGetSourcei(_source, AL_SOURCE_STATE, &playStatus);
if(playStatus != AL_PLAYING)
    alSourcePlay(_source);
ALint-numbuffers过程;
//检查OpenAL是否使用任何队列缓冲区完成
alGetSourcei(源、所有缓冲区、已处理和已处理);
if(numBuffersProcessed GetDecodedDaudioFrames(numBuffersProcessed,audioBuffers,audioBufferSizes);
//将数据缓冲区分配给OpenAL缓冲区
明矾缓冲液;
for(无符号整数i=0;i数据、音频缓冲区大小[i]、_streamingFrequency);
成功=恐怖();
如果(成功!=无错误)
{
控制台日志(“re-alBufferData错误:+Ogre::StringConverter::toString(成功));
返回;
}
alSourceQueueBuffers(_源、1和缓冲区);
成功=恐怖();
如果(成功!=无错误)
{
控制台日志(“对流式缓冲区重新排队时出错:+Ogre::StringConverter::toString(成功)+”
ALint numBuffersProcessed;

// Check if OpenAL is done with any of the queued buffers
alGetSourcei(_source, AL_BUFFERS_PROCESSED, &numBuffersProcessed);
if(numBuffersProcessed <= 0)
    return;

// Fill a number of data buffers with audio from the stream
std::vector<AudiFrame*> audioBuffers;
std::vector<unsigned int> audioBufferSizes;
unsigned int numFilled = FFMPEG_PLAYER->getDecodedAudioFrames(numBuffersProcessed, audioBuffers, audioBufferSizes);

// Assign the data buffers to the OpenAL buffers
ALuint buffer;
for (unsigned int i = 0; i < numFilled; ++i)
{
    // Pop the oldest queued buffer from the source, 
    // fill it with the new data, then re-queue it
    alSourceUnqueueBuffers(_source, 1, &buffer);

    ALenum success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error Unqueuing streaming buffers: " + Ogre::StringConverter::toString(success));
        return;
    }

    alBufferData(buffer, _streamingFormat, audioBuffers[i]->data, audioBufferSizes[i], _streamingFrequency);

    success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error on re- alBufferData: " + Ogre::StringConverter::toString(success));
        return;
    }

    alSourceQueueBuffers(_source, 1, &buffer);

    success = alGetError();
    if(success != AL_NO_ERROR)
    {
        CONSOLE_LOG("Error re-queuing streaming buffers: " + Ogre::StringConverter::toString(success) + " "
                    + alGetString(success));
        return;
    }
}

// Make sure the source is still playing, 
// and restart it if needed.
ALint playStatus;
alGetSourcei(_source, AL_SOURCE_STATE, &playStatus);
if(playStatus != AL_PLAYING)
    alSourcePlay(_source);
int decodeAudioPacket(  AVPacket& p_packet, AVCodecContext* p_audioCodecContext, AVFrame* p_frame,
                        SwrContext* p_swrContext, uint8_t** p_destBuffer, int p_destLinesize,
                        FFmpegVideoPlayer* p_player, VideoInfo& p_videoInfo)
{
    // Decode audio frame
    int got_frame = 0;
    int decoded = avcodec_decode_audio4(p_audioCodecContext, p_frame, &got_frame, &p_packet);
    if (decoded < 0) 
    {
        p_videoInfo.error = "Error decoding audio frame.";
        return decoded;
    }

    if(decoded <= p_packet.size)
    {
        /* Move the unread data to the front and clear the end bits */
        int remaining = p_packet.size - decoded;
        memmove(p_packet.data, &p_packet.data[decoded], remaining);
        av_shrink_packet(&p_packet, remaining);
    }

    // Frame is complete, store it in audio frame queue
    if (got_frame)
    {
        int outputSamples = swr_convert(p_swrContext, 
                                        p_destBuffer, p_destLinesize, 
                                        (const uint8_t**)p_frame->extended_data, p_frame->nb_samples);

        int bufferSize = av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * p_videoInfo.audioNumChannels
                            * outputSamples;

        int64_t duration = p_frame->pkt_duration;
        int64_t dts = p_frame->pkt_dts;

        if (staticOgreLog)
        {
            staticOgreLog->logMessage("Audio frame bufferSize / duration / dts: " 
                    + boost::lexical_cast<std::string>(bufferSize) + " / "
                    + boost::lexical_cast<std::string>(duration) + " / "
                    + boost::lexical_cast<std::string>(dts), Ogre::LML_NORMAL);
        }

        // Create the audio frame
        AudioFrame* frame = new AudioFrame();
        frame->dataSize = bufferSize;
        frame->data = new uint8_t[bufferSize];
        memcpy(frame->data, p_destBuffer[0], bufferSize);
        double timeBase = ((double)p_audioCodecContext->time_base.num) / (double)p_audioCodecContext->time_base.den;
        frame->lifeTime = duration * timeBase;

        p_player->addAudioFrame(frame);
    }

    return decoded;
}
// Initialize SWR context
SwrContext* swrContext = swr_alloc_set_opts(NULL, 
            audioCodecContext->channel_layout, AV_SAMPLE_FMT_FLT, audioCodecContext->sample_rate,
            audioCodecContext->channel_layout, audioCodecContext->sample_fmt, audioCodecContext->sample_rate, 
            0, NULL);
int result = swr_init(swrContext);

// Create destination sample buffer
uint8_t** destBuffer = NULL;
int destBufferLinesize;
av_samples_alloc_array_and_samples( &destBuffer,
                                    &destBufferLinesize,
                                    videoInfo.audioNumChannels,
                                    2048,
                                    AV_SAMPLE_FMT_FLT,
                                    0);