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