我正在解码OGG视频(Theora & vorbis作为编解码器),并希望在播放声音时在屏幕上显示它(使用Ogre 3D)。我可以将图像流解码得很好,并且视频以正确的帧速率完美播放等。FFmpeg + OpenAL - 从视频播放流式传输声音将不起作用
但是,我无法使用OpenAL播放声音。
编辑:我设法让播放声音至少有点像视频中的实际音频。更新了示例代码。
编辑2:我现在能够得到“几乎”正确的声音。我必须将OpenAL设置为使用AL_FORMAT_STEREO_FLOAT32(初始化扩展后)而不是STEREO16。现在声音“只”极高,并且以正确的速度播放。
这是我如何解码音频数据包(在后台线程,等效工作蛮好的视频文件的图像流):
//------------------------------------------------------------------------------
int decodeAudioPacket( AVPacket& p_packet, AVCodecContext* p_audioCodecContext, AVFrame* p_frame,
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;
}
// Frame is complete, store it in audio frame queue
if (got_frame)
{
int bufferSize = av_samples_get_buffer_size(NULL, p_audioCodecContext->channels, p_frame->nb_samples,
p_audioCodecContext->sample_fmt, 0);
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];
if (p_frame->channels == 2)
{
memcpy(frame->data, p_frame->data[0], bufferSize >> 1);
memcpy(frame->data + (bufferSize >> 1), p_frame->data[1], bufferSize >> 1);
}
else
{
memcpy(frame->data, p_frame->data, 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;
}
所以,你可以看到,我解码帧,memcpy它到我自己的结构,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);
的格式和频率我给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);
正如你所看到的,我做的相当重的错误检查。但是我从OpenAL和FFmpeg都没有发现任何错误。 编辑:我听到的声音有点类似于视频中的实际音频,但非常高调和口吃非常多。另外,它似乎在电视噪音之上播放。很奇怪。另外,它的播放速度比正确的音频慢得多。 编辑:2使用AL_FORMAT_STEREO_FLOAT32后,声音以正确的速度播放,但仍然非常高调和口吃(尽管比以前少)。
视频本身没有被破坏,它可以在任何玩家身上玩得很好。 OpenAL也可以在同一个应用程序中播放* .way文件,所以它也可以工作。
任何想法可能是错误的地方或如何正确地做到这一点?
我唯一的猜测是,FFmpeg的解码函数不会产生OpenGL可以读取的数据。但是,就FFmpeg解码示例而言,所以我不知道缺少什么。据我所知,decode_audio4函数将帧解码为原始数据。 OpenAL应该能够处理RAW数据(或者说,不能用于其他任何工作)。
不知何故错过了一个点? – rogerdpack
解码时,我看到PTS都是AV_NOPTS_VALUE。所以我使用dts。这些都是为了。这就是为什么我自己不做任何订单。 – TheSHEEEP
或者你的意思是使用PTS跳过/复制帧进行播放?你在OpenGL中唯一能做的就是重新填充源缓冲区(它有点像后缓冲区,只是有更多的缓冲区)。我不知道如何在那里跳过/复制音频帧,因为我不知道OpenGL在“未来”中需要什么。您重新填充的缓冲区是那些将在X帧中播放的缓冲区,并且您无法知道X.在播放* .wav文件时,OpenGL也不需要被告知跳过/重复帧,所以我确信它本身就是这样做的。 – TheSHEEEP