1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/base/android/video_decoder_job.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/threading/thread.h"
10#include "media/base/android/media_codec_bridge.h"
11#include "media/base/android/media_drm_bridge.h"
12
13namespace media {
14
15class VideoDecoderThread : public base::Thread {
16 public:
17  VideoDecoderThread() : base::Thread("MediaSource_VideoDecoderThread") {
18    Start();
19  }
20};
21
22// TODO(qinmin): Check if it is tolerable to use worker pool to handle all the
23// decoding tasks so that we don't need a global thread here.
24// http://crbug.com/245750
25base::LazyInstance<VideoDecoderThread>::Leaky
26    g_video_decoder_thread = LAZY_INSTANCE_INITIALIZER;
27
28VideoDecoderJob::VideoDecoderJob(
29    const base::Closure& request_data_cb,
30    const base::Closure& request_resources_cb,
31    const base::Closure& on_demuxer_config_changed_cb)
32    : MediaDecoderJob(g_video_decoder_thread.Pointer()->message_loop_proxy(),
33                      request_data_cb,
34                      on_demuxer_config_changed_cb),
35      video_codec_(kUnknownVideoCodec),
36      config_width_(0),
37      config_height_(0),
38      output_width_(0),
39      output_height_(0),
40      request_resources_cb_(request_resources_cb),
41      next_video_data_is_iframe_(true) {
42}
43
44VideoDecoderJob::~VideoDecoderJob() {}
45
46bool VideoDecoderJob::SetVideoSurface(gfx::ScopedJavaSurface surface) {
47  // For an empty surface, always pass it to the |media_codec_bridge_| job so
48  // that it can detach from the current one. Otherwise, don't pass an
49  // unprotected surface if the video content requires a protected one.
50  if (!surface.IsEmpty() && IsProtectedSurfaceRequired() &&
51      !surface.is_protected()) {
52    return false;
53  }
54
55  surface_ =  surface.Pass();
56  need_to_reconfig_decoder_job_ = true;
57  return true;
58}
59
60bool VideoDecoderJob::HasStream() const {
61  return video_codec_ != kUnknownVideoCodec;
62}
63
64void VideoDecoderJob::Flush() {
65  MediaDecoderJob::Flush();
66  next_video_data_is_iframe_ = true;
67}
68
69void VideoDecoderJob::ReleaseDecoderResources() {
70  MediaDecoderJob::ReleaseDecoderResources();
71  surface_ = gfx::ScopedJavaSurface();
72}
73
74void VideoDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
75  video_codec_ = configs.video_codec;
76  config_width_ = configs.video_size.width();
77  config_height_ = configs.video_size.height();
78  set_is_content_encrypted(configs.is_video_encrypted);
79  if (!media_codec_bridge_) {
80    output_width_ = config_width_;
81    output_height_ = config_height_;
82  }
83}
84
85void VideoDecoderJob::ReleaseOutputBuffer(
86    int output_buffer_index,
87    size_t size,
88    bool render_output,
89    base::TimeDelta current_presentation_timestamp,
90    const ReleaseOutputCompletionCallback& callback) {
91  media_codec_bridge_->ReleaseOutputBuffer(output_buffer_index, render_output);
92  callback.Run(current_presentation_timestamp, current_presentation_timestamp);
93}
94
95bool VideoDecoderJob::ComputeTimeToRender() const {
96  return true;
97}
98
99bool VideoDecoderJob::IsCodecReconfigureNeeded(
100    const DemuxerConfigs& configs) const {
101  if (!media_codec_bridge_)
102    return true;
103
104  if (!AreDemuxerConfigsChanged(configs))
105    return false;
106
107  bool only_size_changed = false;
108  if (video_codec_ == configs.video_codec &&
109      is_content_encrypted() == configs.is_video_encrypted) {
110    only_size_changed = true;
111  }
112
113  return !only_size_changed ||
114      !static_cast<VideoCodecBridge*>(media_codec_bridge_.get())->
115          IsAdaptivePlaybackSupported(configs.video_size.width(),
116                                      configs.video_size.height());
117}
118
119bool VideoDecoderJob::AreDemuxerConfigsChanged(
120    const DemuxerConfigs& configs) const {
121  return video_codec_ != configs.video_codec ||
122      is_content_encrypted() != configs.is_video_encrypted ||
123      config_width_ != configs.video_size.width() ||
124      config_height_ != configs.video_size.height();
125}
126
127bool VideoDecoderJob::CreateMediaCodecBridgeInternal() {
128  if (surface_.IsEmpty()) {
129    ReleaseMediaCodecBridge();
130    return false;
131  }
132
133  // If the next data is not iframe, return false so that the player need to
134  // perform a browser seek.
135  if (!next_video_data_is_iframe_)
136    return false;
137
138  bool is_secure = is_content_encrypted() && drm_bridge() &&
139      drm_bridge()->IsProtectedSurfaceRequired();
140
141  media_codec_bridge_.reset(VideoCodecBridge::CreateDecoder(
142      video_codec_, is_secure, gfx::Size(config_width_, config_height_),
143      surface_.j_surface().obj(), GetMediaCrypto().obj()));
144
145  if (!media_codec_bridge_)
146    return false;
147
148  request_resources_cb_.Run();
149  return true;
150}
151
152void VideoDecoderJob::CurrentDataConsumed(bool is_config_change) {
153  next_video_data_is_iframe_ = is_config_change;
154}
155
156bool VideoDecoderJob::UpdateOutputFormat() {
157  if (!media_codec_bridge_)
158    return false;
159  int prev_output_width = output_width_;
160  int prev_output_height = output_height_;
161  media_codec_bridge_->GetOutputFormat(&output_width_, &output_height_);
162  return (output_width_ != prev_output_width) ||
163      (output_height_ != prev_output_height);
164}
165
166bool VideoDecoderJob::IsProtectedSurfaceRequired() {
167  return is_content_encrypted() && drm_bridge() &&
168      drm_bridge()->IsProtectedSurfaceRequired();
169}
170
171}  // namespace media
172