Make AVIWriter threaded and get pixel with 3 async PBOs
Also try to record at a fixed 24fps
This commit is contained in:
parent
d3d7c95b4d
commit
8259026ac1
@ -61,6 +61,7 @@
|
||||
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "tracks/track_manager.hpp"
|
||||
#include "utils/avi_writer.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/profiler.hpp"
|
||||
@ -146,6 +147,7 @@ IrrDriver::IrrDriver()
|
||||
m_last_light_bucket_distance = 0;
|
||||
m_clear_color = video::SColor(255, 100, 101, 140);
|
||||
m_skinning_joint = 0;
|
||||
m_recording = false;
|
||||
|
||||
} // IrrDriver
|
||||
|
||||
@ -1885,8 +1887,22 @@ void IrrDriver::update(float dt)
|
||||
// menu.
|
||||
//if(World::getWorld() && World::getWorld()->isRacePhase())
|
||||
// printRenderStats();
|
||||
#ifndef SERVER_ONLY
|
||||
if (m_recording)
|
||||
AVIWriter::getInstance()->captureFrameBufferImage(dt);
|
||||
#endif
|
||||
} // update
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void IrrDriver::setRecording(bool val)
|
||||
{
|
||||
if (val == false && m_recording == false)
|
||||
return;
|
||||
m_recording = val;
|
||||
if (val == false)
|
||||
AVIWriter::getInstance()->stopRecording();
|
||||
} // setRecording
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void IrrDriver::requestScreenshot()
|
||||
|
@ -163,6 +163,7 @@ private:
|
||||
bool m_lightviz;
|
||||
bool m_distortviz;
|
||||
bool m_boundingboxesviz;
|
||||
bool m_recording;
|
||||
|
||||
/** Background colour to reset a buffer. Can be changed by each track. */
|
||||
irr::video::SColor m_clear_color;
|
||||
@ -414,6 +415,10 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool getBoundingBoxesViz() { return m_boundingboxesviz; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isRecording() const { return m_recording; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setRecording(bool val);
|
||||
// ------------------------------------------------------------------------
|
||||
u32 getRenderPass() { return m_renderpass; }
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<LightNode *> getLights() { return m_lights; }
|
||||
|
@ -1,378 +1,538 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2015 Dawid Gan
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "avi_writer.hpp"
|
||||
|
||||
|
||||
bool AVIWriter::addJUNKChunk(std::string str, unsigned int min_size)
|
||||
{
|
||||
int size = str.size() < min_size ? min_size : str.size() + 1;
|
||||
size = (size + 1) & 0xfffffffe;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = FOURCC('J', 'U', 'N', 'K');
|
||||
chunk.cb = size;
|
||||
|
||||
char* buffer = (char*)calloc(size, 1);
|
||||
strcpy(buffer, str.c_str());
|
||||
|
||||
int num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
num = fwrite(buffer, 1, size * sizeof(char), m_file);
|
||||
free(buffer);
|
||||
if (num != size)
|
||||
goto error;
|
||||
|
||||
m_last_junk_chunk = ftell(m_file);
|
||||
if (m_last_junk_chunk < 0)
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
AVIErrCode AVIWriter::addImage(unsigned char* buffer, int buf_size)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
goto error;
|
||||
|
||||
int num; num = ftell(m_file);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
if (m_total_frames >= MAX_FRAMES)
|
||||
goto size_limit;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = m_chunk_fcc;
|
||||
chunk.cb = buf_size;
|
||||
|
||||
m_index_table[m_total_frames].Offset = num;
|
||||
m_index_table[m_total_frames].Length = chunk.cb;
|
||||
m_index_table[m_total_frames].fcc = chunk.fcc;
|
||||
|
||||
num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
num = fwrite(buffer, 1, buf_size, m_file);
|
||||
if (num != buf_size)
|
||||
goto error;
|
||||
|
||||
int fill_size; fill_size = (sizeof(chunk) + buf_size) & 0x00000001;
|
||||
if (fill_size > 0)
|
||||
{
|
||||
uint32_t filler = 0;
|
||||
num = fwrite(&filler, 1, fill_size, m_file);
|
||||
if (num != fill_size)
|
||||
goto error;
|
||||
}
|
||||
|
||||
m_stream_bytes += sizeof(chunk) + buf_size + fill_size;
|
||||
m_total_frames++;
|
||||
|
||||
num = ftell(m_file);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
if (((num - m_last_junk_chunk) > 20000) && (!addJUNKChunk("", 1)))
|
||||
goto error;
|
||||
|
||||
// check if we reached the file size limit
|
||||
if (num >= MAX_FILE_SIZE)
|
||||
goto size_limit;
|
||||
|
||||
return AVI_SUCCESS;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return AVI_IO_ERR;
|
||||
|
||||
size_limit:
|
||||
closeFile();
|
||||
return AVI_SIZE_LIMIT_ERR;
|
||||
}
|
||||
|
||||
bool AVIWriter::closeFile(bool delete_file)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
|
||||
if (delete_file)
|
||||
goto error;
|
||||
|
||||
// add the index
|
||||
int idx_start; idx_start = ftell(m_file);
|
||||
if (idx_start < 0)
|
||||
goto error;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = FOURCC('i', 'd', 'x', '1');
|
||||
chunk.cb = sizeof(AVIINDEXENTRY) * m_total_frames;
|
||||
|
||||
int num; num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
for (unsigned int i = 0; i < m_total_frames; i++)
|
||||
{
|
||||
AVIINDEXENTRY Index;
|
||||
Index.ckid = m_index_table[i].fcc;
|
||||
Index.dwFlags = AVIIF_KEYFRAME;
|
||||
Index.dwChunkOffset = m_index_table[i].Offset;
|
||||
Index.dwChunkLength = m_index_table[i].Length;
|
||||
|
||||
num = fwrite(&Index, 1, sizeof(Index), m_file);
|
||||
if (num != sizeof(Index))
|
||||
goto error;
|
||||
}
|
||||
|
||||
// update the header
|
||||
if (m_total_frames > 0 && m_msec_per_frame > 0)
|
||||
{
|
||||
num = fseek(m_file, 0, SEEK_END);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
int size; size = ftell(m_file);
|
||||
if (size < 0)
|
||||
goto error;
|
||||
|
||||
num = fseek(m_file, 0, SEEK_SET);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
m_avi_hdr.riff.cb = size - sizeof(m_avi_hdr.riff);
|
||||
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
|
||||
m_avi_hdr.avih.dwMaxBytesPerSec = (uint32_t)(((m_stream_bytes / m_total_frames) *
|
||||
m_format_hdr.strh.dwRate) / m_msec_per_frame + 0.5f);
|
||||
m_avi_hdr.avih.dwTotalFrames = m_total_frames;
|
||||
|
||||
num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||
if (num != sizeof(m_avi_hdr))
|
||||
goto error;
|
||||
|
||||
m_format_hdr.strh.dwScale = m_msec_per_frame;
|
||||
m_format_hdr.strh.dwLength = m_total_frames;
|
||||
|
||||
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||
if (num != sizeof(m_format_hdr))
|
||||
goto error;
|
||||
}
|
||||
|
||||
// update the movi section
|
||||
m_movi_chunk.cb = idx_start - m_movi_start;
|
||||
|
||||
num = fseek(m_file, m_movi_start - sizeof(m_movi_chunk), SEEK_SET);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||
if (num != sizeof(m_movi_chunk))
|
||||
goto error;
|
||||
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(m_file);
|
||||
remove(m_filename.c_str());
|
||||
m_file = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AVIWriter::createFile(std::string filename, AVIFormat avi_format,
|
||||
int width, int height, unsigned int msec_per_frame,
|
||||
int bits, int quality)
|
||||
{
|
||||
if (m_file != NULL)
|
||||
return false;
|
||||
|
||||
if (width < 1 || height < 1)
|
||||
return false;
|
||||
|
||||
m_filename = filename;
|
||||
m_stream_bytes = 0;
|
||||
m_total_frames = 0;
|
||||
m_msec_per_frame = msec_per_frame;
|
||||
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.biPlanes = 1;
|
||||
bitmap_hdr.biBitCount = bits;
|
||||
bitmap_hdr.biCompression = 0;
|
||||
bitmap_hdr.biSizeImage = (width * height * 3 * bitmap_hdr.biPlanes);
|
||||
bitmap_hdr.biXPelsPerMeter = 0;
|
||||
bitmap_hdr.biYPelsPerMeter = 0;
|
||||
bitmap_hdr.biClrUsed = 0;
|
||||
bitmap_hdr.biClrImportant = 0;
|
||||
|
||||
memset(&m_avi_hdr, '\0', sizeof(m_avi_hdr));
|
||||
m_avi_hdr.riff.fcc = FOURCC('R', 'I', 'F', 'F');
|
||||
m_avi_hdr.riff.cb = 0; // update when finished (size of the file - 8)
|
||||
m_avi_hdr.avi = FOURCC('A', 'V', 'I', ' ');
|
||||
m_avi_hdr.list1.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_avi_hdr.list1.cb = 0;
|
||||
m_avi_hdr.hdrl = FOURCC('h', 'd', 'r', 'l');
|
||||
m_avi_hdr.avihhdr.fcc = FOURCC('a', 'v', 'i', 'h');
|
||||
m_avi_hdr.avihhdr.cb = sizeof(m_avi_hdr.avih);
|
||||
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
|
||||
m_avi_hdr.avih.dwMaxBytesPerSec = 0; // update when finished
|
||||
m_avi_hdr.avih.dwPaddingGranularity = 0;
|
||||
m_avi_hdr.avih.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
|
||||
m_avi_hdr.avih.dwTotalFrames = 0; // update when finished
|
||||
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_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) + sizeof(BitmapInfoHeader);
|
||||
m_format_hdr.strl = FOURCC('s', 't', 'r', 'l');
|
||||
m_format_hdr.strhhdr.fcc = FOURCC('s', 't', 'r', 'h');
|
||||
m_format_hdr.strhhdr.cb = sizeof(m_format_hdr.strh);
|
||||
m_format_hdr.strh.fccType = FOURCC('v', 'i', 'd', 's');
|
||||
m_format_hdr.strh.fccHandler = CC_DIB;
|
||||
m_format_hdr.strh.dwFlags = 0;
|
||||
m_format_hdr.strh.wPriority = 0;
|
||||
m_format_hdr.strh.wLanguage = 0;
|
||||
m_format_hdr.strh.dwInitialFrames = 0;
|
||||
m_format_hdr.strh.dwScale = m_msec_per_frame;
|
||||
m_format_hdr.strh.dwRate = 1000;
|
||||
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.dwSampleSize = 0;
|
||||
m_format_hdr.strh.Left = 0;
|
||||
m_format_hdr.strh.Top = 0;
|
||||
m_format_hdr.strh.Right = m_avi_hdr.avih.dwWidth;
|
||||
m_format_hdr.strh.Bottom = m_avi_hdr.avih.dwHeight;
|
||||
m_format_hdr.strfhdr.fcc = FOURCC('s', 't', 'r', 'f');
|
||||
m_format_hdr.strfhdr.cb = sizeof(BitmapInfoHeader);
|
||||
|
||||
//Format specific changes
|
||||
if (avi_format == AVI_FORMAT_JPG)
|
||||
{
|
||||
m_format_hdr.strh.fccHandler = CC_MJPG;
|
||||
bitmap_hdr.biCompression = FOURCC('M', 'J', 'P', 'G');
|
||||
m_chunk_fcc = FOURCC('0', '0', 'd', 'c');
|
||||
}
|
||||
else if (avi_format == AVI_FORMAT_BMP)
|
||||
{
|
||||
bitmap_hdr.biHeight = -height;
|
||||
bitmap_hdr.biCompression = 0;
|
||||
m_chunk_fcc = FOURCC('0', '0', 'd', 'b');
|
||||
}
|
||||
|
||||
const uint32_t fcc_movi = FOURCC('m', 'o', 'v', 'i');
|
||||
|
||||
m_file = fopen(filename.c_str(), "wb");
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
|
||||
int num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||
if (num != sizeof(m_avi_hdr))
|
||||
goto error;
|
||||
|
||||
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||
if (num != sizeof(m_format_hdr))
|
||||
goto error;
|
||||
|
||||
num = fwrite(&bitmap_hdr, 1, sizeof(BitmapInfoHeader), m_file);
|
||||
if (num != sizeof(BitmapInfoHeader))
|
||||
goto error;
|
||||
|
||||
m_end_of_header = ftell(m_file);
|
||||
if (m_end_of_header < 0)
|
||||
goto error;
|
||||
|
||||
if (!addJUNKChunk("", 2840))
|
||||
goto error;
|
||||
|
||||
m_avi_hdr.list1.cb = m_end_of_header - sizeof(m_avi_hdr.riff) -
|
||||
sizeof(m_avi_hdr.avi) - sizeof(m_avi_hdr.list1);
|
||||
m_movi_chunk.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_movi_chunk.cb = 0; // update when finished
|
||||
|
||||
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||
if (num != sizeof(m_movi_chunk))
|
||||
goto error;
|
||||
|
||||
m_movi_start = ftell(m_file);
|
||||
if (m_movi_start < 0)
|
||||
goto error;
|
||||
|
||||
num = fwrite(&fcc_movi, 1, sizeof(fcc_movi), m_file);
|
||||
if (num != sizeof(fcc_movi))
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
cinfo.image_width = width;
|
||||
cinfo.image_height = height;
|
||||
cinfo.input_components = num_channels;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(&cinfo);
|
||||
jpeg_set_quality(&cinfo, quality, true);
|
||||
|
||||
jpeg_mem_dest(&cinfo, &image_output, &buf_length);
|
||||
|
||||
jpeg_start_compress(&cinfo, true);
|
||||
|
||||
JSAMPROW jrow[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height)
|
||||
{
|
||||
jrow[0] = &image_data[cinfo.next_scanline * width * num_channels];
|
||||
jpeg_write_scanlines(&cinfo, jrow, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
return buf_length;
|
||||
}
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2015 Dawid Gan
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "utils/avi_writer.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/vs.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
AVIWriter::AVIWriter()
|
||||
{
|
||||
m_file = NULL;
|
||||
m_pbo_use = 0;
|
||||
m_dt = 0.0f;
|
||||
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);
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
pthread_mutex_init(&m_fbi_mutex, NULL);
|
||||
pthread_cond_init(&m_cond_request, NULL);
|
||||
pthread_create(&m_thread, NULL, &startRoutine, this);
|
||||
} // AVIWriter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* AVIWriter::startRoutine(void *obj)
|
||||
{
|
||||
VS::setThreadName("AVIWriter");
|
||||
AVIWriter* avi_writer = (AVIWriter*)obj;
|
||||
while (true)
|
||||
{
|
||||
pthread_mutex_lock(&avi_writer->m_fbi_mutex);
|
||||
bool waiting = avi_writer->m_fbi_queue.empty();
|
||||
while (waiting)
|
||||
{
|
||||
pthread_cond_wait(&avi_writer->m_cond_request,
|
||||
&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)
|
||||
{
|
||||
avi_writer->closeFile();
|
||||
avi_writer->m_file = NULL;
|
||||
avi_writer->m_fbi_queue.pop_front();
|
||||
pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
|
||||
continue;
|
||||
}
|
||||
avi_writer->m_fbi_queue.pop_front();
|
||||
pthread_mutex_unlock(&avi_writer->m_fbi_mutex);
|
||||
video::IImage* image = irr_driver->getVideoDriver()
|
||||
->createImageFromData(video::ECF_A8R8G8B8,
|
||||
irr_driver->getActualScreenSize(), fbi,
|
||||
true/*ownForeignMemory*/);
|
||||
video::IImage* rgb = irr_driver->getVideoDriver()->createImage
|
||||
(video::ECF_R8G8B8, irr_driver->getActualScreenSize());
|
||||
image->copyTo(rgb);
|
||||
image->drop();
|
||||
rgb->setDeleteMemory(false);
|
||||
fbi = (uint8_t*)rgb->lock();
|
||||
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;
|
||||
uint8_t* tmp_buf = new uint8_t[pitch];
|
||||
for (unsigned i = 0; i < irr_driver->getActualScreenSize().Height;
|
||||
i += 2)
|
||||
{
|
||||
memcpy(tmp_buf, fbi, pitch);
|
||||
memcpy(fbi, p2, pitch);
|
||||
memcpy(p2, tmp_buf, pitch);
|
||||
fbi += pitch;
|
||||
p2 -= pitch;
|
||||
}
|
||||
delete [] tmp_buf;
|
||||
const unsigned size = irr_driver->getActualScreenSize().getArea() * 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;
|
||||
}
|
||||
return NULL;
|
||||
} // startRoutine
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AVIWriter::captureFrameBufferImage(float dt)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
{
|
||||
createFile(AVI_FORMAT_JPG, 24, 70);
|
||||
}
|
||||
|
||||
m_dt += dt;
|
||||
glReadBuffer(GL_BACK);
|
||||
int pbo_read = -1;
|
||||
if (m_pbo_use > 2 && m_dt >= 0.0416666667f)
|
||||
{
|
||||
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;
|
||||
uint8_t* fbi = new uint8_t[size];
|
||||
memcpy(fbi, ptr, size);
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
addFrameBufferImage(fbi);
|
||||
}
|
||||
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);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
} // captureFrameBufferImage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AVIWriter::stopRecording()
|
||||
{
|
||||
assert(m_file != NULL);
|
||||
addFrameBufferImage((uint8_t*)0xAAAB1E5D);
|
||||
} // stopRecording
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AVIWriter::addJUNKChunk(std::string str, unsigned int min_size)
|
||||
{
|
||||
int size = str.size() < min_size ? min_size : str.size() + 1;
|
||||
size = (size + 1) & 0xfffffffe;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = FOURCC('J', 'U', 'N', 'K');
|
||||
chunk.cb = size;
|
||||
|
||||
char* buffer = (char*)calloc(size, 1);
|
||||
strcpy(buffer, str.c_str());
|
||||
|
||||
int num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
num = fwrite(buffer, 1, size * sizeof(char), m_file);
|
||||
free(buffer);
|
||||
if (num != size)
|
||||
goto error;
|
||||
|
||||
m_last_junk_chunk = ftell(m_file);
|
||||
if (m_last_junk_chunk < 0)
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return false;
|
||||
} // addJUNKChunk
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
AVIErrCode AVIWriter::addImage(unsigned char* buffer, int buf_size)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
goto error;
|
||||
|
||||
int num; num = ftell(m_file);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
if (m_total_frames >= MAX_FRAMES)
|
||||
goto size_limit;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = m_chunk_fcc;
|
||||
chunk.cb = buf_size;
|
||||
|
||||
m_index_table[m_total_frames].Offset = num;
|
||||
m_index_table[m_total_frames].Length = chunk.cb;
|
||||
m_index_table[m_total_frames].fcc = chunk.fcc;
|
||||
|
||||
num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
num = fwrite(buffer, 1, buf_size, m_file);
|
||||
if (num != buf_size)
|
||||
goto error;
|
||||
|
||||
int fill_size; fill_size = (sizeof(chunk) + buf_size) & 0x00000001;
|
||||
if (fill_size > 0)
|
||||
{
|
||||
uint32_t filler = 0;
|
||||
num = fwrite(&filler, 1, fill_size, m_file);
|
||||
if (num != fill_size)
|
||||
goto error;
|
||||
}
|
||||
|
||||
m_stream_bytes += sizeof(chunk) + buf_size + fill_size;
|
||||
m_total_frames++;
|
||||
|
||||
num = ftell(m_file);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
if (((num - m_last_junk_chunk) > 20000) && (!addJUNKChunk("", 1)))
|
||||
goto error;
|
||||
|
||||
// check if we reached the file size limit
|
||||
if (num >= MAX_FILE_SIZE)
|
||||
goto size_limit;
|
||||
|
||||
return AVI_SUCCESS;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return AVI_IO_ERR;
|
||||
|
||||
size_limit:
|
||||
closeFile();
|
||||
return AVI_SIZE_LIMIT_ERR;
|
||||
} // addImage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AVIWriter::closeFile(bool delete_file)
|
||||
{
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
|
||||
if (delete_file)
|
||||
goto error;
|
||||
|
||||
// add the index
|
||||
int idx_start; idx_start = ftell(m_file);
|
||||
if (idx_start < 0)
|
||||
goto error;
|
||||
|
||||
CHUNK chunk;
|
||||
chunk.fcc = FOURCC('i', 'd', 'x', '1');
|
||||
chunk.cb = sizeof(AVIINDEXENTRY) * m_total_frames;
|
||||
|
||||
int num; num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||
if (num != sizeof(chunk))
|
||||
goto error;
|
||||
|
||||
for (unsigned int i = 0; i < m_total_frames; i++)
|
||||
{
|
||||
AVIINDEXENTRY Index;
|
||||
Index.ckid = m_index_table[i].fcc;
|
||||
Index.dwFlags = AVIIF_KEYFRAME;
|
||||
Index.dwChunkOffset = m_index_table[i].Offset;
|
||||
Index.dwChunkLength = m_index_table[i].Length;
|
||||
|
||||
num = fwrite(&Index, 1, sizeof(Index), m_file);
|
||||
if (num != sizeof(Index))
|
||||
goto error;
|
||||
}
|
||||
|
||||
// update the header
|
||||
if (m_total_frames > 0 && m_msec_per_frame > 0)
|
||||
{
|
||||
num = fseek(m_file, 0, SEEK_END);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
int size; size = ftell(m_file);
|
||||
if (size < 0)
|
||||
goto error;
|
||||
|
||||
num = fseek(m_file, 0, SEEK_SET);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
m_avi_hdr.riff.cb = size - sizeof(m_avi_hdr.riff);
|
||||
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
|
||||
m_avi_hdr.avih.dwMaxBytesPerSec = (uint32_t)
|
||||
(((m_stream_bytes / m_total_frames) * m_format_hdr.strh.dwRate) /
|
||||
m_msec_per_frame + 0.5f);
|
||||
m_avi_hdr.avih.dwTotalFrames = m_total_frames;
|
||||
|
||||
num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||
if (num != sizeof(m_avi_hdr))
|
||||
goto error;
|
||||
|
||||
m_format_hdr.strh.dwScale = m_msec_per_frame;
|
||||
m_format_hdr.strh.dwLength = m_total_frames;
|
||||
|
||||
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||
if (num != sizeof(m_format_hdr))
|
||||
goto error;
|
||||
}
|
||||
|
||||
// update the movi section
|
||||
m_movi_chunk.cb = idx_start - m_movi_start;
|
||||
|
||||
num = fseek(m_file, m_movi_start - sizeof(m_movi_chunk), SEEK_SET);
|
||||
if (num < 0)
|
||||
goto error;
|
||||
|
||||
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||
if (num != sizeof(m_movi_chunk))
|
||||
goto error;
|
||||
|
||||
fclose(m_file);
|
||||
m_file = NULL;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(m_file);
|
||||
remove(m_filename.c_str());
|
||||
m_file = NULL;
|
||||
return false;
|
||||
} // closeFile
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AVIWriter::createFile(AVIFormat avi_format, int bits, int quality)
|
||||
{
|
||||
if (m_file != NULL)
|
||||
return false;
|
||||
|
||||
int width = irr_driver->getActualScreenSize().Width;
|
||||
int height = irr_driver->getActualScreenSize().Height;
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
tm* timeInfo = localtime(&rawtime);
|
||||
char time_buffer[256];
|
||||
sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
|
||||
timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
|
||||
timeInfo->tm_mday, timeInfo->tm_hour,
|
||||
timeInfo->tm_min, timeInfo->tm_sec);
|
||||
|
||||
std::string track_name = World::getWorld() != NULL ?
|
||||
race_manager->getTrackName() : "menu";
|
||||
|
||||
m_filename = file_manager->getScreenshotDir() + track_name + "-"
|
||||
+ time_buffer + ".avi";
|
||||
m_stream_bytes = 0;
|
||||
m_total_frames = 0;
|
||||
m_msec_per_frame = 42;
|
||||
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.biPlanes = 1;
|
||||
bitmap_hdr.biBitCount = bits;
|
||||
bitmap_hdr.biCompression = 0;
|
||||
bitmap_hdr.biSizeImage = (width * height * 3 * bitmap_hdr.biPlanes);
|
||||
bitmap_hdr.biXPelsPerMeter = 0;
|
||||
bitmap_hdr.biYPelsPerMeter = 0;
|
||||
bitmap_hdr.biClrUsed = 0;
|
||||
bitmap_hdr.biClrImportant = 0;
|
||||
|
||||
memset(&m_avi_hdr, '\0', sizeof(m_avi_hdr));
|
||||
m_avi_hdr.riff.fcc = FOURCC('R', 'I', 'F', 'F');
|
||||
m_avi_hdr.riff.cb = 0; // update when finished (size of the file - 8)
|
||||
m_avi_hdr.avi = FOURCC('A', 'V', 'I', ' ');
|
||||
m_avi_hdr.list1.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_avi_hdr.list1.cb = 0;
|
||||
m_avi_hdr.hdrl = FOURCC('h', 'd', 'r', 'l');
|
||||
m_avi_hdr.avihhdr.fcc = FOURCC('a', 'v', 'i', 'h');
|
||||
m_avi_hdr.avihhdr.cb = sizeof(m_avi_hdr.avih);
|
||||
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
|
||||
m_avi_hdr.avih.dwMaxBytesPerSec = 0; // update when finished
|
||||
m_avi_hdr.avih.dwPaddingGranularity = 0;
|
||||
m_avi_hdr.avih.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
|
||||
m_avi_hdr.avih.dwTotalFrames = 0; // update when finished
|
||||
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_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) +
|
||||
sizeof(BitmapInfoHeader);
|
||||
m_format_hdr.strl = FOURCC('s', 't', 'r', 'l');
|
||||
m_format_hdr.strhhdr.fcc = FOURCC('s', 't', 'r', 'h');
|
||||
m_format_hdr.strhhdr.cb = sizeof(m_format_hdr.strh);
|
||||
m_format_hdr.strh.fccType = FOURCC('v', 'i', 'd', 's');
|
||||
m_format_hdr.strh.fccHandler = CC_DIB;
|
||||
m_format_hdr.strh.dwFlags = 0;
|
||||
m_format_hdr.strh.wPriority = 0;
|
||||
m_format_hdr.strh.wLanguage = 0;
|
||||
m_format_hdr.strh.dwInitialFrames = 0;
|
||||
m_format_hdr.strh.dwScale = m_msec_per_frame;
|
||||
m_format_hdr.strh.dwRate = 1000;
|
||||
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.dwSampleSize = 0;
|
||||
m_format_hdr.strh.Left = 0;
|
||||
m_format_hdr.strh.Top = 0;
|
||||
m_format_hdr.strh.Right = m_avi_hdr.avih.dwWidth;
|
||||
m_format_hdr.strh.Bottom = m_avi_hdr.avih.dwHeight;
|
||||
m_format_hdr.strfhdr.fcc = FOURCC('s', 't', 'r', 'f');
|
||||
m_format_hdr.strfhdr.cb = sizeof(BitmapInfoHeader);
|
||||
|
||||
//Format specific changes
|
||||
if (avi_format == AVI_FORMAT_JPG)
|
||||
{
|
||||
m_format_hdr.strh.fccHandler = CC_MJPG;
|
||||
bitmap_hdr.biCompression = FOURCC('M', 'J', 'P', 'G');
|
||||
m_chunk_fcc = FOURCC('0', '0', 'd', 'c');
|
||||
}
|
||||
else if (avi_format == AVI_FORMAT_BMP)
|
||||
{
|
||||
bitmap_hdr.biHeight = -height;
|
||||
bitmap_hdr.biCompression = 0;
|
||||
m_chunk_fcc = FOURCC('0', '0', 'd', 'b');
|
||||
}
|
||||
|
||||
const uint32_t fcc_movi = FOURCC('m', 'o', 'v', 'i');
|
||||
|
||||
m_file = fopen(m_filename.c_str(), "wb");
|
||||
if (m_file == NULL)
|
||||
return false;
|
||||
|
||||
int num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||
if (num != sizeof(m_avi_hdr))
|
||||
goto error;
|
||||
|
||||
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||
if (num != sizeof(m_format_hdr))
|
||||
goto error;
|
||||
|
||||
num = fwrite(&bitmap_hdr, 1, sizeof(BitmapInfoHeader), m_file);
|
||||
if (num != sizeof(BitmapInfoHeader))
|
||||
goto error;
|
||||
|
||||
m_end_of_header = ftell(m_file);
|
||||
if (m_end_of_header < 0)
|
||||
goto error;
|
||||
|
||||
if (!addJUNKChunk("", 2840))
|
||||
goto error;
|
||||
|
||||
m_avi_hdr.list1.cb = m_end_of_header - sizeof(m_avi_hdr.riff) -
|
||||
sizeof(m_avi_hdr.avi) - sizeof(m_avi_hdr.list1);
|
||||
m_movi_chunk.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||
m_movi_chunk.cb = 0; // update when finished
|
||||
|
||||
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||
if (num != sizeof(m_movi_chunk))
|
||||
goto error;
|
||||
|
||||
m_movi_start = ftell(m_file);
|
||||
if (m_movi_start < 0)
|
||||
goto error;
|
||||
|
||||
num = fwrite(&fcc_movi, 1, sizeof(fcc_movi), m_file);
|
||||
if (num != sizeof(fcc_movi))
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
closeFile(true);
|
||||
return false;
|
||||
} // createFile
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
cinfo.image_width = width;
|
||||
cinfo.image_height = height;
|
||||
cinfo.input_components = num_channels;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(&cinfo);
|
||||
jpeg_set_quality(&cinfo, quality, true);
|
||||
|
||||
jpeg_mem_dest(&cinfo, &image_output, &buf_length);
|
||||
|
||||
jpeg_start_compress(&cinfo, true);
|
||||
|
||||
JSAMPROW jrow[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height)
|
||||
{
|
||||
jrow[0] = &image_data[cinfo.next_scanline * width * num_channels];
|
||||
jpeg_write_scanlines(&cinfo, jrow, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
return buf_length;
|
||||
} // bmpToJpg
|
||||
|
||||
#endif
|
||||
|
@ -16,9 +16,17 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#ifndef SERVER_ONLY
|
||||
|
||||
#include "graphics/gl_headers.hpp"
|
||||
#include "utils/can_be_deleted.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/singleton.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <jpeglib.h>
|
||||
|
||||
#define FOURCC(a,b,c,d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
|
||||
@ -45,17 +53,17 @@ const uint32_t AVIIF_MIDPART = 0x00000060;
|
||||
const uint32_t AVIIF_NOTIME = 0x00000100;
|
||||
const uint32_t AVIIF_COMPUSE = 0x0FFF0000;
|
||||
|
||||
enum AVIFormat
|
||||
enum AVIFormat
|
||||
{
|
||||
AVI_FORMAT_BMP,
|
||||
AVI_FORMAT_JPG
|
||||
AVI_FORMAT_BMP,
|
||||
AVI_FORMAT_JPG
|
||||
};
|
||||
|
||||
enum AVIErrCode
|
||||
{
|
||||
AVI_SUCCESS,
|
||||
AVI_SIZE_LIMIT_ERR,
|
||||
AVI_IO_ERR
|
||||
AVI_SUCCESS,
|
||||
AVI_SIZE_LIMIT_ERR,
|
||||
AVI_IO_ERR
|
||||
};
|
||||
|
||||
const int MAX_FRAMES = 1000000;
|
||||
@ -153,38 +161,71 @@ struct IndexTable
|
||||
};
|
||||
|
||||
|
||||
class AVIWriter
|
||||
class AVIWriter : public CanBeDeleted, public NoCopy,
|
||||
public Singleton<AVIWriter>
|
||||
{
|
||||
private:
|
||||
FILE* m_file;
|
||||
|
||||
std::string m_filename;
|
||||
int m_last_junk_chunk;
|
||||
int m_end_of_header;
|
||||
int m_movi_start;
|
||||
unsigned int m_msec_per_frame;
|
||||
unsigned int m_stream_bytes;
|
||||
unsigned int m_total_frames;
|
||||
|
||||
int m_last_junk_chunk, m_end_of_header, m_movi_start;
|
||||
|
||||
unsigned int m_msec_per_frame, m_stream_bytes, m_total_frames, m_pbo_use;
|
||||
|
||||
float m_dt;
|
||||
|
||||
AVIHeader m_avi_hdr;
|
||||
|
||||
CHUNK m_movi_chunk;
|
||||
|
||||
FormatHeader m_format_hdr;
|
||||
|
||||
IndexTable m_index_table[MAX_FRAMES];
|
||||
|
||||
uint32_t m_chunk_fcc;
|
||||
|
||||
std::list<uint8_t*> m_fbi_queue;
|
||||
|
||||
pthread_t m_thread;
|
||||
|
||||
pthread_mutex_t m_fbi_mutex;
|
||||
|
||||
pthread_cond_t m_cond_request;
|
||||
|
||||
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);
|
||||
// ------------------------------------------------------------------------
|
||||
AVIErrCode addImage(unsigned char* buffer, int size);
|
||||
// ------------------------------------------------------------------------
|
||||
bool closeFile(bool delete_file = false);
|
||||
// ------------------------------------------------------------------------
|
||||
bool createFile(AVIFormat avi_format, int bits, int quality);
|
||||
// ------------------------------------------------------------------------
|
||||
bool addJUNKChunk(std::string str, unsigned int min_size);
|
||||
// ------------------------------------------------------------------------
|
||||
void addFrameBufferImage(uint8_t* fbi)
|
||||
{
|
||||
pthread_mutex_lock(&m_fbi_mutex);
|
||||
m_fbi_queue.push_back(fbi);
|
||||
pthread_cond_signal(&m_cond_request);
|
||||
pthread_mutex_unlock(&m_fbi_mutex);
|
||||
}
|
||||
|
||||
public:
|
||||
AVIWriter() {m_file = NULL;}
|
||||
// ------------------------------------------------------------------------
|
||||
AVIWriter();
|
||||
// ------------------------------------------------------------------------
|
||||
static void* startRoutine(void *obj);
|
||||
// ------------------------------------------------------------------------
|
||||
void captureFrameBufferImage(float dt);
|
||||
// ------------------------------------------------------------------------
|
||||
void stopRecording();
|
||||
|
||||
AVIErrCode addImage(unsigned char* buffer, int size);
|
||||
bool closeFile(bool delete_file = false);
|
||||
bool createFile(std::string filename, AVIFormat avi_format, int width,
|
||||
int height, unsigned int msec_per_frame, int bits,
|
||||
int quality);
|
||||
|
||||
void updateMsecPerFrame(unsigned int value)
|
||||
{m_msec_per_frame = (m_msec_per_frame + value) / 2;}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -134,6 +134,8 @@ enum DebugMenuCommand
|
||||
DEBUG_SCRIPT_CONSOLE,
|
||||
DEBUG_RUN_CUTSCENE,
|
||||
DEBUG_TEXTURE_CONSOLE,
|
||||
DEBUG_START_RECORDING,
|
||||
DEBUG_STOP_RECORDING
|
||||
}; // DebugMenuCommand
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -711,6 +713,12 @@ bool handleContextMenuAction(s32 cmd_id)
|
||||
return false;
|
||||
});
|
||||
break;
|
||||
case DEBUG_START_RECORDING:
|
||||
irr_driver->setRecording(true);
|
||||
break;
|
||||
case DEBUG_STOP_RECORDING:
|
||||
irr_driver->setRecording(false);
|
||||
break;
|
||||
} // switch
|
||||
return false;
|
||||
}
|
||||
@ -793,8 +801,13 @@ bool onEvent(const SEvent &event)
|
||||
sub->addItem(L"Toggle smooth camera", DEBUG_GUI_CAM_SMOOTH);
|
||||
sub->addItem(L"Attach fps camera to kart", DEBUG_GUI_CAM_ATTACH);
|
||||
|
||||
mnu->addItem(L"Change camera target >",-1,true, true);
|
||||
mnu->addItem(L"Recording >",-1,true, true);
|
||||
sub = mnu->getSubMenu(4);
|
||||
sub->addItem(L"Start recording", DEBUG_START_RECORDING);
|
||||
sub->addItem(L"Stop recording", DEBUG_STOP_RECORDING);
|
||||
|
||||
mnu->addItem(L"Change camera target >",-1,true, true);
|
||||
sub = mnu->getSubMenu(5);
|
||||
sub->addItem(L"To kart one", DEBUG_VIEW_KART_ONE);
|
||||
sub->addItem(L"To kart two", DEBUG_VIEW_KART_TWO);
|
||||
sub->addItem(L"To kart three", DEBUG_VIEW_KART_THREE);
|
||||
@ -805,7 +818,7 @@ bool onEvent(const SEvent &event)
|
||||
sub->addItem(L"To kart eight", DEBUG_VIEW_KART_EIGHT);
|
||||
|
||||
mnu->addItem(L"Font >",-1,true, true);
|
||||
sub = mnu->getSubMenu(5);
|
||||
sub = mnu->getSubMenu(6);
|
||||
sub->addItem(L"Dump glyph pages of fonts", DEBUG_FONT_DUMP_GLYPH_PAGE);
|
||||
sub->addItem(L"Reload all fonts", DEBUG_FONT_RELOAD);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user