Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / ppapi / examples / video_decode / video_decode.cc
1 // Copyright (c) 2012 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 <string.h>
6
7 #include <iostream>
8 #include <sstream>
9 #include <list>
10 #include <map>
11 #include <set>
12 #include <vector>
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/dev/video_decoder_client_dev.h"
18 #include "ppapi/cpp/dev/video_decoder_dev.h"
19 #include "ppapi/cpp/graphics_3d.h"
20 #include "ppapi/cpp/graphics_3d_client.h"
21 #include "ppapi/cpp/instance.h"
22 #include "ppapi/cpp/module.h"
23 #include "ppapi/cpp/rect.h"
24 #include "ppapi/cpp/var.h"
25 #include "ppapi/examples/video_decode/testdata.h"
26 #include "ppapi/lib/gl/include/GLES2/gl2.h"
27 #include "ppapi/lib/gl/include/GLES2/gl2ext.h"
28 #include "ppapi/utility/completion_callback_factory.h"
29
30 // Use assert as a poor-man's CHECK, even in non-debug mode.
31 // Since <assert.h> redefines assert on every inclusion (it doesn't use
32 // include-guards), make sure this is the last file #include'd in this file.
33 #undef NDEBUG
34 #include <assert.h>
35
36 // Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
37 // function to preserve line number information in the failure message.
38 #define assertNoGLError() \
39   assert(!gles2_if_->GetError(context_->pp_resource()));
40
41 namespace {
42
43 struct PictureBufferInfo {
44   PP_PictureBuffer_Dev buffer;
45   GLenum texture_target;
46 };
47
48 struct Shader {
49   Shader() : program(0),
50              texcoord_scale_location(0) {}
51
52   GLuint program;
53   GLint texcoord_scale_location;
54 };
55
56 class VideoDecodeDemoInstance : public pp::Instance,
57                                 public pp::Graphics3DClient,
58                                 public pp::VideoDecoderClient_Dev {
59  public:
60   VideoDecodeDemoInstance(PP_Instance instance, pp::Module* module);
61   virtual ~VideoDecodeDemoInstance();
62
63   // pp::Instance implementation (see PPP_Instance).
64   virtual void DidChangeView(const pp::Rect& position,
65                              const pp::Rect& clip_ignored);
66
67   // pp::Graphics3DClient implementation.
68   virtual void Graphics3DContextLost() {
69     // TODO(vrk/fischman): Properly reset after a lost graphics context.  In
70     // particular need to delete context_ and re-create textures.
71     // Probably have to recreate the decoder from scratch, because old textures
72     // can still be outstanding in the decoder!
73     assert(false && "Unexpectedly lost graphics context");
74   }
75
76   // pp::VideoDecoderClient_Dev implementation.
77   virtual void ProvidePictureBuffers(
78       PP_Resource decoder,
79       uint32_t req_num_of_bufs,
80       const PP_Size& dimensions,
81       uint32_t texture_target);
82   virtual void DismissPictureBuffer(PP_Resource decoder,
83                                     int32_t picture_buffer_id);
84   virtual void PictureReady(PP_Resource decoder, const PP_Picture_Dev& picture);
85   virtual void NotifyError(PP_Resource decoder, PP_VideoDecodeError_Dev error);
86
87  private:
88   enum { kNumConcurrentDecodes = 7,
89          kNumDecoders = 2 };  // Baked into viewport rendering.
90
91   // A single decoder's client interface.
92   class DecoderClient {
93    public:
94     DecoderClient(VideoDecodeDemoInstance* gles2,
95                   pp::VideoDecoder_Dev* decoder);
96     ~DecoderClient();
97
98     void DecodeNextNALUs();
99
100     // Per-decoder implementation of part of pp::VideoDecoderClient_Dev.
101     void ProvidePictureBuffers(
102         uint32_t req_num_of_bufs,
103         PP_Size dimensions,
104         uint32_t texture_target);
105     void DismissPictureBuffer(int32_t picture_buffer_id);
106
107     const PictureBufferInfo& GetPictureBufferInfoById(int id);
108     pp::VideoDecoder_Dev* decoder() { return decoder_; }
109
110    private:
111     void DecodeNextNALU();
112     static void GetNextNALUBoundary(size_t start_pos, size_t* end_pos);
113     void DecoderBitstreamDone(int32_t result, int bitstream_buffer_id);
114     void DecoderFlushDone(int32_t result);
115
116     VideoDecodeDemoInstance* gles2_;
117     pp::VideoDecoder_Dev* decoder_;
118     pp::CompletionCallbackFactory<DecoderClient> callback_factory_;
119     int next_picture_buffer_id_;
120     int next_bitstream_buffer_id_;
121     size_t encoded_data_next_pos_to_decode_;
122     std::set<int> bitstream_ids_at_decoder_;
123     // Map of texture buffers indexed by buffer id.
124     typedef std::map<int, PictureBufferInfo> PictureBufferMap;
125     PictureBufferMap picture_buffers_by_id_;
126     // Map of bitstream buffers indexed by id.
127     typedef std::map<int, pp::Buffer_Dev*> BitstreamBufferMap;
128     BitstreamBufferMap bitstream_buffers_by_id_;
129   };
130
131   // Initialize Video Decoders.
132   void InitializeDecoders();
133
134   // GL-related functions.
135   void InitGL();
136   GLuint CreateTexture(int32_t width, int32_t height, GLenum texture_target);
137   void CreateGLObjects();
138   void Create2DProgramOnce();
139   void CreateRectangleARBProgramOnce();
140   Shader CreateProgram(const char* vertex_shader,
141                        const char* fragment_shader);
142   void CreateShader(GLuint program, GLenum type, const char* source, int size);
143   void DeleteTexture(GLuint id);
144   void PaintFinished(int32_t result, PP_Resource decoder,
145                      int picture_buffer_id);
146
147   // Log an error to the developer console and stderr (though the latter may be
148   // closed due to sandboxing or blackholed for other reasons) by creating a
149   // temporary of this type and streaming to it.  Example usage:
150   // LogError(this).s() << "Hello world: " << 42;
151   class LogError {
152    public:
153     LogError(VideoDecodeDemoInstance* demo) : demo_(demo) {}
154     ~LogError() {
155       const std::string& msg = stream_.str();
156       demo_->console_if_->Log(demo_->pp_instance(), PP_LOGLEVEL_ERROR,
157                               pp::Var(msg).pp_var());
158       std::cerr << msg << std::endl;
159     }
160     // Impl note: it would have been nicer to have LogError derive from
161     // std::ostringstream so that it can be streamed to directly, but lookup
162     // rules turn streamed string literals to hex pointers on output.
163     std::ostringstream& s() { return stream_; }
164    private:
165     VideoDecodeDemoInstance* demo_;  // Unowned.
166     std::ostringstream stream_;
167   };
168
169   pp::Size plugin_size_;
170   bool is_painting_;
171   // When decode outpaces render, we queue up decoded pictures for later
172   // painting.  Elements are <decoder,picture>.
173   std::list<std::pair<PP_Resource, PP_Picture_Dev> > pictures_pending_paint_;
174   int num_frames_rendered_;
175   PP_TimeTicks first_frame_delivered_ticks_;
176   PP_TimeTicks last_swap_request_ticks_;
177   PP_TimeTicks swap_ticks_;
178   pp::CompletionCallbackFactory<VideoDecodeDemoInstance> callback_factory_;
179
180   // Unowned pointers.
181   const PPB_Console* console_if_;
182   const PPB_Core* core_if_;
183   const PPB_OpenGLES2* gles2_if_;
184
185   // Owned data.
186   pp::Graphics3D* context_;
187   typedef std::map<int, DecoderClient*> Decoders;
188   Decoders video_decoders_;
189
190   // Shader program to draw GL_TEXTURE_2D target.
191   Shader shader_2d_;
192   // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
193   Shader shader_rectangle_arb_;
194 };
195
196 VideoDecodeDemoInstance::DecoderClient::DecoderClient(
197       VideoDecodeDemoInstance* gles2, pp::VideoDecoder_Dev* decoder)
198     : gles2_(gles2), decoder_(decoder), callback_factory_(this),
199       next_picture_buffer_id_(0),
200       next_bitstream_buffer_id_(0), encoded_data_next_pos_to_decode_(0) {
201 }
202
203 VideoDecodeDemoInstance::DecoderClient::~DecoderClient() {
204   delete decoder_;
205   decoder_ = NULL;
206
207   for (BitstreamBufferMap::iterator it = bitstream_buffers_by_id_.begin();
208        it != bitstream_buffers_by_id_.end(); ++it) {
209     delete it->second;
210   }
211   bitstream_buffers_by_id_.clear();
212
213   for (PictureBufferMap::iterator it = picture_buffers_by_id_.begin();
214        it != picture_buffers_by_id_.end(); ++it) {
215     gles2_->DeleteTexture(it->second.buffer.texture_id);
216   }
217   picture_buffers_by_id_.clear();
218 }
219
220 VideoDecodeDemoInstance::VideoDecodeDemoInstance(PP_Instance instance,
221                                                  pp::Module* module)
222     : pp::Instance(instance), pp::Graphics3DClient(this),
223       pp::VideoDecoderClient_Dev(this),
224       is_painting_(false),
225       num_frames_rendered_(0),
226       first_frame_delivered_ticks_(-1),
227       swap_ticks_(0),
228       callback_factory_(this),
229       context_(NULL) {
230   assert((console_if_ = static_cast<const PPB_Console*>(
231       module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))));
232   assert((core_if_ = static_cast<const PPB_Core*>(
233       module->GetBrowserInterface(PPB_CORE_INTERFACE))));
234   assert((gles2_if_ = static_cast<const PPB_OpenGLES2*>(
235       module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))));
236 }
237
238 VideoDecodeDemoInstance::~VideoDecodeDemoInstance() {
239   if (shader_2d_.program)
240     gles2_if_->DeleteProgram(context_->pp_resource(), shader_2d_.program);
241   if (shader_rectangle_arb_.program) {
242     gles2_if_->DeleteProgram(
243         context_->pp_resource(), shader_rectangle_arb_.program);
244   }
245
246   for (Decoders::iterator it = video_decoders_.begin();
247        it != video_decoders_.end(); ++it) {
248     delete it->second;
249   }
250   video_decoders_.clear();
251   delete context_;
252 }
253
254 void VideoDecodeDemoInstance::DidChangeView(
255     const pp::Rect& position, const pp::Rect& clip_ignored) {
256   if (position.width() == 0 || position.height() == 0)
257     return;
258   if (plugin_size_.width()) {
259     assert(position.size() == plugin_size_);
260     return;
261   }
262   plugin_size_ = position.size();
263
264   // Initialize graphics.
265   InitGL();
266   InitializeDecoders();
267 }
268
269 void VideoDecodeDemoInstance::InitializeDecoders() {
270   assert(video_decoders_.empty());
271   for (int i = 0; i < kNumDecoders; ++i) {
272     DecoderClient* client = new DecoderClient(
273         this, new pp::VideoDecoder_Dev(
274             this, *context_, PP_VIDEODECODER_H264PROFILE_MAIN));
275     assert(!client->decoder()->is_null());
276     assert(video_decoders_.insert(std::make_pair(
277         client->decoder()->pp_resource(), client)).second);
278     client->DecodeNextNALUs();
279   }
280 }
281
282 void VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone(
283     int32_t result, int bitstream_buffer_id) {
284   assert(bitstream_ids_at_decoder_.erase(bitstream_buffer_id) == 1);
285   BitstreamBufferMap::iterator it =
286       bitstream_buffers_by_id_.find(bitstream_buffer_id);
287   assert(it != bitstream_buffers_by_id_.end());
288   delete it->second;
289   bitstream_buffers_by_id_.erase(it);
290   DecodeNextNALUs();
291 }
292
293 void VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone(int32_t result) {
294   assert(result == PP_OK);
295   // Check that each bitstream buffer ID we handed to the decoder got handed
296   // back to us.
297   assert(bitstream_ids_at_decoder_.empty());
298   delete decoder_;
299   decoder_ = NULL;
300 }
301
302 static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
303   return pos + 3 < kDataLen &&
304       encoded[pos] == 0 && encoded[pos + 1] == 0 &&
305       encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
306 }
307
308 void VideoDecodeDemoInstance::DecoderClient::GetNextNALUBoundary(
309     size_t start_pos, size_t* end_pos) {
310   assert(LookingAtNAL(kData, start_pos));
311   *end_pos = start_pos;
312   *end_pos += 4;
313   while (*end_pos + 3 < kDataLen &&
314          !LookingAtNAL(kData, *end_pos)) {
315     ++*end_pos;
316   }
317   if (*end_pos + 3 >= kDataLen) {
318     *end_pos = kDataLen;
319     return;
320   }
321 }
322
323 void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALUs() {
324   while (encoded_data_next_pos_to_decode_ <= kDataLen &&
325          bitstream_ids_at_decoder_.size() < kNumConcurrentDecodes) {
326     DecodeNextNALU();
327   }
328 }
329
330 void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALU() {
331   if (encoded_data_next_pos_to_decode_ == kDataLen) {
332     ++encoded_data_next_pos_to_decode_;
333     pp::CompletionCallback cb = callback_factory_.NewCallback(
334         &VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone);
335     decoder_->Flush(cb);
336     return;
337   }
338   size_t start_pos = encoded_data_next_pos_to_decode_;
339   size_t end_pos;
340   GetNextNALUBoundary(start_pos, &end_pos);
341   pp::Buffer_Dev* buffer = new pp::Buffer_Dev(gles2_, end_pos - start_pos);
342   PP_VideoBitstreamBuffer_Dev bitstream_buffer;
343   int id = ++next_bitstream_buffer_id_;
344   bitstream_buffer.id = id;
345   bitstream_buffer.size = end_pos - start_pos;
346   bitstream_buffer.data = buffer->pp_resource();
347   memcpy(buffer->data(), kData + start_pos, end_pos - start_pos);
348   assert(bitstream_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
349
350   pp::CompletionCallback cb =
351       callback_factory_.NewCallback(
352           &VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone, id);
353   assert(bitstream_ids_at_decoder_.insert(id).second);
354   encoded_data_next_pos_to_decode_ = end_pos;
355   decoder_->Decode(bitstream_buffer, cb);
356 }
357
358 void VideoDecodeDemoInstance::ProvidePictureBuffers(PP_Resource decoder,
359                                                     uint32_t req_num_of_bufs,
360                                                     const PP_Size& dimensions,
361                                                     uint32_t texture_target) {
362   DecoderClient* client = video_decoders_[decoder];
363   assert(client);
364   client->ProvidePictureBuffers(req_num_of_bufs, dimensions, texture_target);
365 }
366
367 void VideoDecodeDemoInstance::DecoderClient::ProvidePictureBuffers(
368     uint32_t req_num_of_bufs,
369     PP_Size dimensions,
370     uint32_t texture_target) {
371   std::vector<PP_PictureBuffer_Dev> buffers;
372   for (uint32_t i = 0; i < req_num_of_bufs; ++i) {
373     PictureBufferInfo info;
374     info.buffer.size = dimensions;
375     info.texture_target = texture_target;
376     info.buffer.texture_id = gles2_->CreateTexture(
377         dimensions.width, dimensions.height, info.texture_target);
378     int id = ++next_picture_buffer_id_;
379     info.buffer.id = id;
380     buffers.push_back(info.buffer);
381     assert(picture_buffers_by_id_.insert(std::make_pair(id, info)).second);
382   }
383   decoder_->AssignPictureBuffers(buffers);
384 }
385
386 const PictureBufferInfo&
387 VideoDecodeDemoInstance::DecoderClient::GetPictureBufferInfoById(
388     int id) {
389   PictureBufferMap::iterator it = picture_buffers_by_id_.find(id);
390   assert(it != picture_buffers_by_id_.end());
391   return it->second;
392 }
393
394 void VideoDecodeDemoInstance::DismissPictureBuffer(PP_Resource decoder,
395                                              int32_t picture_buffer_id) {
396   DecoderClient* client = video_decoders_[decoder];
397   assert(client);
398   client->DismissPictureBuffer(picture_buffer_id);
399 }
400
401 void VideoDecodeDemoInstance::DecoderClient::DismissPictureBuffer(
402     int32_t picture_buffer_id) {
403   gles2_->DeleteTexture(GetPictureBufferInfoById(
404       picture_buffer_id).buffer.texture_id);
405   picture_buffers_by_id_.erase(picture_buffer_id);
406 }
407
408 void VideoDecodeDemoInstance::PictureReady(PP_Resource decoder,
409                                      const PP_Picture_Dev& picture) {
410   if (first_frame_delivered_ticks_ == -1)
411     assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
412   if (is_painting_) {
413     pictures_pending_paint_.push_back(std::make_pair(decoder, picture));
414     return;
415   }
416   DecoderClient* client = video_decoders_[decoder];
417   assert(client);
418   const PictureBufferInfo& info =
419       client->GetPictureBufferInfoById(picture.picture_buffer_id);
420   assert(!is_painting_);
421   is_painting_ = true;
422   int x = 0;
423   int y = 0;
424   if (client != video_decoders_.begin()->second) {
425     x = plugin_size_.width() / kNumDecoders;
426     y = plugin_size_.height() / kNumDecoders;
427   }
428
429   if (info.texture_target == GL_TEXTURE_2D) {
430     Create2DProgramOnce();
431     gles2_if_->UseProgram(context_->pp_resource(), shader_2d_.program);
432     gles2_if_->Uniform2f(
433         context_->pp_resource(), shader_2d_.texcoord_scale_location, 1.0, 1.0);
434   } else {
435     assert(info.texture_target == GL_TEXTURE_RECTANGLE_ARB);
436     CreateRectangleARBProgramOnce();
437     gles2_if_->UseProgram(
438         context_->pp_resource(), shader_rectangle_arb_.program);
439     gles2_if_->Uniform2f(context_->pp_resource(),
440                          shader_rectangle_arb_.texcoord_scale_location,
441                          info.buffer.size.width,
442                          info.buffer.size.height);
443   }
444
445   gles2_if_->Viewport(context_->pp_resource(), x, y,
446                       plugin_size_.width() / kNumDecoders,
447                       plugin_size_.height() / kNumDecoders);
448   gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
449   gles2_if_->BindTexture(
450       context_->pp_resource(), info.texture_target, info.buffer.texture_id);
451   gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4);
452
453   gles2_if_->UseProgram(context_->pp_resource(), 0);
454
455   pp::CompletionCallback cb =
456       callback_factory_.NewCallback(
457           &VideoDecodeDemoInstance::PaintFinished, decoder, info.buffer.id);
458   last_swap_request_ticks_ = core_if_->GetTimeTicks();
459   assert(context_->SwapBuffers(cb) == PP_OK_COMPLETIONPENDING);
460 }
461
462 void VideoDecodeDemoInstance::NotifyError(PP_Resource decoder,
463                                           PP_VideoDecodeError_Dev error) {
464   LogError(this).s() << "Received error: " << error;
465   assert(false && "Unexpected error; see stderr for details");
466 }
467
468 // This object is the global object representing this plugin library as long
469 // as it is loaded.
470 class VideoDecodeDemoModule : public pp::Module {
471  public:
472   VideoDecodeDemoModule() : pp::Module() {}
473   virtual ~VideoDecodeDemoModule() {}
474
475   virtual pp::Instance* CreateInstance(PP_Instance instance) {
476     return new VideoDecodeDemoInstance(instance, this);
477   }
478 };
479
480 void VideoDecodeDemoInstance::InitGL() {
481   assert(plugin_size_.width() && plugin_size_.height());
482   is_painting_ = false;
483
484   assert(!context_);
485   int32_t context_attributes[] = {
486     PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
487     PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
488     PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
489     PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
490     PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
491     PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
492     PP_GRAPHICS3DATTRIB_SAMPLES, 0,
493     PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
494     PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
495     PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
496     PP_GRAPHICS3DATTRIB_NONE,
497   };
498   context_ = new pp::Graphics3D(this, context_attributes);
499   assert(!context_->is_null());
500   assert(BindGraphics(*context_));
501
502   // Clear color bit.
503   gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
504   gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
505
506   assertNoGLError();
507
508   CreateGLObjects();
509 }
510
511 void VideoDecodeDemoInstance::PaintFinished(int32_t result, PP_Resource decoder,
512                                       int picture_buffer_id) {
513   assert(result == PP_OK);
514   swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
515   is_painting_ = false;
516   ++num_frames_rendered_;
517   if (num_frames_rendered_ % 50 == 0) {
518     double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
519     double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
520     double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
521     LogError(this).s() << "Rendered frames: " << num_frames_rendered_
522                        << ", fps: " << fps << ", with average ms/swap of: "
523                        << ms_per_swap;
524   }
525   DecoderClient* client = video_decoders_[decoder];
526   if (client && client->decoder())
527     client->decoder()->ReusePictureBuffer(picture_buffer_id);
528   if (!pictures_pending_paint_.empty()) {
529     std::pair<PP_Resource, PP_Picture_Dev> decoder_picture =
530         pictures_pending_paint_.front();
531     pictures_pending_paint_.pop_front();
532     PictureReady(decoder_picture.first, decoder_picture.second);
533   }
534 }
535
536 GLuint VideoDecodeDemoInstance::CreateTexture(int32_t width,
537                                               int32_t height,
538                                               GLenum texture_target) {
539   GLuint texture_id;
540   gles2_if_->GenTextures(context_->pp_resource(), 1, &texture_id);
541   assertNoGLError();
542   // Assign parameters.
543   gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
544   gles2_if_->BindTexture(context_->pp_resource(), texture_target, texture_id);
545   gles2_if_->TexParameteri(
546       context_->pp_resource(), texture_target, GL_TEXTURE_MIN_FILTER,
547       GL_NEAREST);
548   gles2_if_->TexParameteri(
549       context_->pp_resource(), texture_target, GL_TEXTURE_MAG_FILTER,
550       GL_NEAREST);
551   gles2_if_->TexParameterf(
552       context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_S,
553       GL_CLAMP_TO_EDGE);
554   gles2_if_->TexParameterf(
555       context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_T,
556       GL_CLAMP_TO_EDGE);
557
558   if (texture_target == GL_TEXTURE_2D) {
559     gles2_if_->TexImage2D(
560         context_->pp_resource(), texture_target, 0, GL_RGBA, width, height, 0,
561         GL_RGBA, GL_UNSIGNED_BYTE, NULL);
562   }
563   assertNoGLError();
564   return texture_id;
565 }
566
567 void VideoDecodeDemoInstance::DeleteTexture(GLuint id) {
568   gles2_if_->DeleteTextures(context_->pp_resource(), 1, &id);
569 }
570
571 void VideoDecodeDemoInstance::CreateGLObjects() {
572   // Assign vertex positions and texture coordinates to buffers for use in
573   // shader program.
574   static const float kVertices[] = {
575     -1, 1, -1, -1, 1, 1, 1, -1,  // Position coordinates.
576     0, 1, 0, 0, 1, 1, 1, 0,      // Texture coordinates.
577   };
578
579   GLuint buffer;
580   gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
581   gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
582
583   gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER,
584                         sizeof(kVertices), kVertices, GL_STATIC_DRAW);
585   assertNoGLError();
586 }
587
588 static const char kVertexShader[] =
589     "varying vec2 v_texCoord;            \n"
590     "attribute vec4 a_position;          \n"
591     "attribute vec2 a_texCoord;          \n"
592     "uniform vec2 v_scale;               \n"
593     "void main()                         \n"
594     "{                                   \n"
595     "    v_texCoord = v_scale * a_texCoord; \n"
596     "    gl_Position = a_position;       \n"
597     "}";
598
599 void VideoDecodeDemoInstance::Create2DProgramOnce() {
600   if (shader_2d_.program)
601     return;
602   static const char kFragmentShader2D[] =
603       "precision mediump float;            \n"
604       "varying vec2 v_texCoord;            \n"
605       "uniform sampler2D s_texture;        \n"
606       "void main()                         \n"
607       "{"
608       "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
609       "}";
610   shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
611   assertNoGLError();
612 }
613
614 void VideoDecodeDemoInstance::CreateRectangleARBProgramOnce() {
615   if (shader_rectangle_arb_.program)
616     return;
617   static const char kFragmentShaderRectangle[] =
618       "#extension GL_ARB_texture_rectangle : require\n"
619       "precision mediump float;            \n"
620       "varying vec2 v_texCoord;            \n"
621       "uniform sampler2DRect s_texture;    \n"
622       "void main()                         \n"
623       "{"
624       "    gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
625       "}";
626   shader_rectangle_arb_ =
627       CreateProgram(kVertexShader, kFragmentShaderRectangle);
628 }
629
630 Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
631                                               const char* fragment_shader) {
632   Shader shader;
633
634   // Create shader program.
635   shader.program = gles2_if_->CreateProgram(context_->pp_resource());
636   CreateShader(shader.program, GL_VERTEX_SHADER, vertex_shader,
637                strlen(vertex_shader));
638   CreateShader(shader.program, GL_FRAGMENT_SHADER, fragment_shader,
639                strlen(fragment_shader));
640   gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
641   gles2_if_->UseProgram(context_->pp_resource(), shader.program);
642   gles2_if_->Uniform1i(
643       context_->pp_resource(),
644       gles2_if_->GetUniformLocation(
645           context_->pp_resource(), shader.program, "s_texture"), 0);
646   assertNoGLError();
647
648   shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
649       context_->pp_resource(), shader.program, "v_scale");
650
651   GLint pos_location = gles2_if_->GetAttribLocation(
652       context_->pp_resource(), shader.program, "a_position");
653   GLint tc_location = gles2_if_->GetAttribLocation(
654       context_->pp_resource(), shader.program, "a_texCoord");
655   assertNoGLError();
656
657   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
658   gles2_if_->VertexAttribPointer(context_->pp_resource(), pos_location, 2,
659                                  GL_FLOAT, GL_FALSE, 0, 0);
660   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
661   gles2_if_->VertexAttribPointer(
662       context_->pp_resource(), tc_location, 2, GL_FLOAT, GL_FALSE, 0,
663       static_cast<float*>(0) + 8);  // Skip position coordinates.
664
665   gles2_if_->UseProgram(context_->pp_resource(), 0);
666   assertNoGLError();
667   return shader;
668 }
669
670 void VideoDecodeDemoInstance::CreateShader(
671     GLuint program, GLenum type, const char* source, int size) {
672   GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
673   gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
674   gles2_if_->CompileShader(context_->pp_resource(), shader);
675   gles2_if_->AttachShader(context_->pp_resource(), program, shader);
676   gles2_if_->DeleteShader(context_->pp_resource(), shader);
677 }
678 }  // anonymous namespace
679
680 namespace pp {
681 // Factory function for your specialization of the Module object.
682 Module* CreateModule() {
683   return new VideoDecodeDemoModule();
684 }
685 }  // namespace pp