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_