#include "FakeAudioDeviceModule.h" #include "modules/audio_device/include/audio_device_default.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/platform_thread.h" #include "rtc_base/time_utils.h" #include #include #include namespace tgcalls { class FakeAudioDeviceModuleImpl : public webrtc::webrtc_impl::AudioDeviceModuleDefault { public: static rtc::scoped_refptr Create(webrtc::TaskQueueFactory* taskQueueFactory, std::shared_ptr renderer, std::shared_ptr recorder, FakeAudioDeviceModule::Options options) { return rtc::scoped_refptr( new rtc::RefCountedObject(taskQueueFactory, options, std::move(renderer), std::move(recorder))); } FakeAudioDeviceModuleImpl(webrtc::TaskQueueFactory*, FakeAudioDeviceModule::Options options, std::shared_ptr renderer, std::shared_ptr recorder) : num_channels_{options.num_channels}, samples_per_sec_{options.samples_per_sec}, scheduler_(options.scheduler_), renderer_(std::move(renderer)), recorder_(std::move(recorder)) { if (!scheduler_) { scheduler_ = [](auto f) { std::thread([f = std::move(f)]() { while (true) { double wait = f(); if (wait < 0) { return; } std::this_thread::sleep_for(std::chrono::microseconds (static_cast(wait * 1000000))); } }).detach(); }; } RTC_CHECK(num_channels_ == 1 || num_channels_ == 2); auto good_sample_rate = [](size_t sr) { return sr == 8000 || sr == 16000 || sr == 32000 || sr == 44100 || sr == 48000; }; RTC_CHECK(good_sample_rate(samples_per_sec_)); samples_per_frame_ = samples_per_sec_ / 100; playout_buffer_.resize(samples_per_frame_ * 2 /* 2 in case stereo will be turned on later */, 0); } ~FakeAudioDeviceModuleImpl() override { StopPlayout(); } int32_t PlayoutIsAvailable(bool* available) override { if (available) { *available = true; } return 0; } int32_t StereoPlayoutIsAvailable(bool* available) const override { if (available) { *available = true; } return 0; } int32_t StereoPlayout(bool* enabled) const override { if (enabled) { *enabled = num_channels_ == 2; } return 0; } int32_t SetStereoPlayout(bool enable) override { size_t new_num_channels = enable ? 2 : 1; if (new_num_channels != num_channels_) { return -1; } return 0; } int32_t Init() override { return 0; } int32_t RegisterAudioCallback(webrtc::AudioTransport* callback) override { std::unique_lock lock(render_mutex_); audio_callback_ = callback; return 0; } int32_t StartPlayout() override { std::unique_lock lock(render_mutex_); if (!renderer_) { return 0; } if (rendering_) { return 0; } need_rendering_ = true; rendering_ = true; scheduler_([this]{ return Render() / 1000000.0; }); return 0; } int32_t StopPlayout() override { if (!rendering_) { return 0; } need_rendering_ = false; std::unique_lock lock(render_mutex_); render_cond_.wait(lock, [this]{ return !rendering_; }); return 0; } bool Playing() const override { return rendering_; } int32_t StartRecording() override { std::unique_lock lock(record_mutex_); if (!recorder_) { return 0; } if (recording_) { return 0; } need_recording_ = true; recording_ = true; scheduler_([this]{ return Record() / 1000000.0; }); return 0; } int32_t StopRecording() override { if (!recording_) { return 0; } need_recording_ = false; std::unique_lock lock(record_mutex_); record_cond_.wait(lock, [this]{ return !recording_; }); return 0; } bool Recording() const override { return recording_; } private: int32_t Render() { std::unique_lock lock(render_mutex_); if (!need_rendering_) { rendering_ = false; render_cond_.notify_all(); return -1; } size_t samples_out = 0; int64_t elapsed_time_ms = -1; int64_t ntp_time_ms = -1; size_t bytes_per_sample = 2 * num_channels_; RTC_CHECK(audio_callback_); if (renderer_) { renderer_->BeginFrame(0); } audio_callback_->NeedMorePlayData(samples_per_frame_, bytes_per_sample, num_channels_, samples_per_sec_, playout_buffer_.data(), samples_out, &elapsed_time_ms, &ntp_time_ms); if (renderer_) { renderer_->EndFrame(); } if (samples_out != 0 && renderer_) { AudioFrame frame; frame.audio_samples = playout_buffer_.data(); frame.num_samples = samples_out; frame.bytes_per_sample = bytes_per_sample; frame.num_channels = num_channels_; frame.samples_per_sec = samples_per_sec_; frame.elapsed_time_ms = elapsed_time_ms; frame.ntp_time_ms = ntp_time_ms; renderer_->Render(frame); } int32_t wait_for_us = -1; if (renderer_) { wait_for_us = renderer_->WaitForUs(); } return wait_for_us; } int32_t Record() { std::unique_lock lock(record_mutex_); if (!need_recording_) { recording_ = false; record_cond_.notify_all(); return -1; } auto frame = recorder_->Record(); if (frame.num_samples != 0) { uint32_t new_mic_level; audio_callback_->RecordedDataIsAvailable(frame.audio_samples, frame.num_samples, frame.bytes_per_sample, frame.num_channels, frame.samples_per_sec, 0, 0, 0, false, new_mic_level); } int32_t wait_for_us = -1; if (recorder_) { wait_for_us = recorder_->WaitForUs(); } return wait_for_us; } size_t num_channels_; const uint32_t samples_per_sec_; size_t samples_per_frame_{0}; std::function scheduler_; mutable std::mutex render_mutex_; std::atomic need_rendering_{false}; std::atomic rendering_{false}; std::condition_variable render_cond_; std::unique_ptr renderThread_; mutable std::mutex record_mutex_; std::atomic need_recording_{false}; std::atomic recording_{false}; std::condition_variable record_cond_; std::unique_ptr recordThread_; webrtc::AudioTransport* audio_callback_{nullptr}; const std::shared_ptr renderer_; const std::shared_ptr recorder_; std::vector playout_buffer_; }; std::function(webrtc::TaskQueueFactory*)> FakeAudioDeviceModule::Creator( std::shared_ptr renderer, std::shared_ptr recorder, Options options) { bool is_renderer_empty = bool(renderer); auto boxed_renderer = std::make_shared>(std::move(renderer)); bool is_recorder_empty = bool(recorder); auto boxed_recorder = std::make_shared>(std::move(recorder)); return [boxed_renderer = std::move(boxed_renderer), is_renderer_empty, boxed_recorder = std::move(boxed_recorder), is_recorder_empty, options](webrtc::TaskQueueFactory* task_factory) { RTC_CHECK(is_renderer_empty == bool(*boxed_renderer)); // call only once if renderer exists RTC_CHECK(is_recorder_empty == bool(*boxed_recorder)); // call only once if recorder exists return FakeAudioDeviceModuleImpl::Create(task_factory, std::move(*boxed_renderer), std::move(*boxed_recorder), options); }; } } // namespace tgcalls