Я пытаюсь декодировать видео с помощью FFmpeg, преобразовать его в текстуру openGL и отобразить в движке cocos2dx.Мне удалось сделать это, и он отображает видео, как я хотел, теперь проблема в производительности.Я получаю обновление Sprite каждый кадр (игра имеет фиксированную скорость 60 кадров в секунду, видео - 30 кадров в секунду), поэтому то, что я сделал, я декодировал и преобразовывал кадры взаимозаменяемо, не очень хорошо работал, теперь у меня настроена отдельная нить, где я декодирую вбесконечный цикл while
с sleep()
, чтобы он не перегружал процессор / программу.В настоящее время я установил 2 кадровых буфера pbo и флаг bool
, чтобы сообщить моему циклу потока ffmpeg о декодировании другого кадра, так как я не знаю, как вручную ждать, когда декодировать другой кадр.Я искал в Интернете для решения этой проблемы, но не удалось получить никаких ответов.
Я смотрел на это: Декодирование видео непосредственно в текстуру в отдельном потоке , но это не решило мою проблему, так как это было просто преобразование YUV в RGB внутри шейдеров opengl, которые у меня есть.еще не сделано, но в настоящее время не проблема.
Дополнительная информация, которая может оказаться полезной, заключается в том, что мне не нужно заканчивать поток до выхода из приложения, и я готов использовать любой формат видео, включая без потерь.
Хорошо, основной цикл декодированиявыглядит следующим образом:
//.. this is inside of a constructor / init
//adding thread to array in order to save the thread
global::global_pending_futures.push_back(std::async(std::launch::async, [=] {
while (true) {
if (isPlaying) {
this->decodeLoop();
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(3));
}
}
}));
Причина, по которой я использую bool, чтобы проверить, был ли использован кадр, заключается в том, что основная функция декодирования занимает около 5 мс, чтобы завершить отладку, а затем должна ждать около 11 мс, чтобы отобразить кадр,поэтому я не могу знать, когда был показан кадр, и я также не знаю, сколько времени заняло декодирование.
Функция декодирования:
void video::decodeLoop() { //this should loop in a separate thread
frameData* buff = nullptr;
if (buf1.needsRefill) {
/// buf1.bufferLock.lock();
buff = &buf1;
buf1.needsRefill = false;
firstBuff = true;
}
else if (buf2.needsRefill) {
///buf2.bufferLock.lock();
buff = &buf2;
buf2.needsRefill = false;
firstBuff = false;
}
if (buff == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
return;//error? //wait?
}
//pack pixel buffer?
if (getNextFrame(buff)) {
getCurrentRBGConvertedFrame(buff);
}
else {
loopedTimes++;
if (loopedTimes >= repeatTimes) {
stop();
}
else {
restartVideoPlay(&buf1);//restart both
restartVideoPlay(&buf2);
if (getNextFrame(buff)) {
getCurrentRBGConvertedFrame(buff);
}
}
}
/// buff->bufferLock.unlock();
return;
}
Как вы можете сказать, я сначала проверил,буфер использовался с помощью bool needsRefill и затем декодировал другой кадр.
frameData struct:
struct frameData {
frameData() {};
~frameData() {};
AVFrame* frame;
AVPacket* pkt;
unsigned char* pdata;
bool needsRefill = true;
std::string name = "";
std::mutex bufferLock;
///unsigned int crrFrame
GLuint pboid = 0;
};
И это называется каждый кадр:
void video::actualDraw() { //meant for cocos implementation
if (this->isVisible()) {
if (this->getOpacity() > 0) {
if (isPlaying) {
if (loopedTimes >= repeatTimes) { //ignore -1 because comparing unsgined to signed
this->stop();
}
}
if (isPlaying) {
this->setVisible(true);
if (!display) { //skip frame
///this->getNextFrame();
display = true;
}
else if (display) {
display = false;
auto buff = this->getData();
width = this->getWidth();
height = this->getHeight();
if (buff) {
if (buff->pdata) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buff->pboid);
glBufferData(GL_PIXEL_UNPACK_BUFFER, 3 * (width*height), buff->pdata, GL_DYNAMIC_DRAW);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, 0);///buff->pdata); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
buff->needsRefill = true;
}
}
}
else { this->setVisible(false); }
}
}
}
Функция getData, чтобы указать, какой фреймбуфер использует
video::frameData* video::getData() {
if (firstBuff) {
if (buf1.needsRefill == false) {
///firstBuff = false;
return &buf1;///.pdata;
}
}
else { //if false
if (buf2.needsRefill == false) {
///firstBuff = true;
return &buf2;///.pdata;
}
}
return nullptr;
}
Я не уверен, что еще включить, я вставил весь код в pastebin.video.cpp: https://pastebin.com/cWGT6APn video.h https://pastebin.com/DswAXwXV
Чтобы подвести итог проблемы:
Как правильно реализовать декодирование в отдельном потоке / как оптимизировать текущий код?
В настоящее время видео отстает, когда какой-то другой поток или основной поток становится тяжелым, а затем недостаточно быстро декодируется.