| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // Audio rendering unit utilizing audio output stream provided by browser |
| // process through IPC. |
| // |
| // Relationship of classes. |
| // |
| // AudioOutputController AudioOutputDevice |
| // ^ ^ |
| // | | |
| // v IPC v |
| // MojoAudioOutputStream <---------> AudioOutputIPC (MojoAudioOutputIPC) |
| // |
| // Transportation of audio samples from the render to the browser process |
| // is done by using shared memory in combination with a sync socket pair |
| // to generate a low latency transport. The AudioOutputDevice user registers an |
| // AudioOutputDevice::RenderCallback at construction and will be polled by the |
| // AudioOutputController for audio to be played out by the underlying audio |
| // layers. |
| // |
| // State sequences. |
| // |
| // Task [IO thread] IPC [IO thread] |
| // RequestDeviceAuthorization -> RequestDeviceAuthorizationOnIOThread ------> |
| // RequestDeviceAuthorization -> |
| // <- OnDeviceAuthorized <- AudioMsg_NotifyDeviceAuthorized <- |
| // |
| // Start -> CreateStreamOnIOThread -----> CreateStream ------> |
| // <- OnStreamCreated <- AudioMsg_NotifyStreamCreated <- |
| // ---> PlayOnIOThread -----------> PlayStream --------> |
| // |
| // Optionally Play() / Pause() sequences may occur: |
| // Play -> PlayOnIOThread --------------> PlayStream ---------> |
| // Pause -> PauseOnIOThread ------------> PauseStream --------> |
| // (note that Play() / Pause() sequences before |
| // OnStreamCreated are deferred until OnStreamCreated, with the last valid |
| // state being used) |
| // |
| // AudioOutputDevice::Render => audio transport on audio thread => |
| // | |
| // Stop --> ShutDownOnIOThread --------> CloseStream -> Close |
| // |
| // This class utilizes several threads during its lifetime, namely: |
| // 1. Creating thread. |
| // Must be the main render thread. |
| // 2. Control thread (may be the main render thread or another thread). |
| // The methods: Start(), Stop(), Play(), Pause(), SetVolume() |
| // must be called on the same thread. |
| // 3. IO thread (internal implementation detail - not exposed to public API) |
| // The thread within which this class receives all the IPC messages and |
| // IPC communications can only happen in this thread. |
| // 4. Audio transport thread (See AudioDeviceThread). |
| // Responsible for calling the AudioOutputDeviceThreadCallback |
| // implementation that in turn calls AudioRendererSink::RenderCallback |
| // which feeds audio samples to the audio layer in the browser process using |
| // sync sockets and shared memory. |
| // |
| // Implementation notes: |
| // - The user must call Stop() before deleting the class instance. |
| |
| #ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_ |
| #define MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/unsafe_shared_memory_region.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/thread_annotations.h" |
| #include "base/time/time.h" |
| #include "media/audio/audio_device_thread.h" |
| #include "media/audio/audio_output_ipc.h" |
| #include "media/audio/audio_sink_parameters.h" |
| #include "media/base/audio_parameters.h" |
| #include "media/base/audio_renderer_sink.h" |
| #include "media/base/media_export.h" |
| #include "media/base/output_device_info.h" |
| |
| namespace base { |
| class OneShotTimer; |
| class SingleThreadTaskRunner; |
| } |
| |
| namespace media { |
| class AudioOutputDeviceThreadCallback; |
| |
| class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink, |
| public AudioOutputIPCDelegate { |
| public: |
| // NOTE: Clients must call Initialize() before using. |
| AudioOutputDevice( |
| std::unique_ptr<AudioOutputIPC> ipc, |
| const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, |
| const AudioSinkParameters& sink_params, |
| base::TimeDelta authorization_timeout); |
| |
| // Request authorization to use the device specified in the constructor. |
| void RequestDeviceAuthorization(); |
| |
| // AudioRendererSink implementation. |
| void Initialize(const AudioParameters& params, |
| RenderCallback* callback) override; |
| void Start() override; |
| void Stop() override; |
| void Play() override; |
| void Pause() override; |
| void Flush() override; |
| bool SetVolume(double volume) override; |
| OutputDeviceInfo GetOutputDeviceInfo() override; |
| void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override; |
| bool IsOptimizedForHardwareParameters() override; |
| bool CurrentThreadIsRenderingThread() override; |
| |
| // Methods called on IO thread ---------------------------------------------- |
| // AudioOutputIPCDelegate methods. |
| void OnError() override; |
| void OnDeviceAuthorized(OutputDeviceStatus device_status, |
| const AudioParameters& output_params, |
| const std::string& matched_device_id) override; |
| void OnStreamCreated(base::UnsafeSharedMemoryRegion shared_memory_region, |
| base::SyncSocket::ScopedHandle socket_handle, |
| bool play_automatically) override; |
| void OnIPCClosed() override; |
| |
| protected: |
| // Magic required by ref_counted.h to avoid any code deleting the object |
| // accidentally while there are references to it. |
| friend class base::RefCountedThreadSafe<AudioOutputDevice>; |
| ~AudioOutputDevice() override; |
| |
| private: |
| enum StartupState { |
| IDLE, // Authorization not requested. |
| AUTHORIZATION_REQUESTED, // Sent (possibly completed) device |
| // authorization request. |
| STREAM_CREATION_REQUESTED, // Sent (possibly completed) device creation |
| // request. Can Play()/Pause()/Stop(). |
| }; |
| |
| // This enum is used for UMA, so the only allowed operation on this definition |
| // is to add new states to the bottom, update kMaxValue, and update the |
| // histogram "Media.Audio.Render.StreamCallbackError2". |
| enum Error { |
| kNoError = 0, |
| kErrorDuringCreation = 1, |
| kErrorDuringRendering = 2, |
| kMaxValue = kErrorDuringRendering |
| }; |
| |
| // Methods called on IO thread ---------------------------------------------- |
| // The following methods are tasks posted on the IO thread that need to |
| // be executed on that thread. They use AudioOutputIPC to send IPC messages |
| // upon state changes. |
| void RequestDeviceAuthorizationOnIOThread(); |
| void InitializeOnIOThread(const AudioParameters& params, |
| RenderCallback* callback); |
| void CreateStreamOnIOThread(); |
| void PlayOnIOThread(); |
| void PauseOnIOThread(); |
| void FlushOnIOThread(); |
| void ShutDownOnIOThread(); |
| void SetVolumeOnIOThread(double volume); |
| |
| // Process device authorization result on the IO thread. |
| void ProcessDeviceAuthorizationOnIOThread( |
| OutputDeviceStatus device_status, |
| const AudioParameters& output_params, |
| const std::string& matched_device_id, |
| bool timed_out); |
| |
| void NotifyRenderCallbackOfError(); |
| |
| OutputDeviceInfo GetOutputDeviceInfo_Signaled(); |
| void OnAuthSignal(); |
| |
| const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; |
| |
| AudioParameters audio_parameters_; |
| |
| RenderCallback* callback_; |
| |
| // A pointer to the IPC layer that takes care of sending requests over to |
| // the implementation. May be set to nullptr after errors. |
| std::unique_ptr<AudioOutputIPC> ipc_; |
| |
| // Current state (must only be accessed from the IO thread). See comments for |
| // State enum above. |
| StartupState state_; |
| |
| // For UMA stats. May only be accessed on the IO thread. |
| Error had_error_ = kNoError; |
| |
| // Last set volume. |
| double volume_ = 1.0; |
| |
| // The media session ID used to identify which input device to be started. |
| // Only used by Unified IO. |
| base::UnguessableToken session_id_; |
| |
| // ID of hardware output device to be used (provided |session_id_| is zero) |
| const std::string device_id_; |
| |
| // If |device_id_| is empty and |session_id_| is not, |matched_device_id_| is |
| // received in OnDeviceAuthorized(). |
| std::string matched_device_id_; |
| |
| absl::optional<base::UnguessableToken> processing_id_; |
| |
| // In order to avoid a race between OnStreamCreated and Stop(), we use this |
| // guard to control stopping and starting the audio thread. |
| base::Lock audio_thread_lock_; |
| std::unique_ptr<AudioOutputDeviceThreadCallback> audio_callback_; |
| std::unique_ptr<AudioDeviceThread> audio_thread_ |
| GUARDED_BY(audio_thread_lock_); |
| |
| // Temporary hack to ignore OnStreamCreated() due to the user calling Stop() |
| // so we don't start the audio thread pointing to a potentially freed |
| // |callback_|. |
| // |
| // TODO(scherkus): Replace this by changing AudioRendererSink to either accept |
| // the callback via Start(). See http://crbug.com/151051 for details. |
| bool stopping_hack_ GUARDED_BY(audio_thread_lock_); |
| |
| base::WaitableEvent did_receive_auth_; |
| AudioParameters output_params_; |
| OutputDeviceStatus device_status_; |
| |
| const base::TimeDelta auth_timeout_; |
| std::unique_ptr<base::OneShotTimer> auth_timeout_action_; |
| |
| // Pending callback for OutputDeviceInfo if it has not been received by the |
| // time a call to GetGetOutputDeviceInfoAsync() is called. |
| // |
| // Lock for use ONLY with |pending_device_info_cb_| and |did_receive_auth_|, |
| // if you add more usage of this lock ensure you have not added a deadlock. |
| base::Lock device_info_lock_; |
| OutputDeviceInfoCB pending_device_info_cb_ GUARDED_BY(device_info_lock_); |
| |
| DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice); |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_ |