Commit cec6c171 authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

Fuchsia: Move AudioOutputStreamFuchsia to AudioRenderer2.

Previously AudioOutputStreamFuchsia was using media_client library,
which is deprecated now. Update //media to use AudioRenderer2 FIDL
interface directly.

Bug: 851733
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I356a0ae08798c2eb62d4cb42355aff4c47fbb360
Reviewed-on: https://chromium-review.googlesource.com/1096414
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567376}
parent c6b80124
......@@ -317,7 +317,7 @@ source_set("audio") {
"fuchsia/audio_output_stream_fuchsia.cc",
"fuchsia/audio_output_stream_fuchsia.h",
]
libs += [ "media_client" ]
deps += [ "//third_party/fuchsia-sdk:media" ]
}
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
......
......@@ -53,7 +53,7 @@ class AudioOutputTest : public ::testing::Test {
}
protected:
base::MessageLoop message_loop_;
base::MessageLoopForIO message_loop_;
std::unique_ptr<AudioManager> audio_manager_;
std::unique_ptr<AudioDeviceInfoAccessorForTests> audio_manager_device_info_;
AudioParameters stream_params_;
......
......@@ -10,10 +10,14 @@
namespace media {
AudioThreadImpl::AudioThreadImpl() : thread_("AudioThread") {
base::Thread::Options thread_options;
#if defined(OS_WIN)
thread_.init_com_with_mta(true);
#elif defined(OS_FUCHSIA)
// FIDL-based APIs require async_t, which is initialized on IO thread.
thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
#endif
CHECK(thread_.Start());
CHECK(thread_.StartWithOptions(thread_options));
#if defined(OS_MACOSX)
// On Mac, the audio task runner must belong to the main thread.
......
......@@ -15,16 +15,14 @@ namespace media {
AudioManagerFuchsia::AudioManagerFuchsia(
std::unique_ptr<AudioThread> audio_thread,
AudioLogFactory* audio_log_factory)
: AudioManagerBase(std::move(audio_thread), audio_log_factory),
fuchsia_audio_manager_(fuchsia_audio_manager_create()) {}
: AudioManagerBase(std::move(audio_thread), audio_log_factory) {}
AudioManagerFuchsia::~AudioManagerFuchsia() {
fuchsia_audio_manager_free(fuchsia_audio_manager_);
}
AudioManagerFuchsia::~AudioManagerFuchsia() = default;
bool AudioManagerFuchsia::HasAudioOutputDevices() {
return fuchsia_audio_manager_get_output_devices(fuchsia_audio_manager_,
nullptr, 0) > 0;
// TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
// enumeration. Update this method when that functionality is implemented.
return true;
}
bool AudioManagerFuchsia::HasAudioInputDevices() {
......@@ -40,33 +38,9 @@ void AudioManagerFuchsia::GetAudioInputDeviceNames(
void AudioManagerFuchsia::GetAudioOutputDeviceNames(
AudioDeviceNames* device_names) {
device_names->clear();
std::vector<fuchsia_audio_device_description> descriptions;
descriptions.resize(16);
bool try_again = true;
while (try_again) {
int result = fuchsia_audio_manager_get_output_devices(
fuchsia_audio_manager_, descriptions.data(), descriptions.size());
if (result < 0) {
LOG(ERROR) << "fuchsia_audio_manager_get_output_devices() returned "
<< result;
device_names->clear();
return;
}
// Try again if the buffer was too small.
try_again = static_cast<size_t>(result) > descriptions.size();
descriptions.resize(result);
}
// Create default device if we have any output devices present.
if (!descriptions.empty())
device_names->push_back(AudioDeviceName::CreateDefault());
for (auto& desc : descriptions) {
device_names->push_back(AudioDeviceName(desc.name, desc.id));
}
// TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
// enumeration. Update this method when that functionality is implemented.
*device_names = {AudioDeviceName::CreateDefault()};
}
AudioParameters AudioManagerFuchsia::GetInputStreamParameters(
......@@ -78,32 +52,11 @@ AudioParameters AudioManagerFuchsia::GetInputStreamParameters(
AudioParameters AudioManagerFuchsia::GetPreferredOutputStreamParameters(
const std::string& output_device_id,
const AudioParameters& input_params) {
fuchsia_audio_parameters device_params;
int result = fuchsia_audio_manager_get_output_device_default_parameters(
fuchsia_audio_manager_,
output_device_id == AudioDeviceDescription::kDefaultDeviceId
? nullptr
: const_cast<char*>(output_device_id.c_str()),
&device_params);
if (result < 0) {
LOG(ERROR) << "fuchsia_audio_manager_get_default_output_device_parameters()"
" returned "
<< result;
return AudioParameters();
}
int user_buffer_size = GetUserBufferSize();
if (user_buffer_size > 0)
device_params.buffer_size = user_buffer_size;
int sample_rate = input_params.sample_rate();
if (sample_rate < 8000 || sample_rate > 96000)
sample_rate = device_params.sample_rate;
// TODO(crbug.com/852834): Fuchsia currently doesn't provide an API to get
// device configuration. Update this method when that functionality is
// implemented.
return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
GuessChannelLayout(device_params.num_channels),
sample_rate, device_params.buffer_size);
CHANNEL_LAYOUT_STEREO, 48000, 480);
}
const char* AudioManagerFuchsia::GetName() {
......@@ -122,7 +75,13 @@ AudioOutputStream* AudioManagerFuchsia::MakeLowLatencyOutputStream(
const std::string& device_id,
const LogCallback& log_callback) {
DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
return new AudioOutputStreamFuchsia(this, device_id, params);
if (!device_id.empty() &&
device_id != AudioDeviceDescription::kDefaultDeviceId) {
return nullptr;
}
return new AudioOutputStreamFuchsia(this, params);
}
AudioInputStream* AudioManagerFuchsia::MakeLinearInputStream(
......
......@@ -5,8 +5,6 @@
#ifndef MEDIA_AUDIO_FUCHSIA_AUDIO_MANAGER_FUCHSIA_H_
#define MEDIA_AUDIO_FUCHSIA_AUDIO_MANAGER_FUCHSIA_H_
#include <media/audio.h>
#include "media/audio/audio_manager_base.h"
namespace media {
......@@ -43,18 +41,12 @@ class AudioManagerFuchsia : public AudioManagerBase {
const std::string& device_id,
const LogCallback& log_callback) override;
fuchsia_audio_manager* GetFuchsiaAudioManager() const {
return fuchsia_audio_manager_;
}
protected:
AudioParameters GetPreferredOutputStreamParameters(
const std::string& output_device_id,
const AudioParameters& input_params) override;
private:
fuchsia_audio_manager* fuchsia_audio_manager_;
DISALLOW_COPY_AND_ASSIGN(AudioManagerFuchsia);
};
......
......@@ -4,9 +4,9 @@
#include "media/audio/fuchsia/audio_output_stream_fuchsia.h"
#include <media/audio.h>
#include <zircon/syscalls.h>
#include "base/fuchsia/component_context.h"
#include "media/audio/fuchsia/audio_manager_fuchsia.h"
#include "media/base/audio_sample_types.h"
#include "media/base/audio_timestamp_helper.h"
......@@ -15,43 +15,53 @@ namespace media {
AudioOutputStreamFuchsia::AudioOutputStreamFuchsia(
AudioManagerFuchsia* manager,
const std::string& device_id,
const AudioParameters& parameters)
: manager_(manager),
device_id_(device_id),
parameters_(parameters),
audio_bus_(AudioBus::Create(parameters)),
buffer_(parameters_.frames_per_buffer() * parameters_.channels()) {}
audio_bus_(AudioBus::Create(parameters)) {}
AudioOutputStreamFuchsia::~AudioOutputStreamFuchsia() {
// Close() must be called first.
DCHECK(!stream_);
DCHECK(!audio_renderer_);
}
bool AudioOutputStreamFuchsia::Open() {
DCHECK(!stream_);
fuchsia_audio_parameters fuchsia_params;
fuchsia_params.sample_rate = parameters_.sample_rate();
fuchsia_params.num_channels = parameters_.channels();
fuchsia_params.buffer_size = parameters_.frames_per_buffer();
int result = fuchsia_audio_manager_create_output_stream(
manager_->GetFuchsiaAudioManager(), const_cast<char*>(device_id_.c_str()),
&fuchsia_params, &stream_);
if (result < 0) {
DLOG(ERROR) << "Failed to open audio output " << device_id_
<< " error code: " << result;
DCHECK(!stream_);
return false;
}
DCHECK(!audio_renderer_);
// Connect |audio_renderer_| to the audio service.
fuchsia::media::AudioPtr audio_server =
base::fuchsia::ComponentContext::GetDefault()
->ConnectToService<fuchsia::media::Audio>();
audio_server->CreateRendererV2(audio_renderer_.NewRequest());
audio_renderer_.set_error_handler(
fit::bind_member(this, &AudioOutputStreamFuchsia::OnRendererError));
// Inform the |audio_renderer_| of the format required by the caller.
fuchsia::media::AudioPcmFormat format;
format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
format.channels = parameters_.channels();
format.frames_per_second = parameters_.sample_rate();
audio_renderer_->SetPcmFormat(std::move(format));
// Use number of samples to specify media position.
audio_renderer_->SetPtsUnits(parameters_.sample_rate(), 1);
// Setup OnMinLeadTimeChanged event listener. This event is used to get
// |min_lead_time_|, which indicates how far ahead audio samples need to be
// sent to the renderer.
audio_renderer_.events().OnMinLeadTimeChanged =
fit::bind_member(this, &AudioOutputStreamFuchsia::OnMinLeadTimeChanged);
audio_renderer_->EnableMinLeadTimeEvents(true);
// The renderer may fail initialization asynchronously, which is handled in
// OnRendererError().
return true;
}
void AudioOutputStreamFuchsia::Start(AudioSourceCallback* callback) {
DCHECK(!callback_);
DCHECK(started_time_.is_null());
DCHECK(reference_time_.is_null());
DCHECK(!timer_.IsRunning());
callback_ = callback;
PumpSamples();
......@@ -59,7 +69,8 @@ void AudioOutputStreamFuchsia::Start(AudioSourceCallback* callback) {
void AudioOutputStreamFuchsia::Stop() {
callback_ = nullptr;
started_time_ = base::TimeTicks();
reference_time_ = base::TimeTicks();
audio_renderer_->FlushNoReply();
timer_.Stop();
}
......@@ -74,95 +85,140 @@ void AudioOutputStreamFuchsia::GetVolume(double* volume) {
void AudioOutputStreamFuchsia::Close() {
Stop();
if (stream_) {
fuchsia_audio_output_stream_free(stream_);
stream_ = nullptr;
}
audio_renderer_.Unbind();
// Signal to the manager that we're closed and can be removed. This should be
// the last call in the function as it deletes "this".
// the last call in the function as it deletes |this|.
manager_->ReleaseOutputStream(this);
}
base::TimeTicks AudioOutputStreamFuchsia::GetCurrentStreamTime() {
DCHECK(!started_time_.is_null());
return started_time_ +
DCHECK(!reference_time_.is_null());
return reference_time_ +
AudioTimestampHelper::FramesToTime(stream_position_samples_,
parameters_.sample_rate());
}
bool AudioOutputStreamFuchsia::UpdatePresentationDelay() {
int result = fuchsia_audio_output_stream_get_min_delay(
stream_, &presentation_delay_ns_);
if (result != ZX_OK) {
DLOG(ERROR) << "fuchsia_audio_output_stream_get_min_delay() failed: "
<< result;
callback_->OnError();
size_t AudioOutputStreamFuchsia::GetMinBufferSize() {
// Ensure that |payload_buffer_| fits enough packets to cover min_lead_time_
// plus one extra packet.
int min_packets = (AudioTimestampHelper::TimeToFrames(
min_lead_time_, parameters_.sample_rate()) +
parameters_.frames_per_buffer() - 1) /
parameters_.frames_per_buffer() +
1;
return parameters_.GetBytesPerBuffer(kSampleFormatF32) * min_packets;
}
bool AudioOutputStreamFuchsia::InitializePayloadBuffer() {
size_t buffer_size = GetMinBufferSize();
if (!payload_buffer_.CreateAndMapAnonymous(buffer_size)) {
LOG(WARNING) << "Failed to allocate VMO of size " << buffer_size;
return false;
}
payload_buffer_pos_ = 0;
audio_renderer_->SetPayloadBuffer(
zx::vmo(payload_buffer_.handle().Duplicate().GetHandle()));
return true;
}
void AudioOutputStreamFuchsia::OnMinLeadTimeChanged(int64_t min_lead_time) {
min_lead_time_ = base::TimeDelta::FromNanoseconds(min_lead_time);
// When min_lead_time_ increases we may need to reallocate |payload_buffer_|.
// Code below just unmaps the current buffer. The new buffer will be allocated
// lated in PumpSamples(). This is necessary because VMO allocation may fail
// and it's not possible to report that error here - OnMinLeadTimeChanged()
// may be invoked before Start().
if (payload_buffer_.mapped_size() > 0 &&
GetMinBufferSize() > payload_buffer_.mapped_size()) {
payload_buffer_.Unmap();
}
}
void AudioOutputStreamFuchsia::OnRendererError() {
LOG(WARNING) << "AudioRenderer has failed.";
Stop();
callback_->OnError();
}
void AudioOutputStreamFuchsia::PumpSamples() {
DCHECK(stream_);
DCHECK(audio_renderer_);
// Allocate payload buffer if necessary.
if (!payload_buffer_.mapped_size() && !InitializePayloadBuffer()) {
Stop();
callback_->OnError();
return;
}
base::TimeTicks now = base::TimeTicks::Now();
// Reset stream position if:
// 1. The stream wasn't previously running.
// 2. We missed timer deadline, e.g. after the system was suspended.
if (started_time_.is_null() || now > GetCurrentStreamTime()) {
if (!UpdatePresentationDelay())
return;
base::TimeDelta delay;
if (reference_time_.is_null()) {
delay = min_lead_time_;
} else {
auto stream_time = GetCurrentStreamTime();
started_time_ = base::TimeTicks();
}
// Adjust stream position if we missed timer deadline.
if (now + min_lead_time_ > stream_time) {
stream_position_samples_ += AudioTimestampHelper::TimeToFrames(
now + min_lead_time_ - stream_time, parameters_.sample_rate());
}
base::TimeDelta delay =
base::TimeDelta::FromMicroseconds(presentation_delay_ns_ / 1000);
if (!started_time_.is_null())
delay += GetCurrentStreamTime() - now;
delay = stream_time - now;
}
int frames_filled = callback_->OnMoreData(delay, now, 0, audio_bus_.get());
DCHECK_EQ(frames_filled, audio_bus_->frames());
audio_bus_->Scale(volume_);
size_t packet_size = parameters_.GetBytesPerBuffer(kSampleFormatF32);
DCHECK_LE(payload_buffer_pos_ + packet_size, payload_buffer_.mapped_size());
audio_bus_->ToInterleaved<media::Float32SampleTypeTraits>(
audio_bus_->frames(), buffer_.data());
do {
zx_time_t presentation_time = FUCHSIA_AUDIO_NO_TIMESTAMP;
if (started_time_.is_null()) {
// Presentation time (PTS) needs to be specified only for the first frame
// after stream is started or restarted. Mixer will calculate PTS for all
// following frames. 1us is added to account for the time passed between
// zx_clock_get() and fuchsia_audio_output_stream_write().
zx_time_t zx_now = zx_clock_get(ZX_CLOCK_MONOTONIC);
presentation_time = zx_now + presentation_delay_ns_ + ZX_USEC(1);
started_time_ = base::TimeTicks::FromZxTime(zx_now);
stream_position_samples_ = 0;
}
int result = fuchsia_audio_output_stream_write(
stream_, buffer_.data(), buffer_.size(), presentation_time);
if (result == ZX_ERR_IO_MISSED_DEADLINE) {
DLOG(ERROR) << "AudioOutputStreamFuchsia::PumpSamples() missed deadline, "
"resetting PTS.";
if (!UpdatePresentationDelay())
return;
started_time_ = base::TimeTicks();
} else if (result != ZX_OK) {
DLOG(ERROR) << "fuchsia_audio_output_stream_write() returned " << result;
callback_->OnError();
}
} while (started_time_.is_null());
audio_bus_->frames(),
reinterpret_cast<float*>(
reinterpret_cast<uint8_t*>(payload_buffer_.memory()) +
payload_buffer_pos_));
fuchsia::media::AudioPacket packet;
packet.timestamp = stream_position_samples_;
packet.payload_offset = payload_buffer_pos_;
packet.payload_size = packet_size;
packet.flags = 0;
audio_renderer_->SendPacketNoReply(std::move(packet));
stream_position_samples_ += frames_filled;
payload_buffer_pos_ =
(payload_buffer_pos_ + packet_size) % payload_buffer_.mapped_size();
if (reference_time_.is_null()) {
audio_renderer_->Play(
fuchsia::media::kNoTimestamp, stream_position_samples_ - frames_filled,
[this](int64_t reference_time, int64_t media_time) {
if (!callback_)
return;
reference_time_ = base::TimeTicks::FromZxTime(reference_time);
stream_position_samples_ = media_time;
SchedulePumpSamples(base::TimeTicks::Now());
});
} else {
SchedulePumpSamples(now);
}
}
timer_.Start(FROM_HERE,
GetCurrentStreamTime() - base::TimeTicks::Now() -
parameters_.GetBufferDuration() / 2,
void AudioOutputStreamFuchsia::SchedulePumpSamples(base::TimeTicks now) {
base::TimeTicks next_pump_time = GetCurrentStreamTime() - min_lead_time_ -
parameters_.GetBufferDuration() / 2;
timer_.Start(FROM_HERE, next_pump_time - now,
base::Bind(&AudioOutputStreamFuchsia::PumpSamples,
base::Unretained(this)));
}
......
......@@ -5,8 +5,9 @@
#ifndef MEDIA_AUDIO_FUCHSIA_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#define MEDIA_AUDIO_FUCHSIA_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#include <media/audio.h>
#include <fuchsia/media/cpp/fidl.h>
#include "base/memory/shared_memory.h"
#include "base/timer/timer.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_parameters.h"
......@@ -19,7 +20,6 @@ class AudioOutputStreamFuchsia : public AudioOutputStream {
public:
// Caller must ensure that manager outlives the stream.
AudioOutputStreamFuchsia(AudioManagerFuchsia* manager,
const std::string& device_id,
const AudioParameters& parameters);
// AudioOutputStream interface.
......@@ -33,35 +33,53 @@ class AudioOutputStreamFuchsia : public AudioOutputStream {
private:
~AudioOutputStreamFuchsia() override;
// Returns minimum |payload_buffer_| size for the current |min_lead_time_|.
size_t GetMinBufferSize();
// Allocates and maps |payload_buffer_|.
bool InitializePayloadBuffer();
base::TimeTicks GetCurrentStreamTime();
// Updates |presentation_delay_ns_|.
bool UpdatePresentationDelay();
// Event handler for |audio_renderer_|.
void OnMinLeadTimeChanged(int64_t min_lead_time);
// Error handler for |audio_renderer_|.
void OnRendererError();
// Requests data from AudioSourceCallback, passes it to the mixer and
// schedules |timer_| for the next call.
void PumpSamples();
// Schedules |timer_| to call PumpSamples() when appropriate for the next
// packet.
void SchedulePumpSamples(base::TimeTicks now);
AudioManagerFuchsia* manager_;
std::string device_id_;
AudioParameters parameters_;
// These are used only in PumpSamples(). They are kept here to avoid
// Audio renderer connection.
fuchsia::media::AudioRenderer2Ptr audio_renderer_;
// |audio_bus_| is used only in PumpSamples(). It is kept here to avoid
// reallocating the memory every time.
std::unique_ptr<AudioBus> audio_bus_;
std::vector<float> buffer_;
fuchsia_audio_output_stream* stream_ = nullptr;
base::SharedMemory payload_buffer_;
size_t payload_buffer_pos_ = 0;
AudioSourceCallback* callback_ = nullptr;
double volume_ = 1.0;
base::TimeTicks started_time_;
int64_t stream_position_samples_ = 0;
base::TimeTicks reference_time_;
int64_t stream_position_samples_;
// Total presentation delay for the stream. This value is returned by
// fuchsia_audio_output_stream_get_min_delay()
zx_duration_t presentation_delay_ns_ = 0;
// Current min lead time for the stream. This value is updated by
// AudioRenderer::OnMinLeadTimeChanged event. Assume 50ms until we get the
// first OnMinLeadTimeChanged event.
base::TimeDelta min_lead_time_ = base::TimeDelta::FromMilliseconds(50);
// Timer that's scheduled to call PumpSamples().
base::OneShotTimer timer_;
......
......@@ -206,6 +206,7 @@ fuchsia_sdk_fidl_pkg("media") {
namespace_path = "fuchsia"
sources = [
"audio.fidl",
"audio_capturer.fidl",
"audio_renderer.fidl",
"media_renderer.fidl",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment