Try to maintain a better synchronization with game framerate

This commit is contained in:
Benau 2017-03-20 16:16:26 +08:00
parent 88cbcd202c
commit 78dcabb143
3 changed files with 97 additions and 66 deletions

View File

@ -33,14 +33,17 @@ AVIWriter::AVIWriter()
{ {
m_file = NULL; m_file = NULL;
m_pbo_use = 0; m_pbo_use = 0;
m_dt = 0.0f; m_accumulated_time = 0.0f;
m_remaining_time = 0.0f;
m_img_quality = 0;
m_width = irr_driver->getActualScreenSize().Width;
m_height = irr_driver->getActualScreenSize().Height;
glGenBuffers(3, m_pbo); glGenBuffers(3, m_pbo);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]); glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, glBufferData(GL_PIXEL_PACK_BUFFER, m_width * m_height * 4, NULL,
irr_driver->getActualScreenSize().getArea() * 4, NULL, GL_STREAM_READ);
GL_STREAM_READ);
} }
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
pthread_mutex_init(&m_fbi_mutex, NULL); pthread_mutex_init(&m_fbi_mutex, NULL);
@ -63,15 +66,10 @@ void* AVIWriter::startRoutine(void *obj)
&avi_writer->m_fbi_mutex); &avi_writer->m_fbi_mutex);
waiting = avi_writer->m_fbi_queue.empty(); waiting = avi_writer->m_fbi_queue.empty();
} }
uint8_t* fbi = avi_writer->m_fbi_queue.front(); auto& p = avi_writer->m_fbi_queue.front();
if (fbi == NULL) uint8_t* fbi = p.first;
{ const float dt = p.second;
avi_writer->setCanBeDeleted(); if (dt < 0.0f)
avi_writer->m_fbi_queue.pop_front();
pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
return NULL;
}
if (fbi == (uint8_t*)0xAAAB1E5D)
{ {
avi_writer->closeFile(); avi_writer->closeFile();
avi_writer->m_file = NULL; avi_writer->m_file = NULL;
@ -79,8 +77,21 @@ void* AVIWriter::startRoutine(void *obj)
pthread_mutex_unlock(&avi_writer->m_fbi_mutex); pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
continue; continue;
} }
else if (fbi == NULL)
{
avi_writer->setCanBeDeleted();
avi_writer->m_fbi_queue.pop_front();
pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
return NULL;
}
avi_writer->m_fbi_queue.pop_front(); avi_writer->m_fbi_queue.pop_front();
pthread_mutex_unlock(&avi_writer->m_fbi_mutex); pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
int frame_count = avi_writer->handleFrameBufferImage(fbi, dt);
if (frame_count == -1)
{
delete [] fbi;
continue;
}
video::IImage* image = irr_driver->getVideoDriver() video::IImage* image = irr_driver->getVideoDriver()
->createImageFromData(video::ECF_A8R8G8B8, ->createImageFromData(video::ECF_A8R8G8B8,
irr_driver->getActualScreenSize(), fbi, irr_driver->getActualScreenSize(), fbi,
@ -94,12 +105,12 @@ void* AVIWriter::startRoutine(void *obj)
rgb->unlock(); rgb->unlock();
rgb->drop(); rgb->drop();
uint8_t* orig_fbi = fbi; uint8_t* orig_fbi = fbi;
const int pitch = irr_driver->getActualScreenSize().Width * 3; const unsigned width = avi_writer->m_width;
uint8_t* p2 = fbi + (irr_driver->getActualScreenSize().Height - 1) * const unsigned height = avi_writer->m_height;
pitch; const int pitch = width * 3;
uint8_t* p2 = fbi + (height - 1) * pitch;
uint8_t* tmp_buf = new uint8_t[pitch]; uint8_t* tmp_buf = new uint8_t[pitch];
for (unsigned i = 0; i < irr_driver->getActualScreenSize().Height; for (unsigned i = 0; i < height; i += 2)
i += 2)
{ {
memcpy(tmp_buf, fbi, pitch); memcpy(tmp_buf, fbi, pitch);
memcpy(fbi, p2, pitch); memcpy(fbi, p2, pitch);
@ -108,47 +119,67 @@ void* AVIWriter::startRoutine(void *obj)
p2 -= pitch; p2 -= pitch;
} }
delete [] tmp_buf; delete [] tmp_buf;
const unsigned size = irr_driver->getActualScreenSize().getArea() * 3; const unsigned size = width * height * 3;
uint8_t* jpg = new uint8_t[size]; uint8_t* jpg = new uint8_t[size];
int new_len = bmpToJpg(orig_fbi, jpg, size, int new_len = avi_writer->bmpToJpg(orig_fbi, jpg, size);
irr_driver->getActualScreenSize().Width, delete [] orig_fbi;
irr_driver->getActualScreenSize().Height, 3, 70); while (frame_count != 0)
delete[] orig_fbi; {
avi_writer->addImage(jpg, new_len); avi_writer->addImage(jpg, new_len);
delete[] jpg; frame_count--;
}
delete [] jpg;
} }
return NULL; return NULL;
} // startRoutine } // startRoutine
// ----------------------------------------------------------------------------
int AVIWriter::handleFrameBufferImage(uint8_t* fbi, float dt)
{
const float frame_rate = 0.001f * m_msec_per_frame;
m_accumulated_time += dt;
if (m_accumulated_time < frame_rate && m_remaining_time < frame_rate)
{
return -1;
}
int frame_count = 1;
m_remaining_time += m_accumulated_time - frame_rate;
m_accumulated_time = 0.0f;
while (m_remaining_time > frame_rate)
{
frame_count++;
m_remaining_time -= frame_rate;
}
return frame_count;
} // handleFrameBufferImage
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void AVIWriter::captureFrameBufferImage(float dt) void AVIWriter::captureFrameBufferImage(float dt)
{ {
if (m_file == NULL) if (m_file == NULL)
{ {
createFile(AVI_FORMAT_JPG, 24, 70); createFile(AVI_FORMAT_JPG, 24/*fps*/, 24/*bits*/, 90/*quality*/);
} }
m_dt += dt;
glReadBuffer(GL_BACK); glReadBuffer(GL_BACK);
int pbo_read = -1; int pbo_read = -1;
if (m_pbo_use > 2 && m_dt >= 0.0416666667f) if (m_pbo_use > 3 && m_pbo_use % 3 == 0)
m_pbo_use = 3;
if (m_pbo_use >= 3)
{ {
m_dt = 0.0f;
pbo_read = m_pbo_use % 3; pbo_read = m_pbo_use % 3;
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]); glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]);
void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
const unsigned size = irr_driver->getActualScreenSize().getArea() * 4; const unsigned size = m_width * m_height * 4;
uint8_t* fbi = new uint8_t[size]; uint8_t* fbi = new uint8_t[size];
memcpy(fbi, ptr, size); memcpy(fbi, ptr, size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
addFrameBufferImage(fbi); addFrameBufferImage(fbi, dt);
} }
int pbo_use = m_pbo_use++ % 3; int pbo_use = m_pbo_use++ % 3;
assert(pbo_read == -1 || pbo_use == pbo_read); assert(pbo_read == -1 || pbo_use == pbo_read);
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]); glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]);
glReadPixels(0, 0, irr_driver->getActualScreenSize().Width, glReadPixels(0, 0, m_width, m_height, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
irr_driver->getActualScreenSize().Height, GL_BGRA, GL_UNSIGNED_BYTE,
0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
} // captureFrameBufferImage } // captureFrameBufferImage
@ -156,7 +187,7 @@ void AVIWriter::captureFrameBufferImage(float dt)
void AVIWriter::stopRecording() void AVIWriter::stopRecording()
{ {
assert(m_file != NULL); assert(m_file != NULL);
addFrameBufferImage((uint8_t*)0xAAAB1E5D); addFrameBufferImage(NULL, -1.0f);
} // stopRecording } // stopRecording
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -348,14 +379,13 @@ error:
} // closeFile } // closeFile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality) bool AVIWriter::createFile(AVIFormat avi_format, int fps, int bits,
int quality)
{ {
if (m_file != NULL) if (m_file != NULL)
return false; return false;
int width = irr_driver->getActualScreenSize().Width; m_img_quality = quality;
int height = irr_driver->getActualScreenSize().Height;
time_t rawtime; time_t rawtime;
time(&rawtime); time(&rawtime);
tm* timeInfo = localtime(&rawtime); tm* timeInfo = localtime(&rawtime);
@ -372,19 +402,19 @@ bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality)
+ time_buffer + ".avi"; + time_buffer + ".avi";
m_stream_bytes = 0; m_stream_bytes = 0;
m_total_frames = 0; m_total_frames = 0;
m_msec_per_frame = 42; m_msec_per_frame = unsigned(1000 / fps);
m_movi_start = 0; m_movi_start = 0;
m_last_junk_chunk = 0; m_last_junk_chunk = 0;
m_total_frames = 0; m_total_frames = 0;
BitmapInfoHeader bitmap_hdr; BitmapInfoHeader bitmap_hdr;
bitmap_hdr.biSize = sizeof(BitmapInfoHeader); bitmap_hdr.biSize = sizeof(BitmapInfoHeader);
bitmap_hdr.biWidth = width; bitmap_hdr.biWidth = m_width;
bitmap_hdr.biHeight = height; bitmap_hdr.biHeight = m_height;
bitmap_hdr.biPlanes = 1; bitmap_hdr.biPlanes = 1;
bitmap_hdr.biBitCount = bits; bitmap_hdr.biBitCount = bits;
bitmap_hdr.biCompression = 0; bitmap_hdr.biCompression = 0;
bitmap_hdr.biSizeImage = (width * height * 3 * bitmap_hdr.biPlanes); bitmap_hdr.biSizeImage = (m_width * m_height * 3 * bitmap_hdr.biPlanes);
bitmap_hdr.biXPelsPerMeter = 0; bitmap_hdr.biXPelsPerMeter = 0;
bitmap_hdr.biYPelsPerMeter = 0; bitmap_hdr.biYPelsPerMeter = 0;
bitmap_hdr.biClrUsed = 0; bitmap_hdr.biClrUsed = 0;
@ -407,8 +437,8 @@ bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality)
m_avi_hdr.avih.dwInitialFrames = 0; m_avi_hdr.avih.dwInitialFrames = 0;
m_avi_hdr.avih.dwStreams = 1; // 1 = video, 2 = video and audio m_avi_hdr.avih.dwStreams = 1; // 1 = video, 2 = video and audio
m_avi_hdr.avih.dwSuggestedBufferSize = 0; // can be just 0 m_avi_hdr.avih.dwSuggestedBufferSize = 0; // can be just 0
m_avi_hdr.avih.dwWidth = width; m_avi_hdr.avih.dwWidth = m_width;
m_avi_hdr.avih.dwHeight = height; m_avi_hdr.avih.dwHeight = m_height;
m_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T'); m_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T');
m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) + m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) +
@ -427,7 +457,7 @@ bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality)
m_format_hdr.strh.dwStart = 0; m_format_hdr.strh.dwStart = 0;
m_format_hdr.strh.dwLength = 0; // update when finished m_format_hdr.strh.dwLength = 0; // update when finished
m_format_hdr.strh.dwSuggestedBufferSize = 0; // can be just 0 m_format_hdr.strh.dwSuggestedBufferSize = 0; // can be just 0
m_format_hdr.strh.dwQuality = quality * 100; m_format_hdr.strh.dwQuality = m_img_quality * 100;
m_format_hdr.strh.dwSampleSize = 0; m_format_hdr.strh.dwSampleSize = 0;
m_format_hdr.strh.Left = 0; m_format_hdr.strh.Left = 0;
m_format_hdr.strh.Top = 0; m_format_hdr.strh.Top = 0;
@ -445,7 +475,7 @@ bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality)
} }
else if (avi_format == AVI_FORMAT_BMP) else if (avi_format == AVI_FORMAT_BMP)
{ {
bitmap_hdr.biHeight = -height; bitmap_hdr.biHeight = -m_height;
bitmap_hdr.biCompression = 0; bitmap_hdr.biCompression = 0;
m_chunk_fcc = FOURCC('0', '0', 'd', 'b'); m_chunk_fcc = FOURCC('0', '0', 'd', 'b');
} }
@ -501,8 +531,7 @@ error:
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output, int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length, unsigned int width, unsigned long buf_length)
unsigned int height, int num_channels, int quality)
{ {
struct jpeg_compress_struct cinfo; struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr; struct jpeg_error_mgr jerr;
@ -510,13 +539,13 @@ int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output,
jpeg_create_compress(&cinfo); jpeg_create_compress(&cinfo);
cinfo.image_width = width; cinfo.image_width = m_width;
cinfo.image_height = height; cinfo.image_height = m_height;
cinfo.input_components = num_channels; cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB; cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo); jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, true); jpeg_set_quality(&cinfo, m_img_quality, true);
jpeg_mem_dest(&cinfo, &image_output, &buf_length); jpeg_mem_dest(&cinfo, &image_output, &buf_length);
@ -525,7 +554,7 @@ int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output,
JSAMPROW jrow[1]; JSAMPROW jrow[1];
while (cinfo.next_scanline < cinfo.image_height) while (cinfo.next_scanline < cinfo.image_height)
{ {
jrow[0] = &image_data[cinfo.next_scanline * width * num_channels]; jrow[0] = &image_data[cinfo.next_scanline * m_width * 3];
jpeg_write_scanlines(&cinfo, jrow, 1); jpeg_write_scanlines(&cinfo, jrow, 1);
} }

View File

@ -169,11 +169,12 @@ private:
std::string m_filename; std::string m_filename;
int m_last_junk_chunk, m_end_of_header, m_movi_start; int m_last_junk_chunk, m_end_of_header, m_movi_start, m_img_quality;
unsigned int m_msec_per_frame, m_stream_bytes, m_total_frames, m_pbo_use; unsigned int m_msec_per_frame, m_stream_bytes, m_total_frames, m_pbo_use,
m_width, m_height;
float m_dt; float m_accumulated_time, m_remaining_time;
AVIHeader m_avi_hdr; AVIHeader m_avi_hdr;
@ -185,7 +186,7 @@ private:
uint32_t m_chunk_fcc; uint32_t m_chunk_fcc;
std::list<uint8_t*> m_fbi_queue; std::list<std::pair<uint8_t*, float> > m_fbi_queue;
pthread_t m_thread; pthread_t m_thread;
@ -196,25 +197,26 @@ private:
GLuint m_pbo[3]; GLuint m_pbo[3];
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static int bmpToJpg(unsigned char* image_data, unsigned char* image_output, int bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length, unsigned int width, unsigned long buf_length);
unsigned int height, int num_channels, int quality);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
AVIErrCode addImage(unsigned char* buffer, int size); AVIErrCode addImage(unsigned char* buffer, int size);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool closeFile(bool delete_file = false); bool closeFile(bool delete_file = false);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool createFile(AVIFormat avi_format, int bits, int quality); bool createFile(AVIFormat avi_format, int fps, int bits, int quality);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool addJUNKChunk(std::string str, unsigned int min_size); bool addJUNKChunk(std::string str, unsigned int min_size);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void addFrameBufferImage(uint8_t* fbi) void addFrameBufferImage(uint8_t* fbi, float dt)
{ {
pthread_mutex_lock(&m_fbi_mutex); pthread_mutex_lock(&m_fbi_mutex);
m_fbi_queue.push_back(fbi); m_fbi_queue.emplace_back(fbi, dt);
pthread_cond_signal(&m_cond_request); pthread_cond_signal(&m_cond_request);
pthread_mutex_unlock(&m_fbi_mutex); pthread_mutex_unlock(&m_fbi_mutex);
} }
// ------------------------------------------------------------------------
int handleFrameBufferImage(uint8_t* fbi, float dt);
public: public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -900,4 +900,4 @@ bool isOpen()
return g_debug_menu_visible; return g_debug_menu_visible;
} // isOpen } // isOpen
} // namespace Debug } // namespace Debug