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

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: marking XXX for issues should be resolved before committing. Created 7 years, 10 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/logging.h"
13 #include "base/message_loop.h"
14 #include "content/common/android/surface_callback.h"
15 #include "content/common/gpu/gpu_channel.h"
16 #include "content/common/gpu/media/gles2_external_texture_copier.h"
17 #include "media/base/bitstream_buffer.h"
18 #include "media/video/picture.h"
19 #include "third_party/angle/include/GLES2/gl2.h"
20 #include "third_party/angle/include/GLES2/gl2ext.h"
21
22 using base::android::MethodID;
23 using base::android::ScopedJavaLocalRef;
24
25 namespace content {
26
27 // XXX: drop the below before submitting.
28 #define LOG_LINE() LOG(INFO) << __FUNCTION__
29
30 #undef DCHECK
31 #define DCHECK CHECK
32
33 // Helper macros for dealing with failure. If |result| evaluates false, emit
34 // |log| to ERROR, register |error| with the decoder, and return.
35 #define RETURN_ON_FAILURE(result, log, error) \
36 do { \
37 if (!(result)) { \
38 LOG(ERROR) << log; \
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 DLOG instead of LOG
dwkang1 2013/02/04 14:08:26 Done.
39 client_->NotifyError(error); \
40 state_ = ERROR; \
41 return; \
42 } \
43 } while (0)
44
45 enum { kNumPictureBuffers = 4 };
46
47 // static
48 const base::TimeDelta AndroidVideoDecodeAccelerator::kDecodePollDelay =
49 base::TimeDelta::FromMilliseconds(10);
50
51 AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator(
52 media::VideoDecodeAccelerator::Client* client,
53 const base::Callback<bool(void)>& make_context_current)
54 : client_(client),
55 make_context_current_(make_context_current),
56 codec_(media::MediaCodecBridge::UNKNOWN),
57 state_(NO_ERROR),
58 surface_texture_id_(-1),
59 picturebuffers_requested_(false),
60 io_task_is_running_(false) {
61 LOG_LINE();
62 }
63
64 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
65 LOG_LINE();
66 DCHECK(thread_checker_.CalledOnValidThread());
67 }
68
69 bool AndroidVideoDecodeAccelerator::Initialize(
70 media::VideoCodecProfile profile) {
71 LOG_LINE();
72 DCHECK(media_codec_ == NULL);
73 DCHECK(thread_checker_.CalledOnValidThread());
74
75 if (profile == media::VP8PROFILE_MAIN) {
76 codec_ = media::MediaCodecBridge::VIDEO_VP8;
77 } else if (profile >= media::H264PROFILE_MIN
78 && profile <= media::H264PROFILE_MAX) {
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 && operator goes on previous line (here and elsewh
dwkang1 2013/02/04 14:08:26 Done.
79 codec_ = media::MediaCodecBridge::VIDEO_H264;
80 } else {
81 codec_ = media::MediaCodecBridge::UNKNOWN;
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 unnecessary
dwkang1 2013/02/04 14:08:26 Done.
82 LOG(ERROR) << "Unsupported profile: " << profile;
83 return false;
84 }
85
86 if (!make_context_current_.Run()) {
87 LOG(ERROR) << "Failed to make this decoder's GL context current.";
88 return false;
89 }
90 // XXX: apply the scheme for GL access. http://crbug.com/169433
91 glGenTextures(1, &surface_texture_id_);
92 glActiveTexture(GL_TEXTURE0);
93 glBindTexture(GL_TEXTURE_EXTERNAL_OES, surface_texture_id_);
94
95 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
96 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
97 glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
98 GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
99 glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
100 GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
101
102 surface_texture_ = new SurfaceTextureBridge(surface_texture_id_);
103
104 ConfigureMediaCodec();
105
106 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
107 &AndroidVideoDecodeAccelerator::NotifyInitializeDone,
108 base::AsWeakPtr(this)));
109 return true;
110 }
111
112 void AndroidVideoDecodeAccelerator::DoIOTask() {
113 if (state_ == NO_ERROR) {
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 If this is false, then you don't want to enqueue m
dwkang1 2013/02/04 14:08:26 Done.
114 DequeueOutput();
115 QueueInput();
116 }
117
118 if (!pending_bitstream_buffers_.empty()
119 || !bitstream_buffer_ids_in_decoder_.empty()) {
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 operator goes on previous line
dwkang1 2013/02/04 14:08:26 Done.
120 MessageLoop::current()->PostDelayedTask(
121 FROM_HERE,
122 base::Bind(
123 &AndroidVideoDecodeAccelerator::DoIOTask, base::AsWeakPtr(this)),
124 kDecodePollDelay);
125 } else {
126 io_task_is_running_ = false;
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 IMO this would be more clear as io_task_is_posted_
dwkang1 2013/02/04 14:08:26 Good idea. Thanks,
127 }
128 }
129
130 void AndroidVideoDecodeAccelerator::QueueInput() {
131 if (pending_bitstream_buffers_.empty()) {
132 return;
133 }
134 int input_buf_index =
135 media_codec_->DequeueInputBuffer(0);
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 wrapping necessary?
dwkang1 2013/02/04 14:08:26 Done.
136 if (input_buf_index < 0) {
137 return;
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 DCHECK_EQ TRY_AGAIN_LATER?
dwkang1 2013/02/04 14:08:26 Done.
138 }
139 media::BitstreamBuffer& bitstream_buffer =
140 pending_bitstream_buffers_.front();
141 pending_bitstream_buffers_.pop();
142
143 int flags = 0;
144 if (bitstream_buffer.id() == -1) {
145 flags |= media::MediaCodecBridge::BUFFER_FLAG_END_OF_STREAM;
146 }
147 if (bitstream_buffer.size() > 0) {
148 scoped_ptr<base::SharedMemory> shm(
149 new base::SharedMemory(bitstream_buffer.handle(), true));
150
151 RETURN_ON_FAILURE(shm->Map(bitstream_buffer.size()),
152 "Failed to SharedMemory::Map()",
153 UNREADABLE_INPUT);
154
155 media_codec_->PutToInputBuffer(
156 input_buf_index,
157 static_cast<const uint8*>(shm->memory()),
158 bitstream_buffer.size());
159 }
160 // Abuse the presentation time argument to propagate the bitstream
161 // buffer ID to the output, so we can report it back to the client in
162 // PictureReady().
163 int64 timestamp = bitstream_buffer.id();
164 media_codec_->QueueInputBuffer(
165 input_buf_index, 0, bitstream_buffer.size(), timestamp, flags);
166
167 bitstream_buffer_ids_in_decoder_.push(bitstream_buffer.id());
168 if (bitstream_buffer.id() != -1) {
169 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 This is wrong - NEOBB() must only be called once i
dwkang1 2013/01/29 03:58:48 Got it. Seems that I misunderstood the meaning of
dwkang1 2013/02/04 14:08:26 I checked GpuVideoDecoder implementation and have
170 }
171 }
172
173 void AndroidVideoDecodeAccelerator::DequeueOutput() {
174 if (picturebuffers_requested_ && output_picture_buffers_.empty()) {
175 return;
176 }
177 if (!output_picture_buffers_.empty() && free_picture_ids_.empty()) {
178 // Don't have any picture buffer to send. Need to wait more.
179 return;
180 }
181
182 int32 flag = 0;
183 int64 bitstream_buffer_id = 0;
184 int32 buf_index = 0;
185 do {
186 int32 offset = 0;
187 int32 size = 0;
188 buf_index = media_codec_->DequeueOutputBuffer(
189 0, &offset, &size, &bitstream_buffer_id, &flag);
190 switch (buf_index) {
191 case media::MediaCodecBridge::INFO_TRY_AGAIN_LATER: {
192 return;
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 braces are unnecessary (here and elsewhere in case
dwkang1 2013/02/04 14:08:26 Done.
193 }
194
195 case media::MediaCodecBridge::INFO_OUTPUT_FORMAT_CHANGED: {
196 int32 unused_color_format, width, height;
197 media_codec_->GetOutputFormat(&unused_color_format, &width, &height);
198
199 if (!picturebuffers_requested_) {
200 picturebuffers_requested_ = true;
201 size_ = gfx::Size(width, height);
202 texture_copier_.reset(new Gles2ExternalTextureCopier());
203 texture_copier_->Init(width, height);
204 client_->ProvidePictureBuffers(
205 kNumPictureBuffers,
206 size_,
207 GL_TEXTURE_2D);
208 } else {
209 // TODO(dwkang): support the dynamic resolution change.
210 RETURN_ON_FAILURE(size_.width() == width && size_.height() == height,
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 size_ == gfx::Size(width, height) might be more re
dwkang1 2013/02/04 14:08:26 Done.
211 "Dynamic resolution change is not supported.",
212 PLATFORM_FAILURE);
213 }
214 return;
215 }
216
217 case media::MediaCodecBridge::INFO_OUTPUT_BUFFERS_CHANGED: {
218 media_codec_->GetOutputBuffers();
219 break;
220 }
221 }
222 } while (buf_index < 0);
223
224 if (flag & media::MediaCodecBridge::BUFFER_FLAG_END_OF_STREAM) {
225 if (client_) {
226 client_->NotifyFlushDone();
227 }
228 }
229
230 media_codec_->ReleaseOutputBuffer(buf_index, true);
231
232 CHECK_EQ(bitstream_buffer_ids_in_decoder_.front(), bitstream_buffer_id);
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 Presentation order does not have to match decode o
dwkang1 2013/02/04 14:08:26 Agreed. Haste made waste.
233 bitstream_buffer_ids_in_decoder_.pop();
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 It's also wrong to assume that pictures & bitstrea
dwkang1 2013/02/04 14:08:26 You are correct. removed.
234
235 if (bitstream_buffer_id != -1) {
236 SendCurrentSurfaceToClient(static_cast<int32>(bitstream_buffer_id));
237 }
238 }
239
240 void AndroidVideoDecodeAccelerator::SendCurrentSurfaceToClient(
241 int32 bitstream_id) {
242 DCHECK(thread_checker_.CalledOnValidThread());
243 DCHECK_NE(bitstream_id, -1);
244 DCHECK(!free_picture_ids_.empty());
245
246 int32 picture_buffer_id = free_picture_ids_.front();
247 free_picture_ids_.pop();
248
249 RETURN_ON_FAILURE(make_context_current_.Run(),
250 "Failed to make this decoder's GL context current.",
251 PLATFORM_FAILURE);
252
253 float transfrom_matrix[16];
254 surface_texture_->UpdateTexImage();
255 surface_texture_->GetTransformMatrix(transfrom_matrix);
256
257 OutputBufferMap::const_iterator i =
258 output_picture_buffers_.find(picture_buffer_id);
259 if (i == output_picture_buffers_.end()) {
260 LOG(ERROR) << "Can't find a PuctureBuffer for " << picture_buffer_id;
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 typo: PuctureBuffer
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 RETURN_ON_FAILURE
dwkang1 2013/02/04 14:08:26 Done.
dwkang1 2013/02/04 14:08:26 Done.
261 return;
262 }
263 uint32 picture_buffer_texture_id = i->second.texture_id();
264
265 // Here, we copy |surface_texture_id_| to the picture buffer instead of
266 // setting new texture to |surface_texture_| by calling attachToGLContext()
267 // because:
268 // 1. Once we call detachFrameGLContext(), it deletes the texture previous
269 // attached.
270 // 2. SurfaceTexture requires us to apply a transform matrix when we show
271 // the texture.
272 texture_copier_->Copy(
273 surface_texture_id_, picture_buffer_texture_id, transfrom_matrix);
274
275 client_->PictureReady(
276 media::Picture(picture_buffer_id, bitstream_id));
277 }
278
279 void AndroidVideoDecodeAccelerator::Decode(
280 const media::BitstreamBuffer& bitstream_buffer) {
281 LOG_LINE();
282 DCHECK(thread_checker_.CalledOnValidThread());
283 if (!client_) {
284 return;
285 }
286 pending_bitstream_buffers_.push(bitstream_buffer);
287
288 if (!io_task_is_running_) {
289 io_task_is_running_ = true;
290 MessageLoop::current()->PostDelayedTask(
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 Why not call directly?
dwkang1 2013/02/04 14:08:26 Done.
291 FROM_HERE,
292 base::Bind(
293 &AndroidVideoDecodeAccelerator::DoIOTask, base::AsWeakPtr(this)),
294 base::TimeDelta());
295 }
296 }
297
298 void AndroidVideoDecodeAccelerator::AssignPictureBuffers(
299 const std::vector<media::PictureBuffer>& buffers) {
300 LOG_LINE();
301 DCHECK(thread_checker_.CalledOnValidThread());
302 DCHECK(output_picture_buffers_.empty());
303
304 for (size_t i = 0; i < buffers.size(); ++i) {
305 output_picture_buffers_.insert(std::make_pair(buffers[i].id(), buffers[i]));
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 output_picture_buffers_[buffers[i].id()] = buffers
dwkang1 2013/02/04 14:08:26 It lead a compile error because PictureBuffer does
306 free_picture_ids_.push(buffers[i].id());
307 }
308
309 RETURN_ON_FAILURE(output_picture_buffers_.size() == kNumPictureBuffers,
310 "Invalid picture buffers were passed.",
311 INVALID_ARGUMENT);
312 }
313
314 void AndroidVideoDecodeAccelerator::ReusePictureBuffer(
315 int32 picture_buffer_id) {
316 DCHECK(thread_checker_.CalledOnValidThread());
317 free_picture_ids_.push(picture_buffer_id);
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 Doesn't this potentially require a DoIOTask?
dwkang1 2013/02/04 14:08:26 Done.
318 }
319
320 void AndroidVideoDecodeAccelerator::Flush() {
321 LOG_LINE();
322 DCHECK(thread_checker_.CalledOnValidThread());
323
324 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
325 }
326
327 void AndroidVideoDecodeAccelerator::ConfigureMediaCodec() {
328 DCHECK(surface_texture_.get());
329 DCHECK_NE(media::MediaCodecBridge::UNKNOWN, codec_);
330
331 media_codec_.reset(new media::MediaCodecBridge(codec_));
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 You don't need codec_ to re-configure a MediaCodec
dwkang1 2013/02/04 14:08:26 Unfortunately, I was not able to drop the recreati
332
333 JNIEnv* env = base::android::AttachCurrentThread();
334 CHECK(env);
335 ScopedJavaLocalRef<jclass> cls(
336 base::android::GetClass(env, "android/view/Surface"));
337 jmethodID constructor = MethodID::Get<MethodID::TYPE_INSTANCE>(
338 env, cls.obj(), "<init>", "(Landroid/graphics/SurfaceTexture;)V");
339 ScopedJavaLocalRef<jobject> j_surface(
340 env, env->NewObject(
341 cls.obj(), constructor,
342 surface_texture_->j_surface_texture().obj()));
343
344 // VDA does not pass the container indicated resolution in the initialization
345 // phase. Here, we set 1080p by default.
346 media_codec_->ConfigureVideo(
347 codec_, gfx::Size(1920, 1080), j_surface.obj());
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 Can you give more detail on what Exception MediaCo
dwkang1 2013/02/04 14:08:26 Here is the logcat output when I pass 0x0 on Nexus
348 content::ReleaseSurface(j_surface.obj());
349
350 media_codec_->GetInputBuffers();
351 media_codec_->GetOutputBuffers();
352 }
353
354 void AndroidVideoDecodeAccelerator::Reset() {
355 LOG_LINE();
356 DCHECK(thread_checker_.CalledOnValidThread());
357
358 while(!pending_bitstream_buffers_.empty()) {
359 media::BitstreamBuffer& bitstream_buffer =
360 pending_bitstream_buffers_.front();
361 pending_bitstream_buffers_.pop();
362
363 if (bitstream_buffer.id() != -1) {
364 client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 Please audit all uses of client_ to make sure they
dwkang1 2013/02/04 14:08:26 Done.
365 }
366 }
367 while(!bitstream_buffer_ids_in_decoder_.empty()) {
368 bitstream_buffer_ids_in_decoder_.pop();
369 }
370
371 media_codec_->Stop();
372 ConfigureMediaCodec();
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 I still don't see why this is necessary; why not s
dwkang1 2013/01/29 03:58:48 When I tested "playback complete" and "replay" cas
dwkang1 2013/02/04 14:08:26 As you may know, we had a discussion with Android
373 state_ = NO_ERROR;
374
375 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
376 &AndroidVideoDecodeAccelerator::NotifyResetDone, base::AsWeakPtr(this)));
377 }
378
379 void AndroidVideoDecodeAccelerator::Destroy() {
380 LOG_LINE();
381 DCHECK(thread_checker_.CalledOnValidThread());
382 delete this;
383 }
384
385 void AndroidVideoDecodeAccelerator::NotifyInitializeDone() {
386 if (client_) {
387 client_->NotifyInitializeDone();
388 }
389 }
390
391 void AndroidVideoDecodeAccelerator::NotifyResetDone() {
392 if (client_) {
Ami GONE FROM CHROMIUM 2013/01/28 19:49:45 here and elsewhere, client_ cannot be non-NULL; dr
dwkang1 2013/02/04 14:08:26 Done.
393 client_->NotifyResetDone();
394 }
395 }
396
397 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698