• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2011 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/audio_capturer.h"
7 
8 #include "libcef/browser/alloy/alloy_browser_host_impl.h"
9 #include "libcef/browser/audio_loopback_stream_creator.h"
10 
11 #include "components/mirroring/service/captured_audio_input.h"
12 #include "media/audio/audio_input_device.h"
13 
14 namespace {
15 
TranslateChannelLayout(cef_channel_layout_t channel_layout)16 media::ChannelLayout TranslateChannelLayout(
17     cef_channel_layout_t channel_layout) {
18   // Verify that our enum matches Chromium's values. The enum values match
19   // between those enums and existing values don't ever change, so it's enough
20   // to check that there are no new ones added.
21   static_assert(
22       static_cast<int>(CEF_CHANNEL_LAYOUT_MAX) ==
23           static_cast<int>(media::CHANNEL_LAYOUT_MAX),
24       "cef_channel_layout_t must match the ChannelLayout enum in Chromium");
25   return static_cast<media::ChannelLayout>(channel_layout);
26 }
27 
StreamCreatorHelper(content::WebContents * source_web_contents,CefAudioLoopbackStreamCreator * audio_stream_creator,mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient> client,const media::AudioParameters & params,uint32_t total_segments)28 void StreamCreatorHelper(
29     content::WebContents* source_web_contents,
30     CefAudioLoopbackStreamCreator* audio_stream_creator,
31     mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient> client,
32     const media::AudioParameters& params,
33     uint32_t total_segments) {
34   audio_stream_creator->CreateLoopbackStream(
35       source_web_contents, params, total_segments,
36       base::BindRepeating(
37           [](mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient>
38                  client,
39              mojo::PendingRemote<media::mojom::AudioInputStream> stream,
40              mojo::PendingReceiver<media::mojom::AudioInputStreamClient>
41                  client_receiver,
42              media::mojom::ReadOnlyAudioDataPipePtr data_pipe) {
43             mojo::Remote<mirroring::mojom::AudioStreamCreatorClient>
44                 audio_client(std::move(client));
45             audio_client->StreamCreated(std::move(stream),
46                                         std::move(client_receiver),
47                                         std::move(data_pipe));
48           },
49           base::Passed(&client)));
50 }
51 
52 }  // namespace
53 
CefAudioCapturer(const CefAudioParameters & params,CefRefPtr<AlloyBrowserHostImpl> browser,CefRefPtr<CefAudioHandler> audio_handler)54 CefAudioCapturer::CefAudioCapturer(const CefAudioParameters& params,
55                                    CefRefPtr<AlloyBrowserHostImpl> browser,
56                                    CefRefPtr<CefAudioHandler> audio_handler)
57     : params_(params),
58       browser_(browser),
59       audio_handler_(audio_handler),
60       audio_stream_creator_(std::make_unique<CefAudioLoopbackStreamCreator>()) {
61   media::AudioParameters audio_params(
62       media::AudioParameters::AUDIO_PCM_LINEAR,
63       TranslateChannelLayout(params.channel_layout), params.sample_rate,
64       params.frames_per_buffer);
65 
66   if (!audio_params.IsValid()) {
67     LOG(ERROR) << "Invalid audio parameters";
68     return;
69   }
70 
71   DCHECK(browser_);
72   DCHECK(audio_handler_);
73   DCHECK(browser_->web_contents());
74 
75   channels_ = audio_params.channels();
76   audio_input_device_ = new media::AudioInputDevice(
77       std::make_unique<mirroring::CapturedAudioInput>(base::BindRepeating(
78           &StreamCreatorHelper, base::Unretained(browser_->web_contents()),
79           base::Unretained(audio_stream_creator_.get()))),
80       media::AudioInputDevice::kLoopback,
81       media::AudioInputDevice::DeadStreamDetection::kEnabled);
82 
83   audio_input_device_->Initialize(audio_params, this);
84   audio_input_device_->Start();
85 }
86 
~CefAudioCapturer()87 CefAudioCapturer::~CefAudioCapturer() {
88   StopStream();
89 }
90 
OnCaptureStarted()91 void CefAudioCapturer::OnCaptureStarted() {
92   audio_handler_->OnAudioStreamStarted(browser_, params_, channels_);
93   DCHECK(!capturing_);
94   capturing_ = true;
95 }
96 
Capture(const media::AudioBus * source,base::TimeTicks audio_capture_time,double,bool)97 void CefAudioCapturer::Capture(const media::AudioBus* source,
98                                base::TimeTicks audio_capture_time,
99                                double /*volume*/,
100                                bool /*key_pressed*/) {
101   const int channels = source->channels();
102   std::array<const float*, media::CHANNELS_MAX> data;
103   DCHECK(channels == channels_);
104   DCHECK(channels <= static_cast<int>(data.size()));
105   for (int c = 0; c < channels; ++c) {
106     data[c] = source->channel(c);
107   }
108   base::TimeDelta pts = audio_capture_time - base::TimeTicks::UnixEpoch();
109   audio_handler_->OnAudioStreamPacket(browser_, data.data(), source->frames(),
110                                       pts.InMilliseconds());
111 }
112 
OnCaptureError(media::AudioCapturerSource::ErrorCode code,const std::string & message)113 void CefAudioCapturer::OnCaptureError(
114     media::AudioCapturerSource::ErrorCode code,
115     const std::string& message) {
116   audio_handler_->OnAudioStreamError(browser_, message);
117   StopStream();
118 }
119 
StopStream()120 void CefAudioCapturer::StopStream() {
121   if (audio_input_device_)
122     audio_input_device_->Stop();
123   if (capturing_)
124     audio_handler_->OnAudioStreamStopped(browser_);
125 
126   audio_input_device_ = nullptr;
127   capturing_ = false;
128 }