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 a305ca88a06b2fe23501dbfd72b8ce74cfe715fd..dac68f3e5d4e29c77339c8d1d06da4ccd83a81b2 100644 |
--- a/media/base/android/media_source_player.cc |
+++ b/media/base/android/media_source_player.cc |
@@ -55,7 +55,8 @@ MediaDecoderJob::MediaDecoderJob(base::Thread* thread, bool is_audio) |
thread_(thread), |
needs_flush_(false), |
is_audio_(is_audio), |
- weak_this_(this) { |
+ weak_this_(this), |
+ decoding_(false) { |
} |
MediaDecoderJob::~MediaDecoderJob() {} |
@@ -85,6 +86,7 @@ void MediaDecoderJob::Decode( |
const base::Time& start_wallclock_time, |
const base::TimeDelta& start_presentation_timestamp, |
const MediaDecoderJob::DecoderCallback& callback) { |
+ decoding_ = true; |
acolwell GONE FROM CHROMIUM
2013/06/04 15:19:14
Should there be a DCHECK(!decoding_) here?
qinmin
2013/06/04 19:03:23
Done.
|
thread_->message_loop()->PostTask(FROM_HERE, base::Bind( |
&MediaDecoderJob::DecodeInternal, base::Unretained(this), unit, |
start_wallclock_time, start_presentation_timestamp, needs_flush_, |
@@ -103,6 +105,12 @@ void MediaDecoderJob::DecodeInternal( |
base::TimeDelta timeout = base::TimeDelta::FromMicroseconds( |
kMediaCodecTimeoutInMicroseconds); |
int input_buf_index = media_codec_bridge_->DequeueInputBuffer(timeout); |
+ if (input_buf_index == MediaCodecBridge::INFO_MEDIA_CODEC_ERROR) { |
+ message_loop_->PostTask(FROM_HERE, base::Bind( |
+ callback, false, start_presentation_timestamp, start_wallclock_time, |
+ false)); |
+ return; |
+ } |
// TODO(qinmin): skip frames if video is falling far behind. |
if (input_buf_index >= 0) { |
if (unit.end_of_stream) { |
@@ -179,18 +187,20 @@ void MediaDecoderJob::ReleaseOutputBuffer( |
end_of_stream)); |
} |
+void MediaDecoderJob::OnDecodeCompleted() { |
+ decoding_ = false; |
+} |
+ |
void MediaDecoderJob::Flush() { |
// Do nothing, flush when the next Decode() happens. |
needs_flush_ = true; |
} |
void MediaDecoderJob::Release() { |
- if (thread_->IsRunning() && |
- thread_->message_loop() != base::MessageLoop::current()) { |
+ if (decoding_ && thread_->message_loop() != base::MessageLoop::current()) |
acolwell GONE FROM CHROMIUM
2013/06/04 15:19:14
This doesn't seem right. Shouldn't this code look
qinmin
2013/06/04 19:03:23
Backgroud info: there is a problem in android that
|
thread_->message_loop()->DeleteSoon(FROM_HERE, this); |
- } else { |
+ else |
delete this; |
- } |
} |
VideoDecoderJob::VideoDecoderJob( |
@@ -220,6 +230,7 @@ MediaSourcePlayer::MediaSourcePlayer( |
: MediaPlayerAndroid(player_id, manager), |
pending_event_(NO_EVENT_PENDING), |
active_decoding_tasks_(0), |
+ seek_request_id_(0), |
width_(0), |
height_(0), |
audio_codec_(kUnknownAudioCodec), |
@@ -234,7 +245,6 @@ MediaSourcePlayer::MediaSourcePlayer( |
video_access_unit_index_(0), |
waiting_for_audio_data_(false), |
waiting_for_video_data_(false), |
- use_empty_surface_(true), |
weak_this_(this) { |
} |
@@ -243,54 +253,19 @@ MediaSourcePlayer::~MediaSourcePlayer() { |
} |
void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) { |
- use_empty_surface_ = surface.IsSurfaceEmpty(); |
- |
- // 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; |
- } |
- |
- if (HasVideo()) { |
- video_decoder_job_.reset(new VideoDecoderJob( |
- video_codec_, gfx::Size(width_, height_), surface.j_surface().obj())); |
- } |
- |
- // 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); |
+ surface_ = surface.Pass(); |
- if (pending_event_ & SURFACE_CHANGE_EVENT_PENDING) { |
- // We should already requested a seek in this case. |
- pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING; |
- } else { |
- // Perform a seek so the new decoder can get the I-frame first. |
- pending_event_ |= SEEK_EVENT_PENDING; |
- ProcessPendingEvents(); |
- return; |
- } |
- |
- if (playing_) |
- StartInternal(); |
+ 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. |
acolwell GONE FROM CHROMIUM
2013/06/04 15:19:14
The seek is needed because you have to create a ne
qinmin
2013/06/04 19:03:23
Done.
|
+ pending_event_ |= SEEK_EVENT_PENDING; |
+ ProcessPendingEvents(); |
} |
void MediaSourcePlayer::Start() { |
playing_ = true; |
- if (HasAudio() && !audio_decoder_job_) { |
- audio_decoder_job_.reset(new AudioDecoderJob( |
- audio_codec_, sampling_rate_, num_channels_, |
- &audio_extra_data_[0], audio_extra_data_.size())); |
- } |
- if (HasVideo() && !video_decoder_job_) { |
- // StartInternal() will be delayed until SetVideoSurface() gets called. |
- return; |
- } |
+ CreateMediaDecoderJobs(false); |
StartInternal(); |
} |
@@ -333,6 +308,7 @@ void MediaSourcePlayer::Release() { |
active_decoding_tasks_ = 0; |
playing_ = false; |
pending_event_ = NO_EVENT_PENDING; |
+ surface_ = gfx::ScopedJavaSurface(); |
ReleaseMediaResourcesFromManager(); |
} |
@@ -360,6 +336,12 @@ void MediaSourcePlayer::StartInternal() { |
if (active_decoding_tasks_ > 0 || pending_event_ != NO_EVENT_PENDING) |
return; |
+ // If one of the decoder job is not ready, do nothing. |
+ if ((HasAudio() && !audio_decoder_job_) || |
+ (HasVideo() && !video_decoder_job_)) { |
+ return; |
+ } |
+ |
if (HasAudio()) { |
audio_finished_ = false; |
DecodeMoreAudio(); |
@@ -383,6 +365,15 @@ void MediaSourcePlayer::DemuxerReady( |
video_codec_ = params.video_codec; |
audio_extra_data_ = params.audio_extra_data; |
OnMediaMetadataChanged(duration_, width_, height_, true); |
+ if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) { |
+ CreateMediaDecoderJobs(true); |
+ pending_event_ &= ~CONFIG_CHANGE_EVENT_PENDING; |
+ // If there is a pending surface change, we can merge it with the config |
+ // change. |
+ pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING; |
+ if (playing_ && pending_event_ == NO_EVENT_PENDING) |
+ StartInternal(); |
+ } |
} |
void MediaSourcePlayer::ReadFromDemuxerAck( |
@@ -410,11 +401,13 @@ void MediaSourcePlayer::ReadFromDemuxerAck( |
} |
} |
-void MediaSourcePlayer::OnSeekRequestAck() { |
+void MediaSourcePlayer::OnSeekRequestAck(unsigned request_id) { |
acolwell GONE FROM CHROMIUM
2013/06/04 15:19:14
nit:s/request/seek_request/
qinmin
2013/06/04 19:03:23
Done.
|
+ // Do nothing until the most recent seek request is processed. |
+ if (seek_request_id_ != request_id) |
+ return; |
pending_event_ &= ~SEEK_EVENT_PENDING; |
OnSeekComplete(); |
- if (playing_) |
- StartInternal(); |
+ ProcessPendingEvents(); |
} |
void MediaSourcePlayer::UpdateTimestamps( |
@@ -429,20 +422,30 @@ void MediaSourcePlayer::UpdateTimestamps( |
} |
void MediaSourcePlayer::ProcessPendingEvents() { |
- // Wait for all the decoding jobs to finish before sending a seek request. |
+ // Wait for all the decoding jobs to finish before processing pending tasks. |
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(); |
+ if (pending_event_ & SEEK_EVENT_PENDING) { |
+ ClearDecodingData(); |
+ manager()->OnMediaSeekRequest( |
+ player_id(), last_presentation_timestamp_, ++seek_request_id_); |
+ return; |
+ } |
+ |
+ start_wallclock_time_ = base::Time(); |
+ if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) { |
+ manager()->OnMediaConfigRequest(player_id()); |
+ return; |
+ } |
+ |
+ if (pending_event_ & SURFACE_CHANGE_EVENT_PENDING) { |
+ CreateMediaDecoderJobs(false); |
pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING; |
} |
- ClearDecodingData(); |
- manager()->OnMediaSeekRequest(player_id(), |
- last_presentation_timestamp_, |
- pending_event_ & SURFACE_CHANGE_EVENT_PENDING); |
+ if (playing_ && pending_event_ == NO_EVENT_PENDING) |
+ StartInternal(); |
} |
void MediaSourcePlayer::MediaDecoderCallback( |
@@ -452,6 +455,11 @@ void MediaSourcePlayer::MediaDecoderCallback( |
if (active_decoding_tasks_ > 0) |
active_decoding_tasks_--; |
+ if (is_audio && audio_decoder_job_) |
+ audio_decoder_job_->OnDecodeCompleted(); |
+ if (!is_audio && video_decoder_job_) |
+ video_decoder_job_->OnDecodeCompleted(); |
+ |
if (!decode_succeeded) { |
Release(); |
OnMediaError(MEDIA_ERROR_DECODE); |
@@ -491,6 +499,16 @@ void MediaSourcePlayer::DecodeMoreAudio() { |
return; |
} |
+ if (DemuxerStream::kConfigChanged == |
+ received_audio_.access_units[audio_access_unit_index_].status) { |
+ // Wait for demuxer ready message. |
+ pending_event_ |= CONFIG_CHANGE_EVENT_PENDING; |
+ received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
+ audio_access_unit_index_ = 0; |
+ ProcessPendingEvents(); |
+ return; |
+ } |
+ |
audio_decoder_job_->Decode( |
received_audio_.access_units[audio_access_unit_index_], |
start_wallclock_time_, start_presentation_timestamp_, |
@@ -511,6 +529,16 @@ void MediaSourcePlayer::DecodeMoreVideo() { |
return; |
} |
+ if (DemuxerStream::kConfigChanged == |
+ received_video_.access_units[video_access_unit_index_].status) { |
+ // Wait for demuxer ready message. |
+ pending_event_ |= CONFIG_CHANGE_EVENT_PENDING; |
+ received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params(); |
+ video_access_unit_index_ = 0; |
+ ProcessPendingEvents(); |
+ return; |
+ } |
+ |
video_decoder_job_->Decode( |
received_video_.access_units[video_access_unit_index_], |
start_wallclock_time_, start_presentation_timestamp_, |
@@ -554,4 +582,29 @@ bool MediaSourcePlayer::HasAudio() { |
return kUnknownAudioCodec != audio_codec_; |
} |
+void MediaSourcePlayer::CreateMediaDecoderJobs(bool config_change) { |
+ DCHECK_EQ(0, active_decoding_tasks_); |
+ |
+ // Create audio decoder job only if config changes. |
+ if (HasAudio() && (config_change || !audio_decoder_job_)) { |
acolwell GONE FROM CHROMIUM
2013/06/04 15:19:14
I don't think this is quite right. Audio & Video c
qinmin
2013/06/04 19:03:23
added 2 new variable, |reconfig_audio_decoder_| an
|
+ audio_decoder_job_.reset(new AudioDecoderJob( |
+ audio_codec_, sampling_rate_, num_channels_, |
+ &audio_extra_data_[0], audio_extra_data_.size())); |
+ } |
+ |
+ video_decoder_job_.reset(); |
+ if (!HasVideo() || surface_.IsSurfaceEmpty()) |
+ return; |
+ |
+ // For video, the java surface might have changed even if config does not |
+ // change. So always recreate the video decoder job. |
+ video_decoder_job_.reset(new VideoDecoderJob( |
+ video_codec_, gfx::Size(width_, height_), surface_.j_surface().obj())); |
+ |
+ // 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); |
+} |
+ |
} // namespace media |