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
41namespace {
42
43struct PictureBufferInfo {
44  PP_PictureBuffer_Dev buffer;
45  GLenum texture_target;
46};
47
48struct Shader {
49  Shader() : program(0),
50             texcoord_scale_location(0) {}
51
52  GLuint program;
53  GLint texcoord_scale_location;
54};
55
56class 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
196VideoDecodeDemoInstance::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
203VideoDecodeDemoInstance::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
220VideoDecodeDemoInstance::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      console_if_(static_cast<const PPB_Console*>(
230          module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))),
231      core_if_(static_cast<const PPB_Core*>(
232          module->GetBrowserInterface(PPB_CORE_INTERFACE))),
233      gles2_if_(static_cast<const PPB_OpenGLES2*>(
234          module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))),
235      context_(NULL) {
236  assert(console_if_);
237  assert(core_if_);
238  assert(gles2_if_);
239}
240
241VideoDecodeDemoInstance::~VideoDecodeDemoInstance() {
242  if (shader_2d_.program)
243    gles2_if_->DeleteProgram(context_->pp_resource(), shader_2d_.program);
244  if (shader_rectangle_arb_.program) {
245    gles2_if_->DeleteProgram(
246        context_->pp_resource(), shader_rectangle_arb_.program);
247  }
248
249  for (Decoders::iterator it = video_decoders_.begin();
250       it != video_decoders_.end(); ++it) {
251    delete it->second;
252  }
253  video_decoders_.clear();
254  delete context_;
255}
256
257void VideoDecodeDemoInstance::DidChangeView(
258    const pp::Rect& position, const pp::Rect& clip_ignored) {
259  if (position.width() == 0 || position.height() == 0)
260    return;
261  if (plugin_size_.width()) {
262    assert(position.size() == plugin_size_);
263    return;
264  }
265  plugin_size_ = position.size();
266
267  // Initialize graphics.
268  InitGL();
269  InitializeDecoders();
270}
271
272void VideoDecodeDemoInstance::InitializeDecoders() {
273  assert(video_decoders_.empty());
274  for (int i = 0; i < kNumDecoders; ++i) {
275    DecoderClient* client = new DecoderClient(
276        this, new pp::VideoDecoder_Dev(
277            this, *context_, PP_VIDEODECODER_H264PROFILE_MAIN));
278    assert(!client->decoder()->is_null());
279    assert(video_decoders_.insert(std::make_pair(
280        client->decoder()->pp_resource(), client)).second);
281    client->DecodeNextNALUs();
282  }
283}
284
285void VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone(
286    int32_t result, int bitstream_buffer_id) {
287  assert(bitstream_ids_at_decoder_.erase(bitstream_buffer_id) == 1);
288  BitstreamBufferMap::iterator it =
289      bitstream_buffers_by_id_.find(bitstream_buffer_id);
290  assert(it != bitstream_buffers_by_id_.end());
291  delete it->second;
292  bitstream_buffers_by_id_.erase(it);
293  DecodeNextNALUs();
294}
295
296void VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone(int32_t result) {
297  assert(result == PP_OK);
298  // Check that each bitstream buffer ID we handed to the decoder got handed
299  // back to us.
300  assert(bitstream_ids_at_decoder_.empty());
301  delete decoder_;
302  decoder_ = NULL;
303}
304
305static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
306  return pos + 3 < kDataLen &&
307      encoded[pos] == 0 && encoded[pos + 1] == 0 &&
308      encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
309}
310
311void VideoDecodeDemoInstance::DecoderClient::GetNextNALUBoundary(
312    size_t start_pos, size_t* end_pos) {
313  assert(LookingAtNAL(kData, start_pos));
314  *end_pos = start_pos;
315  *end_pos += 4;
316  while (*end_pos + 3 < kDataLen &&
317         !LookingAtNAL(kData, *end_pos)) {
318    ++*end_pos;
319  }
320  if (*end_pos + 3 >= kDataLen) {
321    *end_pos = kDataLen;
322    return;
323  }
324}
325
326void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALUs() {
327  while (encoded_data_next_pos_to_decode_ <= kDataLen &&
328         bitstream_ids_at_decoder_.size() < kNumConcurrentDecodes) {
329    DecodeNextNALU();
330  }
331}
332
333void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALU() {
334  if (encoded_data_next_pos_to_decode_ == kDataLen) {
335    ++encoded_data_next_pos_to_decode_;
336    pp::CompletionCallback cb = callback_factory_.NewCallback(
337        &VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone);
338    decoder_->Flush(cb);
339    return;
340  }
341  size_t start_pos = encoded_data_next_pos_to_decode_;
342  size_t end_pos;
343  GetNextNALUBoundary(start_pos, &end_pos);
344  pp::Buffer_Dev* buffer = new pp::Buffer_Dev(gles2_, end_pos - start_pos);
345  PP_VideoBitstreamBuffer_Dev bitstream_buffer;
346  int id = ++next_bitstream_buffer_id_;
347  bitstream_buffer.id = id;
348  bitstream_buffer.size = end_pos - start_pos;
349  bitstream_buffer.data = buffer->pp_resource();
350  memcpy(buffer->data(), kData + start_pos, end_pos - start_pos);
351  assert(bitstream_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
352
353  pp::CompletionCallback cb =
354      callback_factory_.NewCallback(
355          &VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone, id);
356  assert(bitstream_ids_at_decoder_.insert(id).second);
357  encoded_data_next_pos_to_decode_ = end_pos;
358  decoder_->Decode(bitstream_buffer, cb);
359}
360
361void VideoDecodeDemoInstance::ProvidePictureBuffers(PP_Resource decoder,
362                                                    uint32_t req_num_of_bufs,
363                                                    const PP_Size& dimensions,
364                                                    uint32_t texture_target) {
365  DecoderClient* client = video_decoders_[decoder];
366  assert(client);
367  client->ProvidePictureBuffers(req_num_of_bufs, dimensions, texture_target);
368}
369
370void VideoDecodeDemoInstance::DecoderClient::ProvidePictureBuffers(
371    uint32_t req_num_of_bufs,
372    PP_Size dimensions,
373    uint32_t texture_target) {
374  std::vector<PP_PictureBuffer_Dev> buffers;
375  for (uint32_t i = 0; i < req_num_of_bufs; ++i) {
376    PictureBufferInfo info;
377    info.buffer.size = dimensions;
378    info.texture_target = texture_target;
379    info.buffer.texture_id = gles2_->CreateTexture(
380        dimensions.width, dimensions.height, info.texture_target);
381    int id = ++next_picture_buffer_id_;
382    info.buffer.id = id;
383    buffers.push_back(info.buffer);
384    assert(picture_buffers_by_id_.insert(std::make_pair(id, info)).second);
385  }
386  decoder_->AssignPictureBuffers(buffers);
387}
388
389const PictureBufferInfo&
390VideoDecodeDemoInstance::DecoderClient::GetPictureBufferInfoById(
391    int id) {
392  PictureBufferMap::iterator it = picture_buffers_by_id_.find(id);
393  assert(it != picture_buffers_by_id_.end());
394  return it->second;
395}
396
397void VideoDecodeDemoInstance::DismissPictureBuffer(PP_Resource decoder,
398                                             int32_t picture_buffer_id) {
399  DecoderClient* client = video_decoders_[decoder];
400  assert(client);
401  client->DismissPictureBuffer(picture_buffer_id);
402}
403
404void VideoDecodeDemoInstance::DecoderClient::DismissPictureBuffer(
405    int32_t picture_buffer_id) {
406  gles2_->DeleteTexture(GetPictureBufferInfoById(
407      picture_buffer_id).buffer.texture_id);
408  picture_buffers_by_id_.erase(picture_buffer_id);
409}
410
411void VideoDecodeDemoInstance::PictureReady(PP_Resource decoder,
412                                     const PP_Picture_Dev& picture) {
413  if (first_frame_delivered_ticks_ == -1)
414    assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
415  if (is_painting_) {
416    pictures_pending_paint_.push_back(std::make_pair(decoder, picture));
417    return;
418  }
419  DecoderClient* client = video_decoders_[decoder];
420  assert(client);
421  const PictureBufferInfo& info =
422      client->GetPictureBufferInfoById(picture.picture_buffer_id);
423  assert(!is_painting_);
424  is_painting_ = true;
425  int x = 0;
426  int y = 0;
427  if (client != video_decoders_.begin()->second) {
428    x = plugin_size_.width() / kNumDecoders;
429    y = plugin_size_.height() / kNumDecoders;
430  }
431
432  if (info.texture_target == GL_TEXTURE_2D) {
433    Create2DProgramOnce();
434    gles2_if_->UseProgram(context_->pp_resource(), shader_2d_.program);
435    gles2_if_->Uniform2f(
436        context_->pp_resource(), shader_2d_.texcoord_scale_location, 1.0, 1.0);
437  } else {
438    assert(info.texture_target == GL_TEXTURE_RECTANGLE_ARB);
439    CreateRectangleARBProgramOnce();
440    gles2_if_->UseProgram(
441        context_->pp_resource(), shader_rectangle_arb_.program);
442    gles2_if_->Uniform2f(context_->pp_resource(),
443                         shader_rectangle_arb_.texcoord_scale_location,
444                         info.buffer.size.width,
445                         info.buffer.size.height);
446  }
447
448  gles2_if_->Viewport(context_->pp_resource(), x, y,
449                      plugin_size_.width() / kNumDecoders,
450                      plugin_size_.height() / kNumDecoders);
451  gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
452  gles2_if_->BindTexture(
453      context_->pp_resource(), info.texture_target, info.buffer.texture_id);
454  gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4);
455
456  gles2_if_->UseProgram(context_->pp_resource(), 0);
457
458  pp::CompletionCallback cb =
459      callback_factory_.NewCallback(
460          &VideoDecodeDemoInstance::PaintFinished, decoder, info.buffer.id);
461  last_swap_request_ticks_ = core_if_->GetTimeTicks();
462  assert(context_->SwapBuffers(cb) == PP_OK_COMPLETIONPENDING);
463}
464
465void VideoDecodeDemoInstance::NotifyError(PP_Resource decoder,
466                                          PP_VideoDecodeError_Dev error) {
467  LogError(this).s() << "Received error: " << error;
468  assert(false && "Unexpected error; see stderr for details");
469}
470
471// This object is the global object representing this plugin library as long
472// as it is loaded.
473class VideoDecodeDemoModule : public pp::Module {
474 public:
475  VideoDecodeDemoModule() : pp::Module() {}
476  virtual ~VideoDecodeDemoModule() {}
477
478  virtual pp::Instance* CreateInstance(PP_Instance instance) {
479    return new VideoDecodeDemoInstance(instance, this);
480  }
481};
482
483void VideoDecodeDemoInstance::InitGL() {
484  assert(plugin_size_.width() && plugin_size_.height());
485  is_painting_ = false;
486
487  assert(!context_);
488  int32_t context_attributes[] = {
489    PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
490    PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
491    PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
492    PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
493    PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
494    PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
495    PP_GRAPHICS3DATTRIB_SAMPLES, 0,
496    PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
497    PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
498    PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
499    PP_GRAPHICS3DATTRIB_NONE,
500  };
501  context_ = new pp::Graphics3D(this, context_attributes);
502  assert(!context_->is_null());
503  assert(BindGraphics(*context_));
504
505  // Clear color bit.
506  gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
507  gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
508
509  assertNoGLError();
510
511  CreateGLObjects();
512}
513
514void VideoDecodeDemoInstance::PaintFinished(int32_t result, PP_Resource decoder,
515                                      int picture_buffer_id) {
516  assert(result == PP_OK);
517  swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
518  is_painting_ = false;
519  ++num_frames_rendered_;
520  if (num_frames_rendered_ % 50 == 0) {
521    double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
522    double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
523    double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
524    LogError(this).s() << "Rendered frames: " << num_frames_rendered_
525                       << ", fps: " << fps << ", with average ms/swap of: "
526                       << ms_per_swap;
527  }
528  DecoderClient* client = video_decoders_[decoder];
529  if (client && client->decoder())
530    client->decoder()->ReusePictureBuffer(picture_buffer_id);
531  if (!pictures_pending_paint_.empty()) {
532    std::pair<PP_Resource, PP_Picture_Dev> decoder_picture =
533        pictures_pending_paint_.front();
534    pictures_pending_paint_.pop_front();
535    PictureReady(decoder_picture.first, decoder_picture.second);
536  }
537}
538
539GLuint VideoDecodeDemoInstance::CreateTexture(int32_t width,
540                                              int32_t height,
541                                              GLenum texture_target) {
542  GLuint texture_id;
543  gles2_if_->GenTextures(context_->pp_resource(), 1, &texture_id);
544  assertNoGLError();
545  // Assign parameters.
546  gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
547  gles2_if_->BindTexture(context_->pp_resource(), texture_target, texture_id);
548  gles2_if_->TexParameteri(
549      context_->pp_resource(), texture_target, GL_TEXTURE_MIN_FILTER,
550      GL_NEAREST);
551  gles2_if_->TexParameteri(
552      context_->pp_resource(), texture_target, GL_TEXTURE_MAG_FILTER,
553      GL_NEAREST);
554  gles2_if_->TexParameterf(
555      context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_S,
556      GL_CLAMP_TO_EDGE);
557  gles2_if_->TexParameterf(
558      context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_T,
559      GL_CLAMP_TO_EDGE);
560
561  if (texture_target == GL_TEXTURE_2D) {
562    gles2_if_->TexImage2D(
563        context_->pp_resource(), texture_target, 0, GL_RGBA, width, height, 0,
564        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
565  }
566  assertNoGLError();
567  return texture_id;
568}
569
570void VideoDecodeDemoInstance::DeleteTexture(GLuint id) {
571  gles2_if_->DeleteTextures(context_->pp_resource(), 1, &id);
572}
573
574void VideoDecodeDemoInstance::CreateGLObjects() {
575  // Assign vertex positions and texture coordinates to buffers for use in
576  // shader program.
577  static const float kVertices[] = {
578    -1, 1, -1, -1, 1, 1, 1, -1,  // Position coordinates.
579    0, 1, 0, 0, 1, 1, 1, 0,      // Texture coordinates.
580  };
581
582  GLuint buffer;
583  gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
584  gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
585
586  gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER,
587                        sizeof(kVertices), kVertices, GL_STATIC_DRAW);
588  assertNoGLError();
589}
590
591static const char kVertexShader[] =
592    "varying vec2 v_texCoord;            \n"
593    "attribute vec4 a_position;          \n"
594    "attribute vec2 a_texCoord;          \n"
595    "uniform vec2 v_scale;               \n"
596    "void main()                         \n"
597    "{                                   \n"
598    "    v_texCoord = v_scale * a_texCoord; \n"
599    "    gl_Position = a_position;       \n"
600    "}";
601
602void VideoDecodeDemoInstance::Create2DProgramOnce() {
603  if (shader_2d_.program)
604    return;
605  static const char kFragmentShader2D[] =
606      "precision mediump float;            \n"
607      "varying vec2 v_texCoord;            \n"
608      "uniform sampler2D s_texture;        \n"
609      "void main()                         \n"
610      "{"
611      "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
612      "}";
613  shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
614  assertNoGLError();
615}
616
617void VideoDecodeDemoInstance::CreateRectangleARBProgramOnce() {
618  if (shader_rectangle_arb_.program)
619    return;
620  static const char kFragmentShaderRectangle[] =
621      "#extension GL_ARB_texture_rectangle : require\n"
622      "precision mediump float;            \n"
623      "varying vec2 v_texCoord;            \n"
624      "uniform sampler2DRect s_texture;    \n"
625      "void main()                         \n"
626      "{"
627      "    gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
628      "}";
629  shader_rectangle_arb_ =
630      CreateProgram(kVertexShader, kFragmentShaderRectangle);
631}
632
633Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
634                                              const char* fragment_shader) {
635  Shader shader;
636
637  // Create shader program.
638  shader.program = gles2_if_->CreateProgram(context_->pp_resource());
639  CreateShader(shader.program, GL_VERTEX_SHADER, vertex_shader,
640               strlen(vertex_shader));
641  CreateShader(shader.program, GL_FRAGMENT_SHADER, fragment_shader,
642               strlen(fragment_shader));
643  gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
644  gles2_if_->UseProgram(context_->pp_resource(), shader.program);
645  gles2_if_->Uniform1i(
646      context_->pp_resource(),
647      gles2_if_->GetUniformLocation(
648          context_->pp_resource(), shader.program, "s_texture"), 0);
649  assertNoGLError();
650
651  shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
652      context_->pp_resource(), shader.program, "v_scale");
653
654  GLint pos_location = gles2_if_->GetAttribLocation(
655      context_->pp_resource(), shader.program, "a_position");
656  GLint tc_location = gles2_if_->GetAttribLocation(
657      context_->pp_resource(), shader.program, "a_texCoord");
658  assertNoGLError();
659
660  gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
661  gles2_if_->VertexAttribPointer(context_->pp_resource(), pos_location, 2,
662                                 GL_FLOAT, GL_FALSE, 0, 0);
663  gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
664  gles2_if_->VertexAttribPointer(
665      context_->pp_resource(), tc_location, 2, GL_FLOAT, GL_FALSE, 0,
666      static_cast<float*>(0) + 8);  // Skip position coordinates.
667
668  gles2_if_->UseProgram(context_->pp_resource(), 0);
669  assertNoGLError();
670  return shader;
671}
672
673void VideoDecodeDemoInstance::CreateShader(
674    GLuint program, GLenum type, const char* source, int size) {
675  GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
676  gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
677  gles2_if_->CompileShader(context_->pp_resource(), shader);
678  gles2_if_->AttachShader(context_->pp_resource(), program, shader);
679  gles2_if_->DeleteShader(context_->pp_resource(), shader);
680}
681}  // anonymous namespace
682
683namespace pp {
684// Factory function for your specialization of the Module object.
685Module* CreateModule() {
686  return new VideoDecodeDemoModule();
687}
688}  // namespace pp
689