Commit 08410d56 authored by Sergey Ulanov's avatar Sergey Ulanov Committed by Commit Bot

Fuchsia: Implement MixerOutputStreamFuchsia using AudioRenderer2.

Previously MixerOutputStreamFuchsia was using media_client library,
which is deprecated now. Update it to use AudioRenderer2 FIDL interface
directly.

Bug: 851733
Change-Id: I72a43369d16ecd626aa7294a6f3500b57bb3731e
Reviewed-on: https://chromium-review.googlesource.com/1100376Reviewed-by: default avatarKenneth MacKay <kmackay@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568159}
parent 767446e0
...@@ -40,9 +40,8 @@ cast_source_set("cma_backend_support") { ...@@ -40,9 +40,8 @@ cast_source_set("cma_backend_support") {
"//chromecast/public", "//chromecast/public",
"//chromecast/public/media", "//chromecast/public/media",
"//media", "//media",
"//third_party/fuchsia-sdk:media",
] ]
libs = [ "media_client" ]
} }
cast_source_set("unit_tests") { cast_source_set("unit_tests") {
...@@ -56,5 +55,6 @@ cast_source_set("unit_tests") { ...@@ -56,5 +55,6 @@ cast_source_set("unit_tests") {
deps = [ deps = [
":cma_backend_support", ":cma_backend_support",
"//testing/gtest", "//testing/gtest",
"//third_party/fuchsia-sdk:media",
] ]
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_ #ifndef CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_ #define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#include <media/audio.h> #include <fuchsia/media/cpp/fidl.h>
#include "base/memory/shared_memory.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chromecast/public/media/mixer_output_stream.h" #include "chromecast/public/media/mixer_output_stream.h"
...@@ -31,21 +32,36 @@ class MixerOutputStreamFuchsia : public MixerOutputStream { ...@@ -31,21 +32,36 @@ class MixerOutputStreamFuchsia : public MixerOutputStream {
void Stop() override; void Stop() override;
private: private:
bool UpdatePresentationDelay(); size_t GetMinBufferSize();
bool InitializePayloadBuffer();
base::TimeTicks GetCurrentStreamTime(); base::TimeTicks GetCurrentStreamTime();
fuchsia_audio_manager* manager_ = nullptr; // Event handlers for |audio_renderer_|.
fuchsia_audio_output_stream* stream_ = nullptr; void OnRendererError();
void OnMinLeadTimeChanged(int64_t min_lead_time);
int sample_rate_ = 0; int sample_rate_ = 0;
int channels_ = 0; int channels_ = 0;
base::TimeTicks started_time_; // Value returned by OptimalWriteFramesCount().
int target_packet_size_ = 0;
// Audio renderer connection.
fuchsia::media::AudioRenderer2Ptr audio_renderer_;
base::SharedMemory payload_buffer_;
size_t payload_buffer_pos_ = 0;
// Set only while stream is playing.
base::TimeTicks reference_time_;
int64_t stream_position_samples_ = 0; int64_t stream_position_samples_ = 0;
// Total presentation delay for the stream. This value is returned by // Current min lead time for the stream. This value is updated by
// fuchsia_audio_output_stream_get_min_delay() // AudioRenderer::OnMinLeadTimeChanged event. Assume 50ms until we get the
zx_duration_t presentation_delay_ns_ = 0; // first OnMinLeadTimeChanged event.
base::TimeDelta min_lead_time_ = base::TimeDelta::FromMilliseconds(50);
DISALLOW_COPY_AND_ASSIGN(MixerOutputStreamFuchsia); DISALLOW_COPY_AND_ASSIGN(MixerOutputStreamFuchsia);
}; };
......
...@@ -4,19 +4,99 @@ ...@@ -4,19 +4,99 @@
#include "chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h" #include "chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h"
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace chromecast { namespace chromecast {
namespace media { namespace media {
constexpr int kSampleRate = 48000;
constexpr int kNumChannels = 2;
class MixerOutputStreamFuchsiaTest : public ::testing::Test { class MixerOutputStreamFuchsiaTest : public ::testing::Test {
protected: protected:
base::MessageLoopForIO message_loop_;
MixerOutputStreamFuchsia output_; MixerOutputStreamFuchsia output_;
}; };
TEST_F(MixerOutputStreamFuchsiaTest, StartAndStop) { TEST_F(MixerOutputStreamFuchsiaTest, StartAndStop) {
EXPECT_TRUE(output_.Start(48000, 2)); EXPECT_TRUE(output_.Start(kSampleRate, kNumChannels));
EXPECT_EQ(output_.GetSampleRate(), 48000); EXPECT_EQ(output_.GetSampleRate(), kSampleRate);
output_.Stop();
}
TEST_F(MixerOutputStreamFuchsiaTest, Play1s) {
EXPECT_TRUE(output_.Start(kSampleRate, kNumChannels));
constexpr base::TimeDelta kTestStreamDuration =
base::TimeDelta::FromMilliseconds(300);
constexpr float kSignalFrequencyHz = 1000;
auto started = base::TimeTicks::Now();
int samples_to_play =
kSampleRate * kTestStreamDuration / base::TimeDelta::FromSeconds(1);
int pos = 0;
while (pos < samples_to_play) {
std::vector<float> buffer;
int num_frames = output_.OptimalWriteFramesCount();
buffer.resize(num_frames * kNumChannels);
for (int i = 0; i < num_frames; ++i) {
float v = sin(2 * M_PI * pos * kSignalFrequencyHz / kSampleRate);
for (int c = 0; c < kNumChannels; ++c) {
buffer[i * kNumChannels + c] = v;
}
pos += 1;
}
bool interrupted = true;
EXPECT_TRUE(output_.Write(buffer.data(), buffer.size(), &interrupted));
// Run message loop to process async events.
base::RunLoop().RunUntilIdle();
}
auto ended = base::TimeTicks::Now();
// Verify that Write() was blocking, allowing 100ms for buffering.
EXPECT_GT(ended - started,
kTestStreamDuration - base::TimeDelta::FromMilliseconds(100));
output_.Stop();
}
TEST_F(MixerOutputStreamFuchsiaTest, PlaybackInterrupted) {
EXPECT_TRUE(output_.Start(kSampleRate, kNumChannels));
std::vector<float> buffer;
int num_frames = output_.OptimalWriteFramesCount();
buffer.resize(num_frames * kNumChannels);
bool interrupted = true;
// First Write() always returns interrupted = false.
EXPECT_TRUE(output_.Write(buffer.data(), buffer.size(), &interrupted));
EXPECT_FALSE(interrupted);
interrupted = true;
// Repeated Write() is expected to return interrupted = false.
EXPECT_TRUE(output_.Write(buffer.data(), buffer.size(), &interrupted));
EXPECT_FALSE(interrupted);
// Run message loop for 100ms before calling Write() again.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(100));
run_loop.Run();
// Write() is called to late, expect interrupted = true.
interrupted = false;
EXPECT_TRUE(output_.Write(buffer.data(), buffer.size(), &interrupted));
EXPECT_TRUE(interrupted);
output_.Stop(); output_.Stop();
} }
......
...@@ -224,6 +224,10 @@ StreamMixer::StreamMixer( ...@@ -224,6 +224,10 @@ StreamMixer::StreamMixer(
if (mixer_thread_) { if (mixer_thread_) {
base::Thread::Options options; base::Thread::Options options;
options.priority = base::ThreadPriority::REALTIME_AUDIO; options.priority = base::ThreadPriority::REALTIME_AUDIO;
#if defined(OS_FUCHSIA)
// MixerOutputStreamFuchsia uses FIDL, which works only on IO threads.
options.message_loop_type = base::MessageLoop::TYPE_IO;
#endif
mixer_thread_->StartWithOptions(options); mixer_thread_->StartWithOptions(options);
mixer_task_runner_ = mixer_thread_->task_runner(); mixer_task_runner_ = mixer_thread_->task_runner();
mixer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&UseHighPriority)); mixer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&UseHighPriority));
......
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