blob: 87f306f5412731ad6c72de7768dff30be477283b [file] [log] [blame]
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include "absl/types/optional.h"
#include "modules/audio_device/win/core_audio_utility_win.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/thread_checker.h"
namespace webrtc {
class AudioDeviceBuffer;
class FineAudioBuffer;
namespace webrtc_win {
// Serves as base class for CoreAudioInput and CoreAudioOutput and supports
// device handling and audio streaming where the direction (input or output)
// is set at constructions by the parent.
// The IAudioSessionEvents interface provides notifications of session-related
// events such as changes in the volume level, display name, and session state.
// This class does not use the default ref-counting memory management method
// provided by IUnknown: calling CoreAudioBase::Release() will not delete the
// object. The client will receive notification from the session manager on
// a separate thread owned and controlled by the manager.
// TODO(henrika): investigate if CoreAudioBase should implement
// IMMNotificationClient as well (might improve support for device changes).
class CoreAudioBase : public IAudioSessionEvents {
enum class Direction {
// TODO(henrika): add more error types.
enum class ErrorType {
template <typename T>
auto as_integer(T const value) -> typename std::underlying_type<T>::type {
return static_cast<typename std::underlying_type<T>::type>(value);
// Callback definition for notifications of new audio data. For input clients,
// it means that "new audio data has now been captured", and for output
// clients, "the output layer now needs new audio data".
typedef std::function<bool(uint64_t device_frequency)> OnDataCallback;
// Callback definition for notifications of run-time error messages. It can
// be called e.g. when an active audio device is removed and an audio stream
// is disconnected (|error| is then set to kStreamDisconnected). Both input
// and output clients implements OnErrorCallback() and will trigger an
// internal restart sequence for kStreamDisconnected.
// This method is currently always called on the audio thread.
// TODO(henrika): add support for more error types.
typedef std::function<bool(ErrorType error)> OnErrorCallback;
void ThreadRun();
CoreAudioBase(const CoreAudioBase&) = delete;
CoreAudioBase& operator=(const CoreAudioBase&) = delete;
explicit CoreAudioBase(Direction direction,
bool automatic_restart,
OnDataCallback data_callback,
OnErrorCallback error_callback);
std::string GetDeviceID(int index) const;
int SetDevice(int index);
int DeviceName(int index, std::string* name, std::string* guid) const;
// Checks if the current device ID is no longer in use (e.g. due to a
// disconnected stream), and if so, switches device to the default audio
// device. Called on the audio thread during restart attempts.
bool SwitchDeviceIfNeeded();
bool Init();
bool Start();
bool Stop();
bool IsVolumeControlAvailable(bool* available) const;
bool Restart();
Direction direction() const { return direction_; }
bool automatic_restart() const { return automatic_restart_; }
// Releases all allocated COM resources in the base class.
void ReleaseCOMObjects();
// Returns number of active devices given the specified |direction_| set
// by the parent (input or output).
int NumberOfActiveDevices() const;
// Returns total number of enumerated audio devices which is the sum of all
// active devices plus two extra (one default and one default
// communications). The value in |direction_| determines if capture or
// render devices are counted.
int NumberOfEnumeratedDevices() const;
bool IsInput() const;
bool IsOutput() const;
bool IsDefaultDevice(int index) const;
bool IsDefaultCommunicationsDevice(int index) const;
bool IsDefaultDeviceId(const std::string& device_id) const;
bool IsDefaultCommunicationsDeviceId(const std::string& device_id) const;
EDataFlow GetDataFlow() const;
bool IsRestarting() const;
int64_t TimeSinceStart() const;
// TODO(henrika): is the existing thread checker in WindowsAudioDeviceModule
// sufficient? As is, we have one top-level protection and then a second
// level here. In addition, calls to Init(), Start() and Stop() are not
// included to allow for support of internal restart (where these methods are
// called on the audio thread).
rtc::ThreadChecker thread_checker_;
rtc::ThreadChecker thread_checker_audio_;
AudioDeviceBuffer* audio_device_buffer_ = nullptr;
bool initialized_ = false;
uint32_t endpoint_buffer_size_frames_ = 0;
Microsoft::WRL::ComPtr<IAudioClock> audio_clock_;
Microsoft::WRL::ComPtr<IAudioClient> audio_client_;
bool is_active_ = false;
int64_t num_data_callbacks_ = 0;
int latency_ms_ = 0;
absl::optional<uint32_t> sample_rate_;
const Direction direction_;
const bool automatic_restart_;
const OnDataCallback on_data_callback_;
const OnErrorCallback on_error_callback_;
ScopedHandle audio_samples_event_;
ScopedHandle stop_event_;
ScopedHandle restart_event_;
int64_t start_time_ = 0;
std::string device_id_;
int device_index_ = -1;
// Used by the IAudioSessionEvents implementations. Currently only utilized
// for debugging purposes.
LONG ref_count_ = 1;
// Set when restart process starts and cleared when restart stops
// successfully. Accessed atomically.
std::atomic<bool> is_restarting_;
std::unique_ptr<rtc::PlatformThread> audio_thread_;
Microsoft::WRL::ComPtr<IAudioSessionControl> audio_session_control_;
void StopThread();
AudioSessionState GetAudioSessionState() const;
// Called on the audio thread when a restart event has been set.
// It will then trigger calls to the installed error callbacks with error
// type set to kStreamDisconnected.
bool HandleRestartEvent();
// IUnknown (required by IAudioSessionEvents and IMMNotificationClient).
ULONG __stdcall AddRef() override;
ULONG __stdcall Release() override;
HRESULT __stdcall QueryInterface(REFIID iid, void** object) override;
// IAudioSessionEvents implementation.
// These methods are called on separate threads owned by the session manager.
// More than one thread can be involved depending on the type of callback
// and audio session.
HRESULT __stdcall OnStateChanged(AudioSessionState new_state) override;
HRESULT __stdcall OnSessionDisconnected(
AudioSessionDisconnectReason disconnect_reason) override;
HRESULT __stdcall OnDisplayNameChanged(LPCWSTR new_display_name,
LPCGUID event_context) override;
HRESULT __stdcall OnIconPathChanged(LPCWSTR new_icon_path,
LPCGUID event_context) override;
HRESULT __stdcall OnSimpleVolumeChanged(float new_simple_volume,
BOOL new_mute,
LPCGUID event_context) override;
HRESULT __stdcall OnChannelVolumeChanged(DWORD channel_count,
float new_channel_volumes[],
DWORD changed_channel,
LPCGUID event_context) override;
HRESULT __stdcall OnGroupingParamChanged(LPCGUID new_grouping_param,
LPCGUID event_context) override;
} // namespace webrtc_win
} // namespace webrtc