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

Side by Side Diff: content/common/gpu/media/android_video_decode_accelerator.cc

Issue 11973010: AndroidVDA by using Android's MediaCodec API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 11 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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 "content/common/gpu/media/android_video_decode_accelerator.h"
6
7 #include <jni.h>
8
9 #include "base/android/jni_android.h"
10 #include "base/android/scoped_java_ref.h"
11 #include "base/bind.h"
12 #include "base/debug/trace_event.h"
13 #include "base/logging.h"
14 #include "base/stl_util.h"
15 #include "base/string_util.h"
16 #include "content/common/android/surface_callback.h"
17 #include "content/common/gpu/gpu_channel.h"
18 #include "content/common/gpu/media/gles2_external_texture_copier.h"
19 #include "media/base/android/media_codec_bridge.h"
20 #include "media/base/bitstream_buffer.h"
21 #include "media/video/picture.h"
22 #include "third_party/angle/include/GLES2/gl2.h"
23 #include "third_party/angle/include/GLES2/gl2ext.h"
24
25 using base::android::MethodID;
26 using base::android::ScopedJavaLocalRef;
27
28 namespace content {
29
30 #define LOG_LINE() VLOG(1) << __FUNCTION__
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 drop before submitting?
dwkang1 2013/01/28 14:54:30 Marked with XXX.
31
32 enum { kNumPictureBuffers = 4 };
33
34 // TODO(dwkang) : For now, we are using very short timeouts for dequeueing
35 // buffers in order to prevent dequeueing for input from blocking it for output,
36 // and vice versa. Decoupling input and output procedure and using a longer
37 // timeout value may improve performance.
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 See my comment on the bridge class; I hope you can
dwkang1 2013/01/28 14:54:30 Done.
38 enum { kDequeueInputBufferTimeOutUs = 10 };
39 enum { kDequeueOutputBufferTimeOutUs = 10 };
40
41 // static
42 const base::TimeDelta AndroidVideoDecodeAccelerator::kDecodePollDelay =
43 base::TimeDelta::FromMilliseconds(10);
44
45 AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator(
46 media::VideoDecodeAccelerator::Client* client,
47 const base::Callback<bool(void)>& make_context_current)
48 : message_loop_(MessageLoop::current()),
49 client_(client),
50 make_context_current_(make_context_current),
51 codec_(UNKNOWN),
52 state_(NO_ERROR),
53 surface_texture_id_(0),
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 -1 to be more obvious?
dwkang1 2013/01/28 14:54:30 Done.
54 picturebuffer_requested_(false),
55 color_format_(0),
56 width_(0),
57 height_(0),
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 should be able to drop once using gfx::Size (which
dwkang1 2013/01/28 14:54:30 Removed.
58 current_bitstream_id_(-1) {
59 LOG_LINE();
60 }
61
62 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
63 LOG_LINE();
64 DCHECK_EQ(message_loop_, MessageLoop::current());
65 }
66
67 bool AndroidVideoDecodeAccelerator::Initialize(
68 media::VideoCodecProfile profile) {
69 LOG_LINE();
70 DCHECK_EQ(message_loop_, MessageLoop::current());
71
72 if (profile == media::VP8PROFILE_MAIN) {
73 codec_ = VP8;
74 } else if (profile >= media::H264PROFILE_MIN
75 && profile <= media::H264PROFILE_MAX) {
76 codec_ = H264;
77 }else {
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 missing space
dwkang1 2013/01/28 14:54:30 Done.
78 LOG(ERROR) << "Unsupported profile: " << profile;
79 return false;
80 }
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Once you pass profile to ConfigureMediaCodec below
dwkang1 2013/01/28 14:54:30 codec_ is used when re-configuring MediaCodec.
81
82 if (media_codec_ == NULL) {
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 How could this *not* be NULL?
dwkang1 2013/01/28 14:54:30 Changed to DCHECK.
83 if (!make_context_current_.Run()) {
84 LOG(ERROR) << "Failed to make this decoder's GL context current.";
85 return false;
86 }
87 glGenTextures(1, &surface_texture_id_);
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Please make sure this file follows the (brand-new)
dwkang1 2013/01/28 14:54:30 Let me address this issue once we finalize how we
88 glActiveTexture(GL_TEXTURE0);
89 glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_id_);
90
91 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
92 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
93 glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
94 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
95 glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
96 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
97
98 surface_texture_ = new SurfaceTextureBridge(surface_texture_id_);
99
100 ConfigureMediaCodec();
101 }
102
103 message_loop_->PostTask(
104 FROM_HERE,
105 base::Bind(
106 &AndroidVideoDecodeAccelerator::DoDecode, base::Unretained(this)));
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Per below, drop.
dwkang1 2013/01/28 14:54:30 Done.
107
108 if (client_)
109 client_->NotifyInitializeDone();
110 return true;
111 }
112
113 void AndroidVideoDecodeAccelerator::DoDecode() {
114 if (state_ == NO_ERROR) {
115 QueueInput();
116 DequeueOutput();
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Shouldn't you drain the output first, to free up d
dwkang1 2013/01/28 14:54:30 Makes sense. Changed.
117 }
118
119 message_loop_->PostDelayedTask(
120 FROM_HERE,
121 base::Bind(
122 &AndroidVideoDecodeAccelerator::DoDecode, base::Unretained(this)),
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Why is Unretained(this) safe here (and also at l.1
dwkang1 2013/01/28 14:54:30 You are right. Changed to base::AsWeakPtr().
123 kDecodePollDelay);
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Oh my goodness... I didn't realize MediaCodec re
dwkang1 2013/01/28 14:54:30 with 10ms, cost was about 3% of cpu rate.
124 }
125
126 void AndroidVideoDecodeAccelerator::QueueInput() {
127 if (!pending_bitstream_buffers_.empty()) {
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 if you reverse the test you can early-return and d
dwkang1 2013/01/28 14:54:30 Done.
128 int input_buf_index =
129 media_codec_->DequeueInputBuffer(kDequeueInputBufferTimeOutUs);
130 if (input_buf_index < 0) {
131 return;
132 }
133 media::BitstreamBuffer& bitstream_buffer =
134 pending_bitstream_buffers_.front();
135 pending_bitstream_buffers_.pop();
136
137 int flags = 0;
138 if (bitstream_buffer.id() == -1) {
139 flags |= media::MediaCodecBridge::BUFFER_FLAG_END_OF_STREAM;
140 }
141 if (bitstream_buffer.size() > 0) {
142 scoped_ptr<base::SharedMemory> shm(
143 new base::SharedMemory(bitstream_buffer.handle(), true));
144 if (!shm->Map(bitstream_buffer.size())) {
145 LOG(ERROR) << "Failed to SharedMemory::Map()";
146 client_->NotifyError(UNREADABLE_INPUT);
147 state_ = ERROR;
148 return;
149 }
150 media_codec_->PutToInputBuffer(
151 input_buf_index,
152 static_cast<const uint8*>(shm->memory()),
153 bitstream_buffer.size());
154 }
155 // Abuse the presentation time argument to propagate the bitstream
156 // buffer ID to the output, so we can report it back to the client in
157 // PictureReady().
158 int64 timestamp = bitstream_buffer.id();
159 media_codec_->QueueInputBuffer(
160 input_buf_index, 0, bitstream_buffer.size(), timestamp, flags);
161
162 if (bitstream_buffer.id() != -1) {
163 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 This is wrong; you should only NotifyEndOfBitstrea
dwkang1 2013/01/28 14:54:30 When I wrote this code, I referred OVDA code. In O
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 See my comment on the next patchset.
164 }
165 }
166 }
167
168 void AndroidVideoDecodeAccelerator::DequeueOutput() {
169 if (picturebuffer_requested_ && picture_map_.empty()) {
170 DLOG(INFO) << "Picture buffers are not ready.";
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 drop
dwkang1 2013/01/28 14:54:30 Done.
171 return;
172 }
173 if (!picture_map_.empty() && free_picture_ids_.empty()) {
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Drop the picture_map_.empty() clause? SendCurrentS
dwkang1 2013/01/28 14:54:30 Before picturebuffers is requested, free_picture_i
174 // Don't have any picture buffer to send. Need to wait more.
175 return;
176 }
177
178 int32 output_offset = 0;
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 this and the other params below don't seem to need
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Declare vars at first use unless there's a reason
dwkang1 2013/01/28 14:54:30 Done.
dwkang1 2013/01/28 14:54:30 Done.
179 int32 output_size = 0;
180 int32 output_flag = 0;
181 int64 timestamp = 0;
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 bitstream_buffer_id
dwkang1 2013/01/28 14:54:30 Done.
182 int32 output_buf_index = 0;
183 do {
184 output_buf_index = media_codec_->DequeueOutputBuffer(
185 kDequeueOutputBufferTimeOutUs, &output_offset, &output_size,
186 &timestamp, &output_flag);
187 switch (output_buf_index) {
188 case media::MediaCodecBridge::INFO_TRY_AGAIN_LATER:
189 return;
190
191 case media::MediaCodecBridge::INFO_OUTPUT_FORMAT_CHANGED:
192 media_codec_->GetOutputFormat(&color_format_, &width_, &height_);
193 DLOG(INFO) << "Output color format: " << color_format_;
194 DLOG(INFO) << "Output size: " << width_ << "x" << height_;
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Drop these two lines
dwkang1 2013/01/28 14:54:30 Done.
195 if (!picturebuffer_requested_) {
196 picturebuffer_requested_ = true;
197 texture_copier_.reset(new Gles2ExternalTextureCopier());
198 texture_copier_->Init(width_, height_);
199 client_->ProvidePictureBuffers(
200 kNumPictureBuffers,
201 gfx::Size(width_, height_),
202 GL_TEXTURE_2D);
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 If you requested a different texture target could
dwkang1 2013/01/28 14:54:30 Actually, using SurfaceTexure.attachToGLContext()
203 }
204 // TODO(dwkang): support the dynamic resolution change.
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 set state to ERROR and NotifyError?
dwkang1 2013/01/28 14:54:30 Done.
205 return;
206
207 case media::MediaCodecBridge::INFO_OUTPUT_BUFFERS_CHANGED:
208 media_codec_->GetOutputBuffers();
209 break;
210 }
211 } while (output_buf_index < 0);
212
213 if (output_flag & media::MediaCodecBridge::BUFFER_FLAG_END_OF_STREAM) {
214 if (client_) {
215 client_->NotifyFlushDone();
216 }
217 }
218
219 media_codec_->ReleaseOutputBuffer(output_buf_index, true);
220 current_bitstream_id_ = static_cast<int32>(timestamp);
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 This doesn't need to be a class member; you can ju
dwkang1 2013/01/28 14:54:30 Done.
221 if (current_bitstream_id_ != -1) {
222 SendCurrentSurfaceToClient();
223 }
224 }
225
226 void AndroidVideoDecodeAccelerator::SendCurrentSurfaceToClient() {
227 LOG_LINE();
228
229 DCHECK_EQ(message_loop_, MessageLoop::current());
230 DCHECK_NE(current_bitstream_id_, -1);
231 DCHECK(!free_picture_ids_.empty());
232
233 int32 picture_buffer_id = free_picture_ids_.front();
234 free_picture_ids_.pop();
235
236 if (!make_context_current_.Run()) {
237 LOG(ERROR) << "Failed to make this decoder's GL context current.";
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Shouldn't this set the state to ERROR and also Not
dwkang1 2013/01/28 14:54:30 Done.
238 return;
239 }
240
241 float mtx[16];
242 surface_texture_->UpdateTexImage();
243 surface_texture_->GetTransformMatrix(mtx);
244 CopyCurrentFrameToPictureBuffer(picture_buffer_id, mtx);
245
246 client_->PictureReady(
247 media::Picture(picture_buffer_id, current_bitstream_id_));
248 current_bitstream_id_ = -1;
249 }
250
251 void AndroidVideoDecodeAccelerator::CopyCurrentFrameToPictureBuffer(
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 single call-site + simple impl == inline into call
dwkang1 2013/01/28 14:54:30 I think it depends on how we will define "simple",
252 int32 picture_buffer_id, float transfrom_matrix[16]) {
253 PictureMap::const_iterator i = picture_map_.find(picture_buffer_id);
254 if (i == picture_map_.end()) {
255 LOG(ERROR) << "Can't find a PuctureBuffer for " << picture_buffer_id;
256 return;
257 }
258 uint32 picture_buffer_texture_id = i->second.texture_id();
259 texture_copier_->Copy(surface_texture_id_, GL_TEXTURE_EXTERNAL_OES,
260 transfrom_matrix,
261 picture_buffer_texture_id, GL_TEXTURE_2D);
262 }
263
264 void AndroidVideoDecodeAccelerator::Decode(
265 const media::BitstreamBuffer& bitstream_buffer) {
266 LOG_LINE();
267 DCHECK_EQ(message_loop_, MessageLoop::current());
268 if (!client_) {
269 return;
270 }
271 pending_bitstream_buffers_.push(bitstream_buffer);
272 }
273
274 void AndroidVideoDecodeAccelerator::AssignPictureBuffers(
275 const std::vector<media::PictureBuffer>& buffers) {
276 LOG_LINE();
277 DCHECK_EQ(message_loop_, MessageLoop::current());
278 DCHECK(picture_map_.empty());
279
280 for (size_t i = 0; i < buffers.size(); ++i) {
281 picture_map_.insert(std::make_pair(buffers[i].id(), buffers[i]));
282 free_picture_ids_.push(buffers[i].id());
283 }
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Add: if (picture_map_.size() != kNumPictureBuffers
dwkang1 2013/01/28 14:54:30 Done.
284 }
285
286 void AndroidVideoDecodeAccelerator::ReusePictureBuffer(
287 int32 picture_buffer_id) {
288 LOG_LINE();
289 DCHECK_EQ(message_loop_, MessageLoop::current());
290 free_picture_ids_.push(picture_buffer_id);
291 }
292
293 void AndroidVideoDecodeAccelerator::Flush() {
294 LOG_LINE();
295 DCHECK_EQ(message_loop_, MessageLoop::current());
296
297 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
298 }
299
300 void AndroidVideoDecodeAccelerator::ConfigureMediaCodec() {
301 DCHECK(surface_texture_.get());
302 DCHECK(codec_ == H264 || codec_ == VP8);
303
304 std::string mime;
305 if (codec_ == VP8) {
306 mime = "video/x-vnd.on2.vp8";
307 } else if (codec_ == H264) {
308 mime = "video/avc";
309 } else {
310 LOG(ERROR) << "Unsupported codec type " << codec_;
311 NOTREACHED();
312 }
313 media_codec_.reset(new media::MediaCodecBridge(mime));
314
315 JNIEnv* env = base::android::AttachCurrentThread();
316 CHECK(env);
317 ScopedJavaLocalRef<jclass> cls(
318 base::android::GetClass(env, "android/view/Surface"));
319 jmethodID constructor = MethodID::Get<MethodID::TYPE_INSTANCE>(
320 env, cls.obj(), "<init>", "(Landroid/graphics/SurfaceTexture;)V");
321 ScopedJavaLocalRef<jobject> j_surface(
322 env, env->NewObject(
323 cls.obj(), constructor,
324 surface_texture_->j_surface_texture().obj()));
325
326 // VDA does not pass the container indicated resolution in the initialization
327 // phase. Here, we set 1080p by default.
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 This is a mistake that is likely to hide bugs, IMO
dwkang1 2013/01/28 14:54:30 Actually, that was my first try, but it lead to Ex
328 media_codec_->ConfigureVideo(
329 mime, 1920, 1080, NULL, 0, NULL, 0, j_surface.obj());
330 content::ReleaseSurface(j_surface.obj());
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 How does this work? Didn't you just ask MediaCode
dwkang1 2013/01/28 14:54:30 If Surface is not registered to the windows manage
331
332 media_codec_->Start();
333 media_codec_->GetInputBuffers();
334 media_codec_->GetOutputBuffers();
335 }
336
337 void AndroidVideoDecodeAccelerator::Reset() {
338 LOG_LINE();
339 DCHECK_EQ(message_loop_, MessageLoop::current());
340
341 while(!pending_bitstream_buffers_.empty()) {
342 media::BitstreamBuffer& bitstream_buffer =
343 pending_bitstream_buffers_.front();
344 pending_bitstream_buffers_.pop();
345
346 if (bitstream_buffer.id() != -1) {
347 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 I don't think the VDA interface requires this (and
dwkang1 2013/01/28 14:54:30 Actually, I referred OVDA code when I wrote this c
348 }
349 }
350 media_codec_->Flush();
351 media_codec_->Stop();
352 media_codec_->Release();
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 Why the Stop/Release/ConfigureMediaCodec dance?
dwkang1 2013/01/28 14:54:30 It's a recommended way to reset MediaCodec. FWIW,
353 ConfigureMediaCodec();
354 state_ = NO_ERROR;
355
356 if (client_) {
357 client_->NotifyResetDone();
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 For complicated reasons, it's not OK to make clien
dwkang1 2013/01/28 14:54:30 Done.
358 }
359 }
360
361 void AndroidVideoDecodeAccelerator::Destroy() {
362 LOG_LINE();
363 DCHECK_EQ(message_loop_, MessageLoop::current());
364 }
Ami GONE FROM CHROMIUM 2013/01/23 01:32:32 This method needs to delete this; or else you hav
dwkang1 2013/01/28 14:54:30 Done.
365
366 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698