Use high resolution timer for AV sync
This commit is contained in:
parent
be103dd666
commit
df758669d3
@ -1895,7 +1895,7 @@ void IrrDriver::update(float dt)
|
|||||||
// printRenderStats();
|
// printRenderStats();
|
||||||
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
if (m_recording)
|
if (m_recording)
|
||||||
AVIWriter::getInstance()->captureFrameBufferImage(dt);
|
AVIWriter::getInstance()->captureFrameBufferImage();
|
||||||
#endif
|
#endif
|
||||||
} // update
|
} // update
|
||||||
|
|
||||||
|
@ -78,7 +78,6 @@ void AVIWriter::resetFrameBufferImage()
|
|||||||
{
|
{
|
||||||
m_pbo_use = 0;
|
m_pbo_use = 0;
|
||||||
m_accumulated_time = 0.0f;
|
m_accumulated_time = 0.0f;
|
||||||
m_remaining_time = 0.0f;
|
|
||||||
} // resetFrameBufferImage
|
} // resetFrameBufferImage
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -166,13 +165,13 @@ void writeFrameHeader(FILE *out, int64_t pts, size_t frame_size)
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
int vpxEncodeFrame(vpx_codec_ctx_t *codec, vpx_image_t *img, int frame_index,
|
int vpxEncodeFrame(vpx_codec_ctx_t *codec, vpx_image_t *img, int frame_index,
|
||||||
int frame_count, FILE *out)
|
FILE *out)
|
||||||
{
|
{
|
||||||
int got_pkts = 0;
|
int got_pkts = 0;
|
||||||
vpx_codec_iter_t iter = NULL;
|
vpx_codec_iter_t iter = NULL;
|
||||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||||
const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index,
|
const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index, 1, 0,
|
||||||
frame_count, 0, VPX_DL_REALTIME);
|
VPX_DL_REALTIME);
|
||||||
if (res != VPX_CODEC_OK)
|
if (res != VPX_CODEC_OK)
|
||||||
{
|
{
|
||||||
Log::error("vpxEncoder", "Failed to encode frame");
|
Log::error("vpxEncoder", "Failed to encode frame");
|
||||||
@ -211,7 +210,7 @@ struct EncoderInfo
|
|||||||
void* AVIWriter::vpxEncoder(void *obj)
|
void* AVIWriter::vpxEncoder(void *obj)
|
||||||
{
|
{
|
||||||
VS::setThreadName("vpxEncoder");
|
VS::setThreadName("vpxEncoder");
|
||||||
FILE* vpx_data = fopen((m_recording_target.getAtomic() + "_video.ivf")
|
FILE* vpx_data = fopen((m_recording_target.getAtomic() + ".ivf")
|
||||||
.c_str(), "wb");
|
.c_str(), "wb");
|
||||||
if (vpx_data == NULL)
|
if (vpx_data == NULL)
|
||||||
{
|
{
|
||||||
@ -284,12 +283,15 @@ void* AVIWriter::vpxEncoder(void *obj)
|
|||||||
free(jpg);
|
free(jpg);
|
||||||
vpx_image_t each_frame;
|
vpx_image_t each_frame;
|
||||||
vpx_img_wrap(&each_frame, VPX_IMG_FMT_I420, width, height, 1, yuv);
|
vpx_img_wrap(&each_frame, VPX_IMG_FMT_I420, width, height, 1, yuv);
|
||||||
vpxEncodeFrame(&codec, &each_frame, frames_encoded++, frame_count,
|
while (frame_count != 0)
|
||||||
vpx_data);
|
{
|
||||||
|
vpxEncodeFrame(&codec, &each_frame, frames_encoded++, vpx_data);
|
||||||
|
frame_count--;
|
||||||
|
}
|
||||||
delete [] yuv;
|
delete [] yuv;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (vpxEncodeFrame(&codec, NULL, -1, 1, vpx_data));
|
while (vpxEncodeFrame(&codec, NULL, -1, vpx_data));
|
||||||
if (vpx_codec_destroy(&codec))
|
if (vpx_codec_destroy(&codec))
|
||||||
{
|
{
|
||||||
Log::error("vpxEncoder", "Failed to destroy codec.");
|
Log::error("vpxEncoder", "Failed to destroy codec.");
|
||||||
@ -301,20 +303,22 @@ void* AVIWriter::vpxEncoder(void *obj)
|
|||||||
return NULL;
|
return NULL;
|
||||||
} // vpxEncoder
|
} // vpxEncoder
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
Synchronised<std::list<std::tuple<uint8_t*, unsigned, int> > > jpg_data;
|
||||||
|
pthread_cond_t vpx_enc_request;
|
||||||
|
pthread_t audio_thread, vpx_enc_thread;
|
||||||
|
EncoderInfo vpx_ei =
|
||||||
|
{
|
||||||
|
.m_data = &jpg_data,
|
||||||
|
.m_enc_request = &vpx_enc_request
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
void* AVIWriter::videoRecord(void *obj)
|
void* AVIWriter::videoRecord(void *obj)
|
||||||
{
|
{
|
||||||
VS::setThreadName("videoRecord");
|
VS::setThreadName("videoRecord");
|
||||||
AVIWriter* avi_writer = (AVIWriter*)obj;
|
AVIWriter* avi_writer = (AVIWriter*)obj;
|
||||||
|
|
||||||
Synchronised<std::list<std::tuple<uint8_t*, unsigned, int> > > jpg_data;
|
|
||||||
pthread_cond_t enc_request;
|
|
||||||
pthread_cond_init(&enc_request, NULL);
|
|
||||||
pthread_t audio_thread, vpx_enc_thread;
|
|
||||||
EncoderInfo ei;
|
|
||||||
ei.m_data = &jpg_data;
|
|
||||||
ei.m_enc_request = &enc_request;
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
avi_writer->m_fbi_queue.lock();
|
avi_writer->m_fbi_queue.lock();
|
||||||
@ -325,13 +329,6 @@ void* AVIWriter::videoRecord(void *obj)
|
|||||||
avi_writer->m_fbi_queue.getMutex());
|
avi_writer->m_fbi_queue.getMutex());
|
||||||
waiting = avi_writer->m_fbi_queue.getData().empty();
|
waiting = avi_writer->m_fbi_queue.getData().empty();
|
||||||
}
|
}
|
||||||
if (avi_writer->m_idle.getAtomic())
|
|
||||||
{
|
|
||||||
avi_writer->m_idle.setAtomic(false);
|
|
||||||
pthread_create(&audio_thread, NULL, &audioRecord,
|
|
||||||
&avi_writer->m_idle);
|
|
||||||
pthread_create(&vpx_enc_thread, NULL, &vpxEncoder, &ei);
|
|
||||||
}
|
|
||||||
auto& p = avi_writer->m_fbi_queue.getData().front();
|
auto& p = avi_writer->m_fbi_queue.getData().front();
|
||||||
uint8_t* fbi = p.first;
|
uint8_t* fbi = p.first;
|
||||||
int frame_count = p.second;
|
int frame_count = p.second;
|
||||||
@ -340,7 +337,7 @@ void* AVIWriter::videoRecord(void *obj)
|
|||||||
avi_writer->m_idle.setAtomic(true);
|
avi_writer->m_idle.setAtomic(true);
|
||||||
jpg_data.lock();
|
jpg_data.lock();
|
||||||
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
||||||
pthread_cond_signal(&enc_request);
|
pthread_cond_signal(vpx_ei.m_enc_request);
|
||||||
jpg_data.unlock();
|
jpg_data.unlock();
|
||||||
pthread_join(audio_thread, NULL);
|
pthread_join(audio_thread, NULL);
|
||||||
pthread_join(vpx_enc_thread, NULL);
|
pthread_join(vpx_enc_thread, NULL);
|
||||||
@ -353,7 +350,7 @@ void* AVIWriter::videoRecord(void *obj)
|
|||||||
avi_writer->m_idle.setAtomic(true);
|
avi_writer->m_idle.setAtomic(true);
|
||||||
jpg_data.lock();
|
jpg_data.lock();
|
||||||
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
||||||
pthread_cond_signal(&enc_request);
|
pthread_cond_signal(vpx_ei.m_enc_request);
|
||||||
jpg_data.unlock();
|
jpg_data.unlock();
|
||||||
pthread_join(audio_thread, NULL);
|
pthread_join(audio_thread, NULL);
|
||||||
pthread_join(vpx_enc_thread, NULL);
|
pthread_join(vpx_enc_thread, NULL);
|
||||||
@ -418,7 +415,7 @@ void* AVIWriter::videoRecord(void *obj)
|
|||||||
jpg = shrinken;
|
jpg = shrinken;
|
||||||
jpg_data.lock();
|
jpg_data.lock();
|
||||||
jpg_data.getData().emplace_back(jpg, size, frame_count);
|
jpg_data.getData().emplace_back(jpg, size, frame_count);
|
||||||
pthread_cond_signal(&enc_request);
|
pthread_cond_signal(vpx_ei.m_enc_request);
|
||||||
jpg_data.unlock();
|
jpg_data.unlock();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -428,7 +425,7 @@ void* AVIWriter::videoRecord(void *obj)
|
|||||||
void* AVIWriter::oggEncoder(void *obj)
|
void* AVIWriter::oggEncoder(void *obj)
|
||||||
{
|
{
|
||||||
VS::setThreadName("oggEncoder");
|
VS::setThreadName("oggEncoder");
|
||||||
FILE* ogg_data = fopen((m_recording_target.getAtomic() + "_audio.ogg")
|
FILE* ogg_data = fopen((m_recording_target.getAtomic() + ".ogg")
|
||||||
.c_str(), "wb");
|
.c_str(), "wb");
|
||||||
if (ogg_data == NULL)
|
if (ogg_data == NULL)
|
||||||
{
|
{
|
||||||
@ -666,35 +663,43 @@ void* AVIWriter::audioRecord(void *obj)
|
|||||||
} // audioRecord
|
} // audioRecord
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
int AVIWriter::getFrameCount(float dt)
|
int AVIWriter::getFrameCount(double rate)
|
||||||
{
|
{
|
||||||
const float frame_rate = 0.001f * m_msec_per_frame;
|
const double frame_rate = 1. / double(UserConfigParams::m_record_fps);
|
||||||
m_accumulated_time += dt;
|
m_accumulated_time += rate;
|
||||||
if (m_accumulated_time < frame_rate && m_remaining_time < frame_rate)
|
if (m_accumulated_time < frame_rate)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int frame_count = 1;
|
int frame_count = 0;
|
||||||
m_remaining_time += m_accumulated_time - frame_rate;
|
while (m_accumulated_time >= frame_rate)
|
||||||
m_accumulated_time = 0.0f;
|
|
||||||
while (m_remaining_time > frame_rate)
|
|
||||||
{
|
{
|
||||||
frame_count++;
|
frame_count++;
|
||||||
m_remaining_time -= frame_rate;
|
m_accumulated_time = m_accumulated_time - frame_rate;
|
||||||
}
|
}
|
||||||
return frame_count;
|
return frame_count;
|
||||||
} // getFrameCount
|
} // getFrameCount
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
void AVIWriter::captureFrameBufferImage(float dt)
|
void AVIWriter::captureFrameBufferImage()
|
||||||
{
|
{
|
||||||
|
if (m_idle.getAtomic())
|
||||||
|
{
|
||||||
|
m_idle.setAtomic(false);
|
||||||
|
pthread_create(&audio_thread, NULL, &audioRecord, &m_idle);
|
||||||
|
pthread_cond_init(vpx_ei.m_enc_request, NULL);
|
||||||
|
pthread_create(&vpx_enc_thread, NULL, &vpxEncoder, &vpx_ei);
|
||||||
|
}
|
||||||
|
auto rate = std::chrono::high_resolution_clock::now() - m_framerate_timer;
|
||||||
|
m_framerate_timer = std::chrono::high_resolution_clock::now();
|
||||||
glReadBuffer(GL_BACK);
|
glReadBuffer(GL_BACK);
|
||||||
int pbo_read = -1;
|
int pbo_read = -1;
|
||||||
if (m_pbo_use > 3 && m_pbo_use % 3 == 0)
|
if (m_pbo_use > 3 && m_pbo_use % 3 == 0)
|
||||||
m_pbo_use = 3;
|
m_pbo_use = 3;
|
||||||
if (m_pbo_use >= 3)
|
if (m_pbo_use >= 3)
|
||||||
{
|
{
|
||||||
int frame_count = getFrameCount(dt);
|
int frame_count = getFrameCount(std::chrono::duration_cast
|
||||||
|
<std::chrono::duration<double> >(rate).count());
|
||||||
if (frame_count != 0)
|
if (frame_count != 0)
|
||||||
{
|
{
|
||||||
pbo_read = m_pbo_use % 3;
|
pbo_read = m_pbo_use % 3;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "utils/singleton.hpp"
|
#include "utils/singleton.hpp"
|
||||||
#include "utils/synchronised.hpp"
|
#include "utils/synchronised.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
@ -176,7 +177,7 @@ private:
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
float m_accumulated_time, m_remaining_time;
|
double m_accumulated_time;
|
||||||
|
|
||||||
AVIFormat m_avi_format;
|
AVIFormat m_avi_format;
|
||||||
|
|
||||||
@ -200,6 +201,8 @@ private:
|
|||||||
|
|
||||||
GLuint m_pbo[3];
|
GLuint m_pbo[3];
|
||||||
|
|
||||||
|
std::chrono::high_resolution_clock::time_point m_framerate_timer;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
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 long buf_length);
|
||||||
@ -220,7 +223,7 @@ private:
|
|||||||
m_fbi_queue.unlock();
|
m_fbi_queue.unlock();
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
int getFrameCount(float dt);
|
int getFrameCount(double rate);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void cleanAllFrameBufferImages()
|
void cleanAllFrameBufferImages()
|
||||||
{
|
{
|
||||||
@ -252,7 +255,7 @@ public:
|
|||||||
m_recording_target.setAtomic(name);
|
m_recording_target.setAtomic(name);
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void captureFrameBufferImage(float dt);
|
void captureFrameBufferImage();
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void resetFrameBufferImage();
|
void resetFrameBufferImage();
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user