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

View File

@ -169,11 +169,12 @@ private:
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;
@ -185,7 +186,7 @@ private:
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;
@ -196,25 +197,26 @@ private:
GLuint m_pbo[3];
// ------------------------------------------------------------------------
static int bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length, unsigned int width,
unsigned int height, int num_channels, int quality);
int bmpToJpg(unsigned char* image_data, unsigned char* image_output,
unsigned long buf_length);
// ------------------------------------------------------------------------
AVIErrCode addImage(unsigned char* buffer, int size);
// ------------------------------------------------------------------------
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);
// ------------------------------------------------------------------------
void addFrameBufferImage(uint8_t* fbi)
void addFrameBufferImage(uint8_t* fbi, float dt)
{
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_mutex_unlock(&m_fbi_mutex);
}
// ------------------------------------------------------------------------
int handleFrameBufferImage(uint8_t* fbi, float dt);
public:
// ------------------------------------------------------------------------

View File

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