| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_AUDIO_MAC_AUDIO_LOOPBACK_INPUT_MAC_IMPL_H_ |
| #define MEDIA_AUDIO_MAC_AUDIO_LOOPBACK_INPUT_MAC_IMPL_H_ |
| |
| #include <string> |
| |
| #include "base/apple/scoped_cftyperef.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "media/audio/agc_audio_stream.h" |
| #include "media/audio/audio_io.h" |
| #include "media/audio/mac/audio_manager_mac.h" |
| #include "media/base/audio_parameters.h" |
| |
| @class NSError; |
| @class SCContentFilter; |
| @class ScreenCaptureKitAudioHelper; |
| @class SCShareableContent; |
| @class SCStream; |
| @class SCStreamConfiguration; |
| @protocol SCStreamDelegate; |
| using CMSampleBufferRef = struct opaqueCMSampleBuffer*; |
| |
| namespace media { |
| |
| class SharedHelper; |
| |
| // Implementation of AudioInputStream using the ScreenCaptureKit (SCK) API for |
| // macOS 13.0 and later, intended solely for system audio loopback capture. |
| // |
| // Overview of operation: |
| // - An instance of SCKAudioInputStream is created by AudioManagerMac. |
| // - Open() is called, prompting enumeration of available shareable content. The |
| // function synchronously waits for the content to be enumerated and sets up |
| // the stream with the created filter. |
| // - Start(sink) is called, causing the stream to start delivering samples. |
| // - Audio samples are being received by OnStreamSample() and forwarded to the |
| // sink. |
| // - Stop() is called, causing the stream to stop. |
| // - Close() is called, causing the stream output to be removed and the stream |
| // to be destroyed. |
| // |
| // API notes: |
| // - ScreenCaptureKit requires TCC screen capture permissions, which are granted |
| // to the browser process and inherited by the audio service. For the |
| // inheritance to work correctly, Chromium must be code signed. |
| // - The audio service sandbox requires +[SCStreamManager |
| // requestUserPermissionForScreenCapture] to be swizzled so that it reports |
| // that permissions have been granted. This is currently done in |
| // AudioManagerMac. |
| class MEDIA_EXPORT API_AVAILABLE(macos(13.0)) SCKAudioInputStream |
| : public AgcAudioStream<AudioInputStream> { |
| using NotifyOnCloseCallback = |
| base::RepeatingCallback<void(AudioInputStream*)>; |
| using StartSCStreamMockingCallback = |
| base::RepeatingCallback<void(SCStream*, |
| SCContentFilter*, |
| SCStreamConfiguration*, |
| id<SCStreamDelegate>)>; |
| |
| public: |
| SCKAudioInputStream(const AudioParameters& params, |
| const std::string& device_id, |
| const AudioManager::LogCallback log_callback, |
| const NotifyOnCloseCallback close_callback); |
| SCKAudioInputStream( |
| const AudioParameters& params, |
| const std::string& device_id, |
| const AudioManager::LogCallback log_callback, |
| const NotifyOnCloseCallback close_callback, |
| const StartSCStreamMockingCallback start_scstream_mocking_callback, |
| const base::TimeDelta shareable_content_timeout); |
| |
| SCKAudioInputStream(const SCKAudioInputStream&) = delete; |
| SCKAudioInputStream(SCKAudioInputStream&&) = delete; |
| SCKAudioInputStream(const SCKAudioInputStream&&) = delete; |
| SCKAudioInputStream& operator=(const SCKAudioInputStream&) = delete; |
| SCKAudioInputStream& operator=(SCKAudioInputStream&&) = delete; |
| SCKAudioInputStream& operator=(const SCKAudioInputStream&&) = delete; |
| |
| ~SCKAudioInputStream() override; |
| |
| // AudioInputStream:: implementation. |
| AudioInputStream::OpenOutcome Open() override; |
| void Start(AudioInputCallback* callback) override; |
| void Stop() override; |
| void Close() override; |
| double GetMaxVolume() override; |
| void SetVolume(double volume) override; |
| double GetVolume() override; |
| bool IsMuted() override; |
| void SetOutputDeviceForAec(const std::string& output_device_id) override; |
| |
| private: |
| // Processes the audio data received from the system. Runs on a SCK thread. |
| void OnStreamSample( |
| base::apple::ScopedCFTypeRef<CMSampleBufferRef> sample_buffer, |
| const double volume); |
| |
| // Invoked when an error occurs while starting or running the stream. Runs on |
| // a SCK thread. |
| void OnStreamError(); |
| |
| // Send log messages to the stream creator. |
| void SendLogMessage(const char* format, ...); |
| |
| // Audio parameters passed to the constructor. |
| const AudioParameters params_; |
| |
| // One of AudioDeviceDescription::kLoopback*. |
| const std::string device_id_; |
| |
| // Wraps the non-interleaved audio buffer received from the system. |
| const std::unique_ptr<AudioBus> audio_bus_; |
| |
| // Receives the processed audio data and errors. Must not be modified while |
| // |shared_helper_| has callbacks set. |
| raw_ptr<AudioInputCallback> sink_; |
| |
| // Callback to send log messages to the client. |
| AudioManager::LogCallback log_callback_; |
| |
| // Called when the stream is closed and can be safely deleted. |
| const NotifyOnCloseCallback close_callback_; |
| |
| // Used by tests to get notified of a new SCStream creation. |
| StartSCStreamMockingCallback start_scstream_mocking_callback_; |
| |
| // Refcounted helper which helps us sync operation between browser and SCK |
| // threads. |
| scoped_refptr<SharedHelper> shared_helper_; |
| |
| // Stream output and delegate registered with the API to receive and handle |
| // samples and errors. |
| ScreenCaptureKitAudioHelper* __strong sck_helper_; |
| |
| // The stream object created by the API. |
| SCStream* __strong stream_; |
| |
| // Serial queue used by the API for new samples. |
| dispatch_queue_t __strong queue_; |
| |
| // The length of time covered by the audio data in a single audio buffer. |
| const base::TimeDelta buffer_frames_duration_; |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_AUDIO_MAC_AUDIO_LOOPBACK_INPUT_MAC_IMPL_H_ |