Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(575)

Unified Diff: media/base/android/media_codec_player.cc

Issue 1242913004: MediaCodecPlayer implementation (stage 3 - browser seek and surface change) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mtplayer-seek
Patch Set: Rebased, changed DCHECKs. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/base/android/media_codec_player.h ('k') | media/base/android/media_codec_player_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 27b5bcadb86711426a3a84c18e7f929932578e7a..e1b7562c4e2f29c940fd6cf776485a9e16426bdb 100644
--- a/media/base/android/media_codec_player.cc
+++ b/media/base/android/media_codec_player.cc
@@ -58,7 +58,7 @@ MediaCodecPlayer::MediaCodecPlayer(
frame_url),
ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
demuxer_(demuxer.Pass()),
- state_(STATE_PAUSED),
+ state_(kStatePaused),
interpolator_(&default_tick_clock_),
pending_start_(false),
pending_seek_(kNoTimestamp()),
@@ -73,6 +73,7 @@ MediaCodecPlayer::MediaCodecPlayer(
base::Bind(&MediaPlayerManager::OnPlaybackComplete, manager, player_id);
seek_done_cb_ =
base::Bind(&MediaPlayerManager::OnSeekComplete, manager, player_id);
+ error_cb_ = base::Bind(&MediaPlayerManager::OnError, manager, player_id);
attach_listener_cb_ = base::Bind(&MediaPlayerAndroid::AttachListener,
WeakPtrForUIThread(), nullptr);
detach_listener_cb_ =
@@ -130,16 +131,62 @@ void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
DVLOG(1) << __FUNCTION__ << (surface.IsEmpty() ? " empty" : " non-empty");
- // I assume that if video decoder already has the surface,
- // there will be two calls:
- // (1) SetVideoSurface(0)
- // (2) SetVideoSurface(new_surface)
- video_decoder_->SetPendingSurface(surface.Pass());
+ // Save the empty-ness before we pass the surface to the decoder.
+ bool surface_is_empty = surface.IsEmpty();
- if (video_decoder_->HasPendingSurface() &&
- state_ == STATE_WAITING_FOR_SURFACE) {
- SetState(STATE_PLAYING);
- StartPlaybackDecoders();
+ // Apparently RemoveVideoSurface() can be called several times in a row,
+ // ignore the second and subsequent calls.
+ if (surface_is_empty && !video_decoder_->HasVideoSurface()) {
+ DVLOG(1) << __FUNCTION__ << ": surface already removed, ignoring";
+ return;
+ }
+
+ video_decoder_->SetVideoSurface(surface.Pass());
+
+ if (surface_is_empty) {
+ // Remove video surface.
+ switch (state_) {
+ case kStatePlaying:
+ if (VideoFinished())
+ break;
+
+ DVLOG(1) << __FUNCTION__ << ": stopping and restarting";
+ // Stop decoders as quickly as possible.
+ StopDecoders(); // synchronous stop
+
+ // Prefetch or wait for initial configuration.
+ if (HasAudio() || HasVideo()) {
+ SetState(kStatePrefetching);
+ StartPrefetchDecoders();
+ } else {
+ SetState(kStateWaitingForConfig);
+ }
+ break;
+
+ default:
+ break; // ignore
+ }
+ } else {
+ // Replace video surface.
+ switch (state_) {
+ case kStateWaitingForSurface:
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
+ break;
+
+ case kStatePlaying:
+ if (VideoFinished())
+ break;
+
+ DVLOG(1) << __FUNCTION__ << ": requesting to stop and restart";
+ SetState(kStateStopping);
+ RequestToStopDecoders();
+ SetPendingStart(true);
+ break;
+
+ default:
+ break; // ignore
+ }
}
}
@@ -149,24 +196,24 @@ void MediaCodecPlayer::Start() {
DVLOG(1) << __FUNCTION__;
switch (state_) {
- case STATE_PAUSED:
+ case kStatePaused:
// Prefetch or wait for initial configuration.
if (HasAudio() || HasVideo()) {
- SetState(STATE_PREFETCHING);
+ SetState(kStatePrefetching);
StartPrefetchDecoders();
} else {
- SetState(STATE_WAITING_FOR_CONFIG);
+ SetState(kStateWaitingForConfig);
}
break;
- case STATE_STOPPING:
- case STATE_WAITING_FOR_SEEK:
+ case kStateStopping:
+ case kStateWaitingForSeek:
SetPendingStart(true);
break;
- case STATE_WAITING_FOR_CONFIG:
- case STATE_PREFETCHING:
- case STATE_PLAYING:
- case STATE_WAITING_FOR_SURFACE:
- case STATE_ERROR:
+ case kStateWaitingForConfig:
+ case kStatePrefetching:
+ case kStatePlaying:
+ case kStateWaitingForSurface:
+ case kStateError:
break; // Ignore
default:
NOTREACHED();
@@ -182,20 +229,20 @@ void MediaCodecPlayer::Pause(bool is_media_related_action) {
SetPendingStart(false);
switch (state_) {
- case STATE_WAITING_FOR_CONFIG:
- case STATE_PREFETCHING:
- case STATE_WAITING_FOR_SURFACE:
- SetState(STATE_PAUSED);
+ case kStateWaitingForConfig:
+ case kStatePrefetching:
+ case kStateWaitingForSurface:
+ SetState(kStatePaused);
StopDecoders();
break;
- case STATE_PLAYING:
- SetState(STATE_STOPPING);
+ case kStatePlaying:
+ SetState(kStateStopping);
RequestToStopDecoders();
break;
- case STATE_PAUSED:
- case STATE_STOPPING:
- case STATE_WAITING_FOR_SEEK:
- case STATE_ERROR:
+ case kStatePaused:
+ case kStateStopping:
+ case kStateWaitingForSeek:
+ case kStateError:
break; // Ignore
default:
NOTREACHED();
@@ -209,31 +256,31 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) {
DVLOG(1) << __FUNCTION__ << " " << timestamp;
switch (state_) {
- case STATE_PAUSED:
- SetState(STATE_WAITING_FOR_SEEK);
+ case kStatePaused:
+ SetState(kStateWaitingForSeek);
RequestDemuxerSeek(timestamp);
break;
- case STATE_WAITING_FOR_CONFIG:
- case STATE_PREFETCHING:
- case STATE_WAITING_FOR_SURFACE:
- SetState(STATE_WAITING_FOR_SEEK);
+ case kStateWaitingForConfig:
+ case kStatePrefetching:
+ case kStateWaitingForSurface:
+ SetState(kStateWaitingForSeek);
StopDecoders();
SetPendingStart(true);
RequestDemuxerSeek(timestamp);
break;
- case STATE_PLAYING:
- SetState(STATE_STOPPING);
+ case kStatePlaying:
+ SetState(kStateStopping);
RequestToStopDecoders();
SetPendingStart(true);
SetPendingSeek(timestamp);
break;
- case STATE_STOPPING:
+ case kStateStopping:
SetPendingSeek(timestamp);
break;
- case STATE_WAITING_FOR_SEEK:
+ case kStateWaitingForSeek:
SetPendingSeek(timestamp);
break;
- case STATE_ERROR:
+ case kStateError:
break; // ignore
default:
NOTREACHED();
@@ -246,8 +293,22 @@ void MediaCodecPlayer::Release() {
DVLOG(1) << __FUNCTION__;
- SetState(STATE_PAUSED);
+ // Stop decoding threads and delete MediaCodecs, but keep IPC between browser
+ // and renderer processes going. Seek should work across and after Release().
+
ReleaseDecoderResources();
+
+ SetPendingStart(false);
+
+ if (state_ != kStateWaitingForSeek)
+ SetState(kStatePaused);
+
+ base::TimeDelta pending_seek_time = GetPendingSeek();
+ if (pending_seek_time != kNoTimestamp()) {
+ SetPendingSeek(kNoTimestamp());
+ SetState(kStateWaitingForSeek);
+ RequestDemuxerSeek(pending_seek_time);
+ }
}
void MediaCodecPlayer::SetVolume(double volume) {
@@ -282,7 +343,7 @@ bool MediaCodecPlayer::IsPlaying() {
// TODO(timav): Use another variable since |state_| should only be accessed on
// Media thread.
- return state_ == STATE_PLAYING || state_ == STATE_STOPPING;
+ return state_ == kStatePlaying || state_ == kStateStopping;
}
bool MediaCodecPlayer::CanPause() {
@@ -354,9 +415,6 @@ void MediaCodecPlayer::OnDemuxerSeekDone(
DVLOG(1) << __FUNCTION__ << " actual_time:" << actual_browser_seek_time;
- if (state_ != STATE_WAITING_FOR_SEEK)
- return; // ignore
-
DCHECK(seek_info_.get());
DCHECK(seek_info_->seek_time != kNoTimestamp());
@@ -375,9 +433,22 @@ void MediaCodecPlayer::OnDemuxerSeekDone(
interpolator_.SetBounds(seek_time, seek_time);
audio_decoder_->SetBaseTimestamp(seek_time);
+ // The Flush() might set the state to kStateError.
+ if (state_ == kStateError) {
+ // Notify the Renderer.
+ if (!seek_info_->is_browser_seek)
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(seek_done_cb_, seek_time));
+
+ seek_info_.reset();
+ return;
+ }
+
+ DCHECK_EQ(kStateWaitingForSeek, state_);
+
base::TimeDelta pending_seek_time = GetPendingSeek();
if (pending_seek_time != kNoTimestamp()) {
- // Keep STATE_WAITING_FOR_SEEK
+ // Keep kStateWaitingForSeek
SetPendingSeek(kNoTimestamp());
RequestDemuxerSeek(pending_seek_time);
return;
@@ -387,13 +458,13 @@ void MediaCodecPlayer::OnDemuxerSeekDone(
SetPendingStart(false);
// Prefetch or wait for initial configuration.
if (HasAudio() || HasVideo()) {
- SetState(STATE_PREFETCHING);
+ SetState(kStatePrefetching);
StartPrefetchDecoders();
} else {
- SetState(STATE_WAITING_FOR_CONFIG);
+ SetState(kStateWaitingForConfig);
}
} else {
- SetState(STATE_PAUSED);
+ SetState(kStatePaused);
}
// Notify the Renderer.
@@ -458,7 +529,7 @@ void MediaCodecPlayer::RequestDemuxerData(DemuxerStream::Type stream_type) {
void MediaCodecPlayer::OnPrefetchDone() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
- if (state_ != STATE_PREFETCHING) {
+ if (state_ != kStatePrefetching) {
DVLOG(1) << __FUNCTION__ << " wrong state " << AsString(state_)
<< " ignoring";
return; // Ignore
@@ -470,17 +541,18 @@ void MediaCodecPlayer::OnPrefetchDone() {
// No configuration at all after prefetching.
// This is an error, initial configuration is expected
// before the first data chunk.
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
+ DCHECK(!internal_error_cb_.is_null());
+ GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
return;
}
- if (HasVideo() && !HasPendingSurface()) {
- SetState(STATE_WAITING_FOR_SURFACE);
+ if (HasVideo() && !video_decoder_->HasVideoSurface()) {
+ SetState(kStateWaitingForSurface);
return;
}
- SetState(STATE_PLAYING);
- StartPlaybackDecoders();
+ SetState(kStatePlaying);
+ StartPlaybackOrBrowserSeek();
}
void MediaCodecPlayer::OnStopDone() {
@@ -496,23 +568,23 @@ void MediaCodecPlayer::OnStopDone() {
base::TimeDelta seek_time;
switch (state_) {
- case STATE_STOPPING: {
+ case kStateStopping: {
base::TimeDelta seek_time = GetPendingSeek();
if (seek_time != kNoTimestamp()) {
- SetState(STATE_WAITING_FOR_SEEK);
+ SetState(kStateWaitingForSeek);
SetPendingSeek(kNoTimestamp());
RequestDemuxerSeek(seek_time);
} else if (HasPendingStart()) {
SetPendingStart(false);
- SetState(STATE_PREFETCHING);
+ SetState(kStatePrefetching);
StartPrefetchDecoders();
} else {
- SetState(STATE_PAUSED);
+ SetState(kStatePaused);
}
} break;
- case STATE_PLAYING:
+ case kStatePlaying:
// Unexpected stop means completion
- SetState(STATE_PAUSED);
+ SetState(kStatePaused);
break;
default:
// DVLOG(0) << __FUNCTION__ << " illegal state: " << AsString(state_);
@@ -520,7 +592,7 @@ void MediaCodecPlayer::OnStopDone() {
// Ignore! There can be a race condition: audio posts OnStopDone,
// then video posts, then first OnStopDone arrives at which point
// both streams are already stopped, then second OnStopDone arrives. When
- // the second one arrives, the state us not STATE_STOPPING any more.
+ // the second one arrives, the state us not kStateStopping any more.
break;
}
@@ -535,20 +607,23 @@ void MediaCodecPlayer::OnError() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- // STATE_ERROR blocks all events
- SetState(STATE_ERROR);
+ // kStateError blocks all events
+ SetState(kStateError);
ReleaseDecoderResources();
+
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(error_cb_, MEDIA_ERROR_DECODE));
}
void MediaCodecPlayer::OnStarvation(DemuxerStream::Type type) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__ << " stream type:" << type;
- if (state_ != STATE_PLAYING)
+ if (state_ != kStatePlaying)
return; // Ignore
- SetState(STATE_STOPPING);
+ SetState(kStateStopping);
RequestToStopDecoders();
SetPendingStart(true);
}
@@ -605,17 +680,6 @@ void MediaCodecPlayer::SetState(PlayerState new_state) {
state_ = new_state;
}
-void MediaCodecPlayer::SetPendingSurface(gfx::ScopedJavaSurface surface) {
- DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
- DVLOG(1) << __FUNCTION__;
-
- video_decoder_->SetPendingSurface(surface.Pass());
-}
-
-bool MediaCodecPlayer::HasPendingSurface() const {
- return video_decoder_->HasPendingSurface();
-}
-
void MediaCodecPlayer::SetPendingStart(bool need_to_start) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__ << ": " << need_to_start;
@@ -665,8 +729,8 @@ void MediaCodecPlayer::SetDemuxerConfigs(const DemuxerConfigs& configs) {
if (configs.video_codec != kUnknownVideoCodec)
video_decoder_->SetDemuxerConfigs(configs);
- if (state_ == STATE_WAITING_FOR_CONFIG) {
- SetState(STATE_PREFETCHING);
+ if (state_ == kStateWaitingForConfig) {
+ SetState(kStatePrefetching);
StartPrefetchDecoders();
}
}
@@ -699,33 +763,63 @@ void MediaCodecPlayer::StartPrefetchDecoders() {
video_decoder_->Prefetch(prefetch_cb);
}
-void MediaCodecPlayer::StartPlaybackDecoders() {
+void MediaCodecPlayer::StartPlaybackOrBrowserSeek() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- // Configure all streams before the start since
- // we may discover that browser seek is required.
+ // TODO(timav): consider replacing this method with posting a
+ // browser seek task (i.e. generate an event) from StartPlaybackDecoders().
+
+ StartStatus status = StartPlaybackDecoders();
+
+ switch (status) {
+ case kStartBrowserSeekRequired:
+ // Browser seek
+ SetState(kStateWaitingForSeek);
+ SetPendingStart(true);
+ StopDecoders();
+ RequestDemuxerSeek(GetInterpolatedTime(), true);
+ break;
+ case kStartFailed:
+ GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_);
+ break;
+ case kStartOk:
+ break;
+ }
+}
+
+MediaCodecPlayer::StartStatus MediaCodecPlayer::StartPlaybackDecoders() {
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
bool do_audio = !AudioFinished();
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.
+ // 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);
- if (do_audio) {
- MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure();
- if (status != MediaCodecDecoder::CONFIG_OK) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
- }
- }
+ // 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
+ // not make sense to configure audio.
if (do_video) {
MediaCodecDecoder::ConfigStatus status = video_decoder_->Configure();
- if (status != MediaCodecDecoder::CONFIG_OK) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
+ switch (status) {
+ case MediaCodecDecoder::kConfigOk:
+ break;
+ case MediaCodecDecoder::kConfigKeyFrameRequired:
+ // TODO(timav): post a task or return the status?
+ return kStartBrowserSeekRequired;
+ case MediaCodecDecoder::kConfigFailure:
+ return kStartFailed;
+ }
+ }
+
+ if (do_audio) {
+ MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure();
+ if (status != MediaCodecDecoder::kConfigOk) {
+ return kStartFailed;
}
}
@@ -737,8 +831,7 @@ void MediaCodecPlayer::StartPlaybackDecoders() {
if (do_audio) {
if (!audio_decoder_->Start(current_time)) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
+ return kStartFailed;
}
// Attach listener on UI thread
@@ -747,18 +840,19 @@ void MediaCodecPlayer::StartPlaybackDecoders() {
if (do_video) {
if (!video_decoder_->Start(current_time)) {
- GetMediaTaskRunner()->PostTask(FROM_HERE, error_cb_);
- return;
+ return kStartFailed;
}
}
+
+ return kStartOk;
}
void MediaCodecPlayer::StopDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- audio_decoder_->SyncStop();
video_decoder_->SyncStop();
+ audio_decoder_->SyncStop();
}
void MediaCodecPlayer::RequestToStopDecoders() {
@@ -789,14 +883,14 @@ void MediaCodecPlayer::RequestDemuxerSeek(base::TimeDelta seek_time,
bool is_browser_seek) {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__ << " " << seek_time
- << (is_browser_seek ? " browser_seek" : "");
+ << (is_browser_seek ? " BROWSER_SEEK" : "");
// Flush decoders before requesting demuxer.
audio_decoder_->Flush();
video_decoder_->Flush();
- // Save active seek data. Logically it is attached to STATE_WAITING_FOR_SEEK.
- DCHECK(state_ == STATE_WAITING_FOR_SEEK);
+ // Save active seek data. Logically it is attached to kStateWaitingForSeek.
+ DCHECK_EQ(kStateWaitingForSeek, state_);
seek_info_.reset(new SeekInfo(seek_time, is_browser_seek));
demuxer_->RequestDemuxerSeek(seek_time, is_browser_seek);
@@ -821,14 +915,15 @@ void MediaCodecPlayer::CreateDecoders() {
DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread());
DVLOG(1) << __FUNCTION__;
- error_cb_ = base::Bind(&MediaCodecPlayer::OnError, media_weak_this_);
+ internal_error_cb_ = base::Bind(&MediaCodecPlayer::OnError, media_weak_this_);
audio_decoder_.reset(new MediaCodecAudioDecoder(
GetMediaTaskRunner(), base::Bind(&MediaCodecPlayer::RequestDemuxerData,
media_weak_this_, DemuxerStream::AUDIO),
base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
DemuxerStream::AUDIO),
- base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
+ base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_),
+ internal_error_cb_,
base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
DemuxerStream::AUDIO)));
@@ -837,7 +932,8 @@ void MediaCodecPlayer::CreateDecoders() {
media_weak_this_, DemuxerStream::VIDEO),
base::Bind(&MediaCodecPlayer::OnStarvation, media_weak_this_,
DemuxerStream::VIDEO),
- base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_), error_cb_,
+ base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_),
+ internal_error_cb_,
base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
DemuxerStream::VIDEO),
base::Bind(&MediaCodecPlayer::OnVideoResolutionChanged, media_weak_this_),
@@ -866,14 +962,14 @@ base::TimeDelta MediaCodecPlayer::GetInterpolatedTime() {
const char* MediaCodecPlayer::AsString(PlayerState state) {
switch (state) {
- RETURN_STRING(STATE_PAUSED);
- RETURN_STRING(STATE_WAITING_FOR_CONFIG);
- RETURN_STRING(STATE_PREFETCHING);
- RETURN_STRING(STATE_PLAYING);
- RETURN_STRING(STATE_STOPPING);
- RETURN_STRING(STATE_WAITING_FOR_SURFACE);
- RETURN_STRING(STATE_WAITING_FOR_SEEK);
- RETURN_STRING(STATE_ERROR);
+ RETURN_STRING(kStatePaused);
+ RETURN_STRING(kStateWaitingForConfig);
+ RETURN_STRING(kStatePrefetching);
+ RETURN_STRING(kStatePlaying);
+ RETURN_STRING(kStateStopping);
+ RETURN_STRING(kStateWaitingForSurface);
+ RETURN_STRING(kStateWaitingForSeek);
+ RETURN_STRING(kStateError);
}
return nullptr; // crash early
}
« no previous file with comments | « media/base/android/media_codec_player.h ('k') | media/base/android/media_codec_player_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698