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; |
} |