Chromium Code Reviews| Index: media/base/android/media_source_player.cc |
| diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc |
| index b29d182d84938638a33561467efe1f6dae3f4c9a..197c7c9ccdcc45b87950a9c67cdc4f290796eaee 100644 |
| --- a/media/base/android/media_source_player.cc |
| +++ b/media/base/android/media_source_player.cc |
| @@ -192,7 +192,8 @@ MediaSourcePlayer::MediaSourcePlayer( |
| int player_id, |
| MediaPlayerManager* manager) |
| : MediaPlayerAndroid(player_id, manager), |
| - pending_play_(false), |
| + pending_event_(NO_EVENT_PENDING), |
| + active_decoding_tasks_(0), |
| width_(0), |
| height_(0), |
| audio_codec_(kUnknownAudioCodec), |
| @@ -207,8 +208,8 @@ MediaSourcePlayer::MediaSourcePlayer( |
| video_access_unit_index_(0), |
| waiting_for_audio_data_(false), |
| waiting_for_video_data_(false), |
| + use_empty_surface_(true), |
| weak_this_(this) { |
| - OnMediaMetadataChanged(duration_, width_, height_, false); |
| } |
| MediaSourcePlayer::~MediaSourcePlayer() { |
| @@ -216,8 +217,15 @@ MediaSourcePlayer::~MediaSourcePlayer() { |
| } |
| void MediaSourcePlayer::SetVideoSurface(jobject surface) { |
| - video_decoder_job_.reset(); |
| - if (!surface) { |
| + use_empty_surface_ = surface ? false : true; |
| + |
| + // If we haven't processed a surface change event, do so now. |
| + if (active_decoding_tasks_ > 0) { |
| + pending_event_ |= SURFACE_CHANGE_EVENT_PENDING; |
| + // Request a seek so that the next decoder will decode an I-frame first. |
| + // Or otherwise, MediaCodec might crash. See b/8950387. |
| + pending_event_ |= SEEK_EVENT_PENDING; |
| + ProcessPendingEvents(); |
| return; |
| } |
| @@ -225,16 +233,27 @@ void MediaSourcePlayer::SetVideoSurface(jobject surface) { |
| base::MessageLoopProxy::current(), video_codec_, |
| gfx::Size(width_, height_), surface)); |
| - if (pending_play_) |
| - StartInternal(); |
| - |
| // Inform the fullscreen view the player is ready. |
| // TODO(qinmin): refactor MediaPlayerBridge so that we have a better way |
| // to inform ContentVideoView. |
| OnMediaMetadataChanged(duration_, width_, height_, true); |
| + |
| + if (pending_event_ & SURFACE_CHANGE_EVENT_PENDING) { |
| + // We should already performed a seek in this case. |
| + pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING; |
|
acolwell GONE FROM CHROMIUM
2013/05/23 20:58:27
nit: DCHECK_EQ(pending_event_ & SEEK_EVENT_PENDING
qinmin
2013/05/23 22:03:36
We cannot guarantee the bit is cleared at this tim
|
| + } else { |
| + // Perform a seek so the new decoder can get the I-frame first. |
| + pending_event_ |= SEEK_EVENT_PENDING; |
| + ProcessPendingEvents(); |
| + return; |
| + } |
| + |
| + if (playing_) |
| + StartInternal(); |
| } |
| void MediaSourcePlayer::Start() { |
| + playing_ = true; |
| if (HasAudio() && !audio_decoder_job_) { |
| audio_decoder_job_.reset(new AudioDecoderJob( |
| base::MessageLoopProxy::current(), audio_codec_, sampling_rate_, |
| @@ -243,7 +262,6 @@ void MediaSourcePlayer::Start() { |
| if (HasVideo() && !video_decoder_job_) { |
| // StartInternal() will be delayed until SetVideoSurface() gets called. |
| - pending_play_ = true; |
| return; |
| } |
| @@ -251,13 +269,12 @@ void MediaSourcePlayer::Start() { |
| } |
| void MediaSourcePlayer::Pause() { |
| - pending_play_ = false; |
| playing_ = false; |
| start_wallclock_time_ = base::Time(); |
| } |
| bool MediaSourcePlayer::IsPlaying() { |
| - return pending_play_ || playing_; |
| + return playing_; |
| } |
| int MediaSourcePlayer::GetVideoWidth() { |
| @@ -270,17 +287,8 @@ int MediaSourcePlayer::GetVideoHeight() { |
| void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) { |
| last_presentation_timestamp_ = timestamp; |
| - start_wallclock_time_ = base::Time(); |
| - last_seek_time_ = base::Time::Now(); |
| - if (audio_decoder_job_) |
| - audio_decoder_job_->Flush(); |
| - if (video_decoder_job_) |
| - video_decoder_job_->Flush(); |
| - received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
| - received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
| - audio_access_unit_index_ = 0; |
| - video_access_unit_index_ = 0; |
| - OnSeekComplete(); |
| + pending_event_ |= SEEK_EVENT_PENDING; |
| + ProcessPendingEvents(); |
| } |
| base::TimeDelta MediaSourcePlayer::GetCurrentTime() { |
| @@ -292,8 +300,12 @@ base::TimeDelta MediaSourcePlayer::GetDuration() { |
| } |
| void MediaSourcePlayer::Release() { |
| + ClearDecodingData(); |
| audio_decoder_job_.reset(); |
| video_decoder_job_.reset(); |
| + active_decoding_tasks_ = 0; |
| + playing_ = false; |
| + pending_event_ = NO_EVENT_PENDING; |
| ReleaseMediaResourcesFromManager(); |
| } |
| @@ -317,9 +329,9 @@ bool MediaSourcePlayer::IsPlayerReady() { |
| } |
| void MediaSourcePlayer::StartInternal() { |
| - if (playing_) |
| + // Do nothing if the decoders are already running. |
| + if (active_decoding_tasks_ > 0 || pending_event_ != NO_EVENT_PENDING) |
| return; |
| - playing_ = true; |
| if (HasAudio()) { |
| audio_finished_ = false; |
| @@ -348,41 +360,77 @@ void MediaSourcePlayer::DemuxerReady( |
| void MediaSourcePlayer::ReadFromDemuxerAck( |
| const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) { |
| + if (params.type == DemuxerStream::AUDIO) |
| + waiting_for_audio_data_ = false; |
| + else |
| + waiting_for_video_data_ = false; |
| + |
| + // If there is a pending seek request, ignore the data from the chunk demuxer. |
| + // The data will be requested later when OnSeekRequestAck() is called. |
| + if (pending_event_ & SEEK_EVENT_PENDING) |
| + return; |
| + |
| if (params.type == DemuxerStream::AUDIO) { |
| DCHECK_EQ(0u, audio_access_unit_index_); |
| received_audio_ = params; |
| - waiting_for_audio_data_ = false; |
| - DecodeMoreAudio(); |
| + if (!pending_event_) |
| + DecodeMoreAudio(); |
| } else { |
| DCHECK_EQ(0u, video_access_unit_index_); |
| received_video_ = params; |
| - waiting_for_video_data_ = false; |
| - DecodeMoreVideo(); |
| + if (!pending_event_) |
| + DecodeMoreVideo(); |
| } |
| } |
| +void MediaSourcePlayer::OnSeekRequestAck() { |
| + pending_event_ &= ~SEEK_EVENT_PENDING; |
| + OnSeekComplete(); |
| + if (playing_) |
| + StartInternal(); |
| +} |
| + |
| void MediaSourcePlayer::UpdateTimestamps( |
| - const base::Time& kickoff_time, |
| const base::TimeDelta& presentation_timestamp, |
| const base::Time& wallclock_time) { |
| - // If the job was posted after last seek, update the presentation time. |
| - // Otherwise, ignore it. |
| - if (kickoff_time > last_seek_time_) { |
| - last_presentation_timestamp_ = presentation_timestamp; |
| - OnTimeUpdated(); |
| - if (start_wallclock_time_.is_null() && playing_) { |
| - start_wallclock_time_ = wallclock_time; |
| - start_presentation_timestamp_ = last_presentation_timestamp_; |
| - } |
| + last_presentation_timestamp_ = presentation_timestamp; |
| + OnTimeUpdated(); |
| + if (start_wallclock_time_.is_null() && playing_) { |
| + start_wallclock_time_ = wallclock_time; |
| + start_presentation_timestamp_ = last_presentation_timestamp_; |
| + } |
| +} |
| + |
| +void MediaSourcePlayer::ProcessPendingEvents() { |
| + // Wait for all the decoding jobs to finish before sending a seek request. |
| + if (active_decoding_tasks_ > 0) |
| + return; |
| + |
| + DCHECK(pending_event_ != NO_EVENT_PENDING); |
| + if (use_empty_surface_ && (pending_event_ & SURFACE_CHANGE_EVENT_PENDING)) { |
| + video_decoder_job_.reset(); |
| + pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING; |
| } |
| + |
| + ClearDecodingData(); |
| + manager()->OnMediaSeekRequest(player_id(), |
| + last_presentation_timestamp_, |
| + pending_event_ & SURFACE_CHANGE_EVENT_PENDING); |
| } |
| void MediaSourcePlayer::MediaDecoderCallback( |
| - bool is_audio, const base::Time& kickoff_time, |
| - const base::TimeDelta& presentation_timestamp, |
| + bool is_audio, const base::TimeDelta& presentation_timestamp, |
| const base::Time& wallclock_time, bool end_of_stream) { |
| + if (active_decoding_tasks_ > 0) |
| + active_decoding_tasks_--; |
| + |
| + if (pending_event_ != NO_EVENT_PENDING) { |
| + ProcessPendingEvents(); |
| + return; |
| + } |
| + |
| if (is_audio || !HasAudio()) |
| - UpdateTimestamps(kickoff_time, presentation_timestamp, wallclock_time); |
| + UpdateTimestamps(presentation_timestamp, wallclock_time); |
| if (end_of_stream) { |
| PlaybackCompleted(is_audio); |
| @@ -413,8 +461,9 @@ void MediaSourcePlayer::DecodeMoreAudio() { |
| received_audio_.access_units[audio_access_unit_index_], |
| start_wallclock_time_, start_presentation_timestamp_, |
| base::Bind(&MediaSourcePlayer::MediaDecoderCallback, |
| - weak_this_.GetWeakPtr(), true, base::Time::Now())); |
| - ++audio_access_unit_index_; |
| + weak_this_.GetWeakPtr(), true)); |
| + active_decoding_tasks_++; |
| + audio_access_unit_index_++; |
| } |
| void MediaSourcePlayer::DecodeMoreVideo() { |
| @@ -432,8 +481,9 @@ void MediaSourcePlayer::DecodeMoreVideo() { |
| received_video_.access_units[video_access_unit_index_], |
| start_wallclock_time_, start_presentation_timestamp_, |
| base::Bind(&MediaSourcePlayer::MediaDecoderCallback, |
| - weak_this_.GetWeakPtr(), false, base::Time::Now())); |
| - ++video_access_unit_index_; |
| + weak_this_.GetWeakPtr(), false)); |
| + active_decoding_tasks_++; |
| + video_access_unit_index_++; |
| } |
| @@ -450,6 +500,18 @@ void MediaSourcePlayer::PlaybackCompleted(bool is_audio) { |
| } |
| } |
| +void MediaSourcePlayer::ClearDecodingData() { |
| + if (audio_decoder_job_) |
| + audio_decoder_job_->Flush(); |
| + if (video_decoder_job_) |
| + video_decoder_job_->Flush(); |
| + start_wallclock_time_ = base::Time(); |
| + received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
| + received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
| + audio_access_unit_index_ = 0; |
| + video_access_unit_index_ = 0; |
| +} |
| + |
| bool MediaSourcePlayer::HasVideo() { |
| return kUnknownVideoCodec != video_codec_; |
| } |