Implement real time vp8 encoding
This commit is contained in:
parent
a68c085e95
commit
be103dd666
@ -119,6 +119,7 @@ if (APPLE)
|
||||
set(JPEG_LIBRARY jpeglib)
|
||||
else()
|
||||
find_package(JPEG REQUIRED)
|
||||
find_library(TURBOJPEG_LIBRARY NAMES turbojpeg)
|
||||
include_directories(${JPEG_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
@ -126,6 +127,9 @@ include(FindPkgConfig)
|
||||
pkg_check_modules(PULSEAUDIO libpulse)
|
||||
include_directories(${PULSEAUDIO_INCLUDEDIR})
|
||||
|
||||
pkg_check_modules(VPX vpx)
|
||||
include_directories(${VPX_INCLUDEDIR})
|
||||
|
||||
if(NOT SERVER_ONLY AND NOT USE_GLES2)
|
||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
|
||||
include_directories("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
|
||||
@ -385,6 +389,8 @@ target_link_libraries(supertuxkart
|
||||
${FREETYPE_LIBRARIES}
|
||||
${JPEG_LIBRARIES}
|
||||
${PULSEAUDIO_LIBRARIES}
|
||||
${TURBOJPEG_LIBRARY}
|
||||
${VPX_LIBRARIES}
|
||||
)
|
||||
|
||||
if(NOT SERVER_ONLY)
|
||||
|
@ -25,9 +25,12 @@
|
||||
#include "utils/translation.hpp"
|
||||
#include "utils/vs.hpp"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <ogg/ogg.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <turbojpeg.h>
|
||||
#include <vorbis/vorbisenc.h>
|
||||
#include <vpx/vpx_encoder.h>
|
||||
#include <vpx/vp8cx.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <cstring>
|
||||
@ -56,7 +59,7 @@ AVIWriter::AVIWriter() : m_idle(true)
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
pthread_cond_init(&m_cond_request, NULL);
|
||||
pthread_create(&m_video_thread, NULL, &videoRecord, this);
|
||||
pthread_create(&m_record_thread, NULL, &videoRecord, this);
|
||||
} // AVIWriter
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -66,7 +69,7 @@ AVIWriter::~AVIWriter()
|
||||
addFrameBufferImage(NULL, 0);
|
||||
if (!waitForReadyToDeleted(2.0f))
|
||||
Log::info("AVIWriter", "AVIWriter not stopping, exiting anyway.");
|
||||
pthread_join(m_video_thread, NULL);
|
||||
pthread_join(m_record_thread, NULL);
|
||||
pthread_cond_destroy(&m_cond_request);
|
||||
} // ~AVIWriter
|
||||
|
||||
@ -78,6 +81,116 @@ void AVIWriter::resetFrameBufferImage()
|
||||
m_remaining_time = 0.0f;
|
||||
} // resetFrameBufferImage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
int jpgToYuv(uint8_t* jpeg_buffer, unsigned jpeg_size, uint8_t** yuv_buffer,
|
||||
TJSAMP* yuv_type, unsigned* yuv_size)
|
||||
{
|
||||
tjhandle handle = NULL;
|
||||
int width, height;
|
||||
TJSAMP subsample;
|
||||
TJCS colorspace;
|
||||
int padding = 1;
|
||||
int ret = 0;
|
||||
handle = tjInitDecompress();
|
||||
ret = tjDecompressHeader3(handle, jpeg_buffer, jpeg_size, &width, &height,
|
||||
(int*)&subsample, (int*)&colorspace);
|
||||
if (ret != 0)
|
||||
{
|
||||
char* err = tjGetErrorStr();
|
||||
Log::error("vpxEncoder", "Jpeg decode error: %s.", err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*yuv_type = subsample;
|
||||
*yuv_size = tjBufSizeYUV2(width, padding, height, subsample);
|
||||
*yuv_buffer = new uint8_t[*yuv_size];
|
||||
ret = tjDecompressToYUV2(handle, jpeg_buffer, jpeg_size, *yuv_buffer,
|
||||
width, padding, height, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
char* err = tjGetErrorStr();
|
||||
Log::error("vpxEncoder", "YUV conversion error: %s.", err);
|
||||
return ret;
|
||||
}
|
||||
tjDestroy(handle);
|
||||
|
||||
return ret;
|
||||
} // jpgToYuv
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void writeLE16(char *const mem, const unsigned int val)
|
||||
{
|
||||
mem[0] = val;
|
||||
mem[1] = val >> 8;
|
||||
} // writeLE16
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void writeLE32(char *const mem, const unsigned int val)
|
||||
{
|
||||
mem[0] = val;
|
||||
mem[1] = val >> 8;
|
||||
mem[2] = val >> 16;
|
||||
mem[3] = val >> 24;
|
||||
} // writeLE32
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void writeFileHeader(FILE *out, const struct vpx_codec_enc_cfg *cfg,
|
||||
unsigned int fourcc, int frame_cnt)
|
||||
{
|
||||
char header[32];
|
||||
header[0] = 'D';
|
||||
header[1] = 'K';
|
||||
header[2] = 'I';
|
||||
header[3] = 'F';
|
||||
writeLE16(header + 4, 0); // version
|
||||
writeLE16(header + 6, 32); // header size
|
||||
writeLE32(header + 8, fourcc); // fourcc
|
||||
writeLE16(header + 12, cfg->g_w); // width
|
||||
writeLE16(header + 14, cfg->g_h); // height
|
||||
writeLE32(header + 16, cfg->g_timebase.den); // rate
|
||||
writeLE32(header + 20, cfg->g_timebase.num); // scale
|
||||
writeLE32(header + 24, frame_cnt); // length
|
||||
writeLE32(header + 28, 0); // unused
|
||||
fwrite(header, 1, 32, out);
|
||||
} // writeFileHeader
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void writeFrameHeader(FILE *out, int64_t pts, size_t frame_size)
|
||||
{
|
||||
char header[12];
|
||||
writeLE32(header, (int)frame_size);
|
||||
writeLE32(header + 4, (int)(pts & 0xFFFFFFFF));
|
||||
writeLE32(header + 8, (int)(pts >> 32));
|
||||
fwrite(header, 1, 12, out);
|
||||
} // writeFrameHeader
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
int vpxEncodeFrame(vpx_codec_ctx_t *codec, vpx_image_t *img, int frame_index,
|
||||
int frame_count, FILE *out)
|
||||
{
|
||||
int got_pkts = 0;
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
const vpx_codec_err_t res = vpx_codec_encode(codec, img, frame_index,
|
||||
frame_count, 0, VPX_DL_REALTIME);
|
||||
if (res != VPX_CODEC_OK)
|
||||
{
|
||||
Log::error("vpxEncoder", "Failed to encode frame");
|
||||
return -1;
|
||||
}
|
||||
while ((pkt = vpx_codec_get_cx_data(codec, &iter)) != NULL)
|
||||
{
|
||||
got_pkts = 1;
|
||||
if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
|
||||
{
|
||||
writeFrameHeader(out, pkt->data.frame.pts,
|
||||
pkt->data.frame.sz);
|
||||
fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, out);
|
||||
}
|
||||
}
|
||||
return got_pkts;
|
||||
} // vpxEncodeFrame
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AVIWriter::resetCaptureFormat()
|
||||
{
|
||||
@ -87,11 +200,121 @@ void AVIWriter::resetCaptureFormat()
|
||||
UserConfigParams::m_record_bmp ? AVI_FORMAT_BMP : AVI_FORMAT_JPG;
|
||||
} // resetCaptureFormat
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
struct EncoderInfo
|
||||
{
|
||||
void* m_data;
|
||||
pthread_cond_t* m_enc_request;
|
||||
}; // EncoderInfo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* AVIWriter::vpxEncoder(void *obj)
|
||||
{
|
||||
VS::setThreadName("vpxEncoder");
|
||||
FILE* vpx_data = fopen((m_recording_target.getAtomic() + "_video.ivf")
|
||||
.c_str(), "wb");
|
||||
if (vpx_data == NULL)
|
||||
{
|
||||
Log::error("oggEncoder", "Failed to encode ogg file");
|
||||
return NULL;
|
||||
}
|
||||
EncoderInfo* ei = (EncoderInfo*)obj;
|
||||
Synchronised<std::list<std::tuple<uint8_t*, unsigned, int> > >* jpg_data =
|
||||
(Synchronised<std::list<std::tuple<uint8_t*, unsigned, int> > >*)
|
||||
ei->m_data;
|
||||
pthread_cond_t* cond_request = ei->m_enc_request;
|
||||
|
||||
vpx_codec_ctx_t codec;
|
||||
vpx_codec_enc_cfg_t cfg;
|
||||
vpx_codec_err_t res = vpx_codec_enc_config_default(vpx_codec_vp8_cx(),
|
||||
&cfg, 0);
|
||||
if (res > 0)
|
||||
{
|
||||
Log::error("vpxEncoder", "Failed to get default codec config.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const unsigned width = irr_driver->getActualScreenSize().Width;
|
||||
const unsigned height = irr_driver->getActualScreenSize().Height;
|
||||
int frames_encoded = 0;
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
cfg.g_timebase.num = 1;
|
||||
cfg.g_timebase.den = UserConfigParams::m_record_fps;
|
||||
writeFileHeader(vpx_data, &cfg, 0x30385056, frames_encoded);
|
||||
if (vpx_codec_enc_init(&codec, vpx_codec_vp8_cx(), &cfg, 0) > 0)
|
||||
{
|
||||
Log::error("vpxEncoder", "Failed to initialize encoder");
|
||||
fclose(vpx_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
jpg_data->lock();
|
||||
bool waiting = jpg_data->getData().empty();
|
||||
while (waiting)
|
||||
{
|
||||
pthread_cond_wait(cond_request, jpg_data->getMutex());
|
||||
waiting = jpg_data->getData().empty();
|
||||
}
|
||||
auto& p = jpg_data->getData().front();
|
||||
uint8_t* jpg = std::get<0>(p);
|
||||
unsigned jpg_size = std::get<1>(p);
|
||||
int frame_count = std::get<2>(p);
|
||||
if (jpg == NULL)
|
||||
{
|
||||
jpg_data->getData().clear();
|
||||
jpg_data->unlock();
|
||||
break;
|
||||
}
|
||||
jpg_data->getData().pop_front();
|
||||
jpg_data->unlock();
|
||||
uint8_t* yuv = NULL;
|
||||
TJSAMP yuv_type;
|
||||
unsigned yuv_size;
|
||||
int ret = jpgToYuv(jpg, jpg_size, &yuv, &yuv_type, &yuv_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
delete [] yuv;
|
||||
free(jpg);
|
||||
continue;
|
||||
}
|
||||
assert(yuv_type == TJSAMP_420 && yuv_size != 0);
|
||||
free(jpg);
|
||||
vpx_image_t each_frame;
|
||||
vpx_img_wrap(&each_frame, VPX_IMG_FMT_I420, width, height, 1, yuv);
|
||||
vpxEncodeFrame(&codec, &each_frame, frames_encoded++, frame_count,
|
||||
vpx_data);
|
||||
delete [] yuv;
|
||||
}
|
||||
|
||||
while (vpxEncodeFrame(&codec, NULL, -1, 1, vpx_data));
|
||||
if (vpx_codec_destroy(&codec))
|
||||
{
|
||||
Log::error("vpxEncoder", "Failed to destroy codec.");
|
||||
return NULL;
|
||||
}
|
||||
rewind(vpx_data);
|
||||
writeFileHeader(vpx_data, &cfg, 0x30385056, frames_encoded);
|
||||
fclose(vpx_data);
|
||||
return NULL;
|
||||
} // vpxEncoder
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* AVIWriter::videoRecord(void *obj)
|
||||
{
|
||||
VS::setThreadName("videoRecord");
|
||||
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)
|
||||
{
|
||||
avi_writer->m_fbi_queue.lock();
|
||||
@ -102,23 +325,40 @@ void* AVIWriter::videoRecord(void *obj)
|
||||
avi_writer->m_fbi_queue.getMutex());
|
||||
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();
|
||||
uint8_t* fbi = p.first;
|
||||
int frame_count = p.second;
|
||||
if (frame_count == -1)
|
||||
{
|
||||
avi_writer->closeFile();
|
||||
avi_writer->m_idle.setAtomic(true);
|
||||
pthread_join(avi_writer->m_audio_thread, NULL);
|
||||
avi_writer->m_fbi_queue.getData().pop_front();
|
||||
jpg_data.lock();
|
||||
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
||||
pthread_cond_signal(&enc_request);
|
||||
jpg_data.unlock();
|
||||
pthread_join(audio_thread, NULL);
|
||||
pthread_join(vpx_enc_thread, NULL);
|
||||
avi_writer->m_fbi_queue.getData().clear();
|
||||
avi_writer->m_fbi_queue.unlock();
|
||||
continue;
|
||||
}
|
||||
else if (fbi == NULL)
|
||||
{
|
||||
avi_writer->closeFile(false/*delete_file*/, true/*exiting*/);
|
||||
avi_writer->m_idle.setAtomic(true);
|
||||
jpg_data.lock();
|
||||
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
|
||||
pthread_cond_signal(&enc_request);
|
||||
jpg_data.unlock();
|
||||
pthread_join(audio_thread, NULL);
|
||||
pthread_join(vpx_enc_thread, NULL);
|
||||
avi_writer->setCanBeDeleted();
|
||||
avi_writer->m_fbi_queue.getData().pop_front();
|
||||
avi_writer->m_fbi_queue.getData().clear();
|
||||
avi_writer->m_fbi_queue.unlock();
|
||||
return NULL;
|
||||
}
|
||||
@ -133,16 +373,6 @@ void* AVIWriter::videoRecord(void *obj)
|
||||
avi_writer->cleanAllFrameBufferImages();
|
||||
continue;
|
||||
}
|
||||
if (avi_writer->m_file == NULL)
|
||||
{
|
||||
bool ret = avi_writer->createFile();
|
||||
if (!ret)
|
||||
{
|
||||
delete [] fbi;
|
||||
avi_writer->cleanAllFrameBufferImages();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
uint8_t* orig_fbi = fbi;
|
||||
const unsigned width = avi_writer->m_width;
|
||||
const unsigned height = avi_writer->m_height;
|
||||
@ -176,39 +406,24 @@ void* AVIWriter::videoRecord(void *obj)
|
||||
}
|
||||
delete [] tmp_buf;
|
||||
size = area * 3;
|
||||
if (avi_writer->m_avi_format == AVI_FORMAT_JPG)
|
||||
{
|
||||
uint8_t* jpg = new uint8_t[size];
|
||||
uint8_t* jpg = (uint8_t*)malloc(size);
|
||||
size = avi_writer->bmpToJpg(orig_fbi + area, jpg, size);
|
||||
delete [] orig_fbi;
|
||||
orig_fbi = jpg;
|
||||
}
|
||||
while (frame_count != 0)
|
||||
uint8_t* shrinken = (uint8_t*)realloc(jpg, size);
|
||||
if (shrinken == NULL)
|
||||
{
|
||||
AVIErrCode code = avi_writer->addImage
|
||||
(avi_writer->m_avi_format == AVI_FORMAT_JPG ? orig_fbi :
|
||||
orig_fbi + area, size);
|
||||
if (code == AVI_SIZE_LIMIT_ERR)
|
||||
{
|
||||
avi_writer->createFile();
|
||||
free(jpg);
|
||||
continue;
|
||||
}
|
||||
else if (code == AVI_IO_ERR)
|
||||
break;
|
||||
frame_count--;
|
||||
}
|
||||
delete [] orig_fbi;
|
||||
jpg = shrinken;
|
||||
jpg_data.lock();
|
||||
jpg_data.getData().emplace_back(jpg, size, frame_count);
|
||||
pthread_cond_signal(&enc_request);
|
||||
jpg_data.unlock();
|
||||
}
|
||||
return NULL;
|
||||
} // videoRecord
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
struct OggEncoderInfo
|
||||
{
|
||||
Synchronised<std::list<int8_t*> >* m_pcm_data;
|
||||
pthread_cond_t* m_enc_request;
|
||||
}; // OggEncoderInfo
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* AVIWriter::oggEncoder(void *obj)
|
||||
{
|
||||
@ -249,9 +464,10 @@ void* AVIWriter::oggEncoder(void *obj)
|
||||
fwrite(og.header, 1, og.header_len, ogg_data);
|
||||
fwrite(og.body, 1, og.body_len, ogg_data);
|
||||
}
|
||||
OggEncoderInfo* oei = (OggEncoderInfo*)obj;
|
||||
Synchronised<std::list<int8_t*> >* pcm_data = oei->m_pcm_data;
|
||||
pthread_cond_t* cond_request = oei->m_enc_request;
|
||||
EncoderInfo* ei = (EncoderInfo*)obj;
|
||||
Synchronised<std::list<int8_t*> >* pcm_data =
|
||||
(Synchronised<std::list<int8_t*> >*)ei->m_data;
|
||||
pthread_cond_t* cond_request = ei->m_enc_request;
|
||||
int eos = 0;
|
||||
while (eos == 0)
|
||||
{
|
||||
@ -276,10 +492,10 @@ void* AVIWriter::oggEncoder(void *obj)
|
||||
float **buffer = vorbis_analysis_buffer(&vd, 1024);
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
buffer[0][i]=((pcm_buf[i * 4 + 1] << 8) |
|
||||
buffer[0][i] = ((pcm_buf[i * 4 + 1] << 8) |
|
||||
(0x00ff & (int)pcm_buf[i * 4])) / 32768.0f;
|
||||
buffer[1][i]=((pcm_buf[i * 4 + 3] << 8) |
|
||||
(0x00ff&(int)pcm_buf[i * 4 + 2])) / 32768.0f;
|
||||
buffer[1][i] = ((pcm_buf[i * 4 + 3] << 8) |
|
||||
(0x00ff & (int)pcm_buf[i * 4 + 2])) / 32768.0f;
|
||||
}
|
||||
vorbis_analysis_wrote(&vd, i);
|
||||
}
|
||||
@ -293,7 +509,7 @@ void* AVIWriter::oggEncoder(void *obj)
|
||||
while (true)
|
||||
{
|
||||
int result = ogg_stream_pageout(&os, &og);
|
||||
if (result==0)
|
||||
if (result == 0)
|
||||
break;
|
||||
fwrite(og.header, 1, og.header_len, ogg_data);
|
||||
fwrite(og.body, 1, og.body_len, ogg_data);
|
||||
@ -387,14 +603,14 @@ void* AVIWriter::audioRecord(void *obj)
|
||||
|
||||
Synchronised<bool>* idle = (Synchronised<bool>*)obj;
|
||||
Synchronised<std::list<int8_t*> > pcm_data;
|
||||
pthread_cond_t m_enc_request;
|
||||
pthread_cond_init(&m_enc_request, NULL);
|
||||
pthread_t m_ogg_enc_thread;
|
||||
pthread_cond_t enc_request;
|
||||
pthread_cond_init(&enc_request, NULL);
|
||||
pthread_t ogg_enc_thread;
|
||||
|
||||
OggEncoderInfo oei;
|
||||
oei.m_pcm_data = &pcm_data;
|
||||
oei.m_enc_request = &m_enc_request;
|
||||
pthread_create(&m_ogg_enc_thread, NULL, &oggEncoder, &oei);
|
||||
EncoderInfo ei;
|
||||
ei.m_data = &pcm_data;
|
||||
ei.m_enc_request = &enc_request;
|
||||
pthread_create(&ogg_enc_thread, NULL, &oggEncoder, &ei);
|
||||
int8_t* each_pcm_buf = new int8_t[frag_size]();
|
||||
unsigned readed = 0;
|
||||
while (true)
|
||||
@ -403,7 +619,7 @@ void* AVIWriter::audioRecord(void *obj)
|
||||
{
|
||||
pcm_data.lock();
|
||||
pcm_data.getData().push_back(each_pcm_buf);
|
||||
pthread_cond_signal(&m_enc_request);
|
||||
pthread_cond_signal(&enc_request);
|
||||
pcm_data.unlock();
|
||||
break;
|
||||
}
|
||||
@ -427,7 +643,7 @@ void* AVIWriter::audioRecord(void *obj)
|
||||
{
|
||||
pcm_data.lock();
|
||||
pcm_data.getData().push_back(each_pcm_buf);
|
||||
pthread_cond_signal(&m_enc_request);
|
||||
pthread_cond_signal(&enc_request);
|
||||
pcm_data.unlock();
|
||||
each_pcm_buf = new int8_t[frag_size]();
|
||||
readed = (unsigned)bytes - copy_size;
|
||||
@ -441,10 +657,10 @@ void* AVIWriter::audioRecord(void *obj)
|
||||
}
|
||||
pcm_data.lock();
|
||||
pcm_data.getData().push_back(NULL);
|
||||
pthread_cond_signal(&m_enc_request);
|
||||
pthread_cond_signal(&enc_request);
|
||||
pcm_data.unlock();
|
||||
pthread_join(m_ogg_enc_thread, NULL);
|
||||
pthread_cond_destroy(&m_enc_request);
|
||||
pthread_join(ogg_enc_thread, NULL);
|
||||
pthread_cond_destroy(&enc_request);
|
||||
|
||||
return NULL;
|
||||
} // audioRecord
|
||||
@ -701,8 +917,6 @@ error:
|
||||
// ----------------------------------------------------------------------------
|
||||
bool AVIWriter::createFile()
|
||||
{
|
||||
m_idle.setAtomic(false);
|
||||
pthread_create(&m_audio_thread, NULL, &audioRecord, &m_idle);
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
tm* timeInfo = localtime(&rawtime);
|
||||
|
@ -194,7 +194,7 @@ private:
|
||||
|
||||
Synchronised<bool> m_idle;
|
||||
|
||||
pthread_t m_video_thread, m_audio_thread;
|
||||
pthread_t m_record_thread;
|
||||
|
||||
pthread_cond_t m_cond_request;
|
||||
|
||||
@ -245,6 +245,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
static void* oggEncoder(void *obj);
|
||||
// ------------------------------------------------------------------------
|
||||
static void* vpxEncoder(void *obj);
|
||||
// ------------------------------------------------------------------------
|
||||
static void setRecordingTarget(const std::string& name)
|
||||
{
|
||||
m_recording_target.setAtomic(name);
|
||||
|
Loading…
Reference in New Issue
Block a user