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 void TrivialAuthorizedCallback(media::OutputDeviceStatus, |
| 17 const media::AudioParameters&, |
| 18 const std::string&) {} |
| 19 |
| 20 } // namespace |
| 21 |
| 22 MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessorCB factory_accessor) |
| 23 : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) { |
| 24 DETACH_FROM_THREAD(thread_checker_); |
| 25 } |
| 26 |
| 27 MojoAudioOutputIPC::~MojoAudioOutputIPC() { |
| 28 DCHECK(!AuthorizationRequested() && !StreamCreationRequested()) |
| 29 << "CloseStream must be called before destructing the AudioOutputIPC"; |
| 30 // No thread check. |
| 31 // Destructing |weak_factory_| on any thread is safe since it's not used after |
| 32 // the final call to CloseStream, where its pointers are invalidated. |
| 33 } |
| 34 |
| 35 void MojoAudioOutputIPC::RequestDeviceAuthorization( |
| 36 media::AudioOutputIPCDelegate* delegate, |
| 37 int session_id, |
| 38 const std::string& device_id, |
| 39 const url::Origin& security_origin) { |
| 40 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 41 DCHECK(delegate); |
| 42 DCHECK(!delegate_); |
| 43 DCHECK(!AuthorizationRequested()); |
| 44 DCHECK(!StreamCreationRequested()); |
| 45 delegate_ = delegate; |
| 46 |
| 47 // We pass in a ScopedClosureRunner to detect the case when the mojo |
| 48 // connection is terminated prior to receiving the response. In this case, |
| 49 // the closure runner will be destructed and call ReceivedDeviceAuthorization |
| 50 // with an error. |
| 51 DoRequestDeviceAuthorization( |
| 52 session_id, device_id, |
| 53 base::BindOnce( |
| 54 &MojoAudioOutputIPC::ReceivedDeviceAuthorization, |
| 55 weak_factory_.GetWeakPtr(), |
| 56 base::ScopedClosureRunner(base::Bind( |
| 57 &MojoAudioOutputIPC::ReceivedDeviceAuthorization, |
| 58 weak_factory_.GetWeakPtr(), |
| 59 base::Passed(base::ScopedClosureRunner()), |
| 60 media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, |
| 61 media::AudioParameters::UnavailableDeviceParams(), |
| 62 std::string())))); |
| 63 } |
| 64 |
| 65 void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate, |
| 66 const media::AudioParameters& params) { |
| 67 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 68 DCHECK(delegate); |
| 69 DCHECK(!StreamCreationRequested()); |
| 70 if (!AuthorizationRequested()) { |
| 71 DCHECK(!delegate_); |
| 72 delegate_ = delegate; |
| 73 // No authorization requested yet. Request one for the default device. |
| 74 // Since the delegate didn't explicitly request authorization, we shouldn't |
| 75 // send a callback to it. |
| 76 if (!DoRequestDeviceAuthorization( |
| 77 0, media::AudioDeviceDescription::kDefaultDeviceId, |
| 78 base::Bind(&TrivialAuthorizedCallback))) { |
| 79 return; |
| 80 } |
| 81 } |
| 82 |
| 83 DCHECK_EQ(delegate_, delegate); |
| 84 // Since the creation callback won't fire if the provider binding is gone |
| 85 // and |this| owns |stream_provider_|, unretained is safe. |
| 86 stream_provider_->Acquire( |
| 87 mojo::MakeRequest(&stream_), params, |
| 88 base::Bind(&MojoAudioOutputIPC::StreamCreated, base::Unretained(this))); |
| 89 |
| 90 // Unretained is safe because |delegate_| must remain valid until |
| 91 // CloseStream is called, and |stream_provider_| is reset in CloseStream. |
| 92 stream_.set_connection_error_handler(base::Bind( |
| 93 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); |
| 94 } |
| 95 |
| 96 void MojoAudioOutputIPC::PlayStream() { |
| 97 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 98 if (stream_.is_bound()) |
| 99 stream_->Play(); |
| 100 } |
| 101 |
| 102 void MojoAudioOutputIPC::PauseStream() { |
| 103 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 104 if (stream_.is_bound()) |
| 105 stream_->Pause(); |
| 106 } |
| 107 |
| 108 void MojoAudioOutputIPC::CloseStream() { |
| 109 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 110 stream_provider_.reset(); |
| 111 stream_.reset(); |
| 112 delegate_ = nullptr; |
| 113 |
| 114 // Cancel any pending callbacks for this stream. |
| 115 weak_factory_.InvalidateWeakPtrs(); |
| 116 } |
| 117 |
| 118 void MojoAudioOutputIPC::SetVolume(double volume) { |
| 119 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 120 if (stream_.is_bound()) |
| 121 stream_->SetVolume(volume); |
| 122 } |
| 123 |
| 124 bool MojoAudioOutputIPC::AuthorizationRequested() { |
| 125 return stream_provider_.is_bound(); |
| 126 } |
| 127 |
| 128 bool MojoAudioOutputIPC::StreamCreationRequested() { |
| 129 return stream_.is_bound(); |
| 130 } |
| 131 |
| 132 media::mojom::AudioOutputStreamProviderRequest |
| 133 MojoAudioOutputIPC::MakeProviderRequest() { |
| 134 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 135 DCHECK(!AuthorizationRequested()); |
| 136 media::mojom::AudioOutputStreamProviderRequest request = |
| 137 mojo::MakeRequest(&stream_provider_); |
| 138 |
| 139 // Unretained is safe because |delegate_| must remain valid until |
| 140 // CloseStream is called, and |stream_provider_| is reset in CloseStream. |
| 141 stream_provider_.set_connection_error_handler(base::Bind( |
| 142 &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); |
| 143 return request; |
| 144 } |
| 145 |
| 146 bool MojoAudioOutputIPC::DoRequestDeviceAuthorization( |
| 147 int session_id, |
| 148 const std::string& device_id, |
| 149 AuthorizationCB callback) { |
| 150 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 151 auto* factory = factory_accessor_.Run(); |
| 152 if (!factory) { |
| 153 LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; |
| 154 |
| 155 media::AudioOutputIPCDelegate* delegate = delegate_; |
| 156 CloseStream(); |
| 157 delegate->OnIPCClosed(); // deletes |this|. |
| 158 return false; |
| 159 } |
| 160 |
| 161 factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id, |
| 162 device_id, std::move(callback)); |
| 163 return true; |
| 164 } |
| 165 |
| 166 void MojoAudioOutputIPC::ReceivedDeviceAuthorization( |
| 167 base::ScopedClosureRunner fallback_closure, |
| 168 media::OutputDeviceStatus status, |
| 169 const media::AudioParameters& params, |
| 170 const std::string& device_id) const { |
| 171 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 172 DCHECK(delegate_); |
| 173 ignore_result(fallback_closure.Release()); |
| 174 delegate_->OnDeviceAuthorized(status, params, device_id); |
| 175 } |
| 176 |
| 177 void MojoAudioOutputIPC::StreamCreated( |
| 178 mojo::ScopedSharedBufferHandle shared_memory, |
| 179 mojo::ScopedHandle socket) { |
| 180 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| 181 DCHECK(delegate_); |
| 182 DCHECK(socket.is_valid()); |
| 183 DCHECK(shared_memory.is_valid()); |
| 184 |
| 185 base::PlatformFile socket_handle; |
| 186 auto result = mojo::UnwrapPlatformFile(std::move(socket), &socket_handle); |
| 187 DCHECK_EQ(result, MOJO_RESULT_OK); |
| 188 |
| 189 base::SharedMemoryHandle memory_handle; |
| 190 bool read_only = false; |
| 191 size_t memory_length = 0; |
| 192 result = mojo::UnwrapSharedMemoryHandle( |
| 193 std::move(shared_memory), &memory_handle, &memory_length, &read_only); |
| 194 DCHECK_EQ(result, MOJO_RESULT_OK); |
| 195 DCHECK(!read_only); |
| 196 |
| 197 delegate_->OnStreamCreated(memory_handle, socket_handle, memory_length); |
| 198 } |
| 199 |
| 200 } // namespace content |
OLD | NEW |