Allow pulseaudio to be loaded dynamically

This commit is contained in:
Benau 2017-04-01 15:21:58 +08:00
parent 294aa3ad48
commit 8a8b388170
7 changed files with 560 additions and 170 deletions

View File

@ -415,7 +415,7 @@ if(NOT SERVER_ONLY)
endif()
if(UNIX AND NOT APPLE)
target_link_libraries(supertuxkart ${X11_LIBRARIES} ${XRANDR_LIBRARIES} ${PULSEAUDIO_LIBRARIES})
target_link_libraries(supertuxkart dl ${X11_LIBRARIES} ${XRANDR_LIBRARIES})
if(USE_LIBBFD)
target_link_libraries(supertuxkart ${LIBBFD_LIBRARIES})
endif()

View File

@ -0,0 +1,512 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// 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.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2)) && !defined(WIN32)
#include "recorder/vorbis_encoder.hpp"
#include "utils/synchronised.hpp"
#include "utils/log.hpp"
#include "utils/vs.hpp"
#include <cstring>
#include <dlfcn.h>
#include <list>
#include <pulse/pulseaudio.h>
#include <string>
namespace Recorder
{
// ========================================================================
void serverInfoCallBack(pa_context* c, const pa_server_info* i, void* data)
{
*(std::string*)data = i->default_sink_name;
} // serverInfoCallBack
// ========================================================================
struct PulseAudioData
{
bool m_loaded;
pa_mainloop* m_loop;
pa_context* m_context;
void* m_dl_handle;
pa_sample_spec m_sample_spec;
std::string m_default_sink;
typedef pa_stream* (*pa_stream_new_t)(pa_context*, const char*,
const pa_sample_spec*, const pa_channel_map*);
pa_stream_new_t pa_stream_new;
typedef int (*pa_stream_connect_record_t)(pa_stream*, const char*,
const pa_buffer_attr*, pa_stream_flags_t);
pa_stream_connect_record_t pa_stream_connect_record;
typedef pa_stream_state_t (*pa_stream_get_state_t)(pa_stream*);
pa_stream_get_state_t pa_stream_get_state;
typedef size_t (*pa_stream_readable_size_t)(pa_stream*);
pa_stream_readable_size_t pa_stream_readable_size;
typedef int (*pa_stream_peek_t)(pa_stream*, const void**, size_t*);
pa_stream_peek_t pa_stream_peek;
typedef int (*pa_stream_drop_t)(pa_stream*);
pa_stream_drop_t pa_stream_drop;
typedef int (*pa_stream_disconnect_t)(pa_stream*);
pa_stream_disconnect_t pa_stream_disconnect;
typedef void (*pa_stream_unref_t)(pa_stream*);
pa_stream_unref_t pa_stream_unref;
typedef pa_mainloop* (*pa_mainloop_new_t)(void);
pa_mainloop_new_t pa_mainloop_new;
typedef pa_mainloop_api* (*pa_mainloop_get_api_t)(pa_mainloop*);
pa_mainloop_get_api_t pa_mainloop_get_api;
typedef pa_context* (*pa_context_new_t)(pa_mainloop_api*, const char*);
pa_context_new_t pa_context_new;
typedef int (*pa_context_connect_t)(pa_context*, const char*,
pa_context_flags_t, const pa_spawn_api*);
pa_context_connect_t pa_context_connect;
typedef int (*pa_mainloop_iterate_t)(pa_mainloop*, int, int*);
pa_mainloop_iterate_t pa_mainloop_iterate;
typedef pa_context_state_t (*pa_context_get_state_t)(pa_context*);
pa_context_get_state_t pa_context_get_state;
typedef pa_operation* (*pa_context_get_server_info_t)(pa_context*,
pa_server_info_cb_t, void*);
pa_context_get_server_info_t pa_context_get_server_info;
typedef pa_operation_state_t (*pa_operation_get_state_t)
(pa_operation*);
pa_operation_get_state_t pa_operation_get_state;
typedef void (*pa_operation_unref_t)(pa_operation*);
pa_operation_unref_t pa_operation_unref;
typedef void (*pa_context_disconnect_t)(pa_context*);
pa_context_disconnect_t pa_context_disconnect;
typedef void (*pa_context_unref_t)(pa_context*);
pa_context_unref_t pa_context_unref;
typedef void (*pa_mainloop_free_t)(pa_mainloop*);
pa_mainloop_free_t pa_mainloop_free;
// --------------------------------------------------------------------
PulseAudioData()
{
m_loaded = false;
m_loop = NULL;
m_context = NULL;
m_dl_handle = NULL;
pa_stream_new = NULL;
pa_stream_connect_record = NULL;
pa_stream_get_state = NULL;
pa_stream_readable_size = NULL;
pa_stream_peek = NULL;
pa_stream_drop = NULL;
pa_stream_disconnect = NULL;
pa_stream_unref = NULL;
pa_mainloop_new = NULL;
pa_mainloop_get_api = NULL;
pa_context_new = NULL;
pa_context_connect = NULL;
pa_mainloop_iterate = NULL;
pa_context_get_state = NULL;
pa_context_get_server_info = NULL;
pa_operation_get_state = NULL;
pa_operation_unref = NULL;
pa_context_disconnect = NULL;
pa_context_unref = NULL;
pa_mainloop_free = NULL;
} // PulseAudioData
// --------------------------------------------------------------------
bool loadPulseAudioLibrary()
{
m_dl_handle = dlopen("libpulse.so", RTLD_LAZY);
if (m_dl_handle == NULL)
{
Log::error("PulseAudioRecorder", "Failed to open PulseAudio"
" library");
return false;
}
pa_stream_new = (pa_stream_new_t)dlsym(m_dl_handle,
"pa_stream_new");
if (pa_stream_new == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_new'");
return false;
}
pa_stream_connect_record = (pa_stream_connect_record_t)dlsym
(m_dl_handle, "pa_stream_connect_record");
if (pa_stream_connect_record == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_connect_record'");
return false;
}
pa_stream_get_state = (pa_stream_get_state_t)dlsym(m_dl_handle,
"pa_stream_get_state");
if (pa_stream_get_state == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_get_state'");
return false;
}
pa_stream_readable_size = (pa_stream_readable_size_t)dlsym
(m_dl_handle, "pa_stream_readable_size");
if (pa_stream_readable_size == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_readable_size'");
return false;
}
pa_stream_peek = (pa_stream_peek_t)dlsym(m_dl_handle,
"pa_stream_peek");
if (pa_stream_peek == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_peek'");
return false;
}
pa_stream_drop = (pa_stream_drop_t)dlsym(m_dl_handle,
"pa_stream_drop");
if (pa_stream_drop == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_drop'");
return false;
}
pa_stream_disconnect = (pa_stream_disconnect_t)dlsym(m_dl_handle,
"pa_stream_disconnect");
if (pa_stream_disconnect == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_disconnect'");
return false;
}
pa_stream_unref = (pa_stream_unref_t)dlsym(m_dl_handle,
"pa_stream_unref");
if (pa_stream_unref == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_stream_unref'");
return false;
}
pa_mainloop_new = (pa_mainloop_new_t)dlsym(m_dl_handle,
"pa_mainloop_new");
if (pa_mainloop_new == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_mainloop_new'");
return false;
}
pa_mainloop_get_api = (pa_mainloop_get_api_t)dlsym(m_dl_handle,
"pa_mainloop_get_api");
if (pa_mainloop_get_api == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_mainloop_get_api'");
return false;
}
pa_context_new = (pa_context_new_t)dlsym(m_dl_handle,
"pa_context_new");
if (pa_context_new == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_new'");
return false;
}
pa_context_connect = (pa_context_connect_t)dlsym(m_dl_handle,
"pa_context_connect");
if (pa_context_connect == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_connect'");
return false;
}
pa_mainloop_iterate = (pa_mainloop_iterate_t)dlsym(m_dl_handle,
"pa_mainloop_iterate");
if (pa_mainloop_iterate == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_mainloop_iterate'");
return false;
}
pa_context_get_state = (pa_context_get_state_t)dlsym(m_dl_handle,
"pa_context_get_state");
if (pa_context_get_state == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_get_state'");
return false;
}
pa_context_get_server_info = (pa_context_get_server_info_t)dlsym
(m_dl_handle, "pa_context_get_server_info");
if (pa_context_get_server_info == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_get_server_info'");
return false;
}
pa_operation_get_state = (pa_operation_get_state_t)dlsym
(m_dl_handle, "pa_operation_get_state");
if (pa_operation_get_state == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_operation_get_state'");
return false;
}
pa_operation_unref = (pa_operation_unref_t)dlsym(m_dl_handle,
"pa_operation_unref");
if (pa_operation_unref == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_operation_unref'");
return false;
}
pa_context_disconnect = (pa_context_disconnect_t)dlsym(m_dl_handle,
"pa_context_disconnect");
if (pa_context_disconnect == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_disconnect'");
return false;
}
pa_context_unref = (pa_context_unref_t)dlsym(m_dl_handle,
"pa_context_unref");
if (pa_context_unref == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_context_unref'");
return false;
}
pa_mainloop_free = (pa_mainloop_free_t)dlsym(m_dl_handle,
"pa_mainloop_free");
if (pa_mainloop_free == NULL)
{
Log::error("PulseAudioRecorder", "Cannot load function"
" 'pa_mainloop_free'");
return false;
}
return true;
} // loadPulseAudioLibrary
// --------------------------------------------------------------------
bool load()
{
if (!loadPulseAudioLibrary())
{
if (m_dl_handle != NULL)
{
dlclose(m_dl_handle);
m_dl_handle = NULL;
}
return false;
}
m_loop = pa_mainloop_new();
if (m_loop == NULL)
{
Log::error("PulseAudioRecorder", "Failed to create mainloop");
return false;
}
m_context = pa_context_new(pa_mainloop_get_api(m_loop),
"audioRecord");
if (m_context == NULL)
{
Log::error("PulseAudioRecorder", "Failed to create context");
return false;
}
pa_context_connect(m_context, NULL, PA_CONTEXT_NOAUTOSPAWN , NULL);
while (true)
{
while (pa_mainloop_iterate(m_loop, 0, NULL) > 0);
pa_context_state_t state = pa_context_get_state(m_context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state))
{
Log::error("PulseAudioRecorder", "Failed to connect to"
" context");
return false;
}
}
pa_operation* pa_op = pa_context_get_server_info(m_context,
serverInfoCallBack, &m_default_sink);
enum pa_operation_state op_state;
while ((op_state =
pa_operation_get_state(pa_op)) == PA_OPERATION_RUNNING)
pa_mainloop_iterate(m_loop, 0, NULL);
pa_operation_unref(pa_op);
if (m_default_sink.empty())
{
Log::error("PulseAudioRecorder", "Failed to get default sink");
return false;
}
m_default_sink += ".monitor";
m_sample_spec.format = PA_SAMPLE_S16LE;
m_sample_spec.rate = 44100;
m_sample_spec.channels = 2;
m_loaded = true;
return true;
} // load
// --------------------------------------------------------------------
~PulseAudioData()
{
if (m_loaded)
{
if (m_context != NULL)
{
pa_context_disconnect(m_context);
pa_context_unref(m_context);
}
if (m_loop != NULL)
{
pa_mainloop_free(m_loop);
}
if (m_dl_handle != NULL)
{
dlclose(m_dl_handle);
}
}
} // ~PulseAudioData
};
// ========================================================================
PulseAudioData g_pa_data;
// ========================================================================
void* audioRecorder(void *obj)
{
VS::setThreadName("audioRecorder");
if (!g_pa_data.m_loaded)
{
if (!g_pa_data.load())
{
Log::error("PulseAudioRecord", "Cannot pulseaudio data");
return NULL;
}
}
pa_stream* stream = g_pa_data.pa_stream_new(g_pa_data.m_context,
"input", &g_pa_data.m_sample_spec, NULL);
if (stream == NULL)
{
Log::error("PulseAudioRecorder", "Failed to create stream");
return NULL;
}
pa_buffer_attr buf_attr;
const unsigned frag_size = 1024 * g_pa_data.m_sample_spec.channels *
sizeof(int16_t);
buf_attr.fragsize = frag_size;
const unsigned max_uint = -1;
buf_attr.maxlength = max_uint;
buf_attr.minreq = max_uint;
buf_attr.prebuf = max_uint;
buf_attr.tlength = max_uint;
g_pa_data.pa_stream_connect_record(stream,
g_pa_data.m_default_sink.c_str(), &buf_attr,
(pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY));
while (true)
{
while (g_pa_data.pa_mainloop_iterate(g_pa_data.m_loop, 0, NULL)
> 0);
pa_stream_state_t state = g_pa_data.pa_stream_get_state(stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state))
{
Log::error("PulseAudioRecorder", "Failed to connect to"
" stream");
return NULL;
}
}
Synchronised<bool>* idle = (Synchronised<bool>*)obj;
Synchronised<std::list<int8_t*> > pcm_data;
pthread_cond_t enc_request;
pthread_cond_init(&enc_request, NULL);
pthread_t vorbis_enc;
Recorder::VorbisEncoderData ved;
ved.m_sample_rate = g_pa_data.m_sample_spec.rate;
ved.m_channels = g_pa_data.m_sample_spec.channels;
ved.m_audio_type = Recorder::VorbisEncoderData::AT_PCM;
ved.m_data = &pcm_data;
ved.m_enc_request = &enc_request;
pthread_create(&vorbis_enc, NULL, &Recorder::vorbisEncoder, &ved);
int8_t* each_pcm_buf = new int8_t[frag_size]();
unsigned readed = 0;
while (true)
{
if (idle->getAtomic())
{
pcm_data.lock();
pcm_data.getData().push_back(each_pcm_buf);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
break;
}
while (g_pa_data.pa_mainloop_iterate(g_pa_data.m_loop, 0, NULL)
> 0);
const void* data;
size_t bytes;
size_t readable = g_pa_data.pa_stream_readable_size(stream);
if (readable == 0)
continue;
g_pa_data.pa_stream_peek(stream, &data, &bytes);
if (data == NULL)
{
if (bytes > 0)
g_pa_data.pa_stream_drop(stream);
continue;
}
bool buf_full = readed + (unsigned)bytes > frag_size;
unsigned copy_size = buf_full ?
frag_size - readed : (unsigned)bytes;
memcpy(each_pcm_buf + readed, data, copy_size);
if (buf_full)
{
pcm_data.lock();
pcm_data.getData().push_back(each_pcm_buf);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
each_pcm_buf = new int8_t[frag_size]();
readed = (unsigned)bytes - copy_size;
memcpy(each_pcm_buf, (uint8_t*)data + copy_size, readed);
}
else
{
readed += (unsigned)bytes;
}
g_pa_data.pa_stream_drop(stream);
}
pcm_data.lock();
pcm_data.getData().push_back(NULL);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
pthread_join(vorbis_enc, NULL);
pthread_cond_destroy(&enc_request);
g_pa_data.pa_stream_disconnect(stream);
g_pa_data.pa_stream_unref(stream);
return NULL;
} // audioRecorder
}
#endif

View File

@ -0,0 +1,30 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2017 SuperTuxKart-Team
//
// 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.
#if !(defined(SERVER_ONLY) || defined(USE_GLES2)) && !defined(WIN32)
#ifndef HEADER_PULSEAUDIO_RECORD_HPP
#define HEADER_PULSEAUDIO_RECORD_HPP
namespace Recorder
{
void* audioRecorder(void *obj);
};
#endif
#endif

View File

@ -115,14 +115,14 @@ namespace Recorder
// ========================================================================
WasapiData g_wasapi_data;
// ========================================================================
void* audioRecord(void *obj)
void* audioRecorder(void *obj)
{
VS::setThreadName("audioRecord");
VS::setThreadName("audioRecorder");
if (!g_wasapi_data.m_loaded)
{
if (!g_wasapi_data.load())
{
Log::error("WasapiRecord", "Failed to load wasapi data");
Log::error("WasapiRecorder", "Failed to load wasapi data");
return NULL;
}
}
@ -138,7 +138,8 @@ namespace Recorder
ved.m_audio_type = VorbisEncoderData::AT_PCM;
if (wav_for_ext->Format.wBitsPerSample != 16)
{
Log::error("WasapiRecord", "Only 16bit PCM is supported.");
Log::error("WasapiRecorder", "Only 16bit PCM is"
" supported.");
return NULL;
}
}
@ -148,14 +149,15 @@ namespace Recorder
ved.m_audio_type = VorbisEncoderData::AT_FLOAT;
if (wav_for_ext->Format.wBitsPerSample != 32)
{
Log::error("WasapiRecord", "Only 32bit float is"
Log::error("WasapiRecorder", "Only 32bit float is"
" supported.");
return NULL;
}
}
else
{
Log::error("WasapiRecord", "Unsupported audio input format.");
Log::error("WasapiRecorder", "Unsupported audio input"
" format.");
return NULL;
}
}
@ -166,13 +168,13 @@ namespace Recorder
ved.m_audio_type = VorbisEncoderData::AT_PCM;
if (g_wasapi_data.m_wav_format->wBitsPerSample != 16)
{
Log::error("WasapiRecord", "Only 16bit PCM is supported.");
Log::error("WasapiRecorder", "Only 16bit PCM is supported.");
return NULL;
}
}
else
{
Log::error("WasapiRecord", "Unsupported audio input format");
Log::error("WasapiRecorder", "Unsupported audio input format");
return NULL;
}
HRESULT hr = g_wasapi_data.m_client->Start();
@ -271,6 +273,6 @@ namespace Recorder
if (FAILED(hr))
return NULL;
return NULL;
} // audioRecord
} // audioRecorder
}
#endif

View File

@ -22,7 +22,7 @@
namespace Recorder
{
void* audioRecord(void *obj);
void* audioRecorder(void *obj);
};
#endif

View File

@ -22,18 +22,13 @@
#include "config/user_config.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/message_queue.hpp"
#include "recorder/pulseaudio_recorder.hpp"
#include "recorder/vorbis_encoder.hpp"
#include "recorder/wasapi_recorder.hpp"
#include "recorder/webm_writer.hpp"
#include "utils/translation.hpp"
#include "utils/vs.hpp"
#include <ogg/ogg.h>
#ifdef WIN32
#include "recorder/wasapi_record.hpp"
#else
#include <pulse/pulseaudio.h>
#endif
#include <turbojpeg.h>
#include <vpx/vpx_encoder.h>
#include <vpx/vp8cx.h>
@ -308,8 +303,8 @@ void* AVIWriter::videoRecord(void *obj)
jpg_data.getData().emplace_back((uint8_t*)NULL, 0, 0);
pthread_cond_signal(vpx_ei.m_enc_request);
jpg_data.unlock();
pthread_join(audio_thread, NULL);
pthread_join(vpx_enc_thread, NULL);
//pthread_join(audio_thread, NULL);
//pthread_join(vpx_enc_thread, NULL);
avi_writer->setCanBeDeleted();
avi_writer->m_fbi_queue.getData().clear();
avi_writer->m_fbi_queue.unlock();
@ -377,148 +372,6 @@ void* AVIWriter::videoRecord(void *obj)
return NULL;
} // videoRecord
#ifndef WIN32
// ----------------------------------------------------------------------------
void serverInfoCallBack(pa_context* c, const pa_server_info* i, void* data)
{
*(std::string*)data = i->default_sink_name;
} // serverInfoCallBack
// ----------------------------------------------------------------------------
void* AVIWriter::audioRecord(void *obj)
{
VS::setThreadName("audioRecord");
pa_mainloop* ml = pa_mainloop_new();
assert(ml);
pa_context* ctx = pa_context_new(pa_mainloop_get_api(ml), "audioRecord");
assert(ctx);
pa_context_connect(ctx, NULL, PA_CONTEXT_NOAUTOSPAWN , NULL);
while (true)
{
while (pa_mainloop_iterate(ml, 0, NULL) > 0);
pa_context_state_t state = pa_context_get_state(ctx);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state))
{
Log::error("audioRecord", "Failed to connect to context");
return NULL;
}
}
std::string default_sink;
pa_operation* pa_op =
pa_context_get_server_info(ctx, serverInfoCallBack, &default_sink);
enum pa_operation_state op_state;
while ((op_state = pa_operation_get_state(pa_op)) == PA_OPERATION_RUNNING)
pa_mainloop_iterate(ml, 0, NULL);
pa_operation_unref(pa_op);
if (default_sink.empty())
{
Log::error("audioRecord", "Failed to get default sink");
return NULL;
}
default_sink += ".monitor";
pa_sample_spec sam_spec;
sam_spec.format = PA_SAMPLE_S16LE;
sam_spec.rate = 44100;
sam_spec.channels = 2;
pa_buffer_attr buf_attr;
const unsigned frag_size = 1024 * sam_spec.channels * sizeof(int16_t);
buf_attr.fragsize = frag_size;
const unsigned max_uint = -1;
buf_attr.maxlength = max_uint;
buf_attr.minreq = max_uint;
buf_attr.prebuf = max_uint;
buf_attr.tlength = max_uint;
pa_stream* stream = pa_stream_new(ctx, "input", &sam_spec, NULL);
assert(stream);
pa_stream_connect_record(stream, default_sink.c_str(), &buf_attr,
(pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY));
while (true)
{
while (pa_mainloop_iterate(ml, 0, NULL) > 0);
pa_stream_state_t state = pa_stream_get_state(stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state))
{
Log::error("audioRecord", "Failed to connect to stream");
return NULL;
}
}
Synchronised<bool>* idle = (Synchronised<bool>*)obj;
Synchronised<std::list<int8_t*> > pcm_data;
pthread_cond_t enc_request;
pthread_cond_init(&enc_request, NULL);
pthread_t vorbis_enc_thread;
Recorder::VorbisEncoderData ved;
ved.m_sample_rate = sam_spec.rate;
ved.m_channels = sam_spec.channels;
ved.m_audio_type = Recorder::VorbisEncoderData::AT_PCM;
ved.m_data = &pcm_data;
ved.m_enc_request = &enc_request;
pthread_create(&vorbis_enc_thread, NULL, &Recorder::vorbisEncoder, &ved);
int8_t* each_pcm_buf = new int8_t[frag_size]();
unsigned readed = 0;
while (true)
{
if (idle->getAtomic() == true)
{
pcm_data.lock();
pcm_data.getData().push_back(each_pcm_buf);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
break;
}
while (pa_mainloop_iterate(ml, 0, NULL) > 0);
const void* data;
size_t bytes;
size_t readable = pa_stream_readable_size(stream);
if (readable == 0)
continue;
pa_stream_peek(stream, &data, &bytes);
if (data == NULL)
{
if (bytes > 0)
pa_stream_drop(stream);
continue;
}
bool buf_full = readed + (unsigned)bytes > frag_size;
unsigned copy_size = buf_full ? frag_size - readed : (unsigned)bytes;
memcpy(each_pcm_buf + readed, data, copy_size);
if (buf_full)
{
pcm_data.lock();
pcm_data.getData().push_back(each_pcm_buf);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
each_pcm_buf = new int8_t[frag_size]();
readed = (unsigned)bytes - copy_size;
memcpy(each_pcm_buf, (uint8_t*)data + copy_size, readed);
}
else
{
readed += (unsigned)bytes;
}
pa_stream_drop(stream);
}
pcm_data.lock();
pcm_data.getData().push_back(NULL);
pthread_cond_signal(&enc_request);
pcm_data.unlock();
pthread_join(vorbis_enc_thread, NULL);
pthread_cond_destroy(&enc_request);
return NULL;
} // audioRecord
#endif
// ----------------------------------------------------------------------------
int AVIWriter::getFrameCount(double rate)
{
@ -543,12 +396,7 @@ void AVIWriter::captureFrameBufferImage()
if (m_idle.getAtomic())
{
m_idle.setAtomic(false);
#ifdef WIN32
pthread_create(&audio_thread, NULL, &Recorder::audioRecord, &m_idle);
#else
pthread_create(&audio_thread, NULL, &audioRecord, &m_idle);
#endif
pthread_create(&audio_thread, NULL, &Recorder::audioRecorder, &m_idle);
pthread_cond_init(vpx_ei.m_enc_request, NULL);
pthread_create(&vpx_enc_thread, NULL, &vpxEncoder, &vpx_ei);
}

View File

@ -244,8 +244,6 @@ public:
// ------------------------------------------------------------------------
static void* videoRecord(void *obj);
// ------------------------------------------------------------------------
static void* audioRecord(void *obj);
// ------------------------------------------------------------------------
static void* vpxEncoder(void *obj);
// ------------------------------------------------------------------------
static void setRecordingTarget(const std::string& name)