blob: cd73bf2690052f0bdfe198cdf5d44d23063654e9 [file] [log] [blame]
// Copyright 2014 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 <list>
#include <memory>
#include <vector>
#include "base/cancelable_callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/time/clock.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/buffering_state.h"
#include "media/base/decryptor.h"
#include "media/base/demuxer_stream.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "media/base/renderer.h"
#include "media/base/video_decoder_config.h"
#include "ui/gfx/geometry/size.h"
namespace base {
class SingleThreadTaskRunner;
namespace media {
class AudioRenderer;
class MediaResource;
class TimeSource;
class VideoRenderer;
class WallClockTimeSource;
class MEDIA_EXPORT RendererImpl : public Renderer {
// Renders audio/video streams using |audio_renderer| and |video_renderer|
// provided. All methods except for GetMediaTime() run on the |task_runner|.
// GetMediaTime() runs on the render main thread because it's part of JS sync
// API.
RendererImpl(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
std::unique_ptr<AudioRenderer> audio_renderer,
std::unique_ptr<VideoRenderer> video_renderer);
~RendererImpl() final;
// Renderer implementation.
void Initialize(MediaResource* media_resource,
RendererClient* client,
const PipelineStatusCB& init_cb) final;
void SetCdm(CdmContext* cdm_context,
const CdmAttachedCB& cdm_attached_cb) final;
void Flush(const base::Closure& flush_cb) final;
void StartPlayingFrom(base::TimeDelta time) final;
void SetPlaybackRate(double playback_rate) final;
void SetVolume(float volume) final;
base::TimeDelta GetMediaTime() final;
// Helper functions for testing purposes. Must be called before Initialize().
void DisableUnderflowForTesting();
void EnableClocklessVideoPlaybackForTesting();
void set_time_source_for_testing(TimeSource* time_source) {
time_source_ = time_source;
void set_video_underflow_threshold_for_testing(base::TimeDelta threshold) {
video_underflow_threshold_ = threshold;
class RendererClientInternal;
enum State {
STATE_INIT_PENDING_CDM, // Initialization is waiting for the CDM to be set.
STATE_INITIALIZING, // Initializing audio/video renderers.
STATE_FLUSHING, // Flushing is in progress.
STATE_FLUSHED, // After initialization or after flush completed.
STATE_PLAYING, // After StartPlayingFrom has been called.
bool GetWallClockTimes(const std::vector<base::TimeDelta>& media_timestamps,
std::vector<base::TimeTicks>* wall_clock_times);
bool HasEncryptedStream();
void FinishInitialization(PipelineStatus status);
// Helper functions and callbacks for Initialize().
void InitializeAudioRenderer();
void OnAudioRendererInitializeDone(PipelineStatus status);
void InitializeVideoRenderer();
void OnVideoRendererInitializeDone(PipelineStatus status);
// Helper functions and callbacks for Flush().
void FlushInternal();
void FlushAudioRenderer();
void OnAudioRendererFlushDone();
void FlushVideoRenderer();
void OnVideoRendererFlushDone();
// This function notifies the renderer that the status of the demuxer |stream|
// has been changed, the new status is |enabled| and the change occured while
// playback position was |time|.
void OnStreamStatusChanged(DemuxerStream* stream,
bool enabled,
base::TimeDelta time);
// Reinitialize audio/video renderer during a demuxer stream switching. The
// renderer must be flushed first, and when the re-init is completed the
// corresponding callback will be invoked to restart playback.
// The |stream| parameter specifies the new demuxer stream, and the |time|
// parameter specifies the time on media timeline where the switch occured.
void ReinitializeAudioRenderer(DemuxerStream* stream, base::TimeDelta time);
void OnAudioRendererReinitialized(DemuxerStream* stream,
base::TimeDelta time,
PipelineStatus status);
void ReinitializeVideoRenderer(DemuxerStream* stream, base::TimeDelta time);
void OnVideoRendererReinitialized(DemuxerStream* stream,
base::TimeDelta time,
PipelineStatus status);
// Restart audio/video renderer playback after a demuxer stream switch or
// after a demuxer stream has been disabled and re-enabled. The |stream|
// parameter specifies which stream needs to be restarted. The |time|
// parameter specifies the position on the media timeline where the playback
// needs to be restarted. It is necessary for demuxers with independent
// streams (e.g. MSE / ChunkDemuxer) to synchronize data reading between those
// streams.
void RestartAudioRenderer(DemuxerStream* stream, base::TimeDelta time);
void RestartVideoRenderer(DemuxerStream* stream, base::TimeDelta time);
// Callback executed by filters to update statistics.
void OnStatisticsUpdate(const PipelineStatistics& stats);
// Collection of callback methods and helpers for tracking changes in
// buffering state and transition from paused/underflow states and playing
// states.
// While in the kPlaying state:
// - A waiting to non-waiting transition indicates preroll has completed
// and StartPlayback() should be called
// - A non-waiting to waiting transition indicates underflow has occurred
// and PausePlayback() should be called
void OnBufferingStateChange(DemuxerStream::Type type,
BufferingState new_buffering_state);
// Handles the buffering notifications that we might get while an audio or a
// video stream is being restarted. In those cases we don't want to report
// underflows immediately and instead give decoders a chance to catch up with
// currently playing stream. Returns true if the buffering nofication has been
// handled and no further processing is necessary, returns false to indicate
// that we should fall back to the regular OnBufferingStateChange logic.
bool HandleRestartedStreamBufferingChanges(
DemuxerStream::Type type,
BufferingState new_buffering_state);
bool WaitingForEnoughData() const;
void PausePlayback();
void StartPlayback();
// Callbacks executed when a renderer has ended.
void OnRendererEnded(DemuxerStream::Type type);
bool PlaybackHasEnded() const;
void RunEndedCallbackIfNeeded();
// Callback executed when a runtime error happens.
void OnError(PipelineStatus error);
void OnWaitingForDecryptionKey();
void OnVideoNaturalSizeChange(const gfx::Size& size);
void OnAudioConfigChange(const AudioDecoderConfig& config);
void OnVideoConfigChange(const VideoDecoderConfig& config);
void OnVideoOpacityChange(bool opaque);
void OnStreamRestartCompleted();
State state_;
// Task runner used to execute pipeline tasks.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
MediaResource* media_resource_;
RendererClient* client_;
// Temporary callback used for Initialize() and Flush().
PipelineStatusCB init_cb_;
base::Closure flush_cb_;
std::unique_ptr<RendererClientInternal> audio_renderer_client_;
std::unique_ptr<RendererClientInternal> video_renderer_client_;
std::unique_ptr<AudioRenderer> audio_renderer_;
std::unique_ptr<VideoRenderer> video_renderer_;
DemuxerStream* current_audio_stream_;
DemuxerStream* current_video_stream_;
// Renderer-provided time source used to control playback.
TimeSource* time_source_;
std::unique_ptr<WallClockTimeSource> wall_clock_time_source_;
bool time_ticking_;
double playback_rate_;
// The time to start playback from after starting/seeking has completed.
base::TimeDelta start_time_;
BufferingState audio_buffering_state_;
BufferingState video_buffering_state_;
// Whether we've received the audio/video ended events.
bool audio_ended_;
bool video_ended_;
CdmContext* cdm_context_;
bool underflow_disabled_for_testing_;
bool clockless_video_playback_enabled_for_testing_;
// Used to defer underflow for video when audio is present.
base::CancelableClosure deferred_video_underflow_cb_;
// Used to defer underflow for audio when restarting audio playback.
base::CancelableClosure deferred_audio_restart_underflow_cb_;
// The amount of time to wait before declaring underflow if the video renderer
// runs out of data but the audio renderer still has enough.
base::TimeDelta video_underflow_threshold_;
// Lock used to protect access to the |restarting_audio_| flag and
// |restarting_audio_time_|.
// TODO(servolk): Get rid of the lock and replace restarting_audio_ with
// std::atomic<bool> when atomics are unbanned in Chromium.
base::Lock restarting_audio_lock_;
bool restarting_audio_ = false;
base::TimeDelta restarting_audio_time_ = kNoTimestamp;
bool restarting_video_ = false;
// Flush operations and media track status changes must be serialized to avoid
// interfering with each other. This list will hold a list of postponed
// actions that need to be completed after the current async operation is
// completed.
std::list<base::Closure> pending_actions_;
// Pending flush indicates that a track change is in the middle of a Flush and
// that another one can't be scheduled at this time. Instead it should be
// added to |pending_actions_|.
bool pending_flush_for_stream_change_ = false;
base::WeakPtr<RendererImpl> weak_this_;
base::WeakPtrFactory<RendererImpl> weak_factory_;
} // namespace media