From 8a8b3881701df405d0c93b6c852c5ab6d33a4244 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 1 Apr 2017 15:21:58 +0800 Subject: [PATCH] Allow pulseaudio to be loaded dynamically --- CMakeLists.txt | 2 +- src/recorder/pulseaudio_recorder.cpp | 512 ++++++++++++++++++ src/recorder/pulseaudio_recorder.hpp | 30 + ...{wasapi_record.cpp => wasapi_recorder.cpp} | 20 +- ...{wasapi_record.hpp => wasapi_recorder.hpp} | 2 +- src/utils/avi_writer.cpp | 162 +----- src/utils/avi_writer.hpp | 2 - 7 files changed, 560 insertions(+), 170 deletions(-) create mode 100644 src/recorder/pulseaudio_recorder.cpp create mode 100644 src/recorder/pulseaudio_recorder.hpp rename src/recorder/{wasapi_record.cpp => wasapi_recorder.cpp} (93%) rename src/recorder/{wasapi_record.hpp => wasapi_recorder.hpp} (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87d9ff5c3..d1015cbc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/src/recorder/pulseaudio_recorder.cpp b/src/recorder/pulseaudio_recorder.cpp new file mode 100644 index 000000000..3e5f003ba --- /dev/null +++ b/src/recorder/pulseaudio_recorder.cpp @@ -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 +#include +#include +#include +#include + +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* idle = (Synchronised*)obj; + Synchronised > 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 diff --git a/src/recorder/pulseaudio_recorder.hpp b/src/recorder/pulseaudio_recorder.hpp new file mode 100644 index 000000000..33b85fc51 --- /dev/null +++ b/src/recorder/pulseaudio_recorder.hpp @@ -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 diff --git a/src/recorder/wasapi_record.cpp b/src/recorder/wasapi_recorder.cpp similarity index 93% rename from src/recorder/wasapi_record.cpp rename to src/recorder/wasapi_recorder.cpp index 3b2dcc409..70db60544 100644 --- a/src/recorder/wasapi_record.cpp +++ b/src/recorder/wasapi_recorder.cpp @@ -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 diff --git a/src/recorder/wasapi_record.hpp b/src/recorder/wasapi_recorder.hpp similarity index 96% rename from src/recorder/wasapi_record.hpp rename to src/recorder/wasapi_recorder.hpp index 002491991..c09ddaf94 100644 --- a/src/recorder/wasapi_record.hpp +++ b/src/recorder/wasapi_recorder.hpp @@ -22,7 +22,7 @@ namespace Recorder { - void* audioRecord(void *obj); + void* audioRecorder(void *obj); }; #endif diff --git a/src/utils/avi_writer.cpp b/src/utils/avi_writer.cpp index 5f9bf9249..c5cf2b65d 100644 --- a/src/utils/avi_writer.cpp +++ b/src/utils/avi_writer.cpp @@ -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 -#ifdef WIN32 - #include "recorder/wasapi_record.hpp" -#else - #include -#endif - #include #include #include @@ -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* idle = (Synchronised*)obj; - Synchronised > 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); } diff --git a/src/utils/avi_writer.hpp b/src/utils/avi_writer.hpp index d6c4756e0..6d202a6b3 100644 --- a/src/utils/avi_writer.hpp +++ b/src/utils/avi_writer.hpp @@ -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)