Commit 042c62af authored by Sergey Ulanov's avatar Sergey Ulanov

Implement MediaPipelineBackend for Fuchsia

The new backend reuses most of ALSA backend. There are two parts that
are different:
 - Added new AudioOutputStreamFuchsia to be used instead of
   AudioOutputStreamAlsa.
 - Added stub for SystemVolumeControl.

Bug: 772488
Change-Id: Icacb070323f30c3d7e6a50bd1f5cf5a58c4251f9
Reviewed-on: https://chromium-review.googlesource.com/722323
Commit-Queue: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Reviewed-by: default avatarJames Robinson <jamesr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#510116}
parent fc2b2f04
......@@ -130,6 +130,9 @@ class SlewVolumeBaseTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(SlewVolumeBaseTest);
};
// ASSERT_DEATH isn't implemented on Fuchsia.
#if defined(ASSERT_DEATH)
TEST_F(SlewVolumeBaseTest, BadSampleRate) {
// String arguments aren't passed to CHECK() in official builds.
#if defined(OFFICIAL_BUILD) && defined(NDEBUG)
......@@ -143,6 +146,8 @@ TEST_F(SlewVolumeBaseTest, BadSlewTime) {
ASSERT_DEATH(slew_volume_->SetMaxSlewTimeMs(-1), "");
}
#endif // defined(ASSERT_DEATH)
class SlewVolumeSteadyStateTest : public SlewVolumeBaseTest {
protected:
SlewVolumeSteadyStateTest() = default;
......
......@@ -12,6 +12,7 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "chromecast/base/task_runner_impl.h"
......@@ -29,6 +30,10 @@
#include "chromecast/media/cma/backend/audio_features.h"
#endif // defined(OS_LINUX)
#if defined(OS_FUCHSIA)
#include <zircon/syscalls.h>
#endif // defined(OS_FUCHSIA)
#define TRACE_FUNCTION_ENTRY0() TRACE_EVENT0("cma", __FUNCTION__)
#define TRACE_FUNCTION_ENTRY1(arg1) \
......@@ -70,7 +75,9 @@ int64_t MonotonicClockNow() {
return static_cast<int64_t>(now.tv_sec) * 1000000 + now.tv_nsec / 1000;
}
#else
#error Need MonotonicClockNow implementation.
int64_t MonotonicClockNow() {
return zx_time_get(ZX_CLOCK_MONOTONIC) / 1000;
}
#endif
} // namespace
......
......@@ -11,7 +11,6 @@ assert(is_fuchsia)
cast_shared_library("libcast_media_1.0_fuchsia") {
sources = [
"cast_media_shlib_fuchsia.cc",
"media_capabilities_shlib_fuchsia.cc",
]
deps = [
......@@ -26,10 +25,9 @@ cast_shared_library("libcast_media_1.0_fuchsia") {
source_set("fuchsia_cma_backend") {
sources = [
"audio_decoder_fuchsia.cc",
"audio_decoder_fuchsia.h",
"media_pipeline_backend_fuchsia.cc",
"media_pipeline_backend_fuchsia.h",
"fuchsia_volume_control.cc",
"mixer_output_stream_fuchsia.cc",
"mixer_output_stream_fuchsia.h",
]
deps = [
......@@ -43,11 +41,10 @@ source_set("fuchsia_cma_backend") {
source_set("unit_tests") {
testonly = true
sources = [
"media_pipeline_backend_fuchsia_unittest.cc",
"mixer_output_stream_fuchsia_unittest.cc",
]
deps = [
":fuchsia_cma_backend",
"//chromecast/media/cma/backend:test_support",
"//testing/gtest",
]
}
// 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 "chromecast/media/cma/backend/fuchsia/audio_decoder_fuchsia.h"
#include "base/memory/ref_counted.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
namespace chromecast {
namespace media {
AudioDecoderFuchsia::AudioDecoderFuchsia() {}
AudioDecoderFuchsia::~AudioDecoderFuchsia() {}
// static
int64_t MediaPipelineBackend::AudioDecoder::GetMinimumBufferedTime(
const AudioConfig& config) {
NOTIMPLEMENTED();
return 2000000; // 200ms
}
bool AudioDecoderFuchsia::Initialize() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return true;
}
bool AudioDecoderFuchsia::Start(int64_t start_pts) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return false;
}
void AudioDecoderFuchsia::Stop() {
NOTIMPLEMENTED();
}
bool AudioDecoderFuchsia::Pause() {
NOTIMPLEMENTED();
return false;
}
bool AudioDecoderFuchsia::Resume() {
NOTIMPLEMENTED();
return false;
}
bool AudioDecoderFuchsia::SetPlaybackRate(float rate) {
NOTIMPLEMENTED();
return false;
}
int64_t AudioDecoderFuchsia::GetCurrentPts() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return 0;
}
bool AudioDecoderFuchsia::SetConfig(const AudioConfig& config) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return IsValidConfig(config);
}
bool AudioDecoderFuchsia::SetVolume(float multiplier) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return true;
}
AudioDecoderFuchsia::RenderingDelay AudioDecoderFuchsia::GetRenderingDelay() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
return RenderingDelay();
}
void AudioDecoderFuchsia::GetStatistics(Statistics* statistics) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
*statistics = stats_;
}
void AudioDecoderFuchsia::SetDelegate(Delegate* delegate) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
NOTIMPLEMENTED();
}
AudioDecoderFuchsia::BufferStatus AudioDecoderFuchsia::PushBuffer(
CastDecoderBuffer* buffer) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
scoped_refptr<DecoderBufferBase> buffer_base(
static_cast<DecoderBufferBase*>(buffer));
NOTIMPLEMENTED();
return MediaPipelineBackend::kBufferSuccess;
}
} // namespace media
} // namespace chromecast
// 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.
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_AUDIO_DECODER_FUCHSIA_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_AUDIO_DECODER_FUCHSIA_H_
#include "base/threading/thread_checker.h"
#include "chromecast/public/media/media_pipeline_backend.h"
namespace chromecast {
namespace media {
class AudioDecoderFuchsia : public MediaPipelineBackend::AudioDecoder {
public:
AudioDecoderFuchsia();
~AudioDecoderFuchsia();
void Initialize();
bool Start(int64_t start_pts);
void Stop();
bool Pause();
bool Resume();
bool SetPlaybackRate(float rate);
int64_t GetCurrentPts() const;
// AudioDecoder interface.
bool SetConfig(const AudioConfig& config) override;
bool SetVolume(float multiplier) override;
RenderingDelay GetRenderingDelay() override;
void GetStatistics(Statistics* statistics) override;
// Decoder interface.
void SetDelegate(Delegate* delegate) override;
BufferStatus PushBuffer(CastDecoderBuffer* buffer) override;
private:
Statistics stats_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(AudioDecoderFuchsia);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_AUDIO_DECODER_FUCHSIA_H_
......@@ -12,7 +12,8 @@
#include "base/threading/thread_task_runner_handle.h"
#include "chromecast/base/init_command_line_shlib.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/media/cma/backend/fuchsia/media_pipeline_backend_fuchsia.h"
#include "chromecast/media/cma/backend/media_pipeline_backend_audio.h"
#include "chromecast/media/cma/backend/stream_mixer.h"
#include "chromecast/public/graphics_types.h"
#include "chromecast/public/media/media_pipeline_device_params.h"
#include "chromecast/public/video_plane.h"
......@@ -77,7 +78,7 @@ MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend(
g_thread_task_runner_handle = new base::ThreadTaskRunnerHandle(task_runner);
}
return new MediaPipelineBackendFuchsia(params);
return new MediaPipelineBackendAudio(params);
}
double CastMediaShlib::GetMediaClockRate() {
......@@ -104,17 +105,17 @@ bool CastMediaShlib::SupportsMediaClockRateChange() {
}
void CastMediaShlib::AddLoopbackAudioObserver(LoopbackAudioObserver* observer) {
NOTIMPLEMENTED();
StreamMixer::Get()->AddLoopbackAudioObserver(observer);
}
void CastMediaShlib::RemoveLoopbackAudioObserver(
LoopbackAudioObserver* observer) {
NOTIMPLEMENTED();
StreamMixer::Get()->RemoveLoopbackAudioObserver(observer);
}
void CastMediaShlib::SetPostProcessorConfig(const std::string& name,
const std::string& config) {
// FIXME
StreamMixer::Get()->SetPostProcessorConfig(name, config);
}
} // namespace media
......
// 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 "chromecast/media/cma/backend/fuchsia/fuchsia_volume_control.h"
#include "base/logging.h"
namespace chromecast {
namespace media {
// static
std::unique_ptr<SystemVolumeControl> SystemVolumeControl::Create(
Delegate* delegate) {
return std::make_unique<FuchsiaVolumeControl>();
}
FuchsiaVolumeControl::FuchsiaVolumeControl() {}
FuchsiaVolumeControl::~FuchsiaVolumeControl() {}
float FuchsiaVolumeControl::GetRoundtripVolume(float volume) {
NOTIMPLEMENTED();
return volume;
}
float FuchsiaVolumeControl::GetVolume() {
NOTIMPLEMENTED();
return 1.0;
}
void FuchsiaVolumeControl::SetVolume(float level) {
NOTIMPLEMENTED();
}
bool FuchsiaVolumeControl::IsMuted() {
NOTIMPLEMENTED();
return false;
}
void FuchsiaVolumeControl::SetMuted(bool muted) {
NOTIMPLEMENTED();
}
} // namespace media
} // namespace chromecast
// 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.
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_FUCHSIA_VOLUME_CONTROL_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_FUCHSIA_VOLUME_CONTROL_H_
#include "chromecast/media/cma/backend/system_volume_control.h"
namespace chromecast {
namespace media {
// SystemVolumeControl implementation for Fuchsia. Current implementation is
// just a stub that doesn't do anything. Volume is still applied in the
// StreamMixer.
class FuchsiaVolumeControl : public SystemVolumeControl {
public:
FuchsiaVolumeControl();
~FuchsiaVolumeControl() override;
// SystemVolumeControl interface.
float GetRoundtripVolume(float volume) override;
float GetVolume() override;
void SetVolume(float level) override;
bool IsMuted() override;
void SetMuted(bool muted) override;
private:
DISALLOW_COPY_AND_ASSIGN(FuchsiaVolumeControl);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_FUCHSIA_VOLUME_CONTROL_H_
// 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 "chromecast/public/media/media_capabilities_shlib.h"
#include "base/strings/string_util.h"
namespace chromecast {
namespace media {
bool MediaCapabilitiesShlib::IsSupportedVideoConfig(VideoCodec codec,
VideoProfile profile,
int level) {
return (codec == kCodecH264 || codec == kCodecVP8 || codec == kCodecVP9);
}
bool MediaCapabilitiesShlib::IsSupportedAudioConfig(const AudioConfig& config) {
return config.codec == kCodecAAC || config.codec == kCodecMP3 ||
config.codec == kCodecPCM || config.codec == kCodecVorbis;
}
} // namespace media
} // namespace chromecast
// 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 "chromecast/media/cma/backend/fuchsia/media_pipeline_backend_fuchsia.h"
#include "chromecast/media/cma/backend/fuchsia/audio_decoder_fuchsia.h"
#include "chromecast/media/cma/backend/video_decoder_null.h"
namespace chromecast {
namespace media {
MediaPipelineBackendFuchsia::MediaPipelineBackendFuchsia(
const MediaPipelineDeviceParams& params)
: params_(params) {}
MediaPipelineBackendFuchsia::~MediaPipelineBackendFuchsia() {}
MediaPipelineBackendFuchsia::AudioDecoder*
MediaPipelineBackendFuchsia::CreateAudioDecoder() {
DCHECK_EQ(kStateUninitialized, state_);
if (audio_decoder_)
return nullptr;
audio_decoder_ = std::make_unique<AudioDecoderFuchsia>();
return audio_decoder_.get();
}
MediaPipelineBackendFuchsia::VideoDecoder*
MediaPipelineBackendFuchsia::CreateVideoDecoder() {
DCHECK_EQ(kStateUninitialized, state_);
if (video_decoder_)
return nullptr;
video_decoder_ = std::make_unique<VideoDecoderNull>();
return video_decoder_.get();
}
bool MediaPipelineBackendFuchsia::Initialize() {
DCHECK_EQ(kStateUninitialized, state_);
if (audio_decoder_ && !audio_decoder_->Initialize())
return false;
state_ = kStateInitialized;
return true;
}
bool MediaPipelineBackendFuchsia::Start(int64_t start_pts) {
DCHECK_EQ(kStateInitialized, state_);
if (audio_decoder_ && !audio_decoder_->Start(start_pts))
return false;
state_ = kStatePlaying;
return true;
}
void MediaPipelineBackendFuchsia::Stop() {
DCHECK(state_ == kStatePlaying || state_ == kStatePaused)
<< "Invalid state: " << state_;
if (audio_decoder_)
audio_decoder_->Stop();
state_ = kStateInitialized;
}
bool MediaPipelineBackendFuchsia::Pause() {
DCHECK_EQ(kStatePlaying, state_);
if (audio_decoder_ && !audio_decoder_->Pause())
return false;
state_ = kStatePaused;
return true;
}
bool MediaPipelineBackendFuchsia::Resume() {
DCHECK_EQ(kStatePaused, state_);
if (audio_decoder_ && !audio_decoder_->Resume())
return false;
state_ = kStatePlaying;
return true;
}
bool MediaPipelineBackendFuchsia::SetPlaybackRate(float rate) {
if (audio_decoder_)
return audio_decoder_->SetPlaybackRate(rate);
return true;
}
int64_t MediaPipelineBackendFuchsia::GetCurrentPts() {
if (audio_decoder_)
return audio_decoder_->GetCurrentPts();
return std::numeric_limits<int64_t>::min();
}
} // namespace media
} // namespace chromecast
// 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.
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_MEDIA_PIPELINE_BACKEND_FUCHSIA_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_MEDIA_PIPELINE_BACKEND_FUCHSIA_H_
#include <memory>
#include "base/macros.h"
#include "chromecast/public/media/media_pipeline_backend.h"
#include "chromecast/public/media/media_pipeline_device_params.h"
namespace chromecast {
namespace media {
class AudioDecoderFuchsia;
class VideoDecoderNull;
class MediaPipelineBackendFuchsia : public MediaPipelineBackend {
public:
explicit MediaPipelineBackendFuchsia(const MediaPipelineDeviceParams& params);
~MediaPipelineBackendFuchsia() override;
// MediaPipelineBackend interface.
AudioDecoder* CreateAudioDecoder() override;
VideoDecoder* CreateVideoDecoder() override;
bool Initialize() override;
bool Start(int64_t start_pts) override;
void Stop() override;
bool Pause() override;
bool Resume() override;
bool SetPlaybackRate(float rate) override;
int64_t GetCurrentPts() override;
private:
// State variable for DCHECKing caller correctness.
enum State {
kStateUninitialized,
kStateInitialized,
kStatePlaying,
kStatePaused,
};
const MediaPipelineDeviceParams params_;
State state_ = kStateUninitialized;
std::unique_ptr<AudioDecoderFuchsia> audio_decoder_;
std::unique_ptr<VideoDecoderNull> video_decoder_;
DISALLOW_COPY_AND_ASSIGN(MediaPipelineBackendFuchsia);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_FUCHSIA_MEDIA_PIPELINE_BACKEND_FUCHSIA_H_
// 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 "chromecast/media/cma/backend/fuchsia/media_pipeline_backend_fuchsia.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/public/volume_control.h"
#include "media/audio/audio_device_description.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
class MediaPipelineBackendFuchsiaTest : public testing::Test {
public:
void SetUp() override {
task_runner_ = std::make_unique<TaskRunnerImpl>();
backend_ =
std::make_unique<MediaPipelineBackendFuchsia>(MediaPipelineDeviceParams(
task_runner_.get(), AudioContentType::kMedia,
::media::AudioDeviceDescription::kDefaultDeviceId));
}
protected:
base::MessageLoop message_loop_;
std::unique_ptr<TaskRunnerImpl> task_runner_;
std::unique_ptr<MediaPipelineBackendFuchsia> backend_;
MediaPipelineBackend::AudioDecoder* audio_decoder_ = nullptr;
};
TEST_F(MediaPipelineBackendFuchsiaTest, Initialize) {
audio_decoder_ = backend_->CreateAudioDecoder();
EXPECT_TRUE(audio_decoder_);
EXPECT_TRUE(backend_->Initialize());
}
} // namespace media
} // namespace chromecast
// 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 "chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h"
#include <media/audio.h>
#include <zircon/syscalls.h>
#include "base/command_line.h"
#include "base/time/time.h"
#include "chromecast/base/chromecast_switches.h"
#include "media/base/audio_sample_types.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/media_switches.h"
namespace chromecast {
namespace media {
// |buffer_size| passed to media_client library when initializing audio output
// stream. Current implementation ignores this parameter, so the value doesn't
// make much difference. StreamMixer by default writes chunks of 768 frames.
constexpr int kDefaultPeriodSize = 768;
// Same value as in MixerOutputStreamAlsa. Currently this value is used to
// simulate blocking Write() similar to ALSA's behavior, see comments in
// MixerOutputStreamFuchsia::Write().
constexpr int kMaxOutputBufferSizeFrames = 4096;
// static
std::unique_ptr<MixerOutputStream> MixerOutputStream::Create() {
return std::make_unique<MixerOutputStreamFuchsia>();
}
MixerOutputStreamFuchsia::MixerOutputStreamFuchsia() {}
MixerOutputStreamFuchsia::~MixerOutputStreamFuchsia() {
if (manager_)
fuchsia_audio_manager_free(manager_);
}
bool MixerOutputStreamFuchsia::IsFixedSampleRate() {
return false;
}
bool MixerOutputStreamFuchsia::Start(int requested_sample_rate, int channels) {
DCHECK(!stream_);
if (!manager_)
manager_ = fuchsia_audio_manager_create();
DCHECK(manager_);
fuchsia_audio_parameters fuchsia_params;
fuchsia_params.sample_rate = requested_sample_rate;
fuchsia_params.num_channels = channels;
fuchsia_params.buffer_size = kDefaultPeriodSize;
int result = fuchsia_audio_manager_create_output_stream(
manager_, nullptr, &fuchsia_params, &stream_);
if (result < 0) {
LOG(ERROR) << "Failed to open audio output, error code: " << result;
DCHECK(!stream_);
return false;
}
if (!UpdatePresentationDelay()) {
fuchsia_audio_output_stream_free(stream_);
stream_ = nullptr;
return false;
}
sample_rate_ = requested_sample_rate;
channels_ = channels;
started_time_ = base::TimeTicks();
return true;
}
int MixerOutputStreamFuchsia::GetSampleRate() {
return sample_rate_;
}
MediaPipelineBackend::AudioDecoder::RenderingDelay
MixerOutputStreamFuchsia::GetRenderingDelay() {
base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta delay =
base::TimeDelta::FromMicroseconds(presentation_delay_ns_ / 1000);
if (!started_time_.is_null()) {
base::TimeTicks stream_time = GetCurrentStreamTime();
if (stream_time > now)
delay += stream_time - now;
}
return MediaPipelineBackend::AudioDecoder::RenderingDelay(
/*delay_microseconds=*/delay.InMicroseconds(),
/*timestamp_microseconds=*/(now - base::TimeTicks()).InMicroseconds());
}
bool MixerOutputStreamFuchsia::GetTimeUntilUnderrun(base::TimeDelta* result) {
if (!started_time_.is_null()) {
*result = GetCurrentStreamTime() - base::TimeTicks::Now();
} else {
*result = base::TimeDelta();
}
return true;
}
bool MixerOutputStreamFuchsia::Write(const float* data,
int data_size,
bool* out_playback_interrupted) {
if (!stream_)
return false;
DCHECK(data_size % channels_ == 0);
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_time_get() and fuchsia_audio_output_stream_write().
zx_time_t zx_now = zx_time_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_, const_cast<float*>(data), data_size, presentation_time);
if (result == ZX_ERR_IO_MISSED_DEADLINE) {
LOG(ERROR) << "MixerOutputStreamFuchsia::PumpSamples() missed deadline, "
"resetting PTS.";
if (!UpdatePresentationDelay()) {
return false;
}
started_time_ = base::TimeTicks();
*out_playback_interrupted = true;
} else if (result != ZX_OK) {
LOG(ERROR) << "fuchsia_audio_output_stream_write() returned " << result;
return false;
}
} while (started_time_.is_null());
int frames = data_size / channels_;
stream_position_samples_ += frames;
// Block the thread to limit amount of buffered data. Currently
// MixerOutputStreamAlsa uses blocking Write() and StreamMixer relies on that
// behavior. Sleep() below replicates the same behavior on Fuchsia.
// TODO(sergeyu): Refactor StreamMixer to work with non-blocking Write().
base::TimeDelta max_buffer_duration =
::media::AudioTimestampHelper::FramesToTime(kMaxOutputBufferSizeFrames,
sample_rate_);
base::TimeDelta current_buffer_duration =
GetCurrentStreamTime() - base::TimeTicks::Now();
if (current_buffer_duration > max_buffer_duration)
base::PlatformThread::Sleep(current_buffer_duration - max_buffer_duration);
return true;
}
void MixerOutputStreamFuchsia::Stop() {
started_time_ = base::TimeTicks();
if (stream_) {
fuchsia_audio_output_stream_free(stream_);
stream_ = nullptr;
}
}
bool MixerOutputStreamFuchsia::UpdatePresentationDelay() {
int result = fuchsia_audio_output_stream_get_min_delay(
stream_, &presentation_delay_ns_);
if (result != ZX_OK) {
LOG(ERROR) << "fuchsia_audio_output_stream_get_min_delay() failed: "
<< result;
return false;
}
return true;
}
base::TimeTicks MixerOutputStreamFuchsia::GetCurrentStreamTime() {
DCHECK(!started_time_.is_null());
return started_time_ + ::media::AudioTimestampHelper::FramesToTime(
stream_position_samples_, sample_rate_);
}
} // namespace media
} // namespace chromecast
// 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.
#ifndef CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
#include "chromecast/media/cma/backend/mixer_output_stream.h"
#include <media/audio.h>
#include "base/time/time.h"
namespace chromecast {
namespace media {
// MixerOutputStream implementation for Fuchsia.
class MixerOutputStreamFuchsia : public MixerOutputStream {
public:
MixerOutputStreamFuchsia();
~MixerOutputStreamFuchsia() override;
// MixerOutputStream interface.
bool IsFixedSampleRate() override;
bool Start(int requested_sample_rate, int channels) override;
bool GetTimeUntilUnderrun(base::TimeDelta* result) override;
int GetSampleRate() override;
MediaPipelineBackend::AudioDecoder::RenderingDelay GetRenderingDelay()
override;
bool Write(const float* data,
int data_size,
bool* out_playback_interrupted) override;
void Stop() override;
private:
bool UpdatePresentationDelay();
base::TimeTicks GetCurrentStreamTime();
fuchsia_audio_manager* manager_ = nullptr;
fuchsia_audio_output_stream* stream_ = nullptr;
int sample_rate_ = 0;
int channels_ = 0;
base::TimeTicks started_time_;
int64_t stream_position_samples_ = 0;
// 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;
DISALLOW_COPY_AND_ASSIGN(MixerOutputStreamFuchsia);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
// 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 "chromecast/media/cma/backend/fuchsia/mixer_output_stream_fuchsia.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
class MixerOutputStreamFuchsiaTest : public ::testing::Test {
protected:
MixerOutputStreamFuchsia output_;
};
TEST_F(MixerOutputStreamFuchsiaTest, StartAndStop) {
EXPECT_TRUE(output_.Start(48000, 2));
EXPECT_EQ(output_.GetSampleRate(), 48000);
output_.Stop();
}
} // namespace media
} // namespace chromecast
\ No newline at end of file
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