1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
media / audio / mac / audio_loopback_input_mac_impl.h [blame]
// 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_