diff options
-rw-r--r-- | media/base/synchronizer.cc | 93 | ||||
-rw-r--r-- | media/base/synchronizer.h | 59 | ||||
-rw-r--r-- | media/build/media.vcproj | 8 | ||||
-rw-r--r-- | media/media_lib.scons | 2 |
4 files changed, 162 insertions, 0 deletions
diff --git a/media/base/synchronizer.cc b/media/base/synchronizer.cc new file mode 100644 index 0000000..f233c7b --- /dev/null +++ b/media/base/synchronizer.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2009 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 "base/logging.h" +#include "media/base/synchronizer.h" + +namespace media { + +const int64 Synchronizer::kMinFrameDelayUs = 0; +const int64 Synchronizer::kMaxFrameDelayUs = 250000; + +Synchronizer::Synchronizer() { +} + +Synchronizer::~Synchronizer() { +} + +void Synchronizer::StartRendering() { + rendering_start_ = base::TimeTicks::Now(); +} + +void Synchronizer::StopRendering() { + rendering_stop_ = base::TimeTicks::Now(); +} + +void Synchronizer::CalculateDelay(base::TimeDelta time, + const StreamSample* now, + const StreamSample* next, + base::TimeDelta* delay_out, + bool* should_skip_out) { + // How long rendering took. + base::TimeDelta render_delta = rendering_stop_ - rendering_start_; + + // The duration to display the sample |now|. + base::TimeDelta duration = + base::TimeDelta::FromMicroseconds(now->GetDuration()); + + // The presentation timestamp (pts) of the sample |now|. + base::TimeDelta now_pts = + base::TimeDelta::FromMicroseconds(now->GetTimestamp()); + + // The presentation timestamp (pts) of the next sample. + base::TimeDelta next_pts; + + // If we were provided the next sample in the stream |next|, use it to + // calculate the actual sample duration as opposed to the expected duration + // provided by the current sample |now|. + // + // We also use |next| to get the exact next timestamp as opposed to assuming + // it will be |now| + |now|'s duration, which may not always be true. + if (next) { + next_pts = base::TimeDelta::FromMicroseconds(next->GetTimestamp()); + duration = next_pts - now_pts; + + // Timestamps appear out of order, so skip this frame. + if (duration.InMicroseconds() < 0) { + *delay_out = base::TimeDelta(); + *should_skip_out = true; + return; + } + } else { + // Assume next presentation timestamp is |now| + |now|'s duration. + next_pts = now_pts + duration; + } + + base::TimeDelta sleep; + if (time == last_time_) { + // The audio time has not changed. To avoid sudden bursts of video after + // we get audio timing information, we try and guess the current time + // using the duration of the frame. + sleep = duration - render_delta; + if (sleep.InMicroseconds() < kMinFrameDelayUs) { + sleep = base::TimeDelta::FromMicroseconds(kMinFrameDelayUs); + } + } else { + // The audio time has changed. The amount of time to delay is equal to + // the time until the next sample from now minus how long it takes to + // render, clamped to within the min/max frame delay constants. + sleep = next_pts - time - render_delta; + if (sleep.InMicroseconds() < kMinFrameDelayUs) { + sleep = base::TimeDelta::FromMicroseconds(kMinFrameDelayUs); + } else if (sleep.InMicroseconds() > kMaxFrameDelayUs) { + sleep = base::TimeDelta::FromMicroseconds(kMaxFrameDelayUs); + } + } + last_time_ = time; + + *delay_out = sleep; + *should_skip_out = false; +} + +} // namespace media diff --git a/media/base/synchronizer.h b/media/base/synchronizer.h new file mode 100644 index 0000000..122c32f --- /dev/null +++ b/media/base/synchronizer.h @@ -0,0 +1,59 @@ +// Copyright (c) 2009 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. + +// Utility class for video renderers to help synchronize against a reference +// clock while also taking rendering duration into account. +// +// Video renderers should maintain an instance of this object for the entire +// lifetime since Synchronizer maintains internal state to improve playback. +// +// VideoRenderer usage is as follows: +// Receive a new frame from the decoder +// Call StartRendering +// Perform colour space conversion and render the frame +// Call StopRendering +// Call CalculateDelay passing in the rendered frame and optional next frame +// Issue delayed task or sleep on thread for returned amount of time + +#ifndef MEDIA_BASE_SYNCHRONIZER_H_ +#define MEDIA_BASE_SYNCHRONIZER_H_ + +#include "base/basictypes.h" +#include "base/time.h" +#include "media/base/buffers.h" + +namespace media { + +class Synchronizer { + public: + Synchronizer(); + ~Synchronizer(); + + // Starts the rendering timer. + void StartRendering(); + + // Stops the rendering timer. + void StopRendering(); + + // Calculates the appropriate amount of delay in microseconds given the + // current time, the current sample and the next sample (may be NULL, + // but is recommended to pass in for smoother playback). + void CalculateDelay(base::TimeDelta time, const StreamSample* now, + const StreamSample* next, base::TimeDelta* delay_out, + bool* should_skip_out); + + private: + static const int64 kMinFrameDelayUs; + static const int64 kMaxFrameDelayUs; + + base::TimeTicks rendering_start_; + base::TimeTicks rendering_stop_; + base::TimeDelta last_time_; + + DISALLOW_COPY_AND_ASSIGN(Synchronizer); +}; + +} // namespace media + +#endif // MEDIA_BASE_SYNCHRONIZER_H_ diff --git a/media/build/media.vcproj b/media/build/media.vcproj index 36d944d..36e1395 100644 --- a/media/build/media.vcproj +++ b/media/build/media.vcproj @@ -176,6 +176,14 @@ RelativePath="..\base\pipeline_impl.h" > </File> + <File + RelativePath="..\base\synchronizer.cc" + > + </File> + <File + RelativePath="..\base\synchronizer.h" + > + </File> </Filter> <Filter Name="audio" diff --git a/media/media_lib.scons b/media/media_lib.scons index 690f902..bb5b7a4 100644 --- a/media/media_lib.scons +++ b/media/media_lib.scons @@ -38,6 +38,8 @@ input_files = ChromeFileList([ 'base/pipeline.h', 'base/pipeline_impl.cc', 'base/pipeline_impl.h', + 'base/synchronizer.cc', + 'base/synchronizer.h', ]), MSVSFilter('audio', [ 'audio/win/audio_manager_win.h', |