Index: media/base/android/media_codec_decoder.cc |
diff --git a/media/base/android/media_codec_decoder.cc b/media/base/android/media_codec_decoder.cc |
index bf0147faba27d097db7df975acf3b1652b7d2c43..ee8f2291b056620626de11212a643c9e8ce3f169 100644 |
--- a/media/base/android/media_codec_decoder.cc |
+++ b/media/base/android/media_codec_decoder.cc |
@@ -46,6 +46,7 @@ MediaCodecDecoder::MediaCodecDecoder( |
stop_done_cb_(stop_done_cb), |
error_cb_(error_cb), |
state_(kStopped), |
+ needs_preroll_(true), |
eos_enqueued_(false), |
completed_(false), |
last_frame_posted_(false), |
@@ -61,37 +62,18 @@ MediaCodecDecoder::MediaCodecDecoder( |
internal_error_cb_ = |
base::Bind(&MediaCodecDecoder::OnCodecError, weak_factory_.GetWeakPtr()); |
+ internal_preroll_done_cb_ = |
+ base::Bind(&MediaCodecDecoder::OnPrerollDone, weak_factory_.GetWeakPtr()); |
request_data_cb_ = |
base::Bind(&MediaCodecDecoder::RequestData, weak_factory_.GetWeakPtr()); |
} |
-MediaCodecDecoder::~MediaCodecDecoder() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << "Decoder::~Decoder()"; |
- |
- // NB: ReleaseDecoderResources() is virtual |
- ReleaseDecoderResources(); |
-} |
+MediaCodecDecoder::~MediaCodecDecoder() {} |
const char* MediaCodecDecoder::class_name() const { |
return "Decoder"; |
} |
-void MediaCodecDecoder::ReleaseDecoderResources() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- // Set [kInEmergencyStop| state to block already posted ProcessNextFrame(). |
- SetState(kInEmergencyStop); |
- |
- decoder_thread_.Stop(); // synchronous |
- |
- SetState(kStopped); |
- media_codec_bridge_.reset(); |
-} |
- |
void MediaCodecDecoder::Flush() { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
@@ -117,6 +99,8 @@ void MediaCodecDecoder::Flush() { |
verify_next_frame_is_key_ = true; |
#endif |
+ needs_preroll_ = true; |
+ |
if (media_codec_bridge_) { |
// MediaCodecBridge::Reset() performs MediaCodecBridge.flush() |
MediaCodecStatus flush_status = media_codec_bridge_->Reset(); |
@@ -134,13 +118,29 @@ void MediaCodecDecoder::ReleaseMediaCodec() { |
DVLOG(1) << class_name() << "::" << __FUNCTION__; |
media_codec_bridge_.reset(); |
+ needs_preroll_ = true; |
} |
bool MediaCodecDecoder::IsPrefetchingOrPlaying() const { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ // Whether decoder needs to be stopped. |
base::AutoLock lock(state_lock_); |
- return state_ == kPrefetching || state_ == kRunning; |
+ switch (state_) { |
+ case kPrefetching: |
+ case kPrefetched: |
+ case kPrerolling: |
+ case kPrerolled: |
+ case kRunning: |
+ return true; |
+ case kStopped: |
+ case kStopping: |
+ case kInEmergencyStop: |
+ case kError: |
+ return false; |
+ } |
+ NOTREACHED(); |
+ return false; |
} |
bool MediaCodecDecoder::IsStopped() const { |
@@ -155,6 +155,12 @@ bool MediaCodecDecoder::IsCompleted() const { |
return completed_; |
} |
+bool MediaCodecDecoder::NotCompletedAndNeedsPreroll() const { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ |
+ return HasStream() && needs_preroll_ && !completed_; |
+} |
+ |
base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() { |
base::android::ScopedJavaLocalRef<jobject> media_crypto; |
@@ -192,12 +198,7 @@ MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { |
DVLOG(1) << class_name() << "::" << __FUNCTION__ |
<< ": needs reconfigure, deleting MediaCodec"; |
needs_reconfigure_ = false; |
- media_codec_bridge_.reset(); |
- |
- // No need to release these buffers since the MediaCodec is deleted, just |
- // remove their indexes from |delayed_buffers_|. |
- |
- ClearDelayedBuffers(false); |
+ ReleaseMediaCodec(); |
} |
MediaCodecDecoder::ConfigStatus result; |
@@ -222,18 +223,14 @@ MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { |
return result; |
} |
-bool MediaCodecDecoder::Start(base::TimeDelta current_time) { |
+bool MediaCodecDecoder::Preroll(base::TimeDelta preroll_timestamp, |
+ const base::Closure& preroll_done_cb) { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " current_time:" << current_time; |
+ << " preroll_timestamp:" << preroll_timestamp; |
DecoderState state = GetState(); |
- if (state == kRunning) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": already started"; |
- return true; // already started |
- } |
- |
if (state != kPrefetched) { |
DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state " |
<< AsString(state) << ", ignoring"; |
@@ -247,10 +244,13 @@ bool MediaCodecDecoder::Start(base::TimeDelta current_time) { |
} |
DCHECK(!decoder_thread_.IsRunning()); |
+ DCHECK(needs_preroll_); |
+ |
+ preroll_done_cb_ = preroll_done_cb; |
// We only synchronize video stream. |
- // When audio is present, the |current_time| is audio time. |
- SynchronizePTSWithTime(current_time); |
+ DissociatePTSFromTime(); // associaton will happen after preroll is done. |
+ preroll_timestamp_ = preroll_timestamp; |
last_frame_posted_ = false; |
@@ -261,7 +261,48 @@ bool MediaCodecDecoder::Start(base::TimeDelta current_time) { |
return false; |
} |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ << " decoder thread started"; |
+ SetState(kPrerolling); |
+ |
+ decoder_thread_.task_runner()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this))); |
+ |
+ return true; |
+} |
+ |
+bool MediaCodecDecoder::Start(base::TimeDelta start_timestamp) { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ |
+ << " start_timestamp:" << start_timestamp; |
+ |
+ DecoderState state = GetState(); |
+ |
+ if (state != kPrefetched && state != kPrerolled) { |
+ DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state " |
+ << AsString(state) << ", ignoring"; |
+ return false; |
+ } |
+ |
+ if (!media_codec_bridge_) { |
+ DVLOG(0) << class_name() << "::" << __FUNCTION__ |
+ << ": not configured, ignoring"; |
+ return false; |
+ } |
+ |
+ // We only synchronize video stream. |
+ AssociateCurrentTimeWithPTS(start_timestamp); |
+ preroll_timestamp_ = base::TimeDelta(); |
+ |
+ // Start the decoder thread |
+ if (!decoder_thread_.IsRunning()) { |
+ last_frame_posted_ = false; |
+ if (!decoder_thread_.Start()) { |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ |
+ << ": cannot start decoder thread"; |
+ return false; |
+ } |
+ } |
SetState(kRunning); |
@@ -283,16 +324,9 @@ void MediaCodecDecoder::SyncStop() { |
return; |
} |
- // After this method returns, decoder thread will not be running. |
+ DoEmergencyStop(); |
- // Set [kInEmergencyStop| state to block already posted ProcessNextFrame(). |
- SetState(kInEmergencyStop); |
- |
- decoder_thread_.Stop(); // synchronous |
- |
- SetState(kStopped); |
- |
- ClearDelayedBuffers(true); // release prior to clearing |delayed_buffers_|. |
+ ReleaseDelayedBuffers(); |
} |
void MediaCodecDecoder::RequestToStop() { |
@@ -309,6 +343,14 @@ void MediaCodecDecoder::RequestToStop() { |
case kRunning: |
SetState(kStopping); |
break; |
+ case kPrerolling: |
+ case kPrerolled: |
+ DCHECK(decoder_thread_.IsRunning()); |
+ // Synchronous stop. |
+ decoder_thread_.Stop(); |
+ SetState(kStopped); |
+ media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); |
+ break; |
case kStopping: |
break; // ignore |
case kStopped: |
@@ -336,9 +378,26 @@ void MediaCodecDecoder::OnLastFrameRendered(bool completed) { |
SetState(kStopped); |
completed_ = completed; |
+ if (completed_ && !preroll_done_cb_.is_null()) |
+ media_task_runner_->PostTask(FROM_HERE, |
+ base::ResetAndReturn(&preroll_done_cb_)); |
+ |
media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); |
} |
+void MediaCodecDecoder::OnPrerollDone() { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__; |
+ |
+ if (GetState() == kPrerolling) { |
+ SetState(kPrerolled); |
+ needs_preroll_ = false; |
+ media_task_runner_->PostTask(FROM_HERE, |
+ base::ResetAndReturn(&preroll_done_cb_)); |
+ } |
+} |
+ |
void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
@@ -371,10 +430,29 @@ void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { |
PrefetchNextChunk(); |
} |
+bool MediaCodecDecoder::IsPrerollingForTests() const { |
+ // UI task runner. |
+ return GetState() == kPrerolling; |
+} |
+ |
int MediaCodecDecoder::NumDelayedRenderTasks() const { |
return 0; |
} |
+void MediaCodecDecoder::DoEmergencyStop() { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__; |
+ |
+ // After this method returns, decoder thread will not be running. |
+ |
+ // Set [kInEmergencyStop| state to block already posted ProcessNextFrame(). |
+ SetState(kInEmergencyStop); |
+ |
+ decoder_thread_.Stop(); // synchronous |
+ |
+ SetState(kStopped); |
+} |
+ |
void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, |
bool has_delayed_tasks) { |
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
@@ -440,7 +518,7 @@ void MediaCodecDecoder::ProcessNextFrame() { |
DecoderState state = GetState(); |
- if (state != kRunning && state != kStopping) { |
+ if (state != kPrerolling && state != kRunning && state != kStopping) { |
DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": not running"; |
return; |
} |
@@ -460,7 +538,7 @@ void MediaCodecDecoder::ProcessNextFrame() { |
return; |
} |
- DCHECK(state == kRunning); |
+ DCHECK(state == kPrerolling || state == kRunning); |
if (!EnqueueInputBuffer()) |
return; |
@@ -605,6 +683,8 @@ bool MediaCodecDecoder::DepleteOutputBufferQueue() { |
MediaCodecStatus status; |
bool eos_encountered = false; |
+ RenderMode render_mode; |
+ |
base::TimeDelta timeout = |
base::TimeDelta::FromMilliseconds(kOutputBufferTimeout); |
@@ -632,8 +712,23 @@ bool MediaCodecDecoder::DepleteOutputBufferQueue() { |
break; |
case MEDIA_CODEC_OK: |
- // We got the decoded frame |
- Render(buffer_index, offset, size, true, pts, eos_encountered); |
+ // We got the decoded frame. |
+ |
+ if (pts < preroll_timestamp_) |
+ render_mode = kRenderSkip; |
+ else if (GetState() == kPrerolling) |
+ render_mode = kRenderAfterPreroll; |
+ else |
+ render_mode = kRenderNow; |
+ |
+ Render(buffer_index, offset, size, render_mode, pts, eos_encountered); |
+ |
+ if (render_mode == kRenderAfterPreroll) { |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ << " pts:" << pts |
+ << " preroll done, stopping frame processing"; |
+ media_task_runner_->PostTask(FROM_HERE, internal_preroll_done_cb_); |
+ return false; |
+ } |
break; |
case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
@@ -686,18 +781,28 @@ void MediaCodecDecoder::SetState(DecoderState state) { |
case x: \ |
return #x; |
+const char* MediaCodecDecoder::AsString(RenderMode render_mode) { |
+ switch (render_mode) { |
+ RETURN_STRING(kRenderSkip); |
+ RETURN_STRING(kRenderAfterPreroll); |
+ RETURN_STRING(kRenderNow); |
+ } |
+ return nullptr; // crash early |
+} |
+ |
const char* MediaCodecDecoder::AsString(DecoderState state) { |
switch (state) { |
RETURN_STRING(kStopped); |
RETURN_STRING(kPrefetching); |
RETURN_STRING(kPrefetched); |
+ RETURN_STRING(kPrerolling); |
+ RETURN_STRING(kPrerolled); |
RETURN_STRING(kRunning); |
RETURN_STRING(kStopping); |
RETURN_STRING(kInEmergencyStop); |
RETURN_STRING(kError); |
- default: |
- return "Unknown DecoderState"; |
} |
+ return nullptr; // crash early |
} |
#undef RETURN_STRING |