Хорошо, в основном я работаю над простым видеоплеером и, возможно, позже задам еще один вопрос об отставании видео \ синхронизации с аудио, но сейчас у меня проблема со звуком. Что мне удалось сделать, так это просмотреть все аудио кадры видео и добавить их в векторный буфер, а затем воспроизвести аудио из этого буфера с помощью 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);
}
}
Поэтому я думаю, что главный вопрос в том, как мне это сделать? Я понятия не имею. Любая помощь приветствуется, спасибо!
Прежде всего, вам нужно создать источник потокового звука 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)
Других решений пока нет …