Use high resolution timer for AV sync

This commit is contained in:
Benau 2017-03-28 00:44:12 +08:00
parent be103dd666
commit df758669d3
3 changed files with 51 additions and 43 deletions

View File

@ -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

View File

@ -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;

View File

@ -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();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------