Chromium Code Reviews| Index: content/browser/renderer_host/media/audio_renderer_host.cc |
| diff --git a/content/browser/renderer_host/media/audio_renderer_host.cc b/content/browser/renderer_host/media/audio_renderer_host.cc |
| index 4ee8318b52146df977496b4cea5b4c97d6235944..7a2fbbc1489ab9552ee9539e4a10d7513c6d2a75 100644 |
| --- a/content/browser/renderer_host/media/audio_renderer_host.cc |
| +++ b/content/browser/renderer_host/media/audio_renderer_host.cc |
| @@ -23,7 +23,6 @@ |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| -#include "content/common/media/audio_messages.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/media_device_id.h" |
| #include "content/public/browser/media_observer.h" |
| @@ -33,6 +32,11 @@ |
| #include "media/audio/audio_streams_tracker.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/limits.h" |
| +#include "media/mojo/interfaces/audio_output.mojom-shared.h" |
| +#include "media/mojo/interfaces/audio_output.mojom.h" |
| +#include "mojo/edk/embedder/embedder.h" |
| +#include "mojo/public/cpp/system/handle.h" |
| +#include "mojo/public/cpp/system/platform_handle.h" |
| using media::AudioBus; |
| using media::AudioManager; |
| @@ -132,15 +136,18 @@ void ValidateRenderFrameId(int render_process_id, |
| } // namespace |
| class AudioRendererHost::AudioEntry |
|
Henrik Grunell
2016/09/01 15:09:07
Maybe this should be renamed, but let's settle the
|
| - : public media::AudioOutputController::EventHandler { |
| + : public media::AudioOutputController::EventHandler, |
| + private media::mojom::AudioOutputStream { |
| public: |
| AudioEntry(AudioRendererHost* host, |
| + CreateStreamCallback callback, |
| int stream_id, |
| int render_frame_id, |
| const media::AudioParameters& params, |
| const std::string& output_device_id, |
| std::unique_ptr<base::SharedMemory> shared_memory, |
| - std::unique_ptr<media::AudioOutputController::SyncReader> reader); |
| + std::unique_ptr<media::AudioOutputController::SyncReader> reader, |
| + media::mojom::AudioOutputStreamClientPtr output_stream_client); |
| ~AudioEntry() override; |
| int stream_id() const { |
| @@ -162,6 +169,15 @@ class AudioRendererHost::AudioEntry |
| bool playing() const { return playing_; } |
| void set_playing(bool playing) { playing_ = playing; } |
| + // media::mojom::AudioOutputStream implementation |
| + void Play() override; |
| + void Pause() override; |
| + void SetVolume(double volume) override; |
| + |
| + void OnConnectionError(); |
| + bool SendStreamStateResponse(media::mojom::AudioOutputStreamState state); |
| + void BindRequest(media::mojom::AudioOutputStreamRequest); |
| + |
| private: |
| // media::AudioOutputController::EventHandler implementation. |
| void OnCreated() override; |
| @@ -184,17 +200,30 @@ class AudioRendererHost::AudioEntry |
| // The AudioOutputController that manages the audio stream. |
| const scoped_refptr<media::AudioOutputController> controller_; |
| + // Used for communication with the bound AudioOutputStreamClient. |
| + media::mojom::AudioOutputStreamClientPtr output_stream_client; |
|
o1ka
2016/09/02 07:31:46
output_stream_client_
|
| + |
| + // Binds |this| to an AudioOutputStreamPtr that is passed to the |
| + // AudioOutputStreamClient (AudioOutputIPC in the renderer process). |
| + std::unique_ptr<mojo::Binding<media::mojom::AudioOutputStream>> binding_; |
| + |
| + CreateStreamCallback callback_; |
| + |
| bool playing_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(AudioEntry); |
| }; |
| AudioRendererHost::AudioEntry::AudioEntry( |
| AudioRendererHost* host, |
| + CreateStreamCallback callback, |
|
o1ka
2016/09/02 07:31:46
const &
|
| int stream_id, |
| int render_frame_id, |
| const media::AudioParameters& params, |
| const std::string& output_device_id, |
| std::unique_ptr<base::SharedMemory> shared_memory, |
| - std::unique_ptr<media::AudioOutputController::SyncReader> reader) |
| + std::unique_ptr<media::AudioOutputController::SyncReader> reader, |
| + media::mojom::AudioOutputStreamClientPtr output_stream_client) |
| : host_(host), |
| stream_id_(stream_id), |
| render_frame_id_(render_frame_id), |
| @@ -205,8 +234,64 @@ AudioRendererHost::AudioEntry::AudioEntry( |
| params, |
| output_device_id, |
| reader_.get())), |
| + output_stream_client(std::move(output_stream_client)), |
| + callback_(callback), |
| playing_(false) { |
| DCHECK(controller_.get()); |
| + |
| + if (output_stream_client.is_bound()) |
| + output_stream_client.set_connection_error_handler( |
| + base::Bind(&AudioEntry::OnConnectionError, base::Unretained(this))); |
| + else |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioRendererHost::OnCloseStream, host_, stream_id_)); |
|
o1ka
2016/09/02 07:31:46
|host_| is a raw pointer which AudioEntry does not
|
| +} |
| + |
| +void AudioRendererHost::AudioEntry::Play() { |
| + controller_->Play(); |
| + host_->audio_log_->OnStarted(stream_id_); |
| +} |
| + |
| +void AudioRendererHost::AudioEntry::Pause() { |
| + controller_->Pause(); |
| + host_->audio_log_->OnStopped(stream_id_); |
| +} |
| + |
| +void AudioRendererHost::AudioEntry::SetVolume(double volume) { |
| + if (volume < 0 || volume > 1.0) |
| + return; |
| + |
| + controller_->SetVolume(volume); |
| + host_->audio_log_->OnSetVolume(stream_id_, volume); |
| +} |
| + |
| +void AudioRendererHost::AudioEntry::OnConnectionError() { |
| + binding_.reset(); |
| + output_stream_client.reset(); |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioRendererHost::CloseStream, host_, stream_id_)); |
|
o1ka
2016/09/02 07:31:46
Same as above.
AudioRendererHost should probably h
|
| +} |
| + |
| +bool AudioRendererHost::AudioEntry::SendStreamStateResponse( |
| + media::mojom::AudioOutputStreamState state) { |
| + if (!output_stream_client.is_bound()) |
| + return false; |
| + |
| + output_stream_client->OnStreamStateChange(state); |
| + return true; |
| +} |
| + |
| +void AudioRendererHost::AudioEntry::BindRequest( |
| + media::mojom::AudioOutputStreamRequest request) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + binding_.reset(new mojo::Binding<media::mojom::AudioOutputStream>( |
| + this, std::move(request))); |
| + binding_->set_connection_error_handler( |
| + base::Bind(&AudioEntry::OnConnectionError, base::Unretained(this))); |
| } |
| AudioRendererHost::AudioEntry::~AudioEntry() {} |
| @@ -220,8 +305,7 @@ AudioRendererHost::AudioRendererHost(int render_process_id, |
| MediaInternals* media_internals, |
| MediaStreamManager* media_stream_manager, |
| const std::string& salt) |
| - : BrowserMessageFilter(AudioMsgStart), |
| - render_process_id_(render_process_id), |
| + : render_process_id_(render_process_id), |
| audio_manager_(audio_manager), |
| mirroring_manager_(mirroring_manager), |
| audio_log_(media_internals->CreateAudioLog( |
| @@ -263,96 +347,76 @@ void AudioRendererHost::GetOutputControllers( |
| base::Bind(&AudioRendererHost::DoGetOutputControllers, this), callback); |
| } |
| -void AudioRendererHost::OnChannelClosing() { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - // Since the IPC sender is gone, close all requested audio streams. |
| - while (!audio_entries_.empty()) { |
| - // Note: OnCloseStream() removes the entries from audio_entries_. |
| - OnCloseStream(audio_entries_.begin()->first); |
| - } |
| - |
| - // Remove any authorizations for streams that were not yet created |
| - authorizations_.clear(); |
| -} |
| - |
| -void AudioRendererHost::OnDestruct() const { |
| - BrowserThread::DeleteOnIOThread::Destruct(this); |
| -} |
| - |
| void AudioRendererHost::AudioEntry::OnCreated() { |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_)); |
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioRendererHost::DoCompleteCreation, |
| + host_, stream_id_, callback_)); |
|
o1ka
2016/09/02 07:31:46
same as above
|
| } |
| void AudioRendererHost::AudioEntry::OnPlaying() { |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged, |
| - host_, |
| - stream_id_, |
| - true)); |
| + output_stream_client->OnStreamStateChange( |
| + ::media::mojom::AudioOutputStreamState::PLAYING); |
| } |
| void AudioRendererHost::AudioEntry::OnPaused() { |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged, |
| - host_, |
| - stream_id_, |
| - false)); |
| + output_stream_client->OnStreamStateChange( |
| + ::media::mojom::AudioOutputStreamState::PAUSED); |
| } |
| void AudioRendererHost::AudioEntry::OnError() { |
| + output_stream_client->OnStreamStateChange( |
| + ::media::mojom::AudioOutputStreamState::ERROR); |
| BrowserThread::PostTask( |
| - BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&AudioRendererHost::ReportErrorAndClose, host_, stream_id_)); |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&AudioRendererHost::OnCloseStream, host_, stream_id_)); |
| } |
| -void AudioRendererHost::DoCompleteCreation(int stream_id) { |
| +void AudioRendererHost::DoCompleteCreation(int stream_id, |
| + CreateStreamCallback callback) { |
|
o1ka
2016/09/02 07:31:46
const &
Max Morin
2016/09/02 10:27:07
mojo gets upset if I do this. This inefficiency is
|
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - if (!PeerHandle()) { |
| - DLOG(WARNING) << "Renderer process handle is invalid."; |
| - ReportErrorAndClose(stream_id); |
| - return; |
| - } |
| - |
| AudioEntry* const entry = LookupById(stream_id); |
| if (!entry) { |
| ReportErrorAndClose(stream_id); |
| return; |
| } |
| - // Once the audio stream is created then complete the creation process by |
| - // mapping shared memory and sharing with the renderer process. |
| - base::SharedMemoryHandle foreign_memory_handle; |
| - if (!entry->shared_memory()->ShareToProcess(PeerHandle(), |
| - &foreign_memory_handle)) { |
| - // If we failed to map and share the shared memory then close the audio |
| - // stream and send an error message. |
| - ReportErrorAndClose(entry->stream_id()); |
| - return; |
| - } |
| + media::mojom::AudioOutputStreamPtr stream_ptr; |
| + entry->BindRequest(mojo::GetProxy(&stream_ptr)); |
| AudioSyncReader* reader = static_cast<AudioSyncReader*>(entry->reader()); |
| - |
| - base::SyncSocket::TransitDescriptor socket_descriptor; |
| - |
| - // If we failed to prepare the sync socket for the renderer then we fail |
| - // the construction of audio stream. |
| - if (!reader->PrepareForeignSocket(PeerHandle(), &socket_descriptor)) { |
| - ReportErrorAndClose(entry->stream_id()); |
| - return; |
| - } |
| - |
| - Send(new AudioMsg_NotifyStreamCreated( |
| - entry->stream_id(), foreign_memory_handle, socket_descriptor, |
| - entry->shared_memory()->requested_size())); |
| + mojo::ScopedHandle socket_handle = |
| + mojo::WrapPlatformFile(reader->GetSyncSocket()); |
| + |
| + base::SharedMemoryHandle shared_memory_handle = |
| + base::SharedMemory::DuplicateHandle(entry->shared_memory()->handle()); |
| + if (!base::SharedMemory::IsHandleValid(shared_memory_handle)) |
| + return ReportErrorAndClose(stream_id); |
| + |
| + mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| + mojo::WrapSharedMemoryHandle(shared_memory_handle, |
| + entry->shared_memory()->requested_size(), |
| + false); |
| + |
| + /* TODO: investigate comment. |
| + // The socket sent from the browser to the renderer is a ForeignSocket, which |
| + // is a part of AudioSyncReader that is owned by AudioEntry. The socket handle |
| + // is closed when the owning AudioEntry destructs. With mojo, the ownership of |
| + // the handle is transferred to the target process. The handle is no longer |
| + // valid in the sending process, and cannot be closed there. If the socket |
| + // handle is closed when the AudioEntry is deleted, an error occurs. To WAR |
| + // this error, duplicate the socket and send the duplicate to the renderer. |
| + #if defined(OS_WIN) |
| + mojo::ScopedHandle socket_handle = |
| + mojo::WrapPlatformFile(socket_descriptor); |
| + #else |
| + mojo::ScopedHandle socket_handle = |
| + mojo::WrapPlatformFile(socket_descriptor.fd); |
| + #endif |
| + */ |
| + |
| + callback.Run(std::move(stream_ptr), std::move(shared_buffer_handle), |
| + std::move(socket_handle)); |
| } |
| void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id, |
| @@ -363,10 +427,9 @@ void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id, |
| if (!entry) |
| return; |
| - Send(new AudioMsg_NotifyStreamStateChanged( |
| - stream_id, |
| - is_playing ? media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING |
| - : media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED)); |
| + entry->SendStreamStateResponse( |
| + is_playing ? media::mojom::AudioOutputStreamState::PLAYING |
| + : media::mojom::AudioOutputStreamState::PAUSED); |
| if (is_playing) { |
| AudioStreamMonitor::StartMonitoringStream( |
| @@ -396,91 +459,13 @@ AudioRendererHost::DoGetOutputControllers() const { |
| return controllers; |
| } |
| -/////////////////////////////////////////////////////////////////////////////// |
| -// IPC Messages handler |
| -bool AudioRendererHost::OnMessageReceived(const IPC::Message& message) { |
| - bool handled = true; |
| - IPC_BEGIN_MESSAGE_MAP(AudioRendererHost, message) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_RequestDeviceAuthorization, |
| - OnRequestDeviceAuthorization) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream, OnCreateStream) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream, OnPlayStream) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) |
| - IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) |
| - IPC_MESSAGE_UNHANDLED(handled = false) |
| - IPC_END_MESSAGE_MAP() |
| - |
| - return handled; |
| -} |
| - |
| -void AudioRendererHost::OnRequestDeviceAuthorization( |
| +void AudioRendererHost::OnDeviceAuthorized( |
| int stream_id, |
| - int render_frame_id, |
| - int session_id, |
| + RequestDeviceAuthorizationCallback callback, |
| const std::string& device_id, |
| - const url::Origin& security_origin) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - const base::TimeTicks auth_start_time = base::TimeTicks::Now(); |
| - |
| - DVLOG(1) << "AudioRendererHost@" << this << "::OnRequestDeviceAuthorization" |
| - << "(stream_id=" << stream_id |
| - << ", render_frame_id=" << render_frame_id |
| - << ", session_id=" << session_id << ", device_id=" << device_id |
| - << ", security_origin=" << security_origin << ")"; |
| - |
| - if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) |
| - return; |
| - |
| - if (!IsValidDeviceId(device_id)) { |
| - UMALogDeviceAuthorizationTime(auth_start_time); |
| - Send(new AudioMsg_NotifyDeviceAuthorized( |
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, |
| - media::AudioParameters::UnavailableDeviceParams(), std::string())); |
| - return; |
| - } |
| - |
| - // If |session_id should be used for output device selection and such output |
| - // device is found, reuse the input device permissions. |
| - if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, |
| - device_id)) { |
| - const StreamDeviceInfo* info = |
| - media_stream_manager_->audio_input_device_manager() |
| - ->GetOpenedDeviceInfoById(session_id); |
| - if (info) { |
| - media::AudioParameters output_params( |
| - media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| - static_cast<media::ChannelLayout>( |
| - info->device.matched_output.channel_layout), |
| - info->device.matched_output.sample_rate, 16, |
| - info->device.matched_output.frames_per_buffer); |
| - output_params.set_effects(info->device.matched_output.effects); |
| - authorizations_.insert(MakeAuthorizationData( |
| - stream_id, true, info->device.matched_output_device_id)); |
| - MaybeFixAudioParameters(&output_params); |
| - UMALogDeviceAuthorizationTime(auth_start_time); |
| - // Hash matched device id and pass it to the renderer |
| - Send(new AudioMsg_NotifyDeviceAuthorized( |
| - stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params, |
| - GetHMACForMediaDeviceID(salt_, security_origin, |
| - info->device.matched_output_device_id))); |
| - return; |
| - } |
| - } |
| - |
| - authorizations_.insert( |
| - MakeAuthorizationData(stream_id, false, std::string())); |
| - CheckOutputDeviceAccess( |
| - render_frame_id, device_id, security_origin, |
| - base::Bind(&AudioRendererHost::OnDeviceAuthorized, this, stream_id, |
| - device_id, security_origin, auth_start_time)); |
| -} |
| - |
| -void AudioRendererHost::OnDeviceAuthorized(int stream_id, |
| - const std::string& device_id, |
| - const url::Origin& security_origin, |
| - base::TimeTicks auth_start_time, |
| - bool have_access) { |
| + const url::Origin& security_origin, |
| + base::TimeTicks auth_start_time, |
| + bool have_access) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| const auto& auth_data = authorizations_.find(stream_id); |
| @@ -493,9 +478,9 @@ void AudioRendererHost::OnDeviceAuthorized(int stream_id, |
| if (!have_access) { |
| authorizations_.erase(auth_data); |
| UMALogDeviceAuthorizationTime(auth_start_time); |
| - Send(new AudioMsg_NotifyDeviceAuthorized( |
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, |
| - media::AudioParameters::UnavailableDeviceParams(), std::string())); |
| + SendAuthorizationMessage( |
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, |
| + media::AudioParameters::UnavailableDeviceParams(), std::string()); |
| return; |
| } |
| @@ -510,19 +495,20 @@ void AudioRendererHost::OnDeviceAuthorized(int stream_id, |
| audio_manager_->GetTaskRunner(), FROM_HERE, |
| base::Bind(&GetDefaultDeviceInfoOnDeviceThread, audio_manager_), |
| base::Bind(&AudioRendererHost::OnDeviceIDTranslated, this, stream_id, |
| - auth_start_time, true)); |
| + auth_start_time, callback, true)); |
| } else { |
| media_stream_manager_->audio_output_device_enumerator()->Enumerate( |
| base::Bind(&AudioRendererHost::TranslateDeviceID, this, device_id, |
| security_origin, |
| base::Bind(&AudioRendererHost::OnDeviceIDTranslated, this, |
| - stream_id, auth_start_time))); |
| + stream_id, auth_start_time, callback))); |
| } |
| } |
| void AudioRendererHost::OnDeviceIDTranslated( |
| int stream_id, |
| base::TimeTicks auth_start_time, |
| + RequestDeviceAuthorizationCallback callback, |
| bool device_found, |
| const AudioOutputDeviceInfo& device_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| @@ -537,9 +523,9 @@ void AudioRendererHost::OnDeviceIDTranslated( |
| if (!device_found) { |
| authorizations_.erase(auth_data); |
| UMALogDeviceAuthorizationTime(auth_start_time); |
| - Send(new AudioMsg_NotifyDeviceAuthorized( |
| - stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, |
| - media::AudioParameters::UnavailableDeviceParams(), std::string())); |
| + SendAuthorizationMessage( |
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, |
| + media::AudioParameters::UnavailableDeviceParams(), std::string()); |
| return; |
| } |
| @@ -549,49 +535,18 @@ void AudioRendererHost::OnDeviceIDTranslated( |
| media::AudioParameters output_params = device_info.output_params; |
| MaybeFixAudioParameters(&output_params); |
| UMALogDeviceAuthorizationTime(auth_start_time); |
| - Send(new AudioMsg_NotifyDeviceAuthorized( |
| - stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params, std::string())); |
| -} |
| - |
| -void AudioRendererHost::OnCreateStream(int stream_id, |
| - int render_frame_id, |
| - const media::AudioParameters& params) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream" |
| - << "(stream_id=" << stream_id << ")"; |
| - |
| - // Determine whether to use the device_unique_id from an authorization, or an |
| - // empty string (i.e., when no previous authorization was requested, assume |
| - // default device). |
| - std::string device_unique_id; |
| - const auto& auth_data = authorizations_.find(stream_id); |
| - if (auth_data != authorizations_.end()) { |
| - CHECK(auth_data->second.first); |
| - device_unique_id.swap(auth_data->second.second); |
| - authorizations_.erase(auth_data); |
| - } |
| - |
| -#if DCHECK_IS_ON() |
| - // When DCHECKs are turned on, hop over to the UI thread to validate the |
| - // |render_frame_id|, then continue stream creation on the IO thread. See |
| - // comment at top of DoCreateStream() for further details. |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, FROM_HERE, |
| - base::Bind(validate_render_frame_id_function_, render_process_id_, |
| - render_frame_id, |
| - base::Bind(&AudioRendererHost::DoCreateStream, this, stream_id, |
| - render_frame_id, params, device_unique_id))); |
| -#else |
| - DoCreateStream(stream_id, render_frame_id, params, device_unique_id, |
| - render_frame_id > 0); |
| -#endif // DCHECK_IS_ON() |
| + SendAuthorizationMessage(callback, stream_id, media::OUTPUT_DEVICE_STATUS_OK, |
| + output_params, std::string()); |
| } |
| -void AudioRendererHost::DoCreateStream(int stream_id, |
| - int render_frame_id, |
| - const media::AudioParameters& params, |
| - const std::string& device_unique_id, |
| - bool render_frame_id_is_valid) { |
| +void AudioRendererHost::DoCreateStream( |
| + int stream_id, |
| + int render_frame_id, |
| + const media::AudioParameters& params, |
| + const std::string& device_unique_id, |
| + media::mojom::AudioOutputStreamClientPtr client, |
| + CreateStreamCallback callback, |
| + bool render_frame_id_is_valid) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // Fail early if either of two sanity-checks fail: |
| @@ -619,6 +574,7 @@ void AudioRendererHost::DoCreateStream(int stream_id, |
| AudioBus::CalculateMemorySize(params); |
| std::unique_ptr<base::SharedMemory> shared_memory(new base::SharedMemory()); |
| if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) { |
| + client->OnStreamStateChange(media::mojom::AudioOutputStreamState::ERROR); |
| SendErrorMessage(stream_id); |
| return; |
| } |
| @@ -635,9 +591,9 @@ void AudioRendererHost::DoCreateStream(int stream_id, |
| if (media_observer) |
| media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id); |
| - std::unique_ptr<AudioEntry> entry( |
| - new AudioEntry(this, stream_id, render_frame_id, params, device_unique_id, |
| - std::move(shared_memory), std::move(reader))); |
| + std::unique_ptr<AudioEntry> entry(new AudioEntry( |
| + this, callback, stream_id, render_frame_id, params, device_unique_id, |
| + std::move(shared_memory), std::move(reader), std::move(client))); |
| if (mirroring_manager_) { |
| mirroring_manager_->AddDiverter( |
| render_process_id_, entry->render_frame_id(), entry->controller()); |
| @@ -696,8 +652,9 @@ void AudioRendererHost::OnSetVolume(int stream_id, double volume) { |
| } |
| void AudioRendererHost::SendErrorMessage(int stream_id) { |
| - Send(new AudioMsg_NotifyStreamStateChanged( |
| - stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR)); |
| + AudioEntry* entry = LookupById(stream_id); |
|
o1ka
2016/09/02 07:31:46
I suspect it's safe to do on IO thread only.
|
| + if (entry) |
| + entry->SendStreamStateResponse(media::mojom::AudioOutputStreamState::ERROR); |
| } |
| void AudioRendererHost::OnCloseStream(int stream_id) { |
| @@ -791,6 +748,132 @@ bool AudioRendererHost::HasActiveAudio() { |
| return !base::AtomicRefCountIsZero(&num_playing_streams_); |
| } |
| +void AudioRendererHost::BindRequest(media::mojom::AudioOutputRequest request) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + binding_.reset( |
| + new mojo::Binding<media::mojom::AudioOutput>(this, std::move(request))); |
| +} |
| + |
| +// media::mojom::AudioOutput implementation |
| +void AudioRendererHost::RequestDeviceAuthorization( |
| + int stream_id, |
| + int render_frame_id, |
| + int session_id, |
| + const mojo::String& device_id, |
| + const url::Origin& origin, |
| + const RequestDeviceAuthorizationCallback& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + const base::TimeTicks auth_start_time = base::TimeTicks::Now(); |
| + |
| + DVLOG(1) << "AudioRendererHost@" << this << "::RequestDeviceAuthorization" |
| + << "(stream_id=" << stream_id |
| + << ", render_frame_id=" << render_frame_id |
| + << ", session_id=" << session_id << ", device_id=" << device_id |
| + << ", origin=" << origin << ")"; |
| + |
| + if (LookupById(stream_id) || IsAuthorizationStarted(stream_id)) |
| + return; |
| + |
| + if (!IsValidDeviceId(device_id)) { |
| + UMALogDeviceAuthorizationTime(auth_start_time); |
| + SendAuthorizationMessage( |
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND, |
| + media::AudioParameters::UnavailableDeviceParams(), std::string()); |
| + return; |
| + } |
| + |
| + // If |session_id should be used for output device selection and such output |
| + // device is found, reuse the input device permissions. |
| + if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, |
| + device_id)) { |
| + const StreamDeviceInfo* info = |
| + media_stream_manager_->audio_input_device_manager() |
| + ->GetOpenedDeviceInfoById(session_id); |
| + if (info) { |
| + media::AudioParameters output_params( |
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| + static_cast<media::ChannelLayout>( |
| + info->device.matched_output.channel_layout), |
| + info->device.matched_output.sample_rate, 16, |
| + info->device.matched_output.frames_per_buffer); |
| + output_params.set_effects(info->device.matched_output.effects); |
| + authorizations_.insert(MakeAuthorizationData( |
| + stream_id, true, info->device.matched_output_device_id)); |
| + MaybeFixAudioParameters(&output_params); |
| + UMALogDeviceAuthorizationTime(auth_start_time); |
| + // Hash matched device id and pass it to the renderer |
| + SendAuthorizationMessage( |
| + callback, stream_id, media::OUTPUT_DEVICE_STATUS_OK, output_params, |
| + GetHMACForMediaDeviceID(salt_, origin, |
| + info->device.matched_output_device_id)); |
| + return; |
| + } |
| + } |
| + |
| + authorizations_.insert( |
| + MakeAuthorizationData(stream_id, false, std::string())); |
| + CheckOutputDeviceAccess( |
| + render_frame_id, device_id, origin, |
| + base::Bind(&AudioRendererHost::OnDeviceAuthorized, this, stream_id, |
| + callback, device_id, origin, auth_start_time)); |
| +} |
| + |
| +void AudioRendererHost::CreateStream( |
| + int stream_id, |
| + int render_frame_id, |
| + media::mojom::AudioOutputStreamClientPtr client, |
| + const media::AudioParameters& params, |
| + const CreateStreamCallback& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DVLOG(1) << "AudioRendererHost@" << this << "::CreateStream" |
| + << "(stream_id=" << stream_id << ")"; |
| + |
| + // Determine whether to use the device_unique_id from an authorization, or an |
| + // empty string (i.e., when no previous authorization was requested, assume |
| + // default device). |
| + std::string device_unique_id; |
| + const auto& auth_data = authorizations_.find(stream_id); |
| + if (auth_data != authorizations_.end()) { |
| + CHECK(auth_data->second.first); |
| + device_unique_id.swap(auth_data->second.second); |
| + authorizations_.erase(auth_data); |
| + } |
| + |
| +#if DCHECK_IS_ON() |
| + // When DCHECKs are turned on, hop over to the UI thread to validate the |
| + // |render_frame_id|, then continue stream creation on the IO thread. See |
| + // comment at top of DoCreateStream() for further details. |
| + BrowserThread::PostTask( |
|
o1ka
2016/09/02 11:30:55
Beware of this bug https://bugs.chromium.org/p/chr
Max Morin
2016/09/02 11:37:30
Thanks for the heads up. Maybe we can eliminate Lo
|
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(validate_render_frame_id_function_, render_process_id_, |
| + render_frame_id, |
| + base::Bind(&AudioRendererHost::DoCreateStream, this, stream_id, |
| + render_frame_id, params, device_unique_id, |
| + base::Passed(&client), callback))); |
| +#else |
| + DoCreateStream(stream_id, render_frame_id, params, device_unique_id, |
| + std::move(client), callback, render_frame_id > 0); |
| +#endif // DCHECK_IS_ON() |
| +} |
| + |
| +void AudioRendererHost::CloseStream(int stream_id) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + OnCloseStream(stream_id); |
| +} |
| + |
| +void AudioRendererHost::SendAuthorizationMessage( |
| + RequestDeviceAuthorizationCallback callback, |
| + int stream_id, |
| + media::OutputDeviceStatus status, |
| + const media::AudioParameters& params, |
| + const std::string& matched_device_id) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + |
| + callback.Run(status, params, matched_device_id); |
| +} |
| + |
| void AudioRendererHost::CheckOutputDeviceAccess( |
| int render_frame_id, |
| const std::string& device_id, |
| @@ -803,8 +886,8 @@ void AudioRendererHost::CheckOutputDeviceAccess( |
| if (!media::AudioDeviceDescription::IsDefaultDevice(device_id) && |
| !MediaStreamManager::IsOriginAllowed(render_process_id_, |
| security_origin)) { |
| - content::bad_message::ReceivedBadMessage(this, |
| - bad_message::ARH_UNAUTHORIZED_URL); |
| + // content::bad_message::ReceivedBadMessage(this, |
| + // bad_message::ARH_UNAUTHORIZED_URL); |
| return; |
| } |