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

Side by Side Diff: content/common/gpu/omx_video_decode_accelerator_unittest.cc

Issue 7088021: OmxVideoDecodeAcceleratorTest is born! (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 // The bulk of this file is support code; sorry about that. Here's an overview
6 // to hopefully help readers of this code:
7 // - RenderingHelper is charged with interacting with X11, EGL, and GLES2.
8 // - ClientState is an enum for the state of the decode client used by the test.
9 // - ClientStateNotification is a barrier abstraction that allows the test code
10 // to be written sequentially and wait for the decode client to see certain
11 // state transitions.
12 // - EglRenderingVDAClient is a VideoDecodeAccelerator::Client implementation
13 // - Finally actual TEST cases are at the bottom of this file, using the above
14 // infrastructure.
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19
20 // Include gtest.h out of order because <X11/X.h> #define's Bool & None, which
21 // gtest uses as struct names (inside a namespace). This means that
22 // #include'ing gtest after anything that pulls in X.h fails to compile.
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 #include "base/at_exit.h"
26 #include "base/file_util.h"
27 #include "base/stl_util-inl.h"
28 #include "base/synchronization/condition_variable.h"
29 #include "base/synchronization/lock.h"
30 #include "base/synchronization/waitable_event.h"
31 #include "base/threading/thread.h"
32 #include "content/common/gpu/omx_video_decode_accelerator.h"
33 #include "third_party/angle/include/EGL/egl.h"
34 #include "third_party/angle/include/GLES2/gl2.h"
35
36 #if !defined(OS_CHROMEOS) || !defined(ARCH_CPU_ARMEL)
37 #error This test (and OmxVideoDecodeAccelerator) are only supported on cros/ARM!
38 #endif
39
40 using media::VideoDecodeAccelerator;
41
42 namespace {
43
44 // General-purpose constants for this test.
45 enum {
46 kFrameWidth = 320,
47 kFrameHeight = 240,
48 };
49
50 // Helper for managing X11, EGL, and GLES2 resources. Because GL state is
51 // thread-specific, all the methods of this class (except for ctor/dtor) CHECK
52 // for being run on a single thread.
53 //
54 // TODO(fischman): consider moving this into media/ if we can de-dup some of the
55 // code that ends up getting copy/pasted all over the place (esp. the GL setup
56 // code).
57 class RenderingHelper {
58 public:
59 explicit RenderingHelper();
60 ~RenderingHelper();
61
62 // Initialize all structures to prepare to render to a window of the specified
63 // dimensions. CHECK-fails if any initialization step fails. After this
64 // returns, texture creation and rendering (swaps) can be requested.
65 // This method can be called multiple times, in which case all
66 // previously-acquired resources and initializations are discarded.
67 void Initialize(int width, int height, base::WaitableEvent* done);
68
69 // Undo the effects of Initialize() and signal |*done|.
70 void UnInitialize(base::WaitableEvent* done);
71
72 // Return a newly-created GLES2 texture id.
73 GLuint CreateTexture();
74
75 // Render |texture_id| to the screen.
76 void RenderTexture(GLuint texture_id);
77
78 EGLDisplay egl_display() { return egl_display_; }
79 EGLContext egl_context() { return egl_context_; }
80
81 private:
82 int width_;
83 int height_;
84 Display* x_display_;
85 Window x_window_;
86 EGLDisplay egl_display_;
87 EGLContext egl_context_;
88 EGLSurface egl_surface_;
89 // Since GL carries per-thread state, we ensure all operations are carried out
90 // on the same thread by remembering where we were Initialized.
91 MessageLoop* message_loop_;
92 };
93
94 RenderingHelper::RenderingHelper() {
95 memset(this, 0, sizeof(this));
96 }
97
98 RenderingHelper::~RenderingHelper() {
99 CHECK_EQ(width_, 0) << "Must call UnInitialize before dtor.";
100 }
101
102 // Helper for Shader creation.
103 static void CreateShader(
104 GLuint program, GLenum type, const char* source, int size) {
105 GLuint shader = glCreateShader(type);
106 glShaderSource(shader, 1, &source, &size);
107 glCompileShader(shader);
108 int result = GL_FALSE;
109 glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
110 CHECK(result);
111 glAttachShader(program, shader);
112 glDeleteShader(shader);
113 CHECK_EQ((int)glGetError(), GL_NO_ERROR);
114 }
115
116 void RenderingHelper::Initialize(
117 int width, int height, base::WaitableEvent* done) {
118 if (width_) {
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 I get what you're doing, but at first glance looks
Ami GONE FROM CHROMIUM 2011/06/03 17:43:59 Done.
119 base::WaitableEvent done(false, false);
120 UnInitialize(&done);
121 done.Wait();
122 }
123
124 CHECK_GT(width, 0);
125 CHECK_GT(height, 0);
126 width_ = width;
127 height_ = height;
128 message_loop_ = MessageLoop::current();
129
130 // X11 initialization.
131 CHECK(x_display_ = XOpenDisplay(NULL));
132 int depth = DefaultDepth(x_display_, DefaultScreen(x_display_));
133 XSetWindowAttributes window_attributes;
134 window_attributes.background_pixel =
135 BlackPixel(x_display_, DefaultScreen(x_display_));
136 window_attributes.override_redirect = true;
137 x_window_ = XCreateWindow(
138 x_display_, DefaultRootWindow(x_display_),
139 0, 0, /* x/y of top-left corner */
140 width_, height_,
141 0 /* border width */,
142 depth, CopyFromParent /* class */, CopyFromParent /* visual */,
143 (CWBackPixel | CWOverrideRedirect), &window_attributes);
144 XStoreName(x_display_, x_window_, "OmxVideoDecodeAcceleratorTest");
145 XSelectInput(x_display_, x_window_, ExposureMask);
146 XMapWindow(x_display_, x_window_);
147
148 // EGL initialization.
149 egl_display_ = eglGetDisplay(x_display_);
150 EGLint major;
151 EGLint minor;
152 CHECK(eglInitialize(egl_display_, &major, &minor)) << eglGetError();
153 EGLint rgba8888[] = {
154 EGL_RED_SIZE, 8,
155 EGL_GREEN_SIZE, 8,
156 EGL_BLUE_SIZE, 8,
157 EGL_ALPHA_SIZE, 8,
158 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
159 EGL_NONE,
160 };
161
162 EGLConfig egl_config;
163 int num_configs;
164 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs))
165 << eglGetError();
166 CHECK_GE(num_configs, 1);
167 egl_surface_ =
168 eglCreateWindowSurface(egl_display_, egl_config, x_window_, NULL);
169 CHECK_NE(egl_surface_, EGL_NO_SURFACE);
170 EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
171 egl_context_ = eglCreateContext(
172 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs);
173 CHECK(eglMakeCurrent(egl_display_, egl_surface_, egl_surface_, egl_context_))
174 << eglGetError();
175
176 // GLES2 initialization. Note: This is pretty much copy/pasted from
177 // media/tools/player_x11/gles_video_renderer.cc, with some simplification
178 // applied.
179 static const float kVertices[] =
180 { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, };
181 static const float kTextureCoordsEgl[] = { 0, 1, 0, 0, 1, 1, 1, 0, };
182 static const char kVertexShader[] =
183 "precision highp float; precision highp int;\n"
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 These shouldn't be necessary. (no ints/floats used
Ami GONE FROM CHROMIUM 2011/06/03 17:43:59 Whoah! Do you actually know GL? I don't! Yay!
184 "varying vec2 interp_tc;\n"
185 "\n"
186 "attribute vec4 in_pos;\n"
187 "attribute vec2 in_tc;\n"
188 "\n"
189 "void main() {\n"
190 " interp_tc = in_tc;\n"
191 " gl_Position = in_pos;\n"
192 "}\n";
193 static const char kFragmentShaderEgl[] =
194 "precision mediump float;\n"
195 "precision mediump int;\n"
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 These shouldn't be necessary. (no ints/floats used
Ami GONE FROM CHROMIUM 2011/06/03 17:43:59 int line can be removed, but removing the float li
196 "varying vec2 interp_tc;\n"
197 "\n"
198 "uniform sampler2D tex;\n"
199 "\n"
200 "void main() {\n"
201 " gl_FragColor = texture2D(tex, interp_tc);\n"
202 "}\n";
203
204 GLuint program = glCreateProgram();
205 CreateShader(program, GL_VERTEX_SHADER, kVertexShader, sizeof(kVertexShader));
206 CreateShader(program, GL_FRAGMENT_SHADER,
207 kFragmentShaderEgl, sizeof(kFragmentShaderEgl));
208 glLinkProgram(program);
209 int result = GL_FALSE;
210 glGetProgramiv(program, GL_LINK_STATUS, &result);
211 CHECK(result);
212 glUseProgram(program);
213 glDeleteProgram(program);
214
215 glUniform1i(glGetUniformLocation(program, "tex"), 0);
216 int pos_location = glGetAttribLocation(program, "in_pos");
217 glEnableVertexAttribArray(pos_location);
218 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices);
219 int tc_location = glGetAttribLocation(program, "in_tc");
220 glEnableVertexAttribArray(tc_location);
221 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0,
222 kTextureCoordsEgl);
223 done->Signal();
224 }
225
226 void RenderingHelper::UnInitialize(base::WaitableEvent* done) {
227 CHECK_EQ(MessageLoop::current(), message_loop_);
228 // Destroy resources acquired in Initialize, in reverse-acquisition order.
229 CHECK(eglMakeCurrent(
230 egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
231 CHECK(eglDestroyContext(egl_display_, egl_context_));
232 CHECK(eglDestroySurface(egl_display_, egl_surface_));
233 CHECK(eglTerminate(egl_display_));
234 CHECK(XUnmapWindow(x_display_, x_window_));
235 CHECK(XDestroyWindow(x_display_, x_window_));
236 // Mimic newly-created object.
237 memset(this, 0, sizeof(this));
238 done->Signal();
239 }
240
241 GLuint RenderingHelper::CreateTexture() {
242 CHECK_EQ(MessageLoop::current(), message_loop_);
243 GLuint texture_id;
244 glGenTextures(1, &texture_id);
245 glBindTexture(GL_TEXTURE_2D, texture_id);
246 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA,
247 GL_UNSIGNED_BYTE, NULL);
248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
249 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
250 CHECK_EQ((int)glGetError(), GL_NO_ERROR);
251 return texture_id;
252 }
253
254 void RenderingHelper::RenderTexture(GLuint texture_id) {
255 CHECK_EQ(MessageLoop::current(), message_loop_);
256 glActiveTexture(GL_TEXTURE0);
257 glBindTexture(GL_TEXTURE_2D, texture_id);
258 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
259 DCHECK_EQ((int)glGetError(), GL_NO_ERROR);
260 eglSwapBuffers(egl_display_, egl_surface_);
261 DCHECK_EQ((int)eglGetError(), EGL_SUCCESS);
262 }
263
264 // State of the EglRenderingVDAClient below.
265 enum ClientState {
266 CS_CREATED,
267 CS_DECODER_SET,
268 CS_INITIALIZED,
269 CS_FLUSHED,
270 CS_DONE,
271 CS_ABORTED,
272 CS_ERROR,
273 CS_DESTROYED,
274 };
275
276 // Helper class allowing one thread to wait on a notification from another.
277 // If notifications come in faster than they are Wait()'d for, they are
278 // accumulated (so exactly as many Wait() calls will unblock as Notify() calls
279 // were made, regardless of order).
280 class ClientStateNotification {
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 SO ELEGANT
281 public:
282 ClientStateNotification();
283 ~ClientStateNotification();
284
285 // Used to notify a single waiter of a ClientState.
286 void Notify(ClientState state);
287 // Used by waiters to wait for the next ClientState Notification.
288 ClientState Wait();
289 private:
290 base::Lock lock_;
291 base::ConditionVariable cv_;
292 std::queue<ClientState> pending_states_for_notification_;
293 };
294
295 ClientStateNotification::ClientStateNotification() : cv_(&lock_) {}
296 ClientStateNotification::~ClientStateNotification() {}
297
298 void ClientStateNotification::Notify(ClientState state) {
299 base::AutoLock auto_lock(lock_);
300 pending_states_for_notification_.push(state);
301 cv_.Signal();
302 }
303
304 ClientState ClientStateNotification::Wait() {
305 base::AutoLock auto_lock(lock_);
306 while (pending_states_for_notification_.empty())
307 cv_.Wait();
308 ClientState ret = pending_states_for_notification_.front();
309 pending_states_for_notification_.pop();
310 return ret;
311 }
312
313 // Client that can accept callbacks from a VideoDecodeAccelerator and is used by
314 // the TESTs below.
315 class EglRenderingVDAClient : public VideoDecodeAccelerator::Client {
316 public:
317 // Doesn't take ownership of |note|, which must outlive |*this|.
318 EglRenderingVDAClient(ClientStateNotification* note);
319 virtual ~EglRenderingVDAClient();
320
321 // VideoDecodeAccelerator::Client implementation.
322 // The heart of the Client.
323 virtual void ProvidePictureBuffers(
324 uint32 requested_num_of_buffers,
325 const gfx::Size& dimensions,
326 VideoDecodeAccelerator::MemoryType type);
327 virtual void DismissPictureBuffer(int32 picture_buffer_id);
328 virtual void PictureReady(const media::Picture& picture);
329 // Simple state changes.
330 virtual void NotifyInitializeDone();
331 virtual void NotifyEndOfStream();
332 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id);
333 virtual void NotifyFlushDone();
334 virtual void NotifyAbortDone();
335 virtual void NotifyError(VideoDecodeAccelerator::Error error);
336
337 // Doesn't take ownership of |decoder|, which must outlive |*this|.
338 void SetDecoder(VideoDecodeAccelerator* decoder);
339
340 // Simple getters for inspecting the state of the Client.
341 ClientState state() { return state_; }
342 VideoDecodeAccelerator::Error error() { return error_; }
343 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; }
344 int num_decoded_frames() { return num_decoded_frames_; }
345 MessageLoop* message_loop() { return thread_.message_loop(); }
346 EGLDisplay egl_display() { return rendering_helper_.egl_display(); }
347 EGLContext egl_context() { return rendering_helper_.egl_context(); }
348
349 private:
350 void SetState(ClientState new_state) {
351 note_->Notify(new_state);
352 state_ = new_state;
353 }
354
355 VideoDecodeAccelerator* decoder_;
356 ClientStateNotification* note_;
357 ClientState state_;
358 VideoDecodeAccelerator::Error error_;
359 int num_decoded_frames_;
360 int num_done_bitstream_buffers_;
361 std::map<int, media::GLESBuffer*> picture_buffers_by_id_;
362 // Required for Thread to work. Not used otherwise.
363 base::ShadowingAtExitManager at_exit_manager_;
364 base::Thread thread_;
365 RenderingHelper rendering_helper_;
366 };
367
368 EglRenderingVDAClient::EglRenderingVDAClient(ClientStateNotification* note)
369 : decoder_(NULL), note_(note), state_(CS_CREATED),
370 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE),
371 num_decoded_frames_(0), num_done_bitstream_buffers_(0),
372 thread_("EglRenderingVDAClientThread") {
373 CHECK(thread_.Start());
374 base::WaitableEvent done(false, false);
375 message_loop()->PostTask(
376 FROM_HERE,
377 base::Bind(&RenderingHelper::Initialize,
378 base::Unretained(&rendering_helper_),
379 static_cast<int>(kFrameWidth), static_cast<int>(kFrameHeight),
380 &done));
381 done.Wait();
382 }
383
384 EglRenderingVDAClient::~EglRenderingVDAClient() {
385 base::WaitableEvent done(false, false);
386 message_loop()->PostTask(
387 FROM_HERE,
388 base::Bind(&RenderingHelper::UnInitialize,
389 base::Unretained(&rendering_helper_),
390 &done));
391 done.Wait();
392 thread_.Stop();
393 STLDeleteValues(&picture_buffers_by_id_);
394 SetState(CS_DESTROYED);
395 }
396
397 void EglRenderingVDAClient::ProvidePictureBuffers(
398 uint32 requested_num_of_buffers,
399 const gfx::Size& dimensions,
400 VideoDecodeAccelerator::MemoryType type) {
401 CHECK_EQ(message_loop(), MessageLoop::current());
402 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE);
403 std::vector<media::GLESBuffer> buffers;
404 CHECK_EQ(dimensions.width(), kFrameWidth);
405 CHECK_EQ(dimensions.height(), kFrameHeight);
406
407 for (uint32 i = 0; i < requested_num_of_buffers; ++i) {
408 uint32 id = picture_buffers_by_id_.size();
409 GLuint texture_id = rendering_helper_.CreateTexture();
410 // TODO(fischman): context_id is always 0. Can it be removed from the API?
411 // (since it's always inferrable from context).
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 Yeah I think for iteration 1 it'd be good to assum
Ami GONE FROM CHROMIUM 2011/06/03 17:43:59 Follow-up CL.
412 media::GLESBuffer* buffer =
413 new media::GLESBuffer(id, dimensions, texture_id, 0 /* context_id */);
414 CHECK(picture_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
415 buffers.push_back(*buffer);
416 }
417 decoder_->AssignGLESBuffers(buffers);
418 CHECK_EQ((int)glGetError(), GL_NO_ERROR);
419 CHECK_EQ((int)eglGetError(), EGL_SUCCESS);
420 }
421
422 void EglRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) {
423 CHECK_EQ(message_loop(), MessageLoop::current());
424 delete picture_buffers_by_id_[picture_buffer_id];
425 CHECK_EQ(1U, picture_buffers_by_id_.erase(picture_buffer_id));
426 }
427
428 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) {
429 CHECK_EQ(message_loop(), MessageLoop::current());
430
431 ++num_decoded_frames_;
432
433 media::GLESBuffer* gles_buffer =
434 picture_buffers_by_id_[picture.picture_buffer_id()];
435 CHECK(gles_buffer);
436 rendering_helper_.RenderTexture(gles_buffer->texture_id());
437
438 decoder_->ReusePictureBuffer(picture.picture_buffer_id());
439 }
440
441 void EglRenderingVDAClient::NotifyInitializeDone() {
442 CHECK_EQ(message_loop(), MessageLoop::current());
443 SetState(CS_INITIALIZED);
444 }
445
446 void EglRenderingVDAClient::NotifyEndOfStream() {
447 CHECK_EQ(message_loop(), MessageLoop::current());
448 SetState(CS_DONE);
449 }
450
451 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer(
452 int32 bitstream_buffer_id) {
453 CHECK_EQ(message_loop(), MessageLoop::current());
454 ++num_done_bitstream_buffers_;
455
456 // TODO(fischman): this is hokey! It should be possible to call Flush() from
457 // outside a callback.
458 if (num_done_bitstream_buffers_ == 1)
459 decoder_->Flush();
460 }
461
462 void EglRenderingVDAClient::NotifyFlushDone() {
463 CHECK_EQ(message_loop(), MessageLoop::current());
464 SetState(CS_FLUSHED);
465 }
466
467 void EglRenderingVDAClient::NotifyAbortDone() {
468 CHECK_EQ(message_loop(), MessageLoop::current());
469 SetState(CS_ABORTED);
470 }
471
472 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) {
473 CHECK_EQ(message_loop(), MessageLoop::current());
474 SetState(CS_ERROR);
475 error_ = error;
476 }
477
478 void EglRenderingVDAClient::SetDecoder(VideoDecodeAccelerator* decoder) {
479 decoder_ = decoder;
480 SetState(CS_DECODER_SET);
481 }
482
483 // Test the most straightforward case possible: data is decoded from a single
484 // chunk and rendered to the screen.
485 TEST(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) {
486 logging::SetMinLogLevel(-1);
487 ClientStateNotification note;
vrk (LEFT CHROMIUM) 2011/06/03 07:55:24 nit: indentation
Ami GONE FROM CHROMIUM 2011/06/03 17:43:59 Done.
488 ClientState state;
489 EglRenderingVDAClient client(&note);
490 OmxVideoDecodeAccelerator decoder(&client, client.message_loop());
491 client.SetDecoder(&decoder);
492 decoder.SetEglState(client.egl_display(), client.egl_context());
493 ASSERT_EQ((state = note.Wait()), CS_DECODER_SET);
494
495 int32 config_array[] = {
496 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC,
497 media::VIDEOCODECFOURCC_H264,
498 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_WIDTH, kFrameWidth,
499 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_HEIGHT, kFrameHeight,
500 media::VIDEOATTRIBUTEKEY_COLORFORMAT_RGBA,
501 media::VIDEOATTRIBUTEKEY_TERMINATOR
502 };
503 std::vector<uint32> config(
504 config_array, config_array + arraysize(config_array));
505 CHECK(decoder.Initialize(config));
506 ASSERT_EQ((state = note.Wait()), CS_INITIALIZED);
507
508 // Set up buffers to pass to Decode.
509 char const* data_filename = "test-25fps.h264";
510 int data_fd = open(data_filename, O_RDONLY);
511 CHECK_GE(data_fd, 0);
512 int64 file_size = -1;
513 CHECK(file_util::GetFileSize(FilePath(std::string(data_filename)),
514 &file_size));
515 base::SharedMemory data_shm(base::FileDescriptor(data_fd, false), true);
516 CHECK(data_shm.Map(file_size));
517 media::BitstreamBuffer bitstream_buffer(0, data_shm.handle(), file_size);
518
519 decoder.Decode(bitstream_buffer);
520 ASSERT_EQ((state = note.Wait()), CS_FLUSHED);
521
522 EXPECT_EQ(client.num_decoded_frames(), 25 /* fps */ * 10 /* seconds */);
523 EXPECT_EQ(client.num_done_bitstream_buffers(), 1);
524 }
525
526 // TODO(fischman, vrk): add more tests! In particular:
527 // - Test that breaking up the data buffers into many Decode() calls works.
528 // - Test decode speed. Ideally we can beat 60fps esp on simple test.mp4.
529 // - Test alternate configurations
530 // - Test failure conditions.
531 // - Test multiple concurrent decoders going at once.
532 // - Test frame size changes mid-stream
533
534 } // anonymous namespace
brettw 2011/06/02 23:55:04 This should just be "} // namespace"
Ami GONE FROM CHROMIUM 2011/06/03 02:13:06 Done.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698