OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/renderer_host/media/audio_output_impl.h" | |
6 | |
7 #include <algorithm> | |
8 #include <memory> | |
9 #include <string> | |
10 #include <utility> | |
11 | |
12 #include "base/memory/shared_memory.h" | |
13 #include "content/browser/media/capture/audio_mirroring_manager.h" | |
14 #include "mojo/public/cpp/system/platform_handle.h" | |
15 | |
16 namespace content { | |
17 | |
18 // static | |
19 AudioOutputImpl::UniquePtr AudioOutputImpl::Create( | |
20 base::SingleThreadTaskRunner* task_runner, | |
21 Host* host, | |
22 int32_t id, | |
23 int render_frame_id, | |
24 int render_process_id, | |
25 const std::string& output_device_id) { | |
26 return UniquePtr(new AudioOutputImpl(task_runner, host, id, render_frame_id, | |
27 render_process_id, output_device_id), | |
28 Deleter()); | |
29 } | |
30 | |
31 AudioOutputImpl::~AudioOutputImpl() { | |
32 DCHECK(task_runner_->BelongsToCurrentThread()); | |
33 } | |
34 | |
35 void AudioOutputImpl::Bind(media::mojom::AudioOutputRequest request) { | |
36 DCHECK(task_runner_->BelongsToCurrentThread()); | |
37 binding_.Bind(std::move(request)); | |
38 binding_.set_connection_error_handler( | |
39 base::Bind(&AudioOutputImpl::OnError, base::Unretained(this))); | |
40 } | |
41 | |
42 void AudioOutputImpl::Close() { | |
43 // Since AudioOutputController is closed asynchronously, we need to take | |
44 // extra care when AudioOutputImpl is deleted. We use a custom deleter for | |
45 // our unique_ptrs to make this extra trouble transparent to the owner. | |
46 // The entire termination sequence is like this: | |
47 // 1. Close is called and we unbind ourselves, if bound. | |
48 // 2. We ask the AudioOutputService to remove us. | |
49 // 3. AudioOutputService removes its reference, causing Deleter::operator() | |
50 // to run. | |
51 // 4. a. Deleter closes controller_, which then deletes us by callback. | |
52 // b. There is no controller, so Deleter just deletes us. | |
53 DCHECK(task_runner_->BelongsToCurrentThread()); | |
54 if (is_closing) | |
55 return; | |
56 is_closing = true; | |
57 binding_.Close(); | |
58 if (IsStarted()) | |
59 host_->NotifyStreamClosed(id_); | |
60 host_->Remove(id_); | |
61 } | |
62 | |
63 void AudioOutputImpl::Start(const media::AudioParameters& params, | |
64 const StartCallback& callback) { | |
65 DCHECK(task_runner_->BelongsToCurrentThread()); | |
66 DCHECK(!is_closing); | |
67 if (IsStarted()) { | |
68 Close(); | |
69 return; | |
70 } | |
71 Init(params); | |
72 if (!IsStarted()) { | |
73 Close(); | |
74 return; | |
75 } | |
76 | |
77 host_->NotifyStreamCreated(id_, controller_.get(), render_frame_id_); | |
78 // We add the controller to AudioMirroringManager in this class since we | |
79 // have to remove it in this class. | |
80 content::AudioMirroringManager::GetInstance()->AddDiverter( | |
81 render_process_id_, render_frame_id_, controller_.get()); | |
82 | |
83 controller_->Play(); | |
84 start_callback_ = std::move(callback); | |
85 } | |
86 | |
87 void AudioOutputImpl::Play() { | |
88 DCHECK(!is_closing); | |
89 DCHECK(task_runner_->BelongsToCurrentThread()); | |
90 if (!IsStarted()) | |
91 Close(); | |
o1ka
2016/09/28 10:24:06
Here and in other places: what is the purpose of C
Max Morin
2016/09/28 12:47:33
Ok, in this case the client might call Play() befo
| |
92 else | |
93 controller_->Play(); | |
94 } | |
95 | |
96 void AudioOutputImpl::Pause() { | |
97 DCHECK(!is_closing); | |
98 DCHECK(task_runner_->BelongsToCurrentThread()); | |
99 if (!IsStarted()) | |
100 Close(); | |
101 else | |
102 controller_->Pause(); | |
103 } | |
104 | |
105 void AudioOutputImpl::SetVolume(double volume) { | |
106 DCHECK(!is_closing); | |
107 DCHECK(task_runner_->BelongsToCurrentThread()); | |
108 if (!IsStarted() || volume < 0.0 || volume > 1.0) | |
o1ka
2016/09/28 10:24:07
Where these volume limits come from? And what if t
Max Morin
2016/09/28 12:47:33
Good question. Needs better comments.
| |
109 Close(); | |
110 else | |
111 controller_->SetVolume(volume); | |
112 } | |
113 | |
114 void AudioOutputImpl::OnCreated() { | |
115 task_runner_->PostTask( | |
116 FROM_HERE, | |
117 base::Bind(&AudioOutputImpl::DoCompleteCreation, base::Unretained(this))); | |
o1ka
2016/09/28 10:24:07
"this" won't survive
| |
118 } | |
119 | |
120 void AudioOutputImpl::OnPlaying() { | |
o1ka
2016/09/28 10:24:07
Thread check? and what if it is called after Clos
| |
121 host_->NotifyStreamStateChanged(id_, true); | |
122 } | |
123 void AudioOutputImpl::OnPaused() { | |
124 host_->NotifyStreamStateChanged(id_, false); | |
o1ka
2016/09/28 10:24:06
Same as above
| |
125 } | |
126 void AudioOutputImpl::OnError() { | |
127 task_runner_->PostTask( | |
128 FROM_HERE, base::Bind(&AudioOutputImpl::Close, base::Unretained(this))); | |
o1ka
2016/09/28 10:24:06
What is the guarantee that "this" will survive unt
Max Morin
2016/09/28 12:47:33
Ok, so we should maybe have weak pointers to *this
| |
129 } | |
130 | |
131 AudioOutputImpl::AudioOutputImpl(base::SingleThreadTaskRunner* task_runner, | |
132 Host* host, | |
133 int32_t id, | |
134 int render_frame_id, | |
135 int render_process_id, | |
136 const std::string& output_device_id) | |
137 : task_runner_(task_runner), | |
138 host_(host), | |
139 id_(id), | |
140 render_frame_id_(render_frame_id), | |
141 render_process_id_(render_process_id), | |
142 output_device_id_(output_device_id), | |
143 binding_(this) { | |
144 DCHECK(task_runner_->BelongsToCurrentThread()); | |
o1ka
2016/09/28 10:24:07
Do you really need it to be constructed on this th
| |
145 } | |
146 | |
147 void AudioOutputImpl::DoCompleteCreation() { | |
148 DCHECK(task_runner_->BelongsToCurrentThread()); | |
149 if (is_closing) | |
o1ka
2016/09/28 10:24:06
is it actually "closing" or "closed"?
Max Morin
2016/09/28 12:47:33
I'll rework this bit.
| |
150 return; | |
151 DCHECK(reader_); | |
152 DCHECK(binding_.is_bound()); | |
153 | |
154 // Now construction is done and we are ready to send the shared memory and the | |
155 // sync socket to the renderer. | |
156 base::SharedMemory* shared_memory = reader_->shared_memory(); | |
157 base::CancelableSyncSocket* foreign_socket = reader_->foreign_socket(); | |
158 DCHECK(shared_memory); | |
159 DCHECK(foreign_socket); | |
160 | |
161 // Wrap the shared memory for sending: | |
162 base::SharedMemoryHandle foreign_memory_handle = | |
163 base::SharedMemory::DuplicateHandle(shared_memory->handle()); | |
164 // TODO: When does this fail? | |
165 DCHECK(base::SharedMemory::IsHandleValid(foreign_memory_handle)) | |
166 << "Invalid memory handle."; | |
167 mojo::ScopedSharedBufferHandle shared_buffer_handle = | |
168 mojo::WrapSharedMemoryHandle(foreign_memory_handle, | |
169 shared_memory->requested_size(), false); | |
170 | |
171 // TODO: When does this fail? | |
172 DCHECK(shared_buffer_handle.is_valid()); | |
173 | |
174 // TODO: take ownership of sync socket handle, crbug.com/647659 | |
175 start_callback_.Run(std::move(shared_buffer_handle), | |
176 mojo::WrapPlatformFile(foreign_socket->handle())); | |
177 } | |
178 | |
179 void AudioOutputImpl::Init(const media::AudioParameters& params) { | |
180 DCHECK(!is_closing); | |
181 DCHECK(task_runner_->BelongsToCurrentThread()); | |
182 reader_ = AudioSyncReader::Create(params); | |
183 if (!reader_) | |
184 return; | |
o1ka
2016/09/28 10:24:06
Is it a normal situation? If not - DCHECK? (we can
Max Morin
2016/09/28 12:47:33
Should probably be a DCHECK, it happens if we can'
| |
185 controller_ = media::AudioOutputController::Create( | |
186 media::AudioManager::Get(), this, params, output_device_id_, | |
187 reader_.get()); | |
188 DCHECK(controller_); | |
189 } | |
190 | |
191 bool AudioOutputImpl::IsStarted() const { | |
192 DCHECK(task_runner_->BelongsToCurrentThread()); | |
193 return controller_ != nullptr; | |
194 } | |
195 | |
196 void AudioOutputImpl::Deleter::operator()(AudioOutputImpl* output) { | |
197 DCHECK(output->task_runner_->BelongsToCurrentThread()); | |
198 DCHECK(output->is_closing); | |
199 if (output->controller_) | |
200 output->controller_->Close(base::Bind(&AudioOutputImpl::DeleteAndUnregister, | |
201 base::Unretained(output))); | |
202 else | |
203 delete output; | |
204 } | |
205 | |
206 // static | |
207 void AudioOutputImpl::DeleteAndUnregister(AudioOutputImpl* output) { | |
208 // This must be done after controller finished closing, see | |
209 // http://crbug.com/474432. | |
210 AudioMirroringManager::GetInstance()->RemoveDiverter( | |
211 output->controller_.get()); | |
212 output->task_runner_->DeleteSoon(FROM_HERE, output); | |
o1ka
2016/09/28 10:24:06
This deletion is a lot of fun, but can we probably
| |
213 } | |
214 | |
215 } // namespace content | |
OLD | NEW |