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 }