| Index: media/base/android/media_decoder_job.cc | 
| diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc | 
| deleted file mode 100644 | 
| index efbbea8f47b6afd69cc92b8cf90860a7a9d418bb..0000000000000000000000000000000000000000 | 
| --- a/media/base/android/media_decoder_job.cc | 
| +++ /dev/null | 
| @@ -1,693 +0,0 @@ | 
| -// Copyright 2013 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_decoder_job.h" | 
| - | 
| -#include "base/bind.h" | 
| -#include "base/callback_helpers.h" | 
| -#include "base/single_thread_task_runner.h" | 
| -#include "base/threading/thread_task_runner_handle.h" | 
| -#include "base/trace_event/trace_event.h" | 
| -#include "media/base/android/media_drm_bridge.h" | 
| -#include "media/base/bind_to_current_loop.h" | 
| -#include "media/base/timestamp_constants.h" | 
| - | 
| -namespace media { | 
| - | 
| -// Timeout value for media codec operations. Because the first | 
| -// DequeInputBuffer() can take about 150 milliseconds, use 250 milliseconds | 
| -// here. See http://b/9357571. | 
| -static const int kMediaCodecTimeoutInMilliseconds = 250; | 
| - | 
| -MediaDecoderJob::MediaDecoderJob( | 
| -    const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner, | 
| -    const base::Closure& request_data_cb, | 
| -    const base::Closure& config_changed_cb) | 
| -    : need_to_reconfig_decoder_job_(false), | 
| -      ui_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 
| -      decoder_task_runner_(decoder_task_runner), | 
| -      needs_flush_(false), | 
| -      input_eos_encountered_(false), | 
| -      output_eos_encountered_(false), | 
| -      skip_eos_enqueue_(true), | 
| -      prerolling_(true), | 
| -      request_data_cb_(request_data_cb), | 
| -      config_changed_cb_(config_changed_cb), | 
| -      current_demuxer_data_index_(0), | 
| -      input_buf_index_(-1), | 
| -      is_content_encrypted_(false), | 
| -      stop_decode_pending_(false), | 
| -      destroy_pending_(false), | 
| -      is_requesting_demuxer_data_(false), | 
| -      is_incoming_data_invalid_(false), | 
| -      release_resources_pending_(false), | 
| -      drm_bridge_(NULL), | 
| -      drain_decoder_(false) { | 
| -  InitializeReceivedData(); | 
| -  eos_unit_.is_end_of_stream = true; | 
| -} | 
| - | 
| -MediaDecoderJob::~MediaDecoderJob() { | 
| -  ReleaseMediaCodecBridge(); | 
| -} | 
| - | 
| -void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { | 
| -  DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(NoAccessUnitsRemainingInChunk(false)); | 
| - | 
| -  TRACE_EVENT_ASYNC_END2( | 
| -      "media", "MediaDecoderJob::RequestData", this, | 
| -      "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO", | 
| -      "Units read", data.access_units.size()); | 
| - | 
| -  if (is_incoming_data_invalid_) { | 
| -    is_incoming_data_invalid_ = false; | 
| - | 
| -    // If there is a pending callback, need to request the data again to get | 
| -    // valid data. | 
| -    if (!data_received_cb_.is_null()) | 
| -      request_data_cb_.Run(); | 
| -    else | 
| -      is_requesting_demuxer_data_ = false; | 
| -    return; | 
| -  } | 
| - | 
| -  size_t next_demuxer_data_index = inactive_demuxer_data_index(); | 
| -  received_data_[next_demuxer_data_index] = data; | 
| -  access_unit_index_[next_demuxer_data_index] = 0; | 
| -  is_requesting_demuxer_data_ = false; | 
| - | 
| -  base::Closure done_cb = base::ResetAndReturn(&data_received_cb_); | 
| - | 
| -  // If this data request is for the inactive chunk, or |data_received_cb_| | 
| -  // was set to null by Flush() or Release(), do nothing. | 
| -  if (done_cb.is_null()) | 
| -    return; | 
| - | 
| -  if (stop_decode_pending_) { | 
| -    DCHECK(is_decoding()); | 
| -    OnDecodeCompleted(MEDIA_CODEC_ABORT, false, kNoTimestamp, kNoTimestamp); | 
| -    return; | 
| -  } | 
| - | 
| -  done_cb.Run(); | 
| -} | 
| - | 
| -void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(data_received_cb_.is_null()); | 
| -  DCHECK(decode_cb_.is_null()); | 
| - | 
| -  if (HasData()) { | 
| -    DVLOG(1) << __FUNCTION__ << " : using previously received data"; | 
| -    ui_task_runner_->PostTask(FROM_HERE, prefetch_cb); | 
| -    return; | 
| -  } | 
| - | 
| -  DVLOG(1) << __FUNCTION__ << " : requesting data"; | 
| -  RequestData(prefetch_cb); | 
| -} | 
| - | 
| -MediaDecoderJob::MediaDecoderJobStatus MediaDecoderJob::Decode( | 
| -    base::TimeTicks start_time_ticks, | 
| -    base::TimeDelta start_presentation_timestamp, | 
| -    const DecoderCallback& callback) { | 
| -  DCHECK(decode_cb_.is_null()); | 
| -  DCHECK(data_received_cb_.is_null()); | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  if (!media_codec_bridge_ || need_to_reconfig_decoder_job_) { | 
| -    if (drain_decoder_) | 
| -      OnDecoderDrained(); | 
| -    MediaDecoderJobStatus status = CreateMediaCodecBridge(); | 
| -    need_to_reconfig_decoder_job_ = (status != STATUS_SUCCESS); | 
| -    skip_eos_enqueue_ = true; | 
| -    if (need_to_reconfig_decoder_job_) | 
| -      return status; | 
| -  } | 
| - | 
| -  decode_cb_ = callback; | 
| - | 
| -  if (!HasData()) { | 
| -    RequestData(base::Bind(&MediaDecoderJob::DecodeCurrentAccessUnit, | 
| -                           base::Unretained(this), | 
| -                           start_time_ticks, | 
| -                           start_presentation_timestamp)); | 
| -    return STATUS_SUCCESS; | 
| -  } | 
| - | 
| -  DecodeCurrentAccessUnit(start_time_ticks, start_presentation_timestamp); | 
| -  return STATUS_SUCCESS; | 
| -} | 
| - | 
| -void MediaDecoderJob::StopDecode() { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(is_decoding()); | 
| -  stop_decode_pending_ = true; | 
| -} | 
| - | 
| -bool MediaDecoderJob::OutputEOSReached() const { | 
| -  return !drain_decoder_ && output_eos_encountered_; | 
| -} | 
| - | 
| -void MediaDecoderJob::SetDrmBridge(MediaDrmBridge* drm_bridge) { | 
| -  drm_bridge_ = drm_bridge; | 
| -  need_to_reconfig_decoder_job_ = true; | 
| -} | 
| - | 
| -void MediaDecoderJob::Flush() { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(data_received_cb_.is_null()); | 
| -  DCHECK(decode_cb_.is_null()); | 
| - | 
| -  // Clean up the received data. | 
| -  current_demuxer_data_index_ = 0; | 
| -  InitializeReceivedData(); | 
| -  if (is_requesting_demuxer_data_) | 
| -    is_incoming_data_invalid_ = true; | 
| -  input_eos_encountered_ = false; | 
| -  output_eos_encountered_ = false; | 
| -  drain_decoder_ = false; | 
| - | 
| -  // Do nothing, flush when the next Decode() happens. | 
| -  needs_flush_ = true; | 
| -} | 
| - | 
| -void MediaDecoderJob::BeginPrerolling(base::TimeDelta preroll_timestamp) { | 
| -  DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")"; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(!is_decoding()); | 
| - | 
| -  preroll_timestamp_ = preroll_timestamp; | 
| -  prerolling_ = true; | 
| -} | 
| - | 
| -void MediaDecoderJob::ReleaseDecoderResources() { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  if (decode_cb_.is_null()) { | 
| -    DCHECK(!drain_decoder_); | 
| -    // Since the decoder job is not decoding data, we can safely destroy | 
| -    // |media_codec_bridge_|. | 
| -    ReleaseMediaCodecBridge(); | 
| -    return; | 
| -  } | 
| - | 
| -  // Release |media_codec_bridge_| once decoding is completed. | 
| -  release_resources_pending_ = true; | 
| -} | 
| - | 
| -jobject MediaDecoderJob::GetMediaCrypto() { | 
| -  return drm_bridge_ ? drm_bridge_->GetMediaCrypto() : nullptr; | 
| -} | 
| - | 
| -bool MediaDecoderJob::SetCurrentFrameToPreviouslyCachedKeyFrame() { | 
| -  const std::vector<AccessUnit>& access_units = | 
| -      received_data_[current_demuxer_data_index_].access_units; | 
| -  // If the current data chunk is empty, the player must be in an initial or | 
| -  // seek state. The next access unit will always be a key frame. | 
| -  if (access_units.size() == 0) | 
| -    return true; | 
| - | 
| -  // Find key frame in all the access units the decoder have decoded, | 
| -  // or is about to decode. | 
| -  int i = std::min(access_unit_index_[current_demuxer_data_index_], | 
| -                   access_units.size() - 1); | 
| -  for (; i >= 0; --i) { | 
| -    // Config change is always the last access unit, and it always come with | 
| -    // a key frame afterwards. | 
| -    if (access_units[i].status == DemuxerStream::kConfigChanged) | 
| -      return true; | 
| -    if (access_units[i].is_key_frame) { | 
| -      access_unit_index_[current_demuxer_data_index_] = i; | 
| -      return true; | 
| -    } | 
| -  } | 
| -  return false; | 
| -} | 
| - | 
| - | 
| -void MediaDecoderJob::Release() { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DVLOG(1) << __FUNCTION__; | 
| - | 
| -  // If the decoder job is still decoding, we cannot delete the job immediately. | 
| -  destroy_pending_ = is_decoding(); | 
| - | 
| -  request_data_cb_.Reset(); | 
| -  data_received_cb_.Reset(); | 
| -  decode_cb_.Reset(); | 
| - | 
| -  if (destroy_pending_) { | 
| -    DVLOG(1) << __FUNCTION__ << " : delete is pending decode completion"; | 
| -    return; | 
| -  } | 
| - | 
| -  delete this; | 
| -} | 
| - | 
| -MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(decoder_task_runner_->BelongsToCurrentThread()); | 
| -  TRACE_EVENT0("media", __FUNCTION__); | 
| - | 
| -  int input_buf_index = input_buf_index_; | 
| -  input_buf_index_ = -1; | 
| - | 
| -  // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge. | 
| -  if (input_buf_index == -1) { | 
| -    base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | 
| -        kMediaCodecTimeoutInMilliseconds); | 
| -    MediaCodecStatus status = | 
| -        media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index); | 
| -    if (status != MEDIA_CODEC_OK) { | 
| -      DVLOG(1) << "DequeueInputBuffer fails: " << status; | 
| -      return status; | 
| -    } | 
| -  } | 
| - | 
| -  // TODO(qinmin): skip frames if video is falling far behind. | 
| -  DCHECK_GE(input_buf_index, 0); | 
| -  if (unit.is_end_of_stream || unit.data.empty()) { | 
| -    media_codec_bridge_->QueueEOS(input_buf_index); | 
| -    return MEDIA_CODEC_INPUT_END_OF_STREAM; | 
| -  } | 
| - | 
| -  if (unit.key_id.empty() || unit.iv.empty()) { | 
| -    DCHECK(unit.iv.empty() || !unit.key_id.empty()); | 
| -    return media_codec_bridge_->QueueInputBuffer( | 
| -        input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); | 
| -  } | 
| - | 
| -  MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer( | 
| -      input_buf_index, &unit.data[0], unit.data.size(), unit.key_id, unit.iv, | 
| -      unit.subsamples.empty() ? NULL : &unit.subsamples[0], | 
| -      unit.subsamples.size(), unit.timestamp); | 
| - | 
| -  // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. | 
| -  // Otherwise MediaDrm will report errors. | 
| -  if (status == MEDIA_CODEC_NO_KEY) | 
| -    input_buf_index_ = input_buf_index; | 
| - | 
| -  return status; | 
| -} | 
| - | 
| -bool MediaDecoderJob::HasData() const { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  // When |input_eos_encountered_| is set, |access_unit_index_| and | 
| -  // |current_demuxer_data_index_| must be pointing to an EOS unit, | 
| -  // or a |kConfigChanged| unit if |drain_decoder_| is true. In both cases, | 
| -  // we'll feed an EOS input unit to drain the decoder until we hit output EOS. | 
| -  DCHECK(!input_eos_encountered_ || !NoAccessUnitsRemainingInChunk(true)); | 
| -  return !NoAccessUnitsRemainingInChunk(true) || | 
| -      !NoAccessUnitsRemainingInChunk(false); | 
| -} | 
| - | 
| -void MediaDecoderJob::RequestData(const base::Closure& done_cb) { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(data_received_cb_.is_null()); | 
| -  DCHECK(!input_eos_encountered_); | 
| -  DCHECK(NoAccessUnitsRemainingInChunk(false)); | 
| - | 
| -  TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this); | 
| - | 
| -  data_received_cb_ = done_cb; | 
| - | 
| -  // If we are already expecting new data, just set the callback and do | 
| -  // nothing. | 
| -  if (is_requesting_demuxer_data_) | 
| -    return; | 
| - | 
| -  // The new incoming data will be stored as the next demuxer data chunk, since | 
| -  // the decoder might still be decoding the current one. | 
| -  size_t next_demuxer_data_index = inactive_demuxer_data_index(); | 
| -  received_data_[next_demuxer_data_index] = DemuxerData(); | 
| -  access_unit_index_[next_demuxer_data_index] = 0; | 
| -  is_requesting_demuxer_data_ = true; | 
| - | 
| -  request_data_cb_.Run(); | 
| -} | 
| - | 
| -void MediaDecoderJob::DecodeCurrentAccessUnit( | 
| -    base::TimeTicks start_time_ticks, | 
| -    base::TimeDelta start_presentation_timestamp) { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(!decode_cb_.is_null()); | 
| - | 
| -  RequestCurrentChunkIfEmpty(); | 
| -  const AccessUnit& access_unit = CurrentAccessUnit(); | 
| -  if (CurrentAccessUnit().status == DemuxerStream::kConfigChanged) { | 
| -    int index = CurrentReceivedDataChunkIndex(); | 
| -    const DemuxerConfigs& configs = received_data_[index].demuxer_configs[0]; | 
| -    bool reconfigure_needed = IsCodecReconfigureNeeded(configs); | 
| -    SetDemuxerConfigs(configs); | 
| -    if (!drain_decoder_) { | 
| -      // If we haven't decoded any data yet, just skip the current access unit | 
| -      // and request the MediaCodec to be recreated on next Decode(). | 
| -      if (skip_eos_enqueue_ || !reconfigure_needed) { | 
| -        need_to_reconfig_decoder_job_ = | 
| -            need_to_reconfig_decoder_job_ || reconfigure_needed; | 
| -        // Report MEDIA_CODEC_OK status so decoder will continue decoding and | 
| -        // MEDIA_CODEC_OUTPUT_FORMAT_CHANGED status will come later. | 
| -        ui_task_runner_->PostTask( | 
| -            FROM_HERE, base::Bind(&MediaDecoderJob::OnDecodeCompleted, | 
| -                                  base::Unretained(this), MEDIA_CODEC_OK, false, | 
| -                                  kNoTimestamp, kNoTimestamp)); | 
| -        return; | 
| -      } | 
| -      // Start draining the decoder so that all the remaining frames are | 
| -      // rendered. | 
| -      drain_decoder_ = true; | 
| -    } | 
| -  } | 
| - | 
| -  DCHECK(!(needs_flush_ && drain_decoder_)); | 
| -  decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | 
| -      &MediaDecoderJob::DecodeInternal, base::Unretained(this), | 
| -      drain_decoder_ ? eos_unit_ : access_unit, | 
| -      start_time_ticks, start_presentation_timestamp, needs_flush_, | 
| -      media::BindToCurrentLoop(base::Bind( | 
| -          &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 
| -  needs_flush_ = false; | 
| -} | 
| - | 
| -void MediaDecoderJob::DecodeInternal( | 
| -    const AccessUnit& unit, | 
| -    base::TimeTicks start_time_ticks, | 
| -    base::TimeDelta start_presentation_timestamp, | 
| -    bool needs_flush, | 
| -    const MediaDecoderJob::DecoderCallback& callback) { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(decoder_task_runner_->BelongsToCurrentThread()); | 
| -  TRACE_EVENT0("media", __FUNCTION__); | 
| - | 
| -  if (needs_flush) { | 
| -    DVLOG(1) << "DecodeInternal needs flush."; | 
| -    input_eos_encountered_ = false; | 
| -    output_eos_encountered_ = false; | 
| -    input_buf_index_ = -1; | 
| -    MediaCodecStatus flush_status = media_codec_bridge_->Flush(); | 
| -    if (flush_status != MEDIA_CODEC_OK) { | 
| -      callback.Run(flush_status, false, kNoTimestamp, kNoTimestamp); | 
| -      return; | 
| -    } | 
| -  } | 
| - | 
| -  // Once output EOS has occurred, we should not be asked to decode again. | 
| -  // MediaCodec has undefined behavior if similarly asked to decode after output | 
| -  // EOS. | 
| -  DCHECK(!output_eos_encountered_); | 
| - | 
| -  // For aborted access unit, just skip it and inform the player. | 
| -  if (unit.status == DemuxerStream::kAborted) { | 
| -    callback.Run(MEDIA_CODEC_ABORT, false, kNoTimestamp, kNoTimestamp); | 
| -    return; | 
| -  } | 
| - | 
| -  if (skip_eos_enqueue_) { | 
| -    if (unit.is_end_of_stream || unit.data.empty()) { | 
| -      input_eos_encountered_ = true; | 
| -      output_eos_encountered_ = true; | 
| -      callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, false, kNoTimestamp, | 
| -                   kNoTimestamp); | 
| -      return; | 
| -    } | 
| - | 
| -    skip_eos_enqueue_ = false; | 
| -  } | 
| - | 
| -  MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 
| -  if (!input_eos_encountered_) { | 
| -    input_status = QueueInputBuffer(unit); | 
| -    if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { | 
| -      input_eos_encountered_ = true; | 
| -    } else if (input_status == MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER) { | 
| -      // In some cases, all buffers must be released to codec before format | 
| -      // change can be resolved. Context: b/21786703 | 
| -      DVLOG(1) << "dequeueInputBuffer gave AGAIN_LATER, dequeue output buffers"; | 
| -    } else if (input_status != MEDIA_CODEC_OK) { | 
| -      callback.Run(input_status, false, kNoTimestamp, kNoTimestamp); | 
| -      return; | 
| -    } | 
| -  } | 
| - | 
| -  int buffer_index = 0; | 
| -  size_t offset = 0; | 
| -  size_t size = 0; | 
| -  base::TimeDelta presentation_timestamp; | 
| - | 
| -  base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | 
| -      kMediaCodecTimeoutInMilliseconds); | 
| - | 
| -  MediaCodecStatus status = MEDIA_CODEC_OK; | 
| -  bool has_format_change = false; | 
| -  // Dequeue the output buffer until a MEDIA_CODEC_OK, MEDIA_CODEC_ERROR or | 
| -  // MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER is received. | 
| -  do { | 
| -    status = media_codec_bridge_->DequeueOutputBuffer( | 
| -        timeout, | 
| -        &buffer_index, | 
| -        &offset, | 
| -        &size, | 
| -        &presentation_timestamp, | 
| -        &output_eos_encountered_, | 
| -        NULL); | 
| -    if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) { | 
| -      // TODO(qinmin): instead of waiting for the next output buffer to be | 
| -      // dequeued, post a task on the UI thread to signal the format change. | 
| -      if (OnOutputFormatChanged()) | 
| -        has_format_change = true; | 
| -      else | 
| -        status = MEDIA_CODEC_ERROR; | 
| -    } | 
| -  } while (status != MEDIA_CODEC_OK && status != MEDIA_CODEC_ERROR && | 
| -           status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER); | 
| - | 
| -  if (status != MEDIA_CODEC_OK) { | 
| -    callback.Run(status, false, kNoTimestamp, kNoTimestamp); | 
| -    return; | 
| -  } | 
| - | 
| -  // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up. | 
| -  if (output_eos_encountered_) | 
| -    status = MEDIA_CODEC_OUTPUT_END_OF_STREAM; | 
| -  else if (has_format_change) | 
| -    status = MEDIA_CODEC_OUTPUT_FORMAT_CHANGED; | 
| - | 
| -  bool render_output  = presentation_timestamp >= preroll_timestamp_ && | 
| -      (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); | 
| -  base::TimeDelta time_to_render; | 
| -  DCHECK(!start_time_ticks.is_null()); | 
| -  if (render_output && ComputeTimeToRender()) { | 
| -    time_to_render = presentation_timestamp - (base::TimeTicks::Now() - | 
| -        start_time_ticks + start_presentation_timestamp); | 
| -  } | 
| - | 
| -  if (time_to_render > base::TimeDelta()) { | 
| -    decoder_task_runner_->PostDelayedTask( | 
| -        FROM_HERE, base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, | 
| -                              base::Unretained(this), buffer_index, offset, | 
| -                              size, render_output, | 
| -                              false,  // this is not a late frame | 
| -                              presentation_timestamp, status, callback), | 
| -        time_to_render); | 
| -    return; | 
| -  } | 
| - | 
| -  // TODO(qinmin): The codec is lagging behind, need to recalculate the | 
| -  // |start_presentation_timestamp_| and |start_time_ticks_| in | 
| -  // media_source_player.cc. | 
| -  DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); | 
| -  if (render_output) { | 
| -    // The player won't expect a timestamp smaller than the | 
| -    // |start_presentation_timestamp|. However, this could happen due to decoder | 
| -    // errors. | 
| -    presentation_timestamp = std::max( | 
| -        presentation_timestamp, start_presentation_timestamp); | 
| -  } else { | 
| -    presentation_timestamp = kNoTimestamp; | 
| -  } | 
| - | 
| -  const bool is_late_frame = (time_to_render < base::TimeDelta()); | 
| -  ReleaseOutputBuffer(buffer_index, offset, size, render_output, is_late_frame, | 
| -                      presentation_timestamp, status, callback); | 
| -} | 
| - | 
| -void MediaDecoderJob::OnDecodeCompleted( | 
| -    MediaCodecStatus status, | 
| -    bool is_late_frame, | 
| -    base::TimeDelta current_presentation_timestamp, | 
| -    base::TimeDelta max_presentation_timestamp) { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| - | 
| -  if (destroy_pending_) { | 
| -    DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; | 
| -    delete this; | 
| -    return; | 
| -  } | 
| - | 
| -  if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) | 
| -    output_eos_encountered_ = true; | 
| - | 
| -  DCHECK(!decode_cb_.is_null()); | 
| - | 
| -  // If output was queued for rendering, then we have completed prerolling. | 
| -  if (current_presentation_timestamp != kNoTimestamp || | 
| -      status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) { | 
| -    prerolling_ = false; | 
| -  } | 
| - | 
| -  switch (status) { | 
| -    case MEDIA_CODEC_OK: | 
| -    case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | 
| -    case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | 
| -    case MEDIA_CODEC_OUTPUT_END_OF_STREAM: | 
| -      if (!input_eos_encountered_) | 
| -        access_unit_index_[current_demuxer_data_index_]++; | 
| -      break; | 
| - | 
| -    case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 
| -    case MEDIA_CODEC_INPUT_END_OF_STREAM: | 
| -    case MEDIA_CODEC_NO_KEY: | 
| -    case MEDIA_CODEC_ABORT: | 
| -    case MEDIA_CODEC_ERROR: | 
| -      // Do nothing. | 
| -      break; | 
| - | 
| -    case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | 
| -      DCHECK(false) << "Invalid output status"; | 
| -      break; | 
| -  }; | 
| - | 
| -  if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM && drain_decoder_) { | 
| -    OnDecoderDrained(); | 
| -    status = MEDIA_CODEC_OK; | 
| -  } | 
| - | 
| -  if (status == MEDIA_CODEC_OUTPUT_FORMAT_CHANGED) { | 
| -    if (UpdateOutputFormat()) | 
| -      config_changed_cb_.Run(); | 
| -    status = MEDIA_CODEC_OK; | 
| -  } | 
| - | 
| -  if (release_resources_pending_) { | 
| -    ReleaseMediaCodecBridge(); | 
| -    release_resources_pending_ = false; | 
| -    if (drain_decoder_) | 
| -      OnDecoderDrained(); | 
| -  } | 
| - | 
| -  stop_decode_pending_ = false; | 
| -  base::ResetAndReturn(&decode_cb_) | 
| -      .Run(status, is_late_frame, current_presentation_timestamp, | 
| -           max_presentation_timestamp); | 
| -} | 
| - | 
| -const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(HasData()); | 
| -  size_t index = CurrentReceivedDataChunkIndex(); | 
| -  return received_data_[index].access_units[access_unit_index_[index]]; | 
| -} | 
| - | 
| -size_t MediaDecoderJob::CurrentReceivedDataChunkIndex() const { | 
| -  return NoAccessUnitsRemainingInChunk(true) ? | 
| -      inactive_demuxer_data_index() : current_demuxer_data_index_; | 
| -} | 
| - | 
| -bool MediaDecoderJob::NoAccessUnitsRemainingInChunk( | 
| -    bool is_active_chunk) const { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  size_t index = is_active_chunk ? current_demuxer_data_index_ : | 
| -      inactive_demuxer_data_index(); | 
| -  return received_data_[index].access_units.size() <= access_unit_index_[index]; | 
| -} | 
| - | 
| -void MediaDecoderJob::RequestCurrentChunkIfEmpty() { | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(HasData()); | 
| -  if (!NoAccessUnitsRemainingInChunk(true)) | 
| -    return; | 
| - | 
| -  // Requests new data if the the last access unit of the next chunk is not EOS. | 
| -  current_demuxer_data_index_ = inactive_demuxer_data_index(); | 
| -  const AccessUnit& last_access_unit = | 
| -      received_data_[current_demuxer_data_index_].access_units.back(); | 
| -  if (!last_access_unit.is_end_of_stream && | 
| -      last_access_unit.status != DemuxerStream::kAborted) { | 
| -    RequestData(base::Closure()); | 
| -  } | 
| -} | 
| - | 
| -void MediaDecoderJob::InitializeReceivedData() { | 
| -  for (size_t i = 0; i < 2; ++i) { | 
| -    received_data_[i] = DemuxerData(); | 
| -    access_unit_index_[i] = 0; | 
| -  } | 
| -} | 
| - | 
| -void MediaDecoderJob::OnDecoderDrained() { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(drain_decoder_); | 
| - | 
| -  input_eos_encountered_ = false; | 
| -  output_eos_encountered_ = false; | 
| -  drain_decoder_ = false; | 
| -  ReleaseMediaCodecBridge(); | 
| -  // Increase the access unit index so that the new decoder will not handle | 
| -  // the config change again. | 
| -  access_unit_index_[current_demuxer_data_index_]++; | 
| -} | 
| - | 
| -MediaDecoderJob::MediaDecoderJobStatus | 
| -    MediaDecoderJob::CreateMediaCodecBridge() { | 
| -  DVLOG(1) << __FUNCTION__; | 
| -  DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
| -  DCHECK(decode_cb_.is_null()); | 
| - | 
| -  if (!HasStream()) { | 
| -    ReleaseMediaCodecBridge(); | 
| -    return STATUS_FAILURE; | 
| -  } | 
| - | 
| -  // Create |media_codec_bridge_| only if config changes. | 
| -  if (media_codec_bridge_ && !need_to_reconfig_decoder_job_) | 
| -    return STATUS_SUCCESS; | 
| - | 
| -  if (is_content_encrypted_ && !GetMediaCrypto()) | 
| -    return STATUS_FAILURE; | 
| - | 
| -  ReleaseMediaCodecBridge(); | 
| -  DVLOG(1) << __FUNCTION__ << " : creating new media codec bridge"; | 
| - | 
| -  return CreateMediaCodecBridgeInternal(); | 
| -} | 
| - | 
| -bool MediaDecoderJob::IsCodecReconfigureNeeded( | 
| -    const DemuxerConfigs& configs) const { | 
| -  if (!AreDemuxerConfigsChanged(configs)) | 
| -    return false; | 
| -  return true; | 
| -} | 
| - | 
| -bool MediaDecoderJob::OnOutputFormatChanged() { | 
| -  return true; | 
| -} | 
| - | 
| -bool MediaDecoderJob::UpdateOutputFormat() { | 
| -  return false; | 
| -} | 
| - | 
| -void MediaDecoderJob::ReleaseMediaCodecBridge() { | 
| -  if (!media_codec_bridge_) | 
| -    return; | 
| - | 
| -  media_codec_bridge_.reset(); | 
| -  input_buf_index_ = -1; | 
| -} | 
| - | 
| -}  // namespace media | 
|  |