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 |
deleted file mode 100644 |
index 51f222138cf9210779ab6127e736ea8363c4632c..0000000000000000000000000000000000000000 |
--- a/media/base/android/media_codec_decoder.cc |
+++ /dev/null |
@@ -1,1009 +0,0 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "media/base/android/media_codec_decoder.h" |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/callback_helpers.h" |
-#include "base/logging.h" |
-#include "media/base/android/media_codec_bridge.h" |
- |
-namespace media { |
- |
-namespace { |
- |
-// Stop requesting new data in the kPrefetching state when the queue size |
-// reaches this limit. |
-const int kPrefetchLimit = 8; |
- |
-// Request new data in the kRunning state if the queue size is less than this. |
-const int kPlaybackLowLimit = 4; |
- |
-// Posting delay of the next frame processing, in milliseconds |
-const int kNextFrameDelay = 1; |
- |
-// Timeout for dequeuing an input buffer from MediaCodec in milliseconds. |
-const int kInputBufferTimeout = 20; |
- |
-// Timeout for dequeuing an output buffer from MediaCodec in milliseconds. |
-const int kOutputBufferTimeout = 20; |
- |
-} // namespace |
- |
-MediaCodecDecoder::MediaCodecDecoder( |
- const char* decoder_thread_name, |
- const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
- FrameStatistics* frame_statistics, |
- const base::Closure& external_request_data_cb, |
- const base::Closure& starvation_cb, |
- const base::Closure& decoder_drained_cb, |
- const base::Closure& stop_done_cb, |
- const base::Closure& waiting_for_decryption_key_cb, |
- const base::Closure& error_cb) |
- : decoder_thread_(decoder_thread_name), |
- media_task_runner_(media_task_runner), |
- frame_statistics_(frame_statistics), |
- needs_reconfigure_(false), |
- drain_decoder_(false), |
- always_reconfigure_for_tests_(false), |
- external_request_data_cb_(external_request_data_cb), |
- starvation_cb_(starvation_cb), |
- decoder_drained_cb_(decoder_drained_cb), |
- stop_done_cb_(stop_done_cb), |
- waiting_for_decryption_key_cb_(waiting_for_decryption_key_cb), |
- error_cb_(error_cb), |
- state_(kStopped), |
- pending_input_buf_index_(-1), |
- is_prepared_(false), |
- eos_enqueued_(false), |
- missing_key_reported_(false), |
- completed_(false), |
- last_frame_posted_(false), |
- is_data_request_in_progress_(false), |
- is_incoming_data_invalid_(false), |
-#ifndef NDEBUG |
- verify_next_frame_is_key_(false), |
-#endif |
- weak_factory_(this) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name; |
- |
- 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() {} |
- |
-const char* MediaCodecDecoder::class_name() const { |
- return "Decoder"; |
-} |
- |
-void MediaCodecDecoder::Flush() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- DCHECK_EQ(GetState(), kStopped); |
- |
- // Flush() is a part of the Seek request. Whenever we request a seek we need |
- // to invalidate the current data request. |
- if (is_data_request_in_progress_) |
- is_incoming_data_invalid_ = true; |
- |
- eos_enqueued_ = false; |
- missing_key_reported_ = false; |
- completed_ = false; |
- drain_decoder_ = false; |
- au_queue_.Flush(); |
- |
- // |is_prepared_| is set on the decoder thread, it shouldn't be running now. |
- DCHECK(!decoder_thread_.IsRunning()); |
- is_prepared_ = false; |
- |
- pending_input_buf_index_ = -1; |
- |
-#ifndef NDEBUG |
- // We check and reset |verify_next_frame_is_key_| on Decoder thread. |
- // We have just DCHECKed that decoder thread is not running. |
- |
- // For video the first frame after flush must be key frame. |
- verify_next_frame_is_key_ = true; |
-#endif |
- |
- if (media_codec_bridge_) { |
- MediaCodecStatus flush_status = media_codec_bridge_->Flush(); |
- if (flush_status != MEDIA_CODEC_OK) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << "MediaCodecBridge::Flush() failed"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- } |
- } |
-} |
- |
-void MediaCodecDecoder::ReleaseMediaCodec() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- DCHECK(!decoder_thread_.IsRunning()); |
- |
- media_codec_bridge_.reset(); |
- |
- // |is_prepared_| is set on the decoder thread, it shouldn't be running now. |
- is_prepared_ = false; |
- |
- pending_input_buf_index_ = -1; |
-} |
- |
-bool MediaCodecDecoder::IsPrefetchingOrPlaying() const { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- // Whether decoder needs to be stopped. |
- base::AutoLock lock(state_lock_); |
- 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 { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- return GetState() == kStopped; |
-} |
- |
-bool MediaCodecDecoder::IsCompleted() const { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- return completed_; |
-} |
- |
-bool MediaCodecDecoder::NotCompletedAndNeedsPreroll() const { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- return HasStream() && !completed_ && |
- (!is_prepared_ || !preroll_timestamp_.is_zero()); |
-} |
- |
-void MediaCodecDecoder::SetPrerollTimestamp(base::TimeDelta preroll_timestamp) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": " << preroll_timestamp; |
- |
- preroll_timestamp_ = preroll_timestamp; |
-} |
- |
-void MediaCodecDecoder::SetNeedsReconfigure() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- needs_reconfigure_ = true; |
-} |
- |
-void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- DCHECK(GetState() == kStopped); |
- |
- prefetch_done_cb_ = prefetch_done_cb; |
- |
- SetState(kPrefetching); |
- PrefetchNextChunk(); |
-} |
- |
-MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure( |
- jobject media_crypto) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- if (GetState() == kError) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state kError"; |
- return kConfigFailure; |
- } |
- |
- if (needs_reconfigure_) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": needs reconfigure, deleting MediaCodec"; |
- needs_reconfigure_ = false; |
- ReleaseMediaCodec(); |
- } |
- |
- if (media_codec_bridge_) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": reconfiguration is not required, ignoring"; |
- return kConfigOk; |
- } |
- |
- // Read all |kConfigChanged| units preceding the data one. |
- AccessUnitQueue::Info au_info = au_queue_.GetInfo(); |
- while (au_info.configs) { |
- SetDemuxerConfigs(*au_info.configs); |
- au_queue_.Advance(); |
- au_info = au_queue_.GetInfo(); |
- } |
- |
- MediaCodecDecoder::ConfigStatus result = ConfigureInternal(media_crypto); |
- |
-#ifndef NDEBUG |
- // We check and reset |verify_next_frame_is_key_| on Decoder thread. |
- // This DCHECK ensures we won't need to lock this variable. |
- DCHECK(!decoder_thread_.IsRunning()); |
- |
- // For video the first frame after reconfiguration must be key frame. |
- if (result == kConfigOk) |
- verify_next_frame_is_key_ = true; |
-#endif |
- |
- return result; |
-} |
- |
-bool MediaCodecDecoder::Preroll(const base::Closure& preroll_done_cb) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " preroll_timestamp:" << preroll_timestamp_; |
- |
- DecoderState state = GetState(); |
- if (state != kPrefetched) { |
- 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; |
- } |
- |
- DCHECK(!decoder_thread_.IsRunning()); |
- |
- preroll_done_cb_ = preroll_done_cb; |
- |
- // We only synchronize video stream. |
- DissociatePTSFromTime(); // associaton will happen after preroll is done. |
- |
- last_frame_posted_ = false; |
- missing_key_reported_ = false; |
- |
- // Start the decoder thread |
- if (!decoder_thread_.Start()) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": cannot start decoder thread"; |
- return false; |
- } |
- |
- 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); |
- |
- DCHECK(preroll_timestamp_.is_zero()); |
- |
- // Start the decoder thread |
- if (!decoder_thread_.IsRunning()) { |
- last_frame_posted_ = false; |
- missing_key_reported_ = false; |
- if (!decoder_thread_.Start()) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": cannot start decoder thread"; |
- return false; |
- } |
- } |
- |
- SetState(kRunning); |
- |
- decoder_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this))); |
- |
- return true; |
-} |
- |
-void MediaCodecDecoder::SyncStop() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- if (GetState() == kError) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": wrong state kError, ignoring"; |
- return; |
- } |
- |
- DoEmergencyStop(); |
- |
- ReleaseDelayedBuffers(); |
-} |
- |
-void MediaCodecDecoder::RequestToStop() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- DecoderState state = GetState(); |
- switch (state) { |
- case kError: |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": wrong state kError, ignoring"; |
- break; |
- 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: |
- case kStopped: |
- break; // ignore |
- case kPrefetching: |
- case kPrefetched: |
- // There is nothing to wait for, we can sent notification right away. |
- DCHECK(!decoder_thread_.IsRunning()); |
- SetState(kStopped); |
- media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); |
- break; |
- default: |
- NOTREACHED(); |
- break; |
- } |
-} |
- |
-void MediaCodecDecoder::OnLastFrameRendered(bool eos_encountered) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " eos_encountered:" << eos_encountered; |
- |
- decoder_thread_.Stop(); // synchronous |
- |
- SetState(kStopped); |
- completed_ = (eos_encountered && !drain_decoder_); |
- |
- missing_key_reported_ = false; |
- |
- // If the stream is completed during preroll we need to report it since |
- // another stream might be running and the player waits for two callbacks. |
- if (completed_ && !preroll_done_cb_.is_null()) { |
- preroll_timestamp_ = base::TimeDelta(); |
- media_task_runner_->PostTask(FROM_HERE, |
- base::ResetAndReturn(&preroll_done_cb_)); |
- } |
- |
- if (eos_encountered && drain_decoder_) { |
- drain_decoder_ = false; |
- eos_enqueued_ = false; |
- ReleaseMediaCodec(); |
- media_task_runner_->PostTask(FROM_HERE, decoder_drained_cb_); |
- } |
- |
- media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); |
-} |
- |
-void MediaCodecDecoder::OnPrerollDone() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " state:" << AsString(GetState()); |
- |
- preroll_timestamp_ = base::TimeDelta(); |
- |
- // The state might be kStopping (?) |
- if (GetState() == kPrerolling) |
- SetState(kPrerolled); |
- |
- if (!preroll_done_cb_.is_null()) |
- base::ResetAndReturn(&preroll_done_cb_).Run(); |
-} |
- |
-void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- // If |data| contains an aborted data, the last AU will have kAborted status. |
- bool aborted_data = |
- !data.access_units.empty() && |
- data.access_units.back().status == DemuxerStream::kAborted; |
- |
-#ifndef NDEBUG |
- const char* explain_if_skipped = |
- is_incoming_data_invalid_ ? " skipped as invalid" |
- : (aborted_data ? " skipped as aborted" : ""); |
- |
- for (const auto& unit : data.access_units) |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ << explain_if_skipped |
- << " au: " << unit; |
- for (const auto& configs : data.demuxer_configs) |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ << " configs: " << configs; |
-#endif |
- |
- if (!is_incoming_data_invalid_ && !aborted_data) |
- au_queue_.PushBack(data); |
- |
- is_incoming_data_invalid_ = false; |
- is_data_request_in_progress_ = false; |
- |
- // Do not request data if we got kAborted. There is no point to request the |
- // data after kAborted and before the OnDemuxerSeekDone. |
- if (GetState() == kPrefetching && !aborted_data) |
- PrefetchNextChunk(); |
-} |
- |
-bool MediaCodecDecoder::IsPrerollingForTests() const { |
- // UI task runner. |
- return GetState() == kPrerolling; |
-} |
- |
-void MediaCodecDecoder::SetAlwaysReconfigureForTests() { |
- // UI task runner. |
- always_reconfigure_for_tests_ = true; |
-} |
- |
-void MediaCodecDecoder::SetCodecCreatedCallbackForTests(base::Closure cb) { |
- // UI task runner. |
- codec_created_for_tests_cb_ = cb; |
-} |
- |
-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); |
- |
- missing_key_reported_ = false; |
-} |
- |
-void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, |
- bool has_delayed_tasks) { |
- DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
- |
- bool last_frame_when_stopping = GetState() == kStopping && !has_delayed_tasks; |
- |
- if (last_frame_when_stopping || eos_encountered) { |
- media_task_runner_->PostTask( |
- FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, |
- weak_factory_.GetWeakPtr(), eos_encountered)); |
- last_frame_posted_ = true; |
- } |
-} |
- |
-void MediaCodecDecoder::OnCodecError() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- // Ignore codec errors from the moment surface is changed till the |
- // |media_codec_bridge_| is deleted. |
- if (needs_reconfigure_) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": needs reconfigure, ignoring"; |
- return; |
- } |
- |
- SetState(kError); |
- error_cb_.Run(); |
-} |
- |
-void MediaCodecDecoder::RequestData() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- // We request data only in kPrefetching, kPrerolling and kRunning states. |
- // For kPrerolling and kRunning this method is posted from Decoder thread, |
- // and by the time it arrives the player might be doing something else, e.g. |
- // seeking, in which case we should not request more data |
- switch (GetState()) { |
- case kPrefetching: |
- case kPrerolling: |
- case kRunning: |
- break; // continue |
- default: |
- return; // skip |
- } |
- |
- // Ensure one data request at a time. |
- if (!is_data_request_in_progress_) { |
- is_data_request_in_progress_ = true; |
- external_request_data_cb_.Run(); |
- } |
-} |
- |
-void MediaCodecDecoder::PrefetchNextChunk() { |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- |
- DVLOG(1) << class_name() << "::" << __FUNCTION__; |
- |
- AccessUnitQueue::Info au_info = au_queue_.GetInfo(); |
- |
- if (eos_enqueued_ || au_info.data_length >= kPrefetchLimit || |
- au_info.has_eos) { |
- // We are done prefetching |
- SetState(kPrefetched); |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << " posting PrefetchDone"; |
- media_task_runner_->PostTask(FROM_HERE, |
- base::ResetAndReturn(&prefetch_done_cb_)); |
- return; |
- } |
- |
- request_data_cb_.Run(); |
-} |
- |
-void MediaCodecDecoder::ProcessNextFrame() { |
- DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
- |
- DVLOG(2) << class_name() << "::" << __FUNCTION__; |
- |
- DecoderState state = GetState(); |
- |
- if (state != kPrerolling && state != kRunning && state != kStopping) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": state: " << AsString(state) << " stopping frame processing"; |
- return; |
- } |
- |
- if (state == kStopping) { |
- if (NumDelayedRenderTasks() == 0 && !last_frame_posted_) { |
- media_task_runner_->PostTask( |
- FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, |
- weak_factory_.GetWeakPtr(), false)); |
- last_frame_posted_ = true; |
- } |
- |
- // We can stop processing, the |au_queue_| and MediaCodec queues can freeze. |
- // We only need to let finish the delayed rendering tasks. |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << " kStopping, returning"; |
- return; |
- } |
- |
- DCHECK(state == kPrerolling || state == kRunning); |
- |
- if (!EnqueueInputBuffer()) |
- return; |
- |
- if (!DepleteOutputBufferQueue()) |
- return; |
- |
- // We need a small delay if we want to stop this thread by |
- // decoder_thread_.Stop() reliably. |
- // The decoder thread message loop processes all pending |
- // (but not delayed) tasks before it can quit; without a delay |
- // the message loop might be forever processing the pendng tasks. |
- decoder_thread_.task_runner()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this)), |
- base::TimeDelta::FromMilliseconds(kNextFrameDelay)); |
-} |
- |
-// Returns false if we should stop decoding process. Right now |
-// it happens if we got MediaCodec error or detected starvation. |
-bool MediaCodecDecoder::EnqueueInputBuffer() { |
- DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
- |
- DVLOG(2) << class_name() << "::" << __FUNCTION__; |
- |
- if (eos_enqueued_) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": EOS enqueued, returning"; |
- return true; // Nothing to do |
- } |
- |
- if (missing_key_reported_) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": NO KEY reported, returning"; |
- return true; // Nothing to do |
- } |
- |
- // Keep the number pending video frames low, ideally maintaining |
- // the same audio and video duration after stop request |
- if (NumDelayedRenderTasks() > 1) { |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ << ": # delayed buffers (" |
- << NumDelayedRenderTasks() << ") exceeds 1, returning"; |
- return true; // Nothing to do |
- } |
- |
- // Get the next frame from the queue. As we go, request more data and |
- // consume |kConfigChanged| units. |
- |
- // |drain_decoder_| can be already set here if we could not dequeue the input |
- // buffer for it right away. |
- |
- AccessUnitQueue::Info au_info; |
- if (!drain_decoder_) { |
- au_info = AdvanceAccessUnitQueue(&drain_decoder_); |
- if (!au_info.length) { |
- // Report starvation and return, Start() will be called again later. |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": starvation detected"; |
- media_task_runner_->PostTask(FROM_HERE, starvation_cb_); |
- return true; |
- } |
- |
- DCHECK(au_info.front_unit); |
- |
-#ifndef NDEBUG |
- if (verify_next_frame_is_key_) { |
- verify_next_frame_is_key_ = false; |
- VerifyUnitIsKeyFrame(au_info.front_unit); |
- } |
-#endif |
- } |
- |
- // Dequeue input buffer |
- |
- int index = pending_input_buf_index_; |
- |
- // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY. |
- // That status does not return this buffer back to the pool of |
- // available input buffers. We have to reuse it in QueueSecureInputBuffer(). |
- if (index == -1) { |
- base::TimeDelta timeout = |
- base::TimeDelta::FromMilliseconds(kInputBufferTimeout); |
- MediaCodecStatus status = |
- media_codec_bridge_->DequeueInputBuffer(timeout, &index); |
- |
- switch (status) { |
- case MEDIA_CODEC_ERROR: |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": MEDIA_CODEC_ERROR DequeueInputBuffer failed"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
- |
- case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
- DVLOG(2) |
- << class_name() << "::" << __FUNCTION__ |
- << ": DequeueInputBuffer returned MediaCodec.INFO_TRY_AGAIN_LATER."; |
- return true; |
- |
- default: |
- break; |
- } |
- |
- DCHECK_EQ(status, MEDIA_CODEC_OK); |
- } |
- |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << ": using input buffer index:" << index; |
- |
- // We got the buffer |
- DCHECK_GE(index, 0); |
- |
- const AccessUnit* unit = au_info.front_unit; |
- |
- if (drain_decoder_ || unit->is_end_of_stream) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; |
- |
- // Check that we are not using the pending input buffer to queue EOS |
- DCHECK(pending_input_buf_index_ == -1); |
- |
- media_codec_bridge_->QueueEOS(index); |
- eos_enqueued_ = true; |
- return true; |
- } |
- |
- DCHECK(unit); |
- DCHECK(!unit->data.empty()); |
- |
- // Pending input buffer is already filled with data. |
- const uint8_t* memory = |
- (pending_input_buf_index_ == -1) ? &unit->data[0] : nullptr; |
- |
- pending_input_buf_index_ = -1; |
- MediaCodecStatus status = MEDIA_CODEC_OK; |
- |
- if (unit->key_id.empty() || unit->iv.empty()) { |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << ": QueueInputBuffer pts:" << unit->timestamp; |
- |
- status = media_codec_bridge_->QueueInputBuffer( |
- index, memory, unit->data.size(), unit->timestamp); |
- } else { |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << ": QueueSecureInputBuffer pts:" << unit->timestamp |
- << " key_id size:" << unit->key_id.size() |
- << " iv size:" << unit->iv.size() |
- << " subsamples size:" << unit->subsamples.size(); |
- |
- status = media_codec_bridge_->QueueSecureInputBuffer( |
- index, memory, unit->data.size(), unit->key_id, unit->iv, |
- unit->subsamples.empty() ? nullptr : &unit->subsamples[0], |
- unit->subsamples.size(), unit->timestamp); |
- } |
- |
- switch (status) { |
- case MEDIA_CODEC_OK: |
- break; |
- |
- case MEDIA_CODEC_ERROR: |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
- |
- case MEDIA_CODEC_NO_KEY: |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": MEDIA_CODEC_NO_KEY"; |
- media_task_runner_->PostTask(FROM_HERE, waiting_for_decryption_key_cb_); |
- |
- // We need to enqueue the same input buffer after we get the key. |
- // The buffer is owned by us (not the MediaCodec) and is filled with data. |
- pending_input_buf_index_ = index; |
- |
- // In response to the |waiting_for_decryption_key_cb_| the player will |
- // request to stop decoder. We need to keep running to properly perform |
- // the stop, but prevent generating more |waiting_for_decryption_key_cb_|. |
- missing_key_reported_ = true; |
- return true; |
- |
- default: |
- NOTREACHED() << class_name() << "::" << __FUNCTION__ |
- << ": unexpected error code " << status; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
- } |
- |
- // Have successfully queued input buffer, go to next access unit. |
- au_queue_.Advance(); |
- return true; |
-} |
- |
-AccessUnitQueue::Info MediaCodecDecoder::AdvanceAccessUnitQueue( |
- bool* drain_decoder) { |
- DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
- DVLOG(2) << class_name() << "::" << __FUNCTION__; |
- |
- // Retrieve access units from the |au_queue_| in a loop until we either get |
- // a non-config front unit or until the queue is empty. |
- |
- DCHECK(drain_decoder != nullptr); |
- |
- AccessUnitQueue::Info au_info; |
- |
- do { |
- // Get current frame |
- au_info = au_queue_.GetInfo(); |
- |
- // Request the data from Demuxer |
- if (au_info.data_length <= kPlaybackLowLimit && !au_info.has_eos) |
- media_task_runner_->PostTask(FROM_HERE, request_data_cb_); |
- |
- if (!au_info.length) |
- break; // Starvation |
- |
- if (au_info.configs) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": received configs " |
- << (*au_info.configs); |
- |
- // Compare the new and current configs. |
- if (IsCodecReconfigureNeeded(*au_info.configs)) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": reconfiguration and decoder drain required"; |
- *drain_decoder = true; |
- } |
- |
- // Replace the current configs. |
- SetDemuxerConfigs(*au_info.configs); |
- |
- // Move to the next frame |
- au_queue_.Advance(); |
- } |
- } while (au_info.configs); |
- |
- return au_info; |
-} |
- |
-// Returns false if there was MediaCodec error. |
-bool MediaCodecDecoder::DepleteOutputBufferQueue() { |
- DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
- |
- DVLOG(2) << class_name() << "::" << __FUNCTION__; |
- |
- int buffer_index = 0; |
- size_t offset = 0; |
- size_t size = 0; |
- base::TimeDelta pts; |
- MediaCodecStatus status; |
- bool eos_encountered = false; |
- |
- RenderMode render_mode; |
- |
- base::TimeDelta timeout = |
- base::TimeDelta::FromMilliseconds(kOutputBufferTimeout); |
- |
- // Extract all output buffers that are available. |
- // Usually there will be only one, but sometimes it is preceeded by |
- // MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED or MEDIA_CODEC_OUTPUT_FORMAT_CHANGED. |
- do { |
- status = media_codec_bridge_->DequeueOutputBuffer( |
- timeout, &buffer_index, &offset, &size, &pts, &eos_encountered, |
- nullptr); |
- |
- // Reset the timeout to 0 for the subsequent DequeueOutputBuffer() calls |
- // to quickly break the loop after we got all currently available buffers. |
- timeout = base::TimeDelta::FromMilliseconds(0); |
- |
- switch (status) { |
- case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
- // Output buffers are replaced in MediaCodecBridge, nothing to do. |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << " MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED"; |
- break; |
- |
- case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; |
- if (!OnOutputFormatChanged()) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << ": OnOutputFormatChanged failed, stopping frame" |
- << " processing"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
- } |
- break; |
- |
- case MEDIA_CODEC_OK: |
- // We got the decoded frame. |
- |
- is_prepared_ = true; |
- |
- if (pts < preroll_timestamp_) |
- render_mode = kRenderSkip; |
- else if (GetState() == kPrerolling) |
- render_mode = kRenderAfterPreroll; |
- else |
- render_mode = kRenderNow; |
- |
- if (!Render(buffer_index, offset, size, render_mode, pts, |
- eos_encountered)) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " Render failed, stopping frame processing"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
- } |
- |
- if (render_mode == kRenderAfterPreroll) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << " pts " << pts |
- << " >= preroll timestamp " << preroll_timestamp_ |
- << " 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: |
- // Nothing to do. |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << " MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER"; |
- break; |
- |
- case MEDIA_CODEC_ERROR: |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- break; |
- |
- default: |
- NOTREACHED(); |
- break; |
- } |
- } while (status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER && |
- status != MEDIA_CODEC_ERROR && !eos_encountered); |
- |
- if (eos_encountered) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ |
- << " EOS dequeued, stopping frame processing"; |
- return false; |
- } |
- |
- if (status == MEDIA_CODEC_ERROR) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << " MediaCodec error, stopping frame processing"; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-MediaCodecDecoder::DecoderState MediaCodecDecoder::GetState() const { |
- base::AutoLock lock(state_lock_); |
- return state_; |
-} |
- |
-void MediaCodecDecoder::SetState(DecoderState state) { |
- DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << AsString(state); |
- |
- base::AutoLock lock(state_lock_); |
- state_ = state; |
-} |
- |
-#undef RETURN_STRING |
-#define RETURN_STRING(x) \ |
- 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); |
- } |
- return nullptr; // crash early |
-} |
- |
-#undef RETURN_STRING |
- |
-} // namespace media |