Home | History | Annotate | Download | only in video_decode
      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 <list>
      9 #include <map>
     10 #include <set>
     11 #include <sstream>
     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
    686