Chromium Code Reviews| 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 |