Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 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/renderer/media/mojo_audio_output_ipc.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "media/audio/audio_device_description.h" | |
| 10 #include "mojo/public/cpp/system/platform_handle.h" | |
| 11 | |
| 12 namespace content { | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 using AuthorizationCallback = | |
| 17 mojom::RendererAudioOutputStreamFactory::RequestDeviceAuthorizationCallback; | |
| 18 | |
| 19 void TrivialAuthorizedCallback(media::OutputDeviceStatus, | |
| 20 const media::AudioParameters&, | |
| 21 const std::string&) {} | |
| 22 | |
| 23 // This class wraps a callback. If this class is destroyed without the callback | |
| 24 // being called, it will call it with an error. This is needed since the | |
| 25 // AudioOutputDevice could otherwise wait forever for device parameters in the | |
| 26 // case of a connection error. | |
| 27 class AuthorizationCallbackWrapper { | |
| 28 public: | |
| 29 static AuthorizationCallback Wrap(AuthorizationCallback callback) { | |
| 30 return base::BindOnce( | |
| 31 AuthorizationCallbackWrapper::Call, | |
| 32 base::Passed(AuthorizationCallbackWrapper(std::move(callback)))); | |
| 33 } | |
| 34 | |
| 35 AuthorizationCallbackWrapper(AuthorizationCallbackWrapper&& other) | |
| 36 : callback_(std::move(other.callback_)) { | |
| 37 // It's not explicitly stated that moving from a OnceCallback resets the | |
| 38 // moved-from callback, so reset it here. | |
| 39 other.callback_ = AuthorizationCallback(); | |
| 40 } | |
| 41 | |
| 42 AuthorizationCallbackWrapper& operator=( | |
| 43 AuthorizationCallbackWrapper&& other) = delete; | |
|
o1ka
2017/05/18 09:16:50
It has a destructor, so move constructor is not ge
Max Morin
2017/05/18 12:30:18
Right, I figured it might generate one since I def
| |
| 44 | |
| 45 ~AuthorizationCallbackWrapper() { | |
| 46 if (callback_) { | |
| 47 std::move(callback_).Run( | |
| 48 media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, | |
| 49 media::AudioParameters::UnavailableDeviceParams(), std::string()); | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 private: | |
| 54 explicit AuthorizationCallbackWrapper(AuthorizationCallback callback) | |
| 55 : callback_(std::move(callback)) {} | |
| 56 | |
| 57 static void Call(AuthorizationCallbackWrapper callback_wrapper, | |
| 58 media::OutputDeviceStatus status, | |
| 59 const media::AudioParameters& params, | |
| 60 const std::string& device_id) { | |
| 61 if (callback_wrapper.callback_) { | |
| 62 std::move(callback_wrapper.callback_).Run(status, params, device_id); | |
| 63 // Make sure we don't call again in destructor: | |
| 64 callback_wrapper.callback_ = AuthorizationCallback(); | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 AuthorizationCallback callback_; | |
| 69 | |
| 70 DISALLOW_COPY_AND_ASSIGN(AuthorizationCallbackWrapper); | |
| 71 }; | |
| 72 | |
| 73 } // namespace | |
| 74 | |
| 75 MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessor factory_accessor) | |
| 76 : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) { | |
| 77 DETACH_FROM_THREAD(thread_checker_); | |
| 78 } | |
| 79 | |
| 80 MojoAudioOutputIPC::~MojoAudioOutputIPC() { | |
| 81 DCHECK(!AuthorizationRequested()); | |
| 82 DCHECK(!StreamCreationRequested()); | |
| 83 // No thread check. | |
| 84 // Destructing |weak_factory_| on any thread is safe since it's not used after | |
| 85 // the final call to CloseStream, where its pointers are invalidated. | |
| 86 } | |
| 87 | |
| 88 void MojoAudioOutputIPC::RequestDeviceAuthorization( | |
| 89 media::AudioOutputIPCDelegate* delegate, | |
| 90 int session_id, | |
| 91 const std::string& device_id, | |
| 92 const url::Origin& security_origin) { | |
| 93 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 94 DCHECK(delegate); | |
| 95 DCHECK(!delegate_); | |
| 96 DCHECK(!AuthorizationRequested()); | |
| 97 DCHECK(!StreamCreationRequested()); | |
| 98 delegate_ = delegate; | |
| 99 | |
| 100 // We wrap the callback here so that we are sure to always get the | |
| 101 // authorization reply, even if the connection is closed. | |
| 102 DoRequestDeviceAuthorization( | |
| 103 session_id, device_id, | |
| 104 AuthorizationCallbackWrapper::Wrap( | |
| 105 base::BindOnce(&MojoAudioOutputIPC::RecievedDeviceAuthorization, | |
| 106 weak_factory_.GetWeakPtr()))); | |
| 107 } | |
| 108 | |
| 109 void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate, | |
| 110 const media::AudioParameters& params) { | |
| 111 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 112 DCHECK(delegate); | |
| 113 DCHECK(!StreamCreationRequested()); | |
| 114 if (!AuthorizationRequested()) { | |
| 115 DCHECK(!delegate_); | |
| 116 delegate_ = delegate; | |
| 117 // No authorization requested yet. Request one for the default device. | |
| 118 // Since the delegate didn't explicitly request authorization, we shouldn't | |
| 119 // send a callback to it. | |
| 120 if (!DoRequestDeviceAuthorization( | |
| 121 0, media::AudioDeviceDescription::kDefaultDeviceId, | |
| 122 base::Bind(&TrivialAuthorizedCallback))) { | |
| 123 return; | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 DCHECK(delegate_ == delegate); | |
| 128 // Since the creation callback won't fire if the provider binding is gone | |
| 129 // and |this| owns |stream_provider_|, unretained is safe. | |
| 130 stream_provider_->Acquire( | |
| 131 mojo::MakeRequest(&stream_), params, | |
| 132 base::Bind(&MojoAudioOutputIPC::StreamCreated, base::Unretained(this))); | |
| 133 | |
| 134 // Unretained is safe because |delegate_| must remain valid until | |
| 135 // CloseStream is called, and |stream_provider_| is reset in CloseStream. | |
| 136 stream_.set_connection_error_handler(base::Bind( | |
| 137 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); | |
| 138 } | |
| 139 | |
| 140 void MojoAudioOutputIPC::PlayStream() { | |
| 141 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 142 if (stream_.is_bound()) | |
| 143 stream_->Play(); | |
| 144 } | |
| 145 | |
| 146 void MojoAudioOutputIPC::PauseStream() { | |
| 147 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 148 if (stream_.is_bound()) | |
| 149 stream_->Pause(); | |
| 150 } | |
| 151 | |
| 152 void MojoAudioOutputIPC::CloseStream() { | |
| 153 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 154 stream_provider_.reset(); | |
| 155 stream_.reset(); | |
| 156 delegate_ = nullptr; | |
| 157 | |
| 158 // Cancel any pending callbacks for this stream. | |
| 159 weak_factory_.InvalidateWeakPtrs(); | |
| 160 } | |
| 161 | |
| 162 void MojoAudioOutputIPC::SetVolume(double volume) { | |
| 163 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 164 if (stream_.is_bound()) | |
| 165 stream_->SetVolume(volume); | |
| 166 } | |
| 167 | |
| 168 bool MojoAudioOutputIPC::AuthorizationRequested() { | |
| 169 return stream_provider_.is_bound(); | |
| 170 } | |
| 171 | |
| 172 bool MojoAudioOutputIPC::StreamCreationRequested() { | |
| 173 return stream_.is_bound(); | |
| 174 } | |
| 175 | |
| 176 media::mojom::AudioOutputStreamProviderRequest | |
| 177 MojoAudioOutputIPC::MakeProviderRequest() { | |
| 178 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 179 DCHECK(!AuthorizationRequested()); | |
| 180 media::mojom::AudioOutputStreamProviderRequest request = | |
| 181 mojo::MakeRequest(&stream_provider_); | |
| 182 | |
| 183 // Unretained is safe because |delegate_| must remain valid until | |
| 184 // CloseStream is called, and |stream_provider_| is reset in CloseStream. | |
| 185 stream_provider_.set_connection_error_handler(base::Bind( | |
| 186 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); | |
| 187 return request; | |
| 188 } | |
| 189 | |
| 190 bool MojoAudioOutputIPC::DoRequestDeviceAuthorization( | |
| 191 int session_id, | |
| 192 const std::string& device_id, | |
| 193 AuthorizationCallback callback) { | |
| 194 auto* factory = factory_accessor_.Run(); | |
|
o1ka
2017/05/18 09:16:50
DCHECK_CALLED_ON_VALID_THREAD? (just for easy read
Max Morin
2017/05/18 12:30:18
Done.
| |
| 195 if (!factory) { | |
| 196 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; | |
| 197 | |
| 198 media::AudioOutputIPCDelegate* delegate = delegate_; | |
| 199 CloseStream(); | |
| 200 delegate->OnIPCClosed(); // deletes |this|. | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 // We wrap the callback here so that we are sure to always get the | |
| 205 // authorization reply, even if the connection is closed. | |
| 206 factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id, | |
| 207 device_id, std::move(callback)); | |
| 208 return true; | |
| 209 } | |
| 210 | |
| 211 void MojoAudioOutputIPC::RecievedDeviceAuthorization( | |
| 212 media::OutputDeviceStatus status, | |
| 213 const media::AudioParameters& params, | |
| 214 const std::string& device_id) const { | |
| 215 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 216 DCHECK(delegate_); | |
| 217 delegate_->OnDeviceAuthorized(status, params, device_id); | |
| 218 } | |
| 219 | |
| 220 void MojoAudioOutputIPC::StreamCreated( | |
| 221 mojo::ScopedSharedBufferHandle shared_memory, | |
| 222 mojo::ScopedHandle socket) { | |
| 223 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | |
| 224 DCHECK(delegate_); | |
| 225 DCHECK(socket.is_valid()); | |
| 226 DCHECK(shared_memory.is_valid()); | |
| 227 | |
| 228 base::PlatformFile socket_handle; | |
| 229 mojo::UnwrapPlatformFile(std::move(socket), &socket_handle); | |
|
o1ka
2017/05/18 09:16:50
Please DCHECK :)
Max Morin
2017/05/18 12:30:18
Done.
| |
| 230 | |
| 231 base::SharedMemoryHandle memory_handle; | |
| 232 bool read_only = false; | |
| 233 size_t memory_length = 0; | |
| 234 auto result = mojo::UnwrapSharedMemoryHandle( | |
| 235 std::move(shared_memory), &memory_handle, &memory_length, &read_only); | |
| 236 DCHECK_EQ(result, MOJO_RESULT_OK); | |
| 237 DCHECK(!read_only); | |
| 238 | |
| 239 delegate_->OnStreamCreated(memory_handle, socket_handle, memory_length); | |
| 240 } | |
| 241 | |
| 242 } // namespace content | |
| OLD | NEW |