blob: 0b4710c2c8e690baa7bf938c82e80530e1b1f5d5 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h"
#include <utility>
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/task/post_task.h"
#include "base/task_runner_util.h"
#include "content/browser/media/media_devices_permission_checker.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/media_device_id.h"
#include "content/public/common/content_features.h"
#include "media/base/audio_parameters.h"
namespace content {
namespace {
void CheckPermissionAndGetSaltAndOrigin(
const std::string& output_device_id,
int render_process_id,
int render_frame_id,
base::OnceCallback<void(const MediaDeviceSaltAndOrigin&)> cb) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto salt_and_origin =
GetMediaDeviceSaltAndOrigin(render_process_id, render_frame_id);
// Check permissions for everything but the default device
if (!media::AudioDeviceDescription::IsDefaultDevice(output_device_id) &&
!MediaDevicesPermissionChecker().CheckPermissionOnUIThread(
blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT, render_process_id,
render_frame_id)) {
// If we're not allowed to use the device, don't call |cb|.
return;
}
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
base::BindOnce(std::move(cb), salt_and_origin));
}
void OldEnumerateOutputDevices(
MediaDevicesManager* media_devices_manager,
base::RepeatingCallback<
void(const MediaDeviceSaltAndOrigin& salt_and_origin,
const MediaDeviceEnumeration& devices)> cb,
const MediaDeviceSaltAndOrigin& salt_and_origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
MediaDevicesManager::BoolDeviceTypes device_types;
device_types[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = true;
media_devices_manager->EnumerateDevices(device_types,
base::BindOnce(cb, salt_and_origin));
}
} // namespace
// static
std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>
RenderFrameAudioInputStreamFactoryHandle::CreateFactory(
OldRenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
content::MediaStreamManager* media_stream_manager,
int render_process_id,
int render_frame_id,
mojom::RendererAudioInputStreamFactoryRequest request) {
std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>
handle(new RenderFrameAudioInputStreamFactoryHandle(
std::move(create_delegate_callback), media_stream_manager,
render_process_id, render_frame_id));
// Unretained is safe since |*handle| must be posted to the IO thread prior to
// deletion.
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&RenderFrameAudioInputStreamFactoryHandle::Init,
base::Unretained(handle.get()), std::move(request)));
return handle;
}
RenderFrameAudioInputStreamFactoryHandle::
~RenderFrameAudioInputStreamFactoryHandle() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
RenderFrameAudioInputStreamFactoryHandle::
RenderFrameAudioInputStreamFactoryHandle(
OldRenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
MediaStreamManager* media_stream_manager,
int render_process_id,
int render_frame_id)
: impl_(std::move(create_delegate_callback),
media_stream_manager,
render_process_id,
render_frame_id),
binding_(&impl_) {}
void RenderFrameAudioInputStreamFactoryHandle::Init(
mojom::RendererAudioInputStreamFactoryRequest request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
binding_.Bind(std::move(request));
}
OldRenderFrameAudioInputStreamFactory::OldRenderFrameAudioInputStreamFactory(
CreateDelegateCallback create_delegate_callback,
MediaStreamManager* media_stream_manager,
int render_process_id,
int render_frame_id)
: create_delegate_callback_(std::move(create_delegate_callback)),
media_stream_manager_(media_stream_manager),
render_process_id_(render_process_id),
render_frame_id_(render_frame_id),
weak_ptr_factory_(this) {
DCHECK(create_delegate_callback_);
// No thread-hostile state has been initialized yet, so we don't have to bind
// to this specific thread.
}
OldRenderFrameAudioInputStreamFactory::
~OldRenderFrameAudioInputStreamFactory() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void OldRenderFrameAudioInputStreamFactory::CreateStream(
mojom::RendererAudioInputStreamFactoryClientPtr client,
int32_t session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count,
audio::mojom::AudioProcessingConfigPtr processing_config) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// |processing_config| gets dropped here. It's not supported outside of the
// audio service. As this class is slated for removal, it will not be updated
// to support audio processing.
#if defined(OS_CHROMEOS)
if (audio_params.channel_layout() ==
media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
media_stream_manager_->audio_input_device_manager()
->RegisterKeyboardMicStream(base::BindOnce(
&OldRenderFrameAudioInputStreamFactory::DoCreateStream,
weak_ptr_factory_.GetWeakPtr(), std::move(client), session_id,
audio_params, automatic_gain_control, shared_memory_count));
return;
}
#endif
DoCreateStream(std::move(client), session_id, audio_params,
automatic_gain_control, shared_memory_count,
AudioInputDeviceManager::KeyboardMicRegistration());
}
void OldRenderFrameAudioInputStreamFactory::DoCreateStream(
mojom::RendererAudioInputStreamFactoryClientPtr client,
int session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count,
AudioInputDeviceManager::KeyboardMicRegistration
keyboard_mic_registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int stream_id = ++next_stream_id_;
media::mojom::AudioLogPtr audio_log_ptr =
MediaInternals::GetInstance()->CreateMojoAudioLog(
media::AudioLogFactory::AUDIO_INPUT_CONTROLLER, stream_id,
render_process_id_, render_frame_id_);
// Unretained is safe since |this| owns |streams_|.
streams_.insert(std::make_unique<AudioInputStreamHandle>(
std::move(client),
base::BindOnce(
create_delegate_callback_,
base::Unretained(media_stream_manager_->audio_input_device_manager()),
std::move(audio_log_ptr), std::move(keyboard_mic_registration),
shared_memory_count, stream_id, session_id, automatic_gain_control,
audio_params),
base::BindOnce(&OldRenderFrameAudioInputStreamFactory::RemoveStream,
weak_ptr_factory_.GetWeakPtr())));
}
void OldRenderFrameAudioInputStreamFactory::AssociateInputAndOutputForAec(
const base::UnguessableToken& input_stream_id,
const std::string& output_device_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsValidDeviceId(output_device_id))
return;
if (media::AudioDeviceDescription::IsDefaultDevice(output_device_id)) {
for (const auto& stream : streams_) {
if (stream->id() == input_stream_id) {
stream->SetOutputDeviceForAec(output_device_id);
return;
}
}
} else {
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(
CheckPermissionAndGetSaltAndOrigin, output_device_id,
render_process_id_, render_frame_id_,
base::BindOnce(
&OldEnumerateOutputDevices,
media_stream_manager_->media_devices_manager(),
base::BindRepeating(&OldRenderFrameAudioInputStreamFactory::
TranslateAndSetOutputDeviceForAec,
weak_ptr_factory_.GetWeakPtr(),
input_stream_id, output_device_id))));
}
}
void OldRenderFrameAudioInputStreamFactory::TranslateAndSetOutputDeviceForAec(
const base::UnguessableToken& input_stream_id,
const std::string& output_device_id,
const MediaDeviceSaltAndOrigin& salt_and_origin,
const MediaDeviceEnumeration& device_array) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::string raw_output_device_id;
for (const auto& device_info :
device_array[blink::MEDIA_DEVICE_TYPE_AUDIO_OUTPUT]) {
if (MediaStreamManager::DoesMediaDeviceIDMatchHMAC(
salt_and_origin.device_id_salt, salt_and_origin.origin,
output_device_id, device_info.device_id)) {
raw_output_device_id = device_info.device_id;
}
}
if (!raw_output_device_id.empty()) {
for (const auto& stream : streams_) {
if (stream->id() == input_stream_id) {
stream->SetOutputDeviceForAec(raw_output_device_id);
return;
}
}
}
}
void OldRenderFrameAudioInputStreamFactory::RemoveStream(
AudioInputStreamHandle* stream) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
streams_.erase(stream);
}
} // namespace content