text: // 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/video_decoder_job.h"
  
  #include "base/bind.h"
  #include "base/lazy_instance.h"
  #include "base/threading/thread.h"
  #include "media/base/android/media_codec_bridge.h"
  #include "media/base/android/media_drm_bridge.h"
  
  namespace media {
  
  class VideoDecoderThread : public base::Thread {
   public:
    VideoDecoderThread() : base::Thread("MediaSource_VideoDecoderThread") {
      Start();
    }
  };
  
  // TODO(qinmin): Check if it is tolerable to use worker pool to handle all the
  // decoding tasks so that we don't need a global thread here.
  // http://crbug.com/245750
  base::LazyInstance<VideoDecoderThread>::Leaky
      g_video_decoder_thread = LAZY_INSTANCE_INITIALIZER;
  
  VideoDecoderJob::VideoDecoderJob(
      const base::Closure& request_data_cb,
      const base::Closure& request_resources_cb,
      const base::Closure& release_resources_cb,
      const base::Closure& on_demuxer_config_changed_cb)
      : MediaDecoderJob(g_video_decoder_thread.Pointer()->message_loop_proxy(),
                        request_data_cb,
                        on_demuxer_config_changed_cb),
        video_codec_(kUnknownVideoCodec),
        width_(0),
        height_(0),
        request_resources_cb_(request_resources_cb),
        release_resources_cb_(release_resources_cb),
        next_video_data_is_iframe_(true) {
  }
  
  VideoDecoderJob::~VideoDecoderJob() {}
  
  bool VideoDecoderJob::SetVideoSurface(gfx::ScopedJavaSurface surface) {
    // For an empty surface, always pass it to the |media_codec_bridge_| job so
    // that it can detach from the current one. Otherwise, don't pass an
    // unprotected surface if the video content requires a protected one.
    if (!surface.IsEmpty() && IsProtectedSurfaceRequired() &&
        !surface.is_protected()) {
      return false;
    }
  
    surface_ =  surface.Pass();
    need_to_reconfig_decoder_job_ = true;
    return true;
  }
  
  bool VideoDecoderJob::HasStream() const {
    return video_codec_ != kUnknownVideoCodec;
  }
  
  void VideoDecoderJob::Flush() {
    MediaDecoderJob::Flush();
    next_video_data_is_iframe_ = true;
  }
  
  void VideoDecoderJob::ReleaseDecoderResources() {
    MediaDecoderJob::ReleaseDecoderResources();
    surface_ = gfx::ScopedJavaSurface();
  }
  
  void VideoDecoderJob::ReleaseOutputBuffer(
      int output_buffer_index,
      size_t size,
      bool render_output,
      base::TimeDelta current_presentation_timestamp,
      const ReleaseOutputCompletionCallback& callback) {
    media_codec_bridge_->ReleaseOutputBuffer(output_buffer_index, render_output);
    callback.Run(current_presentation_timestamp, current_presentation_timestamp);
  }
  
  bool VideoDecoderJob::ComputeTimeToRender() const {
    return true;
  }
  
  void VideoDecoderJob::UpdateDemuxerConfigs(const DemuxerConfigs& configs) {
    video_codec_ = configs.video_codec;
    width_ = configs.video_size.width();
    height_ = configs.video_size.height();
    set_is_content_encrypted(configs.is_video_encrypted);
  }
  
  bool VideoDecoderJob::IsCodecReconfigureNeeded(
      const DemuxerConfigs& configs) const {
    if (!media_codec_bridge_)
      return true;
  
    if (!AreDemuxerConfigsChanged(configs))
      return false;
  
    bool only_size_changed = false;
    if (video_codec_ == configs.video_codec &&
        is_content_encrypted() == configs.is_video_encrypted) {
      only_size_changed = true;
    }
  
    return !only_size_changed ||
        !static_cast<VideoCodecBridge*>(media_codec_bridge_.get())->
            IsAdaptivePlaybackSupported(configs.video_size.width(),
                                        configs.video_size.height());
  }
  
  bool VideoDecoderJob::AreDemuxerConfigsChanged(
      const DemuxerConfigs& configs) const {
    return video_codec_ != configs.video_codec ||
        is_content_encrypted() != configs.is_video_encrypted ||
        width_ != configs.video_size.width() ||
        height_ != configs.video_size.height();
  }
  
  bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
    if (surface_.IsEmpty()) {
      ReleaseMediaCodecBridge();
      return false;
    }
  
    // If the next data is not iframe, return false so that the player need to
    // perform a browser seek.
    if (!next_video_data_is_iframe_)
      return false;
  
    bool is_secure = is_content_encrypted() && drm_bridge() &&
        drm_bridge()->IsProtectedSurfaceRequired();
  
    media_codec_bridge_.reset(VideoCodecBridge::CreateDecoder(
        video_codec_, is_secure, gfx::Size(width_, height_),
        surface_.j_surface().obj(), GetMediaCrypto().obj()));
  
    if (!media_codec_bridge_)
      return false;
  
    request_resources_cb_.Run();
    return true;
  }
  
  void VideoDecoderJob::CurrentDataConsumed(bool is_config_change) {
    next_video_data_is_iframe_ = is_config_change;
  }
  
  void VideoDecoderJob::OnMediaCodecBridgeReleased() {
    release_resources_cb_.Run();
  }
  
  bool VideoDecoderJob::IsProtectedSurfaceRequired() {
    return is_content_encrypted() && drm_bridge() &&
        drm_bridge()->IsProtectedSurfaceRequired();
  }
  
  }  // namespace media
  