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

Side by Side Diff: ppapi/examples/video_decode/video_decode.cc

Issue 270213004: Implement Pepper PPB_VideoDecoder interface. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Make shared memory creation synchronous. Created 6 years, 7 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) 2014 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 <stdio.h>
6 #include <string.h>
7
8 #include <iostream>
9 #include <queue>
10 #include <sstream>
11
12 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/c/ppb_console.h"
14 #include "ppapi/c/ppb_opengles2.h"
15 #include "ppapi/cpp/graphics_3d.h"
16 #include "ppapi/cpp/graphics_3d_client.h"
17 #include "ppapi/cpp/input_event.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/module.h"
20 #include "ppapi/cpp/rect.h"
21 #include "ppapi/cpp/var.h"
22 #include "ppapi/cpp/video_decoder.h"
23 #include "ppapi/examples/video_decode/testdata.h"
24 #include "ppapi/lib/gl/include/GLES2/gl2.h"
25 #include "ppapi/lib/gl/include/GLES2/gl2ext.h"
26 #include "ppapi/utility/completion_callback_factory.h"
27
28 // Use assert as a poor-man's CHECK, even in non-debug mode.
29 // Since <assert.h> redefines assert on every inclusion (it doesn't use
30 // include-guards), make sure this is the last file #include'd in this file.
31 #undef NDEBUG
32 #include <assert.h>
33
34 // Assert |context_| isn't holding any GL Errors. Done as a macro instead of a
35 // function to preserve line number information in the failure message.
36 #define assertNoGLError() assert(!gles2_if_->GetError(context_->pp_resource()));
37
38 namespace {
39
40 struct Shader {
41 Shader() : program(0), texcoord_scale_location(0) {}
42 ~Shader() {}
43
44 GLuint program;
45 GLint texcoord_scale_location;
46 };
47
48 class Decoder;
49 class MyInstance;
50
51 class MyInstance : public pp::Instance, public pp::Graphics3DClient {
52 public:
53 MyInstance(PP_Instance instance, pp::Module* module);
54 virtual ~MyInstance();
55
56 // pp::Instance implementation.
57 virtual void DidChangeView(const pp::Rect& position,
58 const pp::Rect& clip_ignored);
59
60 // pp::Graphics3DClient implementation.
61 virtual void Graphics3DContextLost() {
62 // TODO(vrk/fischman): Properly reset after a lost graphics context. In
63 // particular need to delete context_ and re-create textures.
64 // Probably have to recreate the decoder from scratch, because old textures
65 // can still be outstanding in the decoder!
66 assert(false && "Unexpectedly lost graphics context");
67 }
68
69 void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture);
70
71 private:
72 // Log an error to the developer console and stderr by creating a temporary
73 // object of this type and streaming to it. Example usage:
74 // LogError(this).s() << "Hello world: " << 42;
75 class LogError {
76 public:
77 LogError(MyInstance* instance) : instance_(instance) {}
78 ~LogError() {
79 const std::string& msg = stream_.str();
80 instance_->console_if_->Log(
81 instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var());
82 std::cerr << msg << std::endl;
83 }
84 // Impl note: it would have been nicer to have LogError derive from
85 // std::ostringstream so that it can be streamed to directly, but lookup
86 // rules turn streamed string literals to hex pointers on output.
87 std::ostringstream& s() { return stream_; }
88
89 private:
90 MyInstance* instance_;
91 std::ostringstream stream_;
92 };
93
94 void InitializeDecoders();
95
96 // GL-related functions.
97 void InitGL();
98 void CreateGLObjects();
99 void Create2DProgramOnce();
100 void CreateRectangleARBProgramOnce();
101 Shader CreateProgram(const char* vertex_shader, const char* fragment_shader);
102 void CreateShader(GLuint program, GLenum type, const char* source, int size);
103 void PaintFinished(int32_t result, Decoder* decoder, PP_VideoPicture picture);
104
105 pp::Size plugin_size_;
106 bool is_painting_;
107 // When decode outpaces render, we queue up decoded pictures for later
108 // painting. Elements are <decoder,picture>.
109 typedef std::queue<std::pair<Decoder*, PP_VideoPicture> > PictureQueue;
110 PictureQueue pictures_pending_paint_;
111
112 int num_frames_rendered_;
113 PP_TimeTicks first_frame_delivered_ticks_;
114 PP_TimeTicks last_swap_request_ticks_;
115 PP_TimeTicks swap_ticks_;
116 pp::CompletionCallbackFactory<MyInstance> callback_factory_;
117
118 // Unowned pointers.
119 const PPB_Console* console_if_;
120 const PPB_Core* core_if_;
121 const PPB_OpenGLES2* gles2_if_;
122
123 // Owned data.
124 pp::Graphics3D* context_;
125 typedef std::vector<Decoder*> DecoderList;
126 DecoderList video_decoders_;
127
128 // Shader program to draw GL_TEXTURE_2D target.
129 Shader shader_2d_;
130 // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
131 Shader shader_rectangle_arb_;
132 };
133
134 class Decoder {
135 public:
136 Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
137 ~Decoder();
138
139 int id() const { return id_; }
140 bool ready() const { return !flushing_ && !resetting_; }
141
142 void Seek(int frame);
143 void RecyclePicture(const PP_VideoPicture& picture);
144
145 private:
146 void InitializeDone(int32_t result);
147 void Start(int frame);
148 void DecodeNextFrames();
149 void DecodeDone(int32_t result);
150 void PictureReady(int32_t result, PP_VideoPicture picture);
151 void FlushDone(int32_t result);
152 void ResetDone(int32_t result);
153
154 MyInstance* instance_;
155 int id_;
156
157 pp::VideoDecoder* decoder_;
158 pp::CompletionCallbackFactory<Decoder> callback_factory_;
159
160 size_t encoded_data_next_pos_to_decode_;
161 int next_picture_id_;
162 int seek_frame_;
163 bool flushing_;
164 bool resetting_;
165 };
166
167 // Returns true if the current position is at the start of a NAL unit.
168 static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
169 // H264 frames start with 0, 0, 0, 1 in our test data.
170 return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 &&
171 encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
172 }
173
174 // Find the start and end of the next frame.
175 static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
176 assert(LookingAtNAL(kData, *start_pos));
177 *end_pos = *start_pos;
178 *end_pos += 4;
179 while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) {
180 ++*end_pos;
181 }
182 }
183
184 Decoder::Decoder(MyInstance* instance,
185 int id,
186 const pp::Graphics3D& graphics_3d)
187 : instance_(instance),
188 id_(id),
189 decoder_(new pp::VideoDecoder(instance)),
190 callback_factory_(this),
191 encoded_data_next_pos_to_decode_(0),
192 next_picture_id_(0),
193 seek_frame_(0),
194 flushing_(false),
195 resetting_(false) {
196 assert(!decoder_->is_null());
197 const PP_VideoProfile profile = PP_VIDEOPROFILE_H264MAIN;
198 decoder_->Initialize(graphics_3d,
199 profile,
200 PP_FALSE /* allow_software_fallback */,
201 callback_factory_.NewCallback(&Decoder::InitializeDone));
202 }
203
204 Decoder::~Decoder() {
205 delete decoder_;
206 }
207
208 void Decoder::InitializeDone(int32_t result) {
209 assert(decoder_);
210 assert(result == PP_OK);
211 assert(ready());
212 Start(0);
213 }
214
215 void Decoder::Start(int frame) {
216 assert(decoder_);
217
218 // Skip to |frame|.
219 size_t start_pos = 0;
220 size_t end_pos = 0;
221 for (int i = frame; i > 0; i--)
222 GetNextFrame(&start_pos, &end_pos);
223 encoded_data_next_pos_to_decode_ = end_pos;
224
225 DecodeNextFrames();
226 // Register callback to get the first picture. We call GetPicture
227 // again in PictureReady to keep getting pictures.
228 decoder_->GetPicture(
229 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
230 }
231
232 void Decoder::Seek(int frame) {
233 assert(decoder_);
234 seek_frame_ = frame;
235 resetting_ = true;
236 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
237 }
238
239 void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
240 assert(decoder_);
241 decoder_->RecyclePicture(picture);
242 }
243
244 void Decoder::DecodeNextFrames() {
245 assert(decoder_);
246 while (encoded_data_next_pos_to_decode_ <= kDataLen) {
247 // If we've just reached the end of the bitstream, flush and wait.
248 if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) {
249 flushing_ = true;
250 decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
251 return;
252 }
253
254 // Find the start of the next NALU.
255 size_t start_pos = encoded_data_next_pos_to_decode_;
256 size_t end_pos;
257 GetNextFrame(&start_pos, &end_pos);
258 encoded_data_next_pos_to_decode_ = end_pos;
259 // Decode the NALU.
260 uint32_t size = static_cast<uint32_t>(end_pos - start_pos);
261 int32_t result =
262 decoder_->Decode(next_picture_id_++,
263 size,
264 kData + start_pos,
265 callback_factory_.NewCallback(&Decoder::DecodeDone));
266 if (result != PP_OK) {
267 assert(result == PP_OK_COMPLETIONPENDING);
268 break;
269 }
270 }
271 }
272
273 void Decoder::DecodeDone(int32_t result) {
274 assert(decoder_);
275 assert(result == PP_OK || result == PP_ERROR_ABORTED);
276 if (result == PP_OK && ready())
277 DecodeNextFrames();
278 }
279
280 void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
281 assert(decoder_);
282 assert(result == PP_OK || result == PP_ERROR_ABORTED);
283 if (result == PP_OK) {
284 decoder_->GetPicture(
285 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
286 instance_->PaintPicture(this, picture);
287 }
288 }
289
290 void Decoder::FlushDone(int32_t result) {
291 assert(decoder_);
292 assert(result == PP_OK || result == PP_ERROR_ABORTED);
293 flushing_ = false;
294 }
295
296 void Decoder::ResetDone(int32_t result) {
297 assert(decoder_);
298 assert(result == PP_OK);
299 resetting_ = false;
300 }
301
302 MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
303 : pp::Instance(instance),
304 pp::Graphics3DClient(this),
305 is_painting_(false),
306 num_frames_rendered_(0),
307 first_frame_delivered_ticks_(-1),
308 last_swap_request_ticks_(-1),
309 swap_ticks_(0),
310 callback_factory_(this),
311 context_(NULL) {
312 assert((console_if_ = static_cast<const PPB_Console*>(
313 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))));
314 assert((core_if_ = static_cast<const PPB_Core*>(
315 module->GetBrowserInterface(PPB_CORE_INTERFACE))));
316 assert((gles2_if_ = static_cast<const PPB_OpenGLES2*>(
317 module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))));
318 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
319 }
320
321 MyInstance::~MyInstance() {
322 if (!context_)
323 return;
324
325 PP_Resource graphics_3d = context_->pp_resource();
326 if (shader_2d_.program)
327 gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program);
328 if (shader_rectangle_arb_.program)
329 gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program);
330
331 for (DecoderList::iterator it = video_decoders_.begin();
332 it != video_decoders_.end();
333 ++it)
334 delete *it;
335
336 delete context_;
337 }
338
339 void MyInstance::DidChangeView(const pp::Rect& position,
340 const pp::Rect& clip_ignored) {
341 if (position.width() == 0 || position.height() == 0)
342 return;
343 if (plugin_size_.width()) {
344 assert(position.size() == plugin_size_);
345 return;
346 }
347 plugin_size_ = position.size();
348
349 // Initialize graphics.
350 InitGL();
351 InitializeDecoders();
352 }
353
354 void MyInstance::InitializeDecoders() {
355 assert(video_decoders_.empty());
356 // Create two decoders with ids 0 and 1.
357 video_decoders_.push_back(new Decoder(this, 0, *context_));
358 video_decoders_.push_back(new Decoder(this, 1, *context_));
359 }
360
361 void MyInstance::PaintPicture(Decoder* decoder,
362 const PP_VideoPicture& picture) {
363 if (first_frame_delivered_ticks_ == -1)
364 assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
365 if (is_painting_) {
366 pictures_pending_paint_.push(std::make_pair(decoder, picture));
367 return;
368 }
369
370 assert(!is_painting_);
371 is_painting_ = true;
372 int x = 0;
373 int y = 0;
374 int half_width = plugin_size_.width() / 2;
375 int half_height = plugin_size_.height() / 2;
376 if (decoder->id() != 0) {
377 x = half_width;
378 y = half_height;
379 }
380
381 PP_Resource graphics_3d = context_->pp_resource();
382 if (picture.texture_target == GL_TEXTURE_2D) {
383 Create2DProgramOnce();
384 gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
385 gles2_if_->Uniform2f(
386 graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
387 } else {
388 assert(picture.texture_target == GL_TEXTURE_RECTANGLE_ARB);
389 CreateRectangleARBProgramOnce();
390 gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
391 gles2_if_->Uniform2f(graphics_3d,
392 shader_rectangle_arb_.texcoord_scale_location,
393 picture.texture_size.width,
394 picture.texture_size.height);
395 }
396
397 gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
398 gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
399 gles2_if_->BindTexture(
400 graphics_3d, picture.texture_target, picture.texture_id);
401 gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
402
403 gles2_if_->UseProgram(graphics_3d, 0);
404
405 last_swap_request_ticks_ = core_if_->GetTimeTicks();
406 assert(PP_OK_COMPLETIONPENDING ==
407 context_->SwapBuffers(callback_factory_.NewCallback(
408 &MyInstance::PaintFinished, decoder, picture)));
409 }
410
411 void MyInstance::PaintFinished(int32_t result,
412 Decoder* decoder,
413 PP_VideoPicture picture) {
414 assert(result == PP_OK);
415 swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
416 is_painting_ = false;
417 ++num_frames_rendered_;
418 if (num_frames_rendered_ % 50 == 0) {
419 double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
420 double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
421 double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
422 LogError(this).s() << "Rendered frames: " << num_frames_rendered_
423 << ", fps: " << fps
424 << ", with average ms/swap of: " << ms_per_swap;
425 }
426 decoder->RecyclePicture(picture);
427 // Keep painting as long as we have pictures.
428 if (!pictures_pending_paint_.empty()) {
429 std::pair<Decoder*, PP_VideoPicture> pending =
430 pictures_pending_paint_.front();
431 pictures_pending_paint_.pop();
432 PaintPicture(pending.first, pending.second);
433 }
434 }
435
436 void MyInstance::InitGL() {
437 assert(plugin_size_.width() && plugin_size_.height());
438 is_painting_ = false;
439
440 assert(!context_);
441 int32_t context_attributes[] = {
442 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
443 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
444 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
445 PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
446 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
447 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
448 PP_GRAPHICS3DATTRIB_SAMPLES, 0,
449 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
450 PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
451 PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
452 PP_GRAPHICS3DATTRIB_NONE,
453 };
454 context_ = new pp::Graphics3D(this, context_attributes);
455 assert(!context_->is_null());
456 assert(BindGraphics(*context_));
457
458 // Clear color bit.
459 gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
460 gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
461
462 assertNoGLError();
463
464 CreateGLObjects();
465 }
466
467 void MyInstance::CreateGLObjects() {
468 // Assign vertex positions and texture coordinates to buffers for use in
469 // shader program.
470 static const float kVertices[] = {
471 -1, 1, -1, -1, 1, 1, 1, -1, // Position coordinates.
472 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
473 };
474
475 GLuint buffer;
476 gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
477 gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
478
479 gles2_if_->BufferData(context_->pp_resource(),
480 GL_ARRAY_BUFFER,
481 sizeof(kVertices),
482 kVertices,
483 GL_STATIC_DRAW);
484 assertNoGLError();
485 }
486
487 static const char kVertexShader[] =
488 "varying vec2 v_texCoord; \n"
489 "attribute vec4 a_position; \n"
490 "attribute vec2 a_texCoord; \n"
491 "uniform vec2 v_scale; \n"
492 "void main() \n"
493 "{ \n"
494 " v_texCoord = v_scale * a_texCoord; \n"
495 " gl_Position = a_position; \n"
496 "}";
497
498 void MyInstance::Create2DProgramOnce() {
499 if (shader_2d_.program)
500 return;
501 static const char kFragmentShader2D[] =
502 "precision mediump float; \n"
503 "varying vec2 v_texCoord; \n"
504 "uniform sampler2D s_texture; \n"
505 "void main() \n"
506 "{"
507 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
508 "}";
509 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
510 assertNoGLError();
511 }
512
513 void MyInstance::CreateRectangleARBProgramOnce() {
514 if (shader_rectangle_arb_.program)
515 return;
516 static const char kFragmentShaderRectangle[] =
517 "#extension GL_ARB_texture_rectangle : require\n"
518 "precision mediump float; \n"
519 "varying vec2 v_texCoord; \n"
520 "uniform sampler2DRect s_texture; \n"
521 "void main() \n"
522 "{"
523 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
524 "}";
525 shader_rectangle_arb_ =
526 CreateProgram(kVertexShader, kFragmentShaderRectangle);
527 }
528
529 Shader MyInstance::CreateProgram(const char* vertex_shader,
530 const char* fragment_shader) {
531 Shader shader;
532
533 // Create shader program.
534 shader.program = gles2_if_->CreateProgram(context_->pp_resource());
535 CreateShader(
536 shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
537 CreateShader(shader.program,
538 GL_FRAGMENT_SHADER,
539 fragment_shader,
540 strlen(fragment_shader));
541 gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
542 gles2_if_->UseProgram(context_->pp_resource(), shader.program);
543 gles2_if_->Uniform1i(
544 context_->pp_resource(),
545 gles2_if_->GetUniformLocation(
546 context_->pp_resource(), shader.program, "s_texture"),
547 0);
548 assertNoGLError();
549
550 shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
551 context_->pp_resource(), shader.program, "v_scale");
552
553 GLint pos_location = gles2_if_->GetAttribLocation(
554 context_->pp_resource(), shader.program, "a_position");
555 GLint tc_location = gles2_if_->GetAttribLocation(
556 context_->pp_resource(), shader.program, "a_texCoord");
557 assertNoGLError();
558
559 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
560 gles2_if_->VertexAttribPointer(
561 context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
562 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
563 gles2_if_->VertexAttribPointer(
564 context_->pp_resource(),
565 tc_location,
566 2,
567 GL_FLOAT,
568 GL_FALSE,
569 0,
570 static_cast<float*>(0) + 8); // Skip position coordinates.
571
572 gles2_if_->UseProgram(context_->pp_resource(), 0);
573 assertNoGLError();
574 return shader;
575 }
576
577 void MyInstance::CreateShader(GLuint program,
578 GLenum type,
579 const char* source,
580 int size) {
581 GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
582 gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
583 gles2_if_->CompileShader(context_->pp_resource(), shader);
584 gles2_if_->AttachShader(context_->pp_resource(), program, shader);
585 gles2_if_->DeleteShader(context_->pp_resource(), shader);
586 }
587
588 // This object is the global object representing this plugin library as long
589 // as it is loaded.
590 class MyModule : public pp::Module {
591 public:
592 MyModule() : pp::Module() {}
593 virtual ~MyModule() {}
594
595 virtual pp::Instance* CreateInstance(PP_Instance instance) {
596 return new MyInstance(instance, this);
597 }
598 };
599
600 } // anonymous namespace
601
602 namespace pp {
603 // Factory function for your specialization of the Module object.
604 Module* CreateModule() {
605 return new MyModule();
606 }
607 } // namespace pp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698