Commit fd2235f6 authored by Ken MacKay's avatar Ken MacKay Committed by Commit Bot

[Chromecast] Make CMA GetMediaTime() more accurate

Optionally call GetCurrentPTS() for every call to GetMediaTime(),
instead of every 250ms. Also update the ALSA version of GetCurrentPts()
to be more accurate.

BUG= internal b/64273974

Change-Id: I8fbbae970baba8b6207838756f8ed3513ee629a2
Reviewed-on: https://chromium-review.googlesource.com/661717Reviewed-by: default avatarSergey Volk <servolk@chromium.org>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Commit-Queue: Kenneth MacKay <kmackay@chromium.org>
Cr-Commit-Position: refs/heads/master@{#501524}
parent d1747b9e
......@@ -14,6 +14,7 @@
#include "base/macros.h"
#include "base/trace_event/trace_event.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/media/cma/backend/alsa/alsa_features.h"
#include "chromecast/media/cma/backend/alsa/media_pipeline_backend_alsa.h"
#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
#include "chromecast/media/cma/base/decoder_buffer_base.h"
......@@ -49,10 +50,21 @@ const double kPlaybackRateEpsilon = 0.001;
const CastAudioDecoder::OutputFormat kDecoderSampleFormat =
CastAudioDecoder::kOutputPlanarFloat;
const int64_t kMicrosecondsPerSecond = 1000 * 1000;
const int64_t kInvalidTimestamp = std::numeric_limits<int64_t>::min();
const int64_t kNoPendingOutput = -1;
int64_t MonotonicClockNow() {
timespec now = {0, 0};
#if BUILDFLAG(ALSA_MONOTONIC_RAW_TSTAMPS)
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
#else
clock_gettime(CLOCK_MONOTONIC, &now);
#endif
return static_cast<int64_t>(now.tv_sec) * 1000000 + now.tv_nsec / 1000;
}
} // namespace
AudioDecoderAlsa::RateShifterInfo::RateShifterInfo(float playback_rate)
......@@ -68,7 +80,11 @@ AudioDecoderAlsa::AudioDecoderAlsa(MediaPipelineBackendAlsa* backend)
mixer_error_(false),
rate_shifter_output_(
::media::AudioBus::Create(kNumChannels, kDefaultFramesPerBuffer)),
current_pts_(kInvalidTimestamp),
first_push_pts_(kInvalidTimestamp),
last_push_pts_(kInvalidTimestamp),
last_push_timestamp_(kInvalidTimestamp),
last_push_pts_length_(0),
paused_pts_(kInvalidTimestamp),
pending_output_frames_(kNoPendingOutput),
volume_multiplier_(1.0f),
pool_(new ::media::AudioBufferMemoryPool()),
......@@ -97,7 +113,11 @@ void AudioDecoderAlsa::Initialize() {
pending_buffer_complete_ = false;
got_eos_ = false;
pushed_eos_ = false;
current_pts_ = kInvalidTimestamp;
first_push_pts_ = kInvalidTimestamp;
last_push_pts_ = kInvalidTimestamp;
last_push_timestamp_ = kInvalidTimestamp;
last_push_pts_length_ = 0;
paused_pts_ = kInvalidTimestamp;
pending_output_frames_ = kNoPendingOutput;
last_mixer_delay_.timestamp_microseconds = kInvalidTimestamp;
......@@ -106,7 +126,6 @@ void AudioDecoderAlsa::Initialize() {
bool AudioDecoderAlsa::Start(int64_t start_pts) {
TRACE_FUNCTION_ENTRY0();
current_pts_ = start_pts;
DCHECK(IsValidConfig(config_));
mixer_input_.reset(new StreamMixerAlsaInput(
this, config_.samples_per_second, config_.playout_channel,
......@@ -137,12 +156,14 @@ bool AudioDecoderAlsa::Pause() {
TRACE_FUNCTION_ENTRY0();
DCHECK(mixer_input_);
mixer_input_->SetPaused(true);
paused_pts_ = GetCurrentPts();
return true;
}
bool AudioDecoderAlsa::Resume() {
TRACE_FUNCTION_ENTRY0();
DCHECK(mixer_input_);
paused_pts_ = kInvalidTimestamp;
mixer_input_->SetPaused(false);
return true;
}
......@@ -170,6 +191,22 @@ bool AudioDecoderAlsa::SetPlaybackRate(float rate) {
return true;
}
int64_t AudioDecoderAlsa::GetCurrentPts() const {
if (paused_pts_ != kInvalidTimestamp)
return paused_pts_;
if (last_push_pts_ == kInvalidTimestamp)
return kInvalidTimestamp;
DCHECK(!rate_shifter_info_.empty());
int64_t now = MonotonicClockNow();
int64_t estimate =
last_push_pts_ +
std::min(static_cast<int64_t>((now - last_push_timestamp_) *
rate_shifter_info_.front().rate),
last_push_pts_length_);
return (estimate < first_push_pts_ ? kInvalidTimestamp : estimate);
}
AudioDecoderAlsa::BufferStatus AudioDecoderAlsa::PushBuffer(
CastDecoderBuffer* buffer) {
TRACE_FUNCTION_ENTRY0();
......@@ -182,9 +219,6 @@ AudioDecoderAlsa::BufferStatus AudioDecoderAlsa::PushBuffer(
uint64_t input_bytes = buffer->end_of_stream() ? 0 : buffer->data_size();
scoped_refptr<DecoderBufferBase> buffer_base(
static_cast<DecoderBufferBase*>(buffer));
if (!buffer->end_of_stream()) {
current_pts_ = buffer->timestamp();
}
// If the buffer is already decoded, do not attempt to decode. Call
// OnBufferDecoded asynchronously on the main thread.
......@@ -359,6 +393,23 @@ void AudioDecoderAlsa::OnBufferDecoded(
} else {
int input_frames = decoded->data_size() / (kNumChannels * sizeof(float));
last_push_pts_ = decoded->timestamp();
last_push_pts_length_ =
input_frames * kMicrosecondsPerSecond / config_.samples_per_second;
if (last_push_pts_ != kInvalidTimestamp) {
if (first_push_pts_ == kInvalidTimestamp) {
first_push_pts_ = last_push_pts_;
}
RenderingDelay delay = GetRenderingDelay();
if (delay.timestamp_microseconds == kInvalidTimestamp) {
last_push_pts_ = kInvalidTimestamp;
} else {
last_push_timestamp_ =
delay.timestamp_microseconds + delay.delay_microseconds;
}
}
DCHECK(!rate_shifter_info_.empty());
RateShifterInfo* rate_info = &rate_shifter_info_.front();
// Bypass rate shifter if the rate is 1.0, and there are no frames queued
......
......@@ -46,7 +46,7 @@ class AudioDecoderAlsa : public MediaPipelineBackend::AudioDecoder,
bool Resume();
bool SetPlaybackRate(float rate);
int64_t current_pts() const { return current_pts_; }
int64_t GetCurrentPts() const;
// MediaPipelineBackend::AudioDecoder implementation:
void SetDelegate(
......@@ -104,7 +104,11 @@ class AudioDecoderAlsa : public MediaPipelineBackend::AudioDecoder,
std::deque<RateShifterInfo> rate_shifter_info_;
std::unique_ptr<::media::AudioBus> rate_shifter_output_;
int64_t current_pts_;
int64_t first_push_pts_;
int64_t last_push_pts_;
int64_t last_push_timestamp_;
int64_t last_push_pts_length_;
int64_t paused_pts_;
std::unique_ptr<StreamMixerAlsaInput> mixer_input_;
RenderingDelay last_mixer_delay_;
......
......@@ -89,7 +89,7 @@ bool MediaPipelineBackendAlsa::SetPlaybackRate(float rate) {
int64_t MediaPipelineBackendAlsa::GetCurrentPts() {
if (audio_decoder_)
return audio_decoder_->current_pts();
return audio_decoder_->GetCurrentPts();
return std::numeric_limits<int64_t>::min();
}
......
......@@ -2,6 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//build/config/chromecast_build.gni")
declare_args() {
# Set true to use accurate but more expensive method for GetMediaTime().
cma_use_accurate_media_time = is_cast_audio_only
}
source_set("pipeline") {
sources = [
"audio_decoder_software_wrapper.cc",
......@@ -31,6 +39,7 @@ source_set("pipeline") {
]
deps = [
":cma_pipeline_features",
"//base",
"//chromecast/base/metrics:metrics",
"//chromecast/media/base",
......@@ -44,3 +53,9 @@ source_set("pipeline") {
"//third_party/boringssl",
]
}
buildflag_header("cma_pipeline_features") {
header = "cma_pipeline_features.h"
flags = [ "CMA_USE_ACCURATE_MEDIA_TIME=$cma_use_accurate_media_time" ]
}
......@@ -22,6 +22,7 @@
#include "chromecast/media/cma/base/coded_frame_provider.h"
#include "chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h"
#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
#include "chromecast/media/cma/pipeline/cma_pipeline_features.h"
#include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
#include "chromecast/public/media/media_pipeline_backend.h"
#include "media/base/timestamp_constants.h"
......@@ -228,7 +229,8 @@ void MediaPipelineImpl::StartPlayingFrom(base::TimeDelta time) {
"Cast.Platform.Playing");
// Enable time updates.
last_media_time_ = time;
start_media_time_ = time;
last_media_time_ = ::media::kNoTimestamp;
statistics_rolling_counter_ = 0;
if (!pending_time_update_task_) {
pending_time_update_task_ = true;
......@@ -320,7 +322,13 @@ void MediaPipelineImpl::SetVolume(float volume) {
base::TimeDelta MediaPipelineImpl::GetMediaTime() const {
DCHECK(thread_checker_.CalledOnValidThread());
return last_media_time_;
#if BUILDFLAG(CMA_USE_ACCURATE_MEDIA_TIME)
base::TimeDelta time = base::TimeDelta::FromMicroseconds(
media_pipeline_backend_->GetCurrentPts());
#else
base::TimeDelta time = last_media_time_;
#endif
return (time == ::media::kNoTimestamp ? start_media_time_ : time);
}
bool MediaPipelineImpl::HasAudio() const {
......
......@@ -110,6 +110,7 @@ class MediaPipelineImpl {
// The media time is retrieved at regular intervals.
bool pending_time_update_task_;
base::TimeDelta start_media_time_;
base::TimeDelta last_media_time_;
// Used to make the statistics update period a multiplier of the time update
......
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