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

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

Issue 2276343005: Delete MediaCodecPlayer, it's time! (Closed)
Patch Set: Created 4 years, 4 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_decoder.h ('k') | media/base/android/media_codec_decoder_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_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
« no previous file with comments | « media/base/android/media_codec_decoder.h ('k') | media/base/android/media_codec_decoder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698