好吧,基本上我正在研究一个简单的视频播放器,我可能会问另一个关于滞后视频\同步到音频的问题,但是现在我遇到了音频问题。我设法做的是浏览视频的所有音频帧并将它们添加到矢量缓冲区,然后使用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);
}
}
所以我想最重要的问题是我该怎么办?我一点也不清楚。感谢任何帮助,谢谢!
答案 0 :(得分: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,&amp; bufferID)