Commit 3de19856 authored by jameswest's avatar jameswest Committed by Commit bot

Pass delay and timestamp to AudioSourceCallback::OnMoreData.

Replace audio delay in bytes with audio delay in time and the
timestamp at which the delay was measured. This is needed to
accurately synchronize playback across Cast devices in a multizone
group.

BUG=internal b/29618700
TEST=base, cast, cast_media, content, and media unit tests

Review-Url: https://codereview.chromium.org/2101303004
Cr-Commit-Position: refs/heads/master@{#421999}
parent 0c46e360
......@@ -115,6 +115,9 @@ class BASE_EXPORT TimeDelta {
static constexpr TimeDelta FromSecondsD(double secs);
static constexpr TimeDelta FromMillisecondsD(double ms);
static constexpr TimeDelta FromMicroseconds(int64_t us);
#if defined(OS_POSIX)
static TimeDelta FromTimeSpec(const timespec& ts);
#endif
#if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value);
#endif
......@@ -730,6 +733,10 @@ class BASE_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> {
static TimeTicks FromQPCValue(LONGLONG qpc_value);
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
static TimeTicks FromMachAbsoluteTime(uint64_t mach_absolute_time);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
// Get an estimate of the TimeTick value at the time of the UnixEpoch. Because
// Time and TimeTicks respond differently to user-set time and NTP
// adjustments, this number is only an estimate. Nevertheless, this can be
......
......@@ -25,22 +25,8 @@
namespace {
int64_t ComputeCurrentTicks() {
#if defined(OS_IOS)
// On iOS mach_absolute_time stops while the device is sleeping. Instead use
// now - KERN_BOOTTIME to get a time difference that is not impacted by clock
// changes. KERN_BOOTTIME will be updated by the system whenever the system
// clock change.
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
int kr = sysctl(mib, arraysize(mib), &boottime, &size, nullptr, 0);
DCHECK_EQ(KERN_SUCCESS, kr);
base::TimeDelta time_difference = base::Time::Now() -
(base::Time::FromTimeT(boottime.tv_sec) +
base::TimeDelta::FromMicroseconds(boottime.tv_usec));
return time_difference.InMicroseconds();
#else
#if defined(OS_MACOSX) && !defined(OS_IOS)
int64_t MachAbsoluteTimeToTicks(uint64_t mach_absolute_time) {
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
// Zero-initialization of statics guarantees that denom will be 0 before
......@@ -52,14 +38,10 @@ int64_t ComputeCurrentTicks() {
MACH_DCHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
}
// mach_absolute_time is it when it comes to ticks on the Mac. Other calls
// with less precision (such as TickCount) just call through to
// mach_absolute_time.
// timebase_info converts absolute time tick units into nanoseconds. Convert
// to microseconds up front to stave off overflows.
base::CheckedNumeric<uint64_t> result(
mach_absolute_time() / base::Time::kNanosecondsPerMicrosecond);
base::CheckedNumeric<uint64_t> result(mach_absolute_time /
base::Time::kNanosecondsPerMicrosecond);
result *= timebase_info.numer;
result /= timebase_info.denom;
......@@ -67,6 +49,29 @@ int64_t ComputeCurrentTicks() {
// With numer and denom = 1 (the expected case), the 64-bit absolute time
// reported in nanoseconds is enough to last nearly 585 years.
return base::checked_cast<int64_t>(result.ValueOrDie());
}
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
int64_t ComputeCurrentTicks() {
#if defined(OS_IOS)
// On iOS mach_absolute_time stops while the device is sleeping. Instead use
// now - KERN_BOOTTIME to get a time difference that is not impacted by clock
// changes. KERN_BOOTTIME will be updated by the system whenever the system
// clock change.
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
int kr = sysctl(mib, arraysize(mib), &boottime, &size, nullptr, 0);
DCHECK_EQ(KERN_SUCCESS, kr);
base::TimeDelta time_difference =
base::Time::Now() - (base::Time::FromTimeT(boottime.tv_sec) +
base::TimeDelta::FromMicroseconds(boottime.tv_usec));
return time_difference.InMicroseconds();
#else
// mach_absolute_time is it when it comes to ticks on the Mac. Other calls
// with less precision (such as TickCount) just call through to
// mach_absolute_time.
return MachAbsoluteTimeToTicks(mach_absolute_time());
#endif // defined(OS_IOS)
}
......@@ -263,6 +268,13 @@ bool TimeTicks::IsConsistentAcrossProcesses() {
return true;
}
#if defined(OS_MACOSX) && !defined(OS_IOS)
// static
TimeTicks TimeTicks::FromMachAbsoluteTime(uint64_t mach_absolute_time) {
return TimeTicks(MachAbsoluteTimeToTicks(mach_absolute_time));
}
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
// static
TimeTicks::Clock TimeTicks::GetClock() {
#if defined(OS_IOS)
......
......@@ -110,6 +110,12 @@ int64_t ClockNow(clockid_t clk_id) {
namespace base {
// static
TimeDelta TimeDelta::FromTimeSpec(const timespec& ts) {
return TimeDelta(ts.tv_sec * Time::kMicrosecondsPerSecond +
ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
}
struct timespec TimeDelta::ToTimeSpec() const {
int64_t microseconds = InMicroseconds();
time_t seconds = 0;
......
......@@ -806,22 +806,29 @@ TEST(TimeDelta, FromAndIn) {
#if defined(OS_POSIX)
TEST(TimeDelta, TimeSpecConversion) {
struct timespec result = TimeDelta::FromSeconds(0).ToTimeSpec();
TimeDelta delta = TimeDelta::FromSeconds(0);
struct timespec result = delta.ToTimeSpec();
EXPECT_EQ(result.tv_sec, 0);
EXPECT_EQ(result.tv_nsec, 0);
EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
result = TimeDelta::FromSeconds(1).ToTimeSpec();
delta = TimeDelta::FromSeconds(1);
result = delta.ToTimeSpec();
EXPECT_EQ(result.tv_sec, 1);
EXPECT_EQ(result.tv_nsec, 0);
EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
result = TimeDelta::FromMicroseconds(1).ToTimeSpec();
delta = TimeDelta::FromMicroseconds(1);
result = delta.ToTimeSpec();
EXPECT_EQ(result.tv_sec, 0);
EXPECT_EQ(result.tv_nsec, 1000);
EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
result = TimeDelta::FromMicroseconds(
Time::kMicrosecondsPerSecond + 1).ToTimeSpec();
delta = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerSecond + 1);
result = delta.ToTimeSpec();
EXPECT_EQ(result.tv_sec, 1);
EXPECT_EQ(result.tv_nsec, 1000);
EXPECT_EQ(delta, TimeDelta::FromTimeSpec(result));
}
#endif // OS_POSIX
......
......@@ -4,8 +4,10 @@
#include "chromecast/media/audio/cast_audio_mixer.h"
#include "base/logging.h"
#include "chromecast/media/audio/cast_audio_manager.h"
#include "chromecast/media/audio/cast_audio_output_stream.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/channel_layout.h"
namespace {
......@@ -140,8 +142,9 @@ class CastAudioMixer::MixerProxyStream
uint32_t frames_delayed) override {
DCHECK(source_callback_);
uint32_t bytes_delay = frames_delayed * input_params_.GetBytesPerFrame();
source_callback_->OnMoreData(audio_bus, bytes_delay, 0);
const base::TimeDelta delay = ::media::AudioTimestampHelper::FramesToTime(
frames_delayed, input_params_.sample_rate());
source_callback_->OnMoreData(delay, base::TimeTicks::Now(), 0, audio_bus);
return volume_;
}
......@@ -245,13 +248,14 @@ void CastAudioMixer::RemoveInput(
output_stream_->Stop();
}
int CastAudioMixer::OnMoreData(::media::AudioBus* dest,
uint32_t total_bytes_delay,
uint32_t frames_skipped) {
int CastAudioMixer::OnMoreData(base::TimeDelta delay,
base::TimeTicks /* delay_timestamp */,
int /* prior_frames_skipped */,
::media::AudioBus* dest) {
DCHECK(thread_checker_.CalledOnValidThread());
uint32_t frames_delayed =
total_bytes_delay / output_params_.GetBytesPerFrame();
uint32_t frames_delayed = ::media::AudioTimestampHelper::TimeToFrames(
delay, output_params_.sample_rate());
mixer_->ConvertWithDelay(frames_delayed, dest);
return dest->frames();
}
......@@ -282,4 +286,4 @@ void CastAudioMixer::OnError(::media::AudioOutputStream* stream) {
}
} // namespace media
} // namespace chromecast
\ No newline at end of file
} // namespace chromecast
......@@ -5,11 +5,13 @@
#ifndef CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_MIXER_H_
#define CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_MIXER_H_
#include <memory>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_converter.h"
#include "media/base/audio_parameters.h"
......@@ -27,7 +29,7 @@ class CastAudioMixer : public ::media::AudioOutputStream::AudioSourceCallback {
using RealStreamFactory = base::Callback<::media::AudioOutputStream*(
const ::media::AudioParameters&)>;
CastAudioMixer(const RealStreamFactory& real_stream_factory);
explicit CastAudioMixer(const RealStreamFactory& real_stream_factory);
~CastAudioMixer() override;
virtual ::media::AudioOutputStream* MakeStream(
......@@ -38,9 +40,11 @@ class CastAudioMixer : public ::media::AudioOutputStream::AudioSourceCallback {
class MixerProxyStream;
// ::media::AudioOutputStream::AudioSourceCallback implementation
int OnMoreData(::media::AudioBus* dest,
uint32_t total_bytes_delay,
uint32_t frames_skipped) override;
int OnMoreData(base::TimeDelta delay,
base::TimeTicks delay_timestamp,
int prior_frames_skipped,
::media::AudioBus* dest) override;
void OnError(::media::AudioOutputStream* stream) override;
// MixedAudioOutputStreams call Register on opening and AddInput on starting.
......
......@@ -12,6 +12,7 @@
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "chromecast/media/audio/cast_audio_manager.h"
#include "chromecast/media/audio/cast_audio_output_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -38,22 +39,21 @@ class MockAudioSourceCallback
: public ::media::AudioOutputStream::AudioSourceCallback {
public:
MockAudioSourceCallback() {
ON_CALL(*this, OnMoreData(_, _, _))
ON_CALL(*this, OnMoreData(_, _, _, _))
.WillByDefault(Invoke(this, &MockAudioSourceCallback::OnMoreDataImpl));
}
MOCK_METHOD3(OnMoreData,
int(::media::AudioBus* audio_bus,
uint32_t total_bytes_delay,
uint32_t frames_skipped));
MOCK_METHOD4(OnMoreData,
int(base::TimeDelta, base::TimeTicks, int, ::media::AudioBus*));
MOCK_METHOD1(OnError, void(::media::AudioOutputStream* stream));
private:
int OnMoreDataImpl(::media::AudioBus* audio_bus,
uint32_t total_bytes_delay,
uint32_t frames_skipped) {
audio_bus->Zero();
return audio_bus->frames();
int OnMoreDataImpl(base::TimeDelta /* delay */,
base::TimeTicks /* delay_timestamp */,
int /* prior_frames_skipped */,
::media::AudioBus* dest) {
dest->Zero();
return dest->frames();
}
};
......@@ -70,11 +70,11 @@ class MockCastAudioOutputStream : public CastAudioOutputStream {
MOCK_METHOD1(SetVolume, void(double volume));
MOCK_METHOD1(GetVolume, void(double* volume));
void SignalPull(AudioSourceCallback* source_callback,
uint32_t total_bytes_delay) {
void SignalPull(AudioSourceCallback* source_callback, base::TimeDelta delay) {
std::unique_ptr<::media::AudioBus> audio_bus =
::media::AudioBus::Create(GetAudioParams());
source_callback->OnMoreData(audio_bus.get(), total_bytes_delay, 0);
source_callback->OnMoreData(delay, base::TimeTicks::Now(), 0,
audio_bus.get());
}
void SignalError(AudioSourceCallback* source_callback) {
......@@ -109,7 +109,7 @@ class MockCastAudioManager : public CastAudioManager {
class MockCastAudioMixer : public CastAudioMixer {
public:
MockCastAudioMixer(const RealStreamFactory& real_stream_factory)
explicit MockCastAudioMixer(const RealStreamFactory& real_stream_factory)
: CastAudioMixer(real_stream_factory) {
ON_CALL(*this, MakeStream(_, _))
.WillByDefault(Invoke(this, &MockCastAudioMixer::MakeStreamConcrete));
......@@ -295,8 +295,8 @@ TEST_F(CastAudioMixerTest, MultiStreamCycle) {
sources.erase(sources.begin());
for (auto& source : sources)
EXPECT_CALL(*source, OnMoreData(_, _, _));
mock_mixer_stream().SignalPull(&mock_mixer(), 0);
EXPECT_CALL(*source, OnMoreData(_, _, _, _));
mock_mixer_stream().SignalPull(&mock_mixer(), base::TimeDelta());
EXPECT_CALL(mock_manager(), ReleaseOutputStream(stream));
stream->Close();
......@@ -434,7 +434,7 @@ TEST_F(CastAudioMixerTest, OnErrorNoRecovery) {
streams.front()->Close();
}
TEST_F(CastAudioMixerTest, ByteDelays) {
TEST_F(CastAudioMixerTest, Delay) {
MockAudioSourceCallback source;
::media::AudioOutputStream* stream = CreateMixerStream();
......@@ -446,10 +446,11 @@ TEST_F(CastAudioMixerTest, ByteDelays) {
EXPECT_CALL(mock_mixer_stream(), Start(&mock_mixer()));
stream->Start(&source);
// The |total_bytes_delay| is the same because the Mixer and stream are
// |delay| is the same because the Mixer and stream are
// using the same AudioParameters.
EXPECT_CALL(source, OnMoreData(_, 100, 0));
mock_mixer_stream().SignalPull(&mock_mixer(), 100);
base::TimeDelta delay = base::TimeDelta::FromMicroseconds(1000);
EXPECT_CALL(source, OnMoreData(delay, _, 0, _));
mock_mixer_stream().SignalPull(&mock_mixer(), delay);
EXPECT_CALL(mock_mixer_stream(), Stop());
EXPECT_CALL(mock_mixer_stream(), Close());
......
......@@ -4,6 +4,9 @@
#include "chromecast/media/audio/cast_audio_output_stream.h"
#include <algorithm>
#include <string>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "chromecast/base/metrics/cast_metrics_helper.h"
......@@ -270,11 +273,9 @@ void CastAudioOutputStream::PushBuffer() {
const base::TimeTicks now = base::TimeTicks::Now();
base::TimeDelta queue_delay =
std::max(base::TimeDelta(), next_push_time_ - now);
uint32_t bytes_delay = queue_delay.InMicroseconds() *
audio_params_.GetBytesPerSecond() / 1000000;
int frame_count =
source_callback_->OnMoreData(audio_bus_.get(), bytes_delay, 0);
VLOG(3) << "frames_filled=" << frame_count << " with latency=" << bytes_delay;
source_callback_->OnMoreData(queue_delay, now, 0, audio_bus_.get());
VLOG(3) << "frames_filled=" << frame_count << " with latency=" << queue_delay;
DCHECK_EQ(frame_count, audio_bus_->frames());
DCHECK_EQ(static_cast<int>(decoder_buffer_->data_size()),
......
......@@ -5,6 +5,8 @@
#ifndef CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_OUTPUT_STREAM_H_
#define CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_OUTPUT_STREAM_H_
#include <memory>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
......
......@@ -4,6 +4,9 @@
#include "chromecast/media/audio/cast_audio_output_stream.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "chromecast/base/metrics/cast_metrics_test_helper.h"
......@@ -152,11 +155,12 @@ class FakeAudioSourceCallback
bool error() const { return error_; }
// ::media::AudioOutputStream::AudioSourceCallback overrides.
int OnMoreData(::media::AudioBus* audio_bus,
uint32_t total_bytes_delay,
uint32_t frames_skipped) override {
audio_bus->Zero();
return audio_bus->frames();
int OnMoreData(base::TimeDelta /* delay */,
base::TimeTicks /* delay_timestamp */,
int /* prior_frames_skipped */,
::media::AudioBus* dest) override {
dest->Zero();
return dest->frames();
}
void OnError(::media::AudioOutputStream* stream) override { error_ = true; }
......@@ -166,7 +170,8 @@ class FakeAudioSourceCallback
class FakeAudioManager : public CastAudioManager {
public:
FakeAudioManager(scoped_refptr<base::SingleThreadTaskRunner> task_runner)
explicit FakeAudioManager(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: CastAudioManager(task_runner, task_runner, nullptr, nullptr, nullptr),
media_pipeline_backend_(nullptr) {}
~FakeAudioManager() override {}
......
......@@ -7,6 +7,8 @@
#include <stdint.h>
#include <list>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
......@@ -15,6 +17,7 @@
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/browser/media/capture/web_contents_tracker.h"
#include "content/public/browser/browser_thread.h"
......@@ -308,7 +311,8 @@ class WebContentsAudioInputStreamTest : public testing::TestWithParam<bool> {
// 20 Audio buses are enough for all test cases.
const int kAudioBusesNumber = 20;
for (int i = 0; i < kAudioBusesNumber; i++) {
int frames = source->OnMoreData(audio_data.get(), 0, 0);
int frames = source->OnMoreData(
base::TimeDelta(), base::TimeTicks::Now(), 0, audio_data.get());
std::unique_ptr<media::AudioBus> copy = AudioBus::Create(params);
audio_data->CopyTo(copy.get());
out->OnData(std::move(copy), now);
......
......@@ -4,9 +4,13 @@
#include "content/browser/media/capture/web_contents_audio_muter.h"
#include <memory>
#include <set>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "content/browser/media/capture/audio_mirroring_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
......@@ -50,7 +54,8 @@ class AudioDiscarder : public media::AudioOutputStream {
~AudioDiscarder() override {}
void FetchAudioData(AudioSourceCallback* callback) {
callback->OnMoreData(audio_bus_.get(), 0, 0);
callback->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
audio_bus_.get());
}
// Calls FetchAudioData() at regular intervals and discards the data.
......
......@@ -6,11 +6,14 @@
#include <stdint.h>
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "content/renderer/media/media_stream_audio_source.h"
#include "media/audio/simple_sources.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -154,7 +157,8 @@ class AudioTrackRecorderTest : public TestWithParam<ATRTestParams> {
first_params_.channels(), first_params_.sample_rate() *
kMediaStreamAudioTrackBufferDurationMs /
base::Time::kMillisecondsPerSecond));
first_source_.OnMoreData(bus.get(), 0, 0);
first_source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
bus.get());
return bus;
}
std::unique_ptr<media::AudioBus> GetSecondSourceAudioBus() {
......@@ -162,7 +166,8 @@ class AudioTrackRecorderTest : public TestWithParam<ATRTestParams> {
second_params_.channels(), second_params_.sample_rate() *
kMediaStreamAudioTrackBufferDurationMs /
base::Time::kMillisecondsPerSecond));
second_source_.OnMoreData(bus.get(), 0, 0);
second_source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
bus.get());
return bus;
}
......
......@@ -4,9 +4,12 @@
#include <stddef.h>
#include <string>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/child/child_process.h"
#include "content/renderer/media/media_recorder_handler.h"
#include "content/renderer/media/mock_media_stream_registry.h"
......@@ -117,7 +120,8 @@ class MediaRecorderHandlerTest : public TestWithParam<MediaRecorderTestParams>,
std::unique_ptr<media::AudioBus> bus(media::AudioBus::Create(
kTestAudioChannels,
kTestAudioSampleRate * kTestAudioBufferDurationMs / 1000));
audio_source_.OnMoreData(bus.get(), 0, 0);
audio_source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
bus.get());
return bus;
}
......
......@@ -37,16 +37,19 @@
#include <stddef.h>
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/free_deleter.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "media/audio/alsa/alsa_util.h"
#include "media/audio/alsa/alsa_wrapper.h"
#include "media/audio/alsa/audio_manager_alsa.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/channel_mixer.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"
......@@ -125,7 +128,7 @@ std::ostream& operator<<(std::ostream& os,
case AlsaPcmOutputStream::kIsClosed:
os << "kIsClosed";
break;
};
}
return os;
}
......@@ -151,7 +154,8 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name,
packet_size_(params.GetBytesPerBuffer()),
latency_(std::max(
base::TimeDelta::FromMicroseconds(kMinLatencyMicros),
FramesToTimeDelta(params.frames_per_buffer() * 2, sample_rate_))),
AudioTimestampHelper::FramesToTime(params.frames_per_buffer() * 2,
sample_rate_))),
bytes_per_output_frame_(bytes_per_frame_),
alsa_buffer_frames_(0),
stop_stream_(false),
......@@ -164,6 +168,7 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name,
volume_(1.0f),
source_callback_(NULL),
audio_bus_(AudioBus::Create(params)),
tick_clock_(new base::DefaultTickClock()),
weak_factory_(this) {
DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_);
......@@ -345,6 +350,12 @@ void AlsaPcmOutputStream::GetVolume(double* volume) {
*volume = volume_;
}
void AlsaPcmOutputStream::SetTickClockForTesting(
std::unique_ptr<base::TickClock> tick_clock) {
DCHECK(tick_clock);
tick_clock_ = std::move(tick_clock);
}
void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
DCHECK(CalledOnValidThread());
......@@ -361,13 +372,14 @@ void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
// WritePacket() consumes only the current chunk of data.
if (!buffer_->forward_bytes()) {
// Before making a request to source for data we need to determine the
// delay (in bytes) for the requested data to be played.
const uint32_t hardware_delay = GetCurrentDelay() * bytes_per_frame_;
// delay for the requested data to be played.
const base::TimeDelta delay =
AudioTimestampHelper::FramesToTime(GetCurrentDelay(), sample_rate_);
scoped_refptr<media::DataBuffer> packet =
new media::DataBuffer(packet_size_);
int frames_filled = RunDataCallback(
audio_bus_.get(), hardware_delay);
int frames_filled =
RunDataCallback(delay, tick_clock_->NowTicks(), audio_bus_.get());
size_t packet_size = frames_filled * bytes_per_frame_;
DCHECK_LE(packet_size, packet_size_);
......@@ -516,7 +528,7 @@ void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) {
} else if (available_frames < kTargetFramesAvailable) {
// Schedule the next write for the moment when the available buffer of the
// sound card hits |kTargetFramesAvailable|.
next_fill_time = FramesToTimeDelta(
next_fill_time = AudioTimestampHelper::FramesToTime(
kTargetFramesAvailable - available_frames, sample_rate_);
} else if (!source_exhausted) {
// The sound card has |kTargetFramesAvailable| or more frames available.
......@@ -534,13 +546,6 @@ void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) {
next_fill_time);
}
// static
base::TimeDelta AlsaPcmOutputStream::FramesToTimeDelta(int frames,
double sample_rate) {
return base::TimeDelta::FromMicroseconds(
frames * base::Time::kMicrosecondsPerSecond / sample_rate);
}
std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32_t channels) {
// Constants specified by the ALSA API for device hints.
static const int kGetAllDevices = -1;
......@@ -777,12 +782,13 @@ AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() {
return state_;
}
int AlsaPcmOutputStream::RunDataCallback(AudioBus* audio_bus,
uint32_t total_bytes_delay) {
int AlsaPcmOutputStream::RunDataCallback(base::TimeDelta delay,
base::TimeTicks delay_timestamp,
AudioBus* audio_bus) {
TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback");
if (source_callback_)
return source_callback_->OnMoreData(audio_bus, total_bytes_delay, 0);
return source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus);
return 0;
}
......
......@@ -34,6 +34,7 @@
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/non_thread_safe.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_parameters.h"
......@@ -84,6 +85,8 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream,
void SetVolume(double volume) override;
void GetVolume(double* volume) override;
void SetTickClockForTesting(std::unique_ptr<base::TickClock> tick_clock);
private:
friend class AlsaPcmOutputStreamTest;
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
......@@ -129,7 +132,6 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream,
void ScheduleNextWrite(bool source_exhausted);
// Utility functions for talking with the ALSA API.
static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate);
std::string FindDeviceForChannels(uint32_t channels);
snd_pcm_sframes_t GetAvailableFrames();
snd_pcm_sframes_t GetCurrentDelay();
......@@ -152,7 +154,9 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream,
// is passed into the output stream, but ownership is not transfered which
// requires a synchronization on access of the |source_callback_| to avoid
// using a deleted callback.
int RunDataCallback(AudioBus* audio_bus, uint32_t total_bytes_delay);
int RunDataCallback(base::TimeDelta delay,
base::TimeTicks delay_timestamp,
AudioBus* audio_bus);
void RunErrorCallback(int code);
// Changes the AudioSourceCallback to proxy calls to. Pass in NULL to
......@@ -208,6 +212,8 @@ class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream,
std::unique_ptr<ChannelMixer> channel_mixer_;
std::unique_ptr<AudioBus> mixed_audio_bus_;
std::unique_ptr<base::TickClock> tick_clock_;
// Allows us to run tasks on the AlsaPcmOutputStream instance which are
// bound by its lifetime.
// NOTE: Weak pointers must be invalidated before all other member variables.
......
......@@ -5,15 +5,19 @@
#include <stdint.h>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/test_message_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "media/audio/alsa/alsa_output.h"
#include "media/audio/alsa/alsa_wrapper.h"
#include "media/audio/alsa/audio_manager_alsa.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/mock_audio_source_callback.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/data_buffer.h"
#include "media/base/seekable_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -215,7 +219,7 @@ char AlsaPcmOutputStreamTest::kGenericSurround50[] = "surround50";
// Custom action to clear a memory buffer.
ACTION(ClearBuffer) {
arg0->Zero();
arg3->Zero();
}
TEST_F(AlsaPcmOutputStreamTest, ConstructedState) {
......@@ -281,7 +285,