| Index: media/base/android/media_codec_player.cc
|
| diff --git a/media/base/android/media_codec_player.cc b/media/base/android/media_codec_player.cc
|
| index e1b7562c4e2f29c940fd6cf776485a9e16426bdb..554c9af7aaab9cf79c91de36711ba2cd48b53f0b 100644
|
| --- a/media/base/android/media_codec_player.cc
|
| +++ b/media/base/android/media_codec_player.cc
|
| @@ -482,6 +482,24 @@ void MediaCodecPlayer::OnDemuxerDurationChanged(
|
| duration_ = duration;
|
| }
|
|
|
| +void MediaCodecPlayer::SetDecodersTimeCallbackForTests(
|
| + DecodersTimeCallback cb) {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + decoders_time_cb_ = cb;
|
| +}
|
| +
|
| +bool MediaCodecPlayer::IsPrerollingForTests(DemuxerStream::Type type) const {
|
| + DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(audio_decoder_ && video_decoder_);
|
| +
|
| + if (type == DemuxerStream::AUDIO)
|
| + return audio_decoder_->IsPrerollingForTests();
|
| + else if (type == DemuxerStream::VIDEO)
|
| + return video_decoder_->IsPrerollingForTests();
|
| + else
|
| + return false;
|
| +}
|
| +
|
| // Events from Player, called on UI thread
|
|
|
| void MediaCodecPlayer::OnMediaMetadataChanged(base::TimeDelta duration,
|
| @@ -555,12 +573,26 @@ void MediaCodecPlayer::OnPrefetchDone() {
|
| StartPlaybackOrBrowserSeek();
|
| }
|
|
|
| +void MediaCodecPlayer::OnPrerollDone() {
|
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| + DVLOG(1) << __FUNCTION__;
|
| +
|
| + DCHECK(interpolator_.interpolating());
|
| +
|
| + StartStatus status = StartDecoders(interpolator_.GetInterpolatedTime());
|
| + if (status != kStartOk)
|
| + GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
|
| +}
|
| +
|
| void MediaCodecPlayer::OnStopDone() {
|
| DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| DVLOG(1) << __FUNCTION__;
|
|
|
| - if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped()))
|
| + if (!(audio_decoder_->IsStopped() && video_decoder_->IsStopped())) {
|
| + DVLOG(1) << __FUNCTION__ << " both audio and video has to be stopped"
|
| + << ", ignoring";
|
| return; // Wait until other stream is stopped
|
| + }
|
|
|
| // At this point decoder threads should not be running
|
| if (interpolator_.interpolating())
|
| @@ -636,13 +668,22 @@ void MediaCodecPlayer::OnTimeIntervalUpdate(DemuxerStream::Type type,
|
| DVLOG(2) << __FUNCTION__ << ": stream type:" << type << " [" << now_playing
|
| << "," << last_buffered << "]";
|
|
|
| + // For testing only: report time interval as we receive it from decoders
|
| + // as an indication of what is being rendered.
|
| + if (!decoders_time_cb_.is_null()) {
|
| + ui_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(decoders_time_cb_, type, now_playing, last_buffered));
|
| + }
|
| +
|
| // I assume that audio stream cannot be added after we get configs by
|
| // OnDemuxerConfigsAvailable(), but that audio can finish early.
|
|
|
| if (type == DemuxerStream::VIDEO) {
|
| // Ignore video PTS if there is audio stream or if it's behind current
|
| // time as set by audio stream.
|
| - if (!AudioFinished() || now_playing < interpolator_.GetInterpolatedTime())
|
| + if (!AudioFinished() ||
|
| + (HasAudio() && now_playing < interpolator_.GetInterpolatedTime()))
|
| return;
|
| }
|
|
|
| @@ -792,15 +833,38 @@ MediaCodecPlayer::StartStatus MediaCodecPlayer::StartPlaybackDecoders() {
|
| DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| DVLOG(1) << __FUNCTION__;
|
|
|
| - bool do_audio = !AudioFinished();
|
| - bool do_video = !VideoFinished();
|
| + // Configure all streams before the start since we may discover that browser
|
| + // seek is required.
|
| + MediaCodecPlayer::StartStatus status = ConfigureDecoders();
|
| + if (status != kStartOk)
|
| + return status;
|
| +
|
| + // At this point decoder threads should not be running.
|
| + if (!interpolator_.interpolating())
|
| + interpolator_.StartInterpolating();
|
| +
|
| + base::TimeDelta current_time = GetInterpolatedTime();
|
| +
|
| + bool preroll_required = false;
|
| + status = MaybePrerollDecoders(current_time, &preroll_required);
|
| + if (preroll_required)
|
| + return status;
|
| +
|
| + return StartDecoders(current_time);
|
| +}
|
| +
|
| +MediaCodecPlayer::StartStatus MediaCodecPlayer::ConfigureDecoders() {
|
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| + DVLOG(1) << __FUNCTION__;
|
| +
|
| + const bool do_audio = !AudioFinished();
|
| + const bool do_video = !VideoFinished();
|
|
|
| // If there is nothing to play, the state machine should determine this at the
|
| // prefetch state and never call this method.
|
| DCHECK(do_audio || do_video);
|
|
|
| - // Configure all streams before the start since we may discover that browser
|
| - // seek is required. Start with video: if browser seek is required it would
|
| + // Start with video: if browser seek is required it would
|
| // not make sense to configure audio.
|
|
|
| if (do_video) {
|
| @@ -823,25 +887,77 @@ MediaCodecPlayer::StartStatus MediaCodecPlayer::StartPlaybackDecoders() {
|
| }
|
| }
|
|
|
| - // At this point decoder threads should not be running.
|
| - if (!interpolator_.interpolating())
|
| - interpolator_.StartInterpolating();
|
| + return kStartOk;
|
| +}
|
|
|
| - base::TimeDelta current_time = GetInterpolatedTime();
|
| +MediaCodecPlayer::StartStatus MediaCodecPlayer::MaybePrerollDecoders(
|
| + base::TimeDelta current_time,
|
| + bool* preroll_required) {
|
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| + DVLOG(1) << __FUNCTION__ << " current_time:" << current_time;
|
|
|
| - if (do_audio) {
|
| - if (!audio_decoder_->Start(current_time)) {
|
| + // If requested, preroll is always done in the beginning of the playback,
|
| + // after prefetch. The request might not happen at all though, in which case
|
| + // we won't have prerolling phase. We need the prerolling when we (re)create
|
| + // the decoder, because its configuration and initialization (getting input,
|
| + // but not making output) can take time, and after the seek because there
|
| + // could be some data to be skipped and there is again initialization after
|
| + // the flush.
|
| +
|
| + int count = 0;
|
| + const bool do_audio_preroll = audio_decoder_->NotCompletedAndNeedsPreroll();
|
| + if (do_audio_preroll)
|
| + ++count;
|
| +
|
| + const bool do_video_preroll = video_decoder_->NotCompletedAndNeedsPreroll();
|
| + if (do_video_preroll)
|
| + ++count;
|
| +
|
| + if (count == 0) {
|
| + DVLOG(1) << __FUNCTION__ << ": preroll is not required, skipping";
|
| + *preroll_required = false;
|
| + return kStartOk;
|
| + }
|
| +
|
| + *preroll_required = true;
|
| +
|
| + DCHECK(count > 0);
|
| + DCHECK(do_audio_preroll || do_video_preroll);
|
| +
|
| + DVLOG(1) << __FUNCTION__ << ": preroll for " << count << " stream(s)";
|
| +
|
| + base::Closure preroll_cb = base::BarrierClosure(
|
| + count, base::Bind(&MediaCodecPlayer::OnPrerollDone, media_weak_this_));
|
| +
|
| + if (do_audio_preroll) {
|
| + if (!audio_decoder_->Preroll(current_time, preroll_cb))
|
| + return kStartFailed;
|
| + }
|
| +
|
| + if (do_video_preroll) {
|
| + if (!video_decoder_->Preroll(current_time, preroll_cb))
|
| + return kStartFailed;
|
| + }
|
| +
|
| + return kStartOk;
|
| +}
|
| +
|
| +MediaCodecPlayer::StartStatus MediaCodecPlayer::StartDecoders(
|
| + base::TimeDelta current_time) {
|
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
|
| + DVLOG(1) << __FUNCTION__ << " current_time:" << current_time;
|
| +
|
| + if (!AudioFinished()) {
|
| + if (!audio_decoder_->Start(current_time))
|
| return kStartFailed;
|
| - }
|
|
|
| // Attach listener on UI thread
|
| ui_task_runner_->PostTask(FROM_HERE, attach_listener_cb_);
|
| }
|
|
|
| - if (do_video) {
|
| - if (!video_decoder_->Start(current_time)) {
|
| + if (!VideoFinished()) {
|
| + if (!video_decoder_->Start(current_time))
|
| return kStartFailed;
|
| - }
|
| }
|
|
|
| return kStartOk;
|
|
|