From cacbdbd668b64c508e20d4d9d629735a0fd0c4f0 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 1 Apr 2017 00:52:38 +0800 Subject: [PATCH] Allow recording audio in windows using wasapi --- CMakeLists.txt | 21 ++- cmake/FindOggVorbis.cmake | 2 +- src/recorder/vorbis_encode.cpp | 155 +++++++++++++++++++ src/recorder/vorbis_encode.hpp | 45 ++++++ src/recorder/wasapi_record.cpp | 273 +++++++++++++++++++++++++++++++++ src/recorder/wasapi_record.hpp | 30 ++++ src/recorder/webm_writer.cpp | 118 ++++++++------ src/recorder/webm_writer.hpp | 5 + src/utils/avi_writer.cpp | 16 +- src/utils/avi_writer.hpp | 5 + 10 files changed, 612 insertions(+), 58 deletions(-) create mode 100644 src/recorder/vorbis_encode.cpp create mode 100644 src/recorder/vorbis_encode.hpp create mode 100644 src/recorder/wasapi_record.cpp create mode 100644 src/recorder/wasapi_record.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 083610c0a..87d9ff5c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -119,16 +119,24 @@ if (APPLE) set(JPEG_LIBRARY jpeglib) else() find_package(JPEG REQUIRED) - find_library(TURBOJPEG_LIBRARY NAMES turbojpeg) + find_library(TURBOJPEG_LIBRARY NAMES turbojpeg libturbojpeg PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") mark_as_advanced(TURBOJPEG_LIBRARY) include_directories(${JPEG_INCLUDE_DIR}) endif() -include(FindPkgConfig) -pkg_check_modules(PULSEAUDIO libpulse) -include_directories(${PULSEAUDIO_INCLUDEDIR}) +if (UNIX AND NOT APPLE) + include(FindPkgConfig) + pkg_check_modules(PULSEAUDIO libpulse) + include_directories(${PULSEAUDIO_INCLUDEDIR}) +endif() + +if (UNIX AND NOT APPLE) + pkg_check_modules(VPX vpx) +else() + find_path(VPX_INCLUDEDIR NAMES vpx/vpx_codec.h PATHS "${PROJECT_SOURCE_DIR}/dependencies/include") + find_library(VPX_LIBRARIES NAMES vpxmt PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") +endif() -pkg_check_modules(VPX vpx) include_directories(${VPX_INCLUDEDIR}) if(NOT SERVER_ONLY AND NOT USE_GLES2) @@ -394,7 +402,6 @@ target_link_libraries(supertuxkart ${OPENAL_LIBRARY} ${FREETYPE_LIBRARIES} ${JPEG_LIBRARIES} - ${PULSEAUDIO_LIBRARIES} ${TURBOJPEG_LIBRARY} ${VPX_LIBRARIES} ) @@ -408,7 +415,7 @@ if(NOT SERVER_ONLY) endif() if(UNIX AND NOT APPLE) - target_link_libraries(supertuxkart ${X11_LIBRARIES} ${XRANDR_LIBRARIES}) + target_link_libraries(supertuxkart ${X11_LIBRARIES} ${XRANDR_LIBRARIES} ${PULSEAUDIO_LIBRARIES}) if(USE_LIBBFD) target_link_libraries(supertuxkart ${LIBBFD_LIBRARIES}) endif() diff --git a/cmake/FindOggVorbis.cmake b/cmake/FindOggVorbis.cmake index 25794aa30..e30056cf9 100644 --- a/cmake/FindOggVorbis.cmake +++ b/cmake/FindOggVorbis.cmake @@ -15,7 +15,7 @@ find_path(OGGVORBIS_VORBIS_INCLUDE_DIR NAMES vorbis/vorbisfile.h PATHS "${PROJEC find_library(OGGVORBIS_OGG_LIBRARY NAMES ogg Ogg libogg PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") find_library(OGGVORBIS_VORBIS_LIBRARY NAMES vorbis Vorbis libvorbis PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") find_library(OGGVORBIS_VORBISFILE_LIBRARY NAMES vorbisfile libvorbisfile PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") -find_library(OGGVORBIS_VORBISENC_LIBRARY NAMES vorbisenc) +find_library(OGGVORBIS_VORBISENC_LIBRARY NAMES vorbisenc libvorbisenc PATHS "${PROJECT_SOURCE_DIR}/dependencies/lib") if (APPLE) set(OGGVORBIS_OGG_INCLUDE_DIR "/Library/Frameworks/Ogg.framework/Headers/") diff --git a/src/recorder/vorbis_encode.cpp b/src/recorder/vorbis_encode.cpp new file mode 100644 index 000000000..a4c989fc4 --- /dev/null +++ b/src/recorder/vorbis_encode.cpp @@ -0,0 +1,155 @@ +// 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)) + +#include "recorder/vorbis_encode.hpp" +#include "utils/avi_writer.hpp" +#include "utils/log.hpp" +#include "utils/vs.hpp" + +#include +#include + +namespace Recorder +{ + void* vorbisEncoder(void *obj) + { + VS::setThreadName("vorbisEncoder"); + VorbisEncoderData* ved = (VorbisEncoderData*)obj; + vorbis_info vi; + vorbis_dsp_state vd; + vorbis_block vb; + vorbis_info_init(&vi); + vorbis_encode_init(&vi, ved->m_channels, ved->m_sample_rate, -1, + 112000, -1); + vorbis_analysis_init(&vd, &vi); + vorbis_block_init(&vd, &vb); + vorbis_comment vc; + vorbis_comment_init(&vc); + vorbis_comment_add_tag(&vc, "ENCODER", "STK vorbis encoder"); + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, + &header_code); + if (header.bytes > 255 || header_comm.bytes > 255) + { + Log::error("vorbisEncoder", "Header is too long."); + return NULL; + } + FILE* vb_data = fopen((AVIWriter::getRecordingTarget() + ".vb_data") + .c_str(), "wb"); + if (vb_data == NULL) + { + Log::error("vorbisEncoder", "Failed to open file for encoding" + " vorbis."); + return NULL; + } + fwrite(&ved->m_sample_rate, 1, sizeof(uint32_t), vb_data); + fwrite(&ved->m_channels, 1, sizeof(uint32_t), vb_data); + const uint32_t all = header.bytes + header_comm.bytes + + header_code.bytes + 3; + fwrite(&all, 1, sizeof(uint32_t), vb_data); + uint8_t size = 2; + fwrite(&size, 1, sizeof(uint8_t), vb_data); + size = (uint8_t)header.bytes; + fwrite(&size, 1, sizeof(uint8_t), vb_data); + size = (uint8_t)header_comm.bytes; + fwrite(&size, 1, sizeof(uint8_t), vb_data); + fwrite(header.packet, 1, header.bytes, vb_data); + fwrite(header_comm.packet, 1, header_comm.bytes, vb_data); + fwrite(header_code.packet, 1, header_code.bytes, vb_data); + Synchronised >* audio_data = + (Synchronised >*)ved->m_data; + pthread_cond_t* cond_request = ved->m_enc_request; + ogg_packet op; + int64_t last_timestamp = 0; + const unsigned channels = ved->m_channels; + while (true) + { + audio_data->lock(); + bool waiting = audio_data->getData().empty(); + while (waiting) + { + pthread_cond_wait(cond_request, audio_data->getMutex()); + waiting = audio_data->getData().empty(); + } + int8_t* audio_buf = audio_data->getData().front(); + audio_data->getData().pop_front(); + audio_data->unlock(); + long i = 0; + if (audio_buf == NULL) + { + break; + } + else + { + float **buffer = vorbis_analysis_buffer(&vd, 1024); + if (ved->m_audio_type == VorbisEncoderData::AT_PCM) + { + for (i = 0; i < 1024; i++) + { + buffer[0][i] = ((audio_buf[i * 4 + 1] << 8) | + (0x00ff & (int)audio_buf[i * 4])) / 32768.0f; + buffer[1][i] = ((audio_buf[i * 4 + 3] << 8) | + (0x00ff & (int)audio_buf[i * 4 + 2])) / 32768.0f; + } + } + else + { + float* float_buf = reinterpret_cast(audio_buf); + for (unsigned j = 0; j < channels; j++) + { + for (i = 0; i < 1024; i++) + { + buffer[j][i] = float_buf[i * channels + j]; + } + } + } + vorbis_analysis_wrote(&vd, i); + } + while (vorbis_analysis_blockout(&vd, &vb) == 1) + { + vorbis_analysis(&vb, NULL); + vorbis_bitrate_addblock(&vb); + while (vorbis_bitrate_flushpacket(&vd, &op)) + { + if (op.granulepos > 0) + { + uint32_t frame_size = (uint32_t)op.bytes; + fwrite(&frame_size, 1, sizeof(uint32_t), vb_data); + fwrite(&last_timestamp, 1, sizeof(int64_t), vb_data); + fwrite(op.packet, 1, frame_size, vb_data); + double s = + (double)op.granulepos / 44100. * 1000000000.; + last_timestamp = (int64_t)s; + } + } + } + delete[] audio_buf; + } + vorbis_block_clear(&vb); + vorbis_dsp_clear(&vd); + vorbis_comment_clear(&vc); + vorbis_info_clear(&vi); + fclose(vb_data); + return NULL; + + } // vorbisEncode +} +#endif diff --git a/src/recorder/vorbis_encode.hpp b/src/recorder/vorbis_encode.hpp new file mode 100644 index 000000000..134d37e37 --- /dev/null +++ b/src/recorder/vorbis_encode.hpp @@ -0,0 +1,45 @@ +// 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)) + +#ifndef HEADER_VORBIS_ENCODE_HPP +#define HEADER_VORBIS_ENCODE_HPP + +#include "utils/no_copy.hpp" +#include "utils/types.hpp" + +#include + +namespace Recorder +{ + struct VorbisEncoderData : public NoCopy + { + enum AudioType { AT_FLOAT, AT_PCM }; + void* m_data; + pthread_cond_t* m_enc_request; + uint32_t m_sample_rate; + uint32_t m_channels; + AudioType m_audio_type; + }; + + void* vorbisEncoder(void *obj); +}; + +#endif + +#endif diff --git a/src/recorder/wasapi_record.cpp b/src/recorder/wasapi_record.cpp new file mode 100644 index 000000000..bb910cf1c --- /dev/null +++ b/src/recorder/wasapi_record.cpp @@ -0,0 +1,273 @@ +// 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_encode.hpp" +#include "utils/synchronised.hpp" +#include "utils/log.hpp" +#include "utils/vs.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace Recorder +{ + // ======================================================================== + const REFERENCE_TIME REFTIMES_PER_SEC = 10000000; + // ======================================================================== + struct WasapiData + { + bool m_loaded; + IMMDeviceEnumerator* m_dev_enum; + IMMDevice* m_dev; + IAudioClient* m_client; + IAudioCaptureClient* m_capture_client; + WAVEFORMATEX* m_wav_format; + uint32_t m_buffer_size; + WasapiData() + { + m_loaded = false; + m_dev_enum = NULL; + m_dev = NULL; + m_client = NULL; + m_capture_client = NULL; + m_wav_format = NULL; + } // WasapiData + bool load() + { + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, + CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), + (void**)&m_dev_enum); + if (FAILED(hr)) + return false; + + hr = m_dev_enum->GetDefaultAudioEndpoint(eRender, eConsole, + &m_dev); + if (FAILED(hr)) + return false; + + hr = m_dev->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, + (void**)&m_client); + if (FAILED(hr)) + return false; + + hr = m_client->GetMixFormat(&m_wav_format); + if (FAILED(hr)) + return false; + + hr = m_client->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, 0, + m_wav_format, NULL); + if (FAILED(hr)) + return false; + + hr = m_client->GetBufferSize(&m_buffer_size); + if (FAILED(hr)) + return false; + + hr = m_client->GetService(__uuidof(IAudioCaptureClient), + (void**)&m_capture_client); + if (FAILED(hr)) + return false; + + m_loaded = true; + return true; + } // load + ~WasapiData() + { + if (m_loaded) + { + CoTaskMemFree(m_wav_format); + if (m_dev_enum) + m_dev_enum->Release(); + if (m_dev) + m_dev->Release(); + if (m_client) + m_client->Release(); + if (m_capture_client) + m_capture_client->Release(); + } + } // ~WasapiData + }; + // ======================================================================== + WasapiData g_wasapi_data; + // ======================================================================== + void* audioRecord(void *obj) + { + VS::setThreadName("audioRecord"); + if (!g_wasapi_data.m_loaded) + { + if (!g_wasapi_data.load()) + { + Log::error("WasapiRecord", "Failed to load wasapi data"); + return NULL; + } + } + VorbisEncoderData ved = {}; + if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + WAVEFORMATEXTENSIBLE* wav_for_ext = + (WAVEFORMATEXTENSIBLE*)g_wasapi_data.m_wav_format; + ved.m_channels = wav_for_ext->Format.nChannels; + ved.m_sample_rate = wav_for_ext->Format.nSamplesPerSec; + if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, wav_for_ext->SubFormat)) + { + ved.m_audio_type = VorbisEncoderData::AT_PCM; + if (wav_for_ext->Format.wBitsPerSample != 16) + { + Log::error("WasapiRecord", "Only 16bit PCM is supported."); + return NULL; + } + } + else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wav_for_ext + ->SubFormat)) + { + ved.m_audio_type = VorbisEncoderData::AT_FLOAT; + if (wav_for_ext->Format.wBitsPerSample != 32) + { + Log::error("WasapiRecord", "Only 32bit float is" + " supported."); + return NULL; + } + } + else + { + Log::error("WasapiRecord", "Unsupported audio input format."); + return NULL; + } + } + else if (g_wasapi_data.m_wav_format->wFormatTag == WAVE_FORMAT_PCM) + { + ved.m_channels = g_wasapi_data.m_wav_format->nChannels; + ved.m_sample_rate = g_wasapi_data.m_wav_format->nSamplesPerSec; + ved.m_audio_type = VorbisEncoderData::AT_PCM; + if (g_wasapi_data.m_wav_format->wBitsPerSample != 16) + { + Log::error("WasapiRecord", "Only 16bit PCM is supported."); + return NULL; + } + } + else + { + Log::error("WasapiRecord", "Unsupported audio input format"); + return NULL; + } + HRESULT hr = g_wasapi_data.m_client->Start(); + if (FAILED(hr)) + return NULL; + REFERENCE_TIME duration = REFTIMES_PER_SEC * + g_wasapi_data.m_buffer_size / g_wasapi_data.m_wav_format + ->nSamplesPerSec; + + Synchronised* idle = (Synchronised*)obj; + Synchronised > pcm_data; + pthread_cond_t enc_request; + pthread_cond_init(&enc_request, NULL); + pthread_t vorbis_enc; + ved.m_data = &pcm_data; + ved.m_enc_request = &enc_request; + pthread_create(&vorbis_enc, NULL, &Recorder::vorbisEncoder, &ved); + const unsigned frag_size = 1024 * ved.m_channels * + (g_wasapi_data.m_wav_format->wBitsPerSample / 8); + 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; + } + REFERENCE_TIME sleep_time = duration / 10000 / 2; + Sleep((uint32_t)sleep_time); + uint32_t packet_length; + hr = g_wasapi_data.m_capture_client->GetNextPacketSize( + &packet_length); + if (FAILED(hr)) + return NULL; + while (packet_length != 0) + { + BYTE* data; + uint32_t frame_size; + DWORD flags; + hr = g_wasapi_data.m_capture_client->GetBuffer(&data, + &frame_size, &flags, NULL, NULL); + if (FAILED(hr)) + return NULL; + const unsigned bytes = ved.m_channels * + (g_wasapi_data.m_wav_format->wBitsPerSample / 8) * + frame_size; + bool buf_full = readed + bytes > frag_size; + unsigned copy_size = buf_full ? frag_size - readed : bytes; + if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) + { + 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; + if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT)) + { + memcpy(each_pcm_buf, (uint8_t*)data + copy_size, + readed); + } + } + else + { + readed += (unsigned)bytes; + } + hr = g_wasapi_data.m_capture_client->ReleaseBuffer(frame_size); + if (FAILED(hr)) + return NULL; + if (idle->getAtomic()) + { + break; + } + hr = g_wasapi_data.m_capture_client->GetNextPacketSize( + &frame_size); + if (FAILED(hr)) + return NULL; + } + } + 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); + + hr = g_wasapi_data.m_client->Stop(); + if (FAILED(hr)) + return NULL; + return NULL; + } // audioRecord +} +#endif diff --git a/src/recorder/wasapi_record.hpp b/src/recorder/wasapi_record.hpp new file mode 100644 index 000000000..002491991 --- /dev/null +++ b/src/recorder/wasapi_record.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_WASAPI_RECORD_HPP +#define HEADER_WASAPI_RECORD_HPP + +namespace Recorder +{ + void* audioRecord(void *obj); +}; + +#endif + +#endif diff --git a/src/recorder/webm_writer.cpp b/src/recorder/webm_writer.cpp index 8345b8f72..0347d58de 100644 --- a/src/recorder/webm_writer.cpp +++ b/src/recorder/webm_writer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace Recorder @@ -56,53 +57,63 @@ namespace Recorder Log::error("writeWebm", "Could not initialize muxer segment."); return; } - uint64_t aud_track = muxer_segment.AddAudioTrack(44100, 2, 0); - if (!aud_track) - { - Log::error("writeWebm", "Could not add audio track."); - return; - } - mkvmuxer::AudioTrack* const at = static_cast( - muxer_segment.GetTrackByNumber(aud_track)); - if (!at) - { - Log::error("writeWebm", "Could not get audio track."); - return; - } - FILE* input = fopen(audio.c_str(), "rb"); - uint32_t codec_private_size; - fread(&codec_private_size, 1, sizeof(uint32_t), input); - uint8_t* buf = (uint8_t*)malloc(1024 * 1024); - fread(buf, 1, codec_private_size, input); - if (!at->SetCodecPrivate(buf, codec_private_size)) - { - Log::warn("writeWebm", "Could not add audio private data."); - return; - } std::list audio_frames; - while (fread(buf, 1, 12, input) == 12) + uint8_t* buf = (uint8_t*)malloc(1024 * 1024); + FILE* input = NULL; + struct stat st; + int result = stat(audio.c_str(), &st); + if (result == 0) { - uint32_t frame_size; - int64_t timestamp; - memcpy(&frame_size, buf, sizeof(uint32_t)); - memcpy(×tamp, buf + sizeof(uint32_t), sizeof(int64_t)); - fread(buf, 1, frame_size, input); - mkvmuxer::Frame* audio_frame = new mkvmuxer::Frame(); - if (!audio_frame->Init(buf, frame_size)) + input = fopen(audio.c_str(), "rb"); + uint32_t sample_rate, channels; + fread(&sample_rate, 1, sizeof(uint32_t), input); + fread(&channels, 1, sizeof(uint32_t), input); + uint64_t aud_track = muxer_segment.AddAudioTrack(sample_rate, + channels, 0); + if (!aud_track) { - Log::error("writeWebm", "Failed to construct a frame."); + Log::error("writeWebm", "Could not add audio track."); return; } - audio_frame->set_track_number(aud_track); - audio_frame->set_timestamp(timestamp); - audio_frame->set_is_key(true); - audio_frames.push_back(audio_frame); + mkvmuxer::AudioTrack* const at = static_cast + (muxer_segment.GetTrackByNumber(aud_track)); + if (!at) + { + Log::error("writeWebm", "Could not get audio track."); + return; + } + uint32_t codec_private_size; + fread(&codec_private_size, 1, sizeof(uint32_t), input); + fread(buf, 1, codec_private_size, input); + if (!at->SetCodecPrivate(buf, codec_private_size)) + { + Log::warn("writeWebm", "Could not add audio private data."); + return; + } + while (fread(buf, 1, 12, input) == 12) + { + uint32_t frame_size; + int64_t timestamp; + memcpy(&frame_size, buf, sizeof(uint32_t)); + memcpy(×tamp, buf + sizeof(uint32_t), sizeof(int64_t)); + fread(buf, 1, frame_size, input); + mkvmuxer::Frame* audio_frame = new mkvmuxer::Frame(); + if (!audio_frame->Init(buf, frame_size)) + { + Log::error("writeWebm", "Failed to construct a frame."); + return; + } + audio_frame->set_track_number(aud_track); + audio_frame->set_timestamp(timestamp); + audio_frame->set_is_key(true); + audio_frames.push_back(audio_frame); + } + fclose(input); + if (remove(audio.c_str()) != 0) + { + Log::warn("writeWebm", "Failed to remove audio data file"); + } } - if (remove(audio.c_str()) != 0) - { - Log::warn("writeWebm", "Failed to remove audio data file"); - } - fclose(input); uint64_t vid_track = muxer_segment.AddVideoTrack( irr_driver->getActualScreenSize().Width, irr_driver->getActualScreenSize().Height, 0); @@ -139,17 +150,26 @@ namespace Recorder muxer_frame.set_track_number(vid_track); muxer_frame.set_timestamp(timestamp); muxer_frame.set_is_key((flag & VPX_FRAME_IS_KEY) != 0); - mkvmuxer::Frame* cur_aud_frame = audio_frames.front(); - while (cur_aud_frame->timestamp() < (uint64_t)timestamp) + mkvmuxer::Frame* cur_aud_frame = + audio_frames.empty() ? NULL : audio_frames.front(); + if (cur_aud_frame != NULL) { - if (!muxer_segment.AddGenericFrame(cur_aud_frame)) + while (cur_aud_frame->timestamp() < (uint64_t)timestamp) { - Log::error("writeWebm", "Could not add audio frame."); - return; + if (!muxer_segment.AddGenericFrame(cur_aud_frame)) + { + Log::error("writeWebm", "Could not add audio frame."); + return; + } + delete cur_aud_frame; + audio_frames.pop_front(); + if (audio_frames.empty()) + { + cur_aud_frame = NULL; + break; + } + cur_aud_frame = audio_frames.front(); } - delete cur_aud_frame; - audio_frames.pop_front(); - cur_aud_frame = audio_frames.front(); } if (!muxer_segment.AddGenericFrame(&muxer_frame)) { diff --git a/src/recorder/webm_writer.hpp b/src/recorder/webm_writer.hpp index e0c287fbb..126f38ac2 100644 --- a/src/recorder/webm_writer.hpp +++ b/src/recorder/webm_writer.hpp @@ -17,6 +17,9 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if !(defined(SERVER_ONLY) || defined(USE_GLES2)) + +#ifndef HEADER_WEBM_WRITER_HPP +#define HEADER_WEBM_WRITER_HPP #include namespace Recorder @@ -24,3 +27,5 @@ namespace Recorder void writeWebm(const std::string& video, const std::string& audio); }; #endif + +#endif diff --git a/src/utils/avi_writer.cpp b/src/utils/avi_writer.cpp index cb41e429d..4f0b029dc 100644 --- a/src/utils/avi_writer.cpp +++ b/src/utils/avi_writer.cpp @@ -27,7 +27,12 @@ #include "utils/vs.hpp" #include -#include +#ifdef WIN32 + #include "recorder/wasapi_record.hpp" +#else + #include +#endif + #include #include #include @@ -405,6 +410,8 @@ void* AVIWriter::vorbisEncoder(void *obj) const uint32_t all = header.bytes + header_comm.bytes + header_code.bytes + 3; fwrite(&all, 1, sizeof(uint32_t), vb_data); + fwrite(&all, 1, sizeof(uint32_t), vb_data); + fwrite(&all, 1, sizeof(uint32_t), vb_data); uint8_t size = 2; fwrite(&size, 1, sizeof(uint8_t), vb_data); size = (uint8_t)header.bytes; @@ -477,6 +484,7 @@ void* AVIWriter::vorbisEncoder(void *obj) } // vorbisEncoder +#ifndef WIN32 // ---------------------------------------------------------------------------- void serverInfoCallBack(pa_context* c, const pa_server_info* i, void* data) { @@ -614,6 +622,7 @@ void* AVIWriter::audioRecord(void *obj) return NULL; } // audioRecord +#endif // ---------------------------------------------------------------------------- int AVIWriter::getFrameCount(double rate) { @@ -638,7 +647,12 @@ 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_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 5e6d3ff08..d1f3cdc9e 100644 --- a/src/utils/avi_writer.hpp +++ b/src/utils/avi_writer.hpp @@ -255,6 +255,11 @@ public: m_recording_target.setAtomic(name); } // ------------------------------------------------------------------------ + static std::string getRecordingTarget() + { + return m_recording_target.getAtomic(); + } + // ------------------------------------------------------------------------ void captureFrameBufferImage(); // ------------------------------------------------------------------------ void resetFrameBufferImage();