summaryrefslogtreecommitdiffstats
path: root/ppapi/examples/video_decode/video_decode_dev.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ppapi/examples/video_decode/video_decode_dev.cc')
-rw-r--r--ppapi/examples/video_decode/video_decode_dev.cc685
1 files changed, 685 insertions, 0 deletions
diff --git a/ppapi/examples/video_decode/video_decode_dev.cc b/ppapi/examples/video_decode/video_decode_dev.cc
new file mode 100644
index 0000000..d6cb592
--- /dev/null
+++ b/ppapi/examples/video_decode/video_decode_dev.cc
@@ -0,0 +1,685 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+#include <sstream>
+#include <vector>
+
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_console.h"
+#include "ppapi/c/ppb_opengles2.h"
+#include "ppapi/cpp/dev/video_decoder_client_dev.h"
+#include "ppapi/cpp/dev/video_decoder_dev.h"
+#include "ppapi/cpp/graphics_3d.h"
+#include "ppapi/cpp/graphics_3d_client.h"
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/module.h"
+#include "ppapi/cpp/rect.h"
+#include "ppapi/cpp/var.h"
+#include "ppapi/examples/video_decode/testdata.h"
+#include "ppapi/lib/gl/include/GLES2/gl2.h"
+#include "ppapi/lib/gl/include/GLES2/gl2ext.h"
+#include "ppapi/utility/completion_callback_factory.h"
+
+// Use assert as a poor-man's CHECK, even in non-debug mode.
+// Since <assert.h> redefines assert on every inclusion (it doesn't use
+// include-guards), make sure this is the last file #include'd in this file.
+#undef NDEBUG
+#include <assert.h>
+
+// Assert |context_| isn't holding any GL Errors. Done as a macro instead of a
+// function to preserve line number information in the failure message.
+#define assertNoGLError() \
+ assert(!gles2_if_->GetError(context_->pp_resource()));
+
+namespace {
+
+struct PictureBufferInfo {
+ PP_PictureBuffer_Dev buffer;
+ GLenum texture_target;
+};
+
+struct Shader {
+ Shader() : program(0),
+ texcoord_scale_location(0) {}
+
+ GLuint program;
+ GLint texcoord_scale_location;
+};
+
+class VideoDecodeDemoInstance : public pp::Instance,
+ public pp::Graphics3DClient,
+ public pp::VideoDecoderClient_Dev {
+ public:
+ VideoDecodeDemoInstance(PP_Instance instance, pp::Module* module);
+ virtual ~VideoDecodeDemoInstance();
+
+ // pp::Instance implementation (see PPP_Instance).
+ virtual void DidChangeView(const pp::Rect& position,
+ const pp::Rect& clip_ignored);
+
+ // pp::Graphics3DClient implementation.
+ virtual void Graphics3DContextLost() {
+ // TODO(vrk/fischman): Properly reset after a lost graphics context. In
+ // particular need to delete context_ and re-create textures.
+ // Probably have to recreate the decoder from scratch, because old textures
+ // can still be outstanding in the decoder!
+ assert(false && "Unexpectedly lost graphics context");
+ }
+
+ // pp::VideoDecoderClient_Dev implementation.
+ virtual void ProvidePictureBuffers(
+ PP_Resource decoder,
+ uint32_t req_num_of_bufs,
+ const PP_Size& dimensions,
+ uint32_t texture_target);
+ virtual void DismissPictureBuffer(PP_Resource decoder,
+ int32_t picture_buffer_id);
+ virtual void PictureReady(PP_Resource decoder, const PP_Picture_Dev& picture);
+ virtual void NotifyError(PP_Resource decoder, PP_VideoDecodeError_Dev error);
+
+ private:
+ enum { kNumConcurrentDecodes = 7,
+ kNumDecoders = 2 }; // Baked into viewport rendering.
+
+ // A single decoder's client interface.
+ class DecoderClient {
+ public:
+ DecoderClient(VideoDecodeDemoInstance* gles2,
+ pp::VideoDecoder_Dev* decoder);
+ ~DecoderClient();
+
+ void DecodeNextNALUs();
+
+ // Per-decoder implementation of part of pp::VideoDecoderClient_Dev.
+ void ProvidePictureBuffers(
+ uint32_t req_num_of_bufs,
+ PP_Size dimensions,
+ uint32_t texture_target);
+ void DismissPictureBuffer(int32_t picture_buffer_id);
+
+ const PictureBufferInfo& GetPictureBufferInfoById(int id);
+ pp::VideoDecoder_Dev* decoder() { return decoder_; }
+
+ private:
+ void DecodeNextNALU();
+ static void GetNextNALUBoundary(size_t start_pos, size_t* end_pos);
+ void DecoderBitstreamDone(int32_t result, int bitstream_buffer_id);
+ void DecoderFlushDone(int32_t result);
+
+ VideoDecodeDemoInstance* gles2_;
+ pp::VideoDecoder_Dev* decoder_;
+ pp::CompletionCallbackFactory<DecoderClient> callback_factory_;
+ int next_picture_buffer_id_;
+ int next_bitstream_buffer_id_;
+ size_t encoded_data_next_pos_to_decode_;
+ std::set<int> bitstream_ids_at_decoder_;
+ // Map of texture buffers indexed by buffer id.
+ typedef std::map<int, PictureBufferInfo> PictureBufferMap;
+ PictureBufferMap picture_buffers_by_id_;
+ // Map of bitstream buffers indexed by id.
+ typedef std::map<int, pp::Buffer_Dev*> BitstreamBufferMap;
+ BitstreamBufferMap bitstream_buffers_by_id_;
+ };
+
+ // Initialize Video Decoders.
+ void InitializeDecoders();
+
+ // GL-related functions.
+ void InitGL();
+ GLuint CreateTexture(int32_t width, int32_t height, GLenum texture_target);
+ void CreateGLObjects();
+ void Create2DProgramOnce();
+ void CreateRectangleARBProgramOnce();
+ Shader CreateProgram(const char* vertex_shader,
+ const char* fragment_shader);
+ void CreateShader(GLuint program, GLenum type, const char* source, int size);
+ void DeleteTexture(GLuint id);
+ void PaintFinished(int32_t result, PP_Resource decoder,
+ int picture_buffer_id);
+
+ // Log an error to the developer console and stderr (though the latter may be
+ // closed due to sandboxing or blackholed for other reasons) by creating a
+ // temporary of this type and streaming to it. Example usage:
+ // LogError(this).s() << "Hello world: " << 42;
+ class LogError {
+ public:
+ LogError(VideoDecodeDemoInstance* demo) : demo_(demo) {}
+ ~LogError() {
+ const std::string& msg = stream_.str();
+ demo_->console_if_->Log(demo_->pp_instance(), PP_LOGLEVEL_ERROR,
+ pp::Var(msg).pp_var());
+ std::cerr << msg << std::endl;
+ }
+ // Impl note: it would have been nicer to have LogError derive from
+ // std::ostringstream so that it can be streamed to directly, but lookup
+ // rules turn streamed string literals to hex pointers on output.
+ std::ostringstream& s() { return stream_; }
+ private:
+ VideoDecodeDemoInstance* demo_; // Unowned.
+ std::ostringstream stream_;
+ };
+
+ pp::Size plugin_size_;
+ bool is_painting_;
+ // When decode outpaces render, we queue up decoded pictures for later
+ // painting. Elements are <decoder,picture>.
+ std::list<std::pair<PP_Resource, PP_Picture_Dev> > pictures_pending_paint_;
+ int num_frames_rendered_;
+ PP_TimeTicks first_frame_delivered_ticks_;
+ PP_TimeTicks last_swap_request_ticks_;
+ PP_TimeTicks swap_ticks_;
+ pp::CompletionCallbackFactory<VideoDecodeDemoInstance> callback_factory_;
+
+ // Unowned pointers.
+ const PPB_Console* console_if_;
+ const PPB_Core* core_if_;
+ const PPB_OpenGLES2* gles2_if_;
+
+ // Owned data.
+ pp::Graphics3D* context_;
+ typedef std::map<int, DecoderClient*> Decoders;
+ Decoders video_decoders_;
+
+ // Shader program to draw GL_TEXTURE_2D target.
+ Shader shader_2d_;
+ // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
+ Shader shader_rectangle_arb_;
+};
+
+VideoDecodeDemoInstance::DecoderClient::DecoderClient(
+ VideoDecodeDemoInstance* gles2, pp::VideoDecoder_Dev* decoder)
+ : gles2_(gles2), decoder_(decoder), callback_factory_(this),
+ next_picture_buffer_id_(0),
+ next_bitstream_buffer_id_(0), encoded_data_next_pos_to_decode_(0) {
+}
+
+VideoDecodeDemoInstance::DecoderClient::~DecoderClient() {
+ delete decoder_;
+ decoder_ = NULL;
+
+ for (BitstreamBufferMap::iterator it = bitstream_buffers_by_id_.begin();
+ it != bitstream_buffers_by_id_.end(); ++it) {
+ delete it->second;
+ }
+ bitstream_buffers_by_id_.clear();
+
+ for (PictureBufferMap::iterator it = picture_buffers_by_id_.begin();
+ it != picture_buffers_by_id_.end(); ++it) {
+ gles2_->DeleteTexture(it->second.buffer.texture_id);
+ }
+ picture_buffers_by_id_.clear();
+}
+
+VideoDecodeDemoInstance::VideoDecodeDemoInstance(PP_Instance instance,
+ pp::Module* module)
+ : pp::Instance(instance), pp::Graphics3DClient(this),
+ pp::VideoDecoderClient_Dev(this),
+ is_painting_(false),
+ num_frames_rendered_(0),
+ first_frame_delivered_ticks_(-1),
+ swap_ticks_(0),
+ callback_factory_(this),
+ context_(NULL) {
+ assert((console_if_ = static_cast<const PPB_Console*>(
+ module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))));
+ assert((core_if_ = static_cast<const PPB_Core*>(
+ module->GetBrowserInterface(PPB_CORE_INTERFACE))));
+ assert((gles2_if_ = static_cast<const PPB_OpenGLES2*>(
+ module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))));
+}
+
+VideoDecodeDemoInstance::~VideoDecodeDemoInstance() {
+ if (shader_2d_.program)
+ gles2_if_->DeleteProgram(context_->pp_resource(), shader_2d_.program);
+ if (shader_rectangle_arb_.program) {
+ gles2_if_->DeleteProgram(
+ context_->pp_resource(), shader_rectangle_arb_.program);
+ }
+
+ for (Decoders::iterator it = video_decoders_.begin();
+ it != video_decoders_.end(); ++it) {
+ delete it->second;
+ }
+ video_decoders_.clear();
+ delete context_;
+}
+
+void VideoDecodeDemoInstance::DidChangeView(
+ const pp::Rect& position, const pp::Rect& clip_ignored) {
+ if (position.width() == 0 || position.height() == 0)
+ return;
+ if (plugin_size_.width()) {
+ assert(position.size() == plugin_size_);
+ return;
+ }
+ plugin_size_ = position.size();
+
+ // Initialize graphics.
+ InitGL();
+ InitializeDecoders();
+}
+
+void VideoDecodeDemoInstance::InitializeDecoders() {
+ assert(video_decoders_.empty());
+ for (int i = 0; i < kNumDecoders; ++i) {
+ DecoderClient* client = new DecoderClient(
+ this, new pp::VideoDecoder_Dev(
+ this, *context_, PP_VIDEODECODER_H264PROFILE_MAIN));
+ assert(!client->decoder()->is_null());
+ assert(video_decoders_.insert(std::make_pair(
+ client->decoder()->pp_resource(), client)).second);
+ client->DecodeNextNALUs();
+ }
+}
+
+void VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone(
+ int32_t result, int bitstream_buffer_id) {
+ assert(bitstream_ids_at_decoder_.erase(bitstream_buffer_id) == 1);
+ BitstreamBufferMap::iterator it =
+ bitstream_buffers_by_id_.find(bitstream_buffer_id);
+ assert(it != bitstream_buffers_by_id_.end());
+ delete it->second;
+ bitstream_buffers_by_id_.erase(it);
+ DecodeNextNALUs();
+}
+
+void VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone(int32_t result) {
+ assert(result == PP_OK);
+ // Check that each bitstream buffer ID we handed to the decoder got handed
+ // back to us.
+ assert(bitstream_ids_at_decoder_.empty());
+ delete decoder_;
+ decoder_ = NULL;
+}
+
+static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
+ return pos + 3 < kDataLen &&
+ encoded[pos] == 0 && encoded[pos + 1] == 0 &&
+ encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
+}
+
+void VideoDecodeDemoInstance::DecoderClient::GetNextNALUBoundary(
+ size_t start_pos, size_t* end_pos) {
+ assert(LookingAtNAL(kData, start_pos));
+ *end_pos = start_pos;
+ *end_pos += 4;
+ while (*end_pos + 3 < kDataLen &&
+ !LookingAtNAL(kData, *end_pos)) {
+ ++*end_pos;
+ }
+ if (*end_pos + 3 >= kDataLen) {
+ *end_pos = kDataLen;
+ return;
+ }
+}
+
+void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALUs() {
+ while (encoded_data_next_pos_to_decode_ <= kDataLen &&
+ bitstream_ids_at_decoder_.size() < kNumConcurrentDecodes) {
+ DecodeNextNALU();
+ }
+}
+
+void VideoDecodeDemoInstance::DecoderClient::DecodeNextNALU() {
+ if (encoded_data_next_pos_to_decode_ == kDataLen) {
+ ++encoded_data_next_pos_to_decode_;
+ pp::CompletionCallback cb = callback_factory_.NewCallback(
+ &VideoDecodeDemoInstance::DecoderClient::DecoderFlushDone);
+ decoder_->Flush(cb);
+ return;
+ }
+ size_t start_pos = encoded_data_next_pos_to_decode_;
+ size_t end_pos;
+ GetNextNALUBoundary(start_pos, &end_pos);
+ pp::Buffer_Dev* buffer = new pp::Buffer_Dev(gles2_, end_pos - start_pos);
+ PP_VideoBitstreamBuffer_Dev bitstream_buffer;
+ int id = ++next_bitstream_buffer_id_;
+ bitstream_buffer.id = id;
+ bitstream_buffer.size = end_pos - start_pos;
+ bitstream_buffer.data = buffer->pp_resource();
+ memcpy(buffer->data(), kData + start_pos, end_pos - start_pos);
+ assert(bitstream_buffers_by_id_.insert(std::make_pair(id, buffer)).second);
+
+ pp::CompletionCallback cb =
+ callback_factory_.NewCallback(
+ &VideoDecodeDemoInstance::DecoderClient::DecoderBitstreamDone, id);
+ assert(bitstream_ids_at_decoder_.insert(id).second);
+ encoded_data_next_pos_to_decode_ = end_pos;
+ decoder_->Decode(bitstream_buffer, cb);
+}
+
+void VideoDecodeDemoInstance::ProvidePictureBuffers(PP_Resource decoder,
+ uint32_t req_num_of_bufs,
+ const PP_Size& dimensions,
+ uint32_t texture_target) {
+ DecoderClient* client = video_decoders_[decoder];
+ assert(client);
+ client->ProvidePictureBuffers(req_num_of_bufs, dimensions, texture_target);
+}
+
+void VideoDecodeDemoInstance::DecoderClient::ProvidePictureBuffers(
+ uint32_t req_num_of_bufs,
+ PP_Size dimensions,
+ uint32_t texture_target) {
+ std::vector<PP_PictureBuffer_Dev> buffers;
+ for (uint32_t i = 0; i < req_num_of_bufs; ++i) {
+ PictureBufferInfo info;
+ info.buffer.size = dimensions;
+ info.texture_target = texture_target;
+ info.buffer.texture_id = gles2_->CreateTexture(
+ dimensions.width, dimensions.height, info.texture_target);
+ int id = ++next_picture_buffer_id_;
+ info.buffer.id = id;
+ buffers.push_back(info.buffer);
+ assert(picture_buffers_by_id_.insert(std::make_pair(id, info)).second);
+ }
+ decoder_->AssignPictureBuffers(buffers);
+}
+
+const PictureBufferInfo&
+VideoDecodeDemoInstance::DecoderClient::GetPictureBufferInfoById(
+ int id) {
+ PictureBufferMap::iterator it = picture_buffers_by_id_.find(id);
+ assert(it != picture_buffers_by_id_.end());
+ return it->second;
+}
+
+void VideoDecodeDemoInstance::DismissPictureBuffer(PP_Resource decoder,
+ int32_t picture_buffer_id) {
+ DecoderClient* client = video_decoders_[decoder];
+ assert(client);
+ client->DismissPictureBuffer(picture_buffer_id);
+}
+
+void VideoDecodeDemoInstance::DecoderClient::DismissPictureBuffer(
+ int32_t picture_buffer_id) {
+ gles2_->DeleteTexture(GetPictureBufferInfoById(
+ picture_buffer_id).buffer.texture_id);
+ picture_buffers_by_id_.erase(picture_buffer_id);
+}
+
+void VideoDecodeDemoInstance::PictureReady(PP_Resource decoder,
+ const PP_Picture_Dev& picture) {
+ if (first_frame_delivered_ticks_ == -1)
+ assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
+ if (is_painting_) {
+ pictures_pending_paint_.push_back(std::make_pair(decoder, picture));
+ return;
+ }
+ DecoderClient* client = video_decoders_[decoder];
+ assert(client);
+ const PictureBufferInfo& info =
+ client->GetPictureBufferInfoById(picture.picture_buffer_id);
+ assert(!is_painting_);
+ is_painting_ = true;
+ int x = 0;
+ int y = 0;
+ if (client != video_decoders_.begin()->second) {
+ x = plugin_size_.width() / kNumDecoders;
+ y = plugin_size_.height() / kNumDecoders;
+ }
+
+ if (info.texture_target == GL_TEXTURE_2D) {
+ Create2DProgramOnce();
+ gles2_if_->UseProgram(context_->pp_resource(), shader_2d_.program);
+ gles2_if_->Uniform2f(
+ context_->pp_resource(), shader_2d_.texcoord_scale_location, 1.0, 1.0);
+ } else {
+ assert(info.texture_target == GL_TEXTURE_RECTANGLE_ARB);
+ CreateRectangleARBProgramOnce();
+ gles2_if_->UseProgram(
+ context_->pp_resource(), shader_rectangle_arb_.program);
+ gles2_if_->Uniform2f(context_->pp_resource(),
+ shader_rectangle_arb_.texcoord_scale_location,
+ info.buffer.size.width,
+ info.buffer.size.height);
+ }
+
+ gles2_if_->Viewport(context_->pp_resource(), x, y,
+ plugin_size_.width() / kNumDecoders,
+ plugin_size_.height() / kNumDecoders);
+ gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
+ gles2_if_->BindTexture(
+ context_->pp_resource(), info.texture_target, info.buffer.texture_id);
+ gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4);
+
+ gles2_if_->UseProgram(context_->pp_resource(), 0);
+
+ pp::CompletionCallback cb =
+ callback_factory_.NewCallback(
+ &VideoDecodeDemoInstance::PaintFinished, decoder, info.buffer.id);
+ last_swap_request_ticks_ = core_if_->GetTimeTicks();
+ assert(context_->SwapBuffers(cb) == PP_OK_COMPLETIONPENDING);
+}
+
+void VideoDecodeDemoInstance::NotifyError(PP_Resource decoder,
+ PP_VideoDecodeError_Dev error) {
+ LogError(this).s() << "Received error: " << error;
+ assert(false && "Unexpected error; see stderr for details");
+}
+
+// This object is the global object representing this plugin library as long
+// as it is loaded.
+class VideoDecodeDemoModule : public pp::Module {
+ public:
+ VideoDecodeDemoModule() : pp::Module() {}
+ virtual ~VideoDecodeDemoModule() {}
+
+ virtual pp::Instance* CreateInstance(PP_Instance instance) {
+ return new VideoDecodeDemoInstance(instance, this);
+ }
+};
+
+void VideoDecodeDemoInstance::InitGL() {
+ assert(plugin_size_.width() && plugin_size_.height());
+ is_painting_ = false;
+
+ assert(!context_);
+ int32_t context_attributes[] = {
+ PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
+ PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
+ PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
+ PP_GRAPHICS3DATTRIB_SAMPLES, 0,
+ PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
+ PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
+ PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
+ PP_GRAPHICS3DATTRIB_NONE,
+ };
+ context_ = new pp::Graphics3D(this, context_attributes);
+ assert(!context_->is_null());
+ assert(BindGraphics(*context_));
+
+ // Clear color bit.
+ gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
+ gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
+
+ assertNoGLError();
+
+ CreateGLObjects();
+}
+
+void VideoDecodeDemoInstance::PaintFinished(int32_t result, PP_Resource decoder,
+ int picture_buffer_id) {
+ assert(result == PP_OK);
+ swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
+ is_painting_ = false;
+ ++num_frames_rendered_;
+ if (num_frames_rendered_ % 50 == 0) {
+ double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
+ double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
+ double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
+ LogError(this).s() << "Rendered frames: " << num_frames_rendered_
+ << ", fps: " << fps << ", with average ms/swap of: "
+ << ms_per_swap;
+ }
+ DecoderClient* client = video_decoders_[decoder];
+ if (client && client->decoder())
+ client->decoder()->ReusePictureBuffer(picture_buffer_id);
+ if (!pictures_pending_paint_.empty()) {
+ std::pair<PP_Resource, PP_Picture_Dev> decoder_picture =
+ pictures_pending_paint_.front();
+ pictures_pending_paint_.pop_front();
+ PictureReady(decoder_picture.first, decoder_picture.second);
+ }
+}
+
+GLuint VideoDecodeDemoInstance::CreateTexture(int32_t width,
+ int32_t height,
+ GLenum texture_target) {
+ GLuint texture_id;
+ gles2_if_->GenTextures(context_->pp_resource(), 1, &texture_id);
+ assertNoGLError();
+ // Assign parameters.
+ gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
+ gles2_if_->BindTexture(context_->pp_resource(), texture_target, texture_id);
+ gles2_if_->TexParameteri(
+ context_->pp_resource(), texture_target, GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ gles2_if_->TexParameteri(
+ context_->pp_resource(), texture_target, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ gles2_if_->TexParameterf(
+ context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE);
+ gles2_if_->TexParameterf(
+ context_->pp_resource(), texture_target, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_EDGE);
+
+ if (texture_target == GL_TEXTURE_2D) {
+ gles2_if_->TexImage2D(
+ context_->pp_resource(), texture_target, 0, GL_RGBA, width, height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ }
+ assertNoGLError();
+ return texture_id;
+}
+
+void VideoDecodeDemoInstance::DeleteTexture(GLuint id) {
+ gles2_if_->DeleteTextures(context_->pp_resource(), 1, &id);
+}
+
+void VideoDecodeDemoInstance::CreateGLObjects() {
+ // Assign vertex positions and texture coordinates to buffers for use in
+ // shader program.
+ static const float kVertices[] = {
+ -1, 1, -1, -1, 1, 1, 1, -1, // Position coordinates.
+ 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
+ };
+
+ GLuint buffer;
+ gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
+ gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
+
+ gles2_if_->BufferData(context_->pp_resource(), GL_ARRAY_BUFFER,
+ sizeof(kVertices), kVertices, GL_STATIC_DRAW);
+ assertNoGLError();
+}
+
+static const char kVertexShader[] =
+ "varying vec2 v_texCoord; \n"
+ "attribute vec4 a_position; \n"
+ "attribute vec2 a_texCoord; \n"
+ "uniform vec2 v_scale; \n"
+ "void main() \n"
+ "{ \n"
+ " v_texCoord = v_scale * a_texCoord; \n"
+ " gl_Position = a_position; \n"
+ "}";
+
+void VideoDecodeDemoInstance::Create2DProgramOnce() {
+ if (shader_2d_.program)
+ return;
+ static const char kFragmentShader2D[] =
+ "precision mediump float; \n"
+ "varying vec2 v_texCoord; \n"
+ "uniform sampler2D s_texture; \n"
+ "void main() \n"
+ "{"
+ " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
+ "}";
+ shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
+ assertNoGLError();
+}
+
+void VideoDecodeDemoInstance::CreateRectangleARBProgramOnce() {
+ if (shader_rectangle_arb_.program)
+ return;
+ static const char kFragmentShaderRectangle[] =
+ "#extension GL_ARB_texture_rectangle : require\n"
+ "precision mediump float; \n"
+ "varying vec2 v_texCoord; \n"
+ "uniform sampler2DRect s_texture; \n"
+ "void main() \n"
+ "{"
+ " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
+ "}";
+ shader_rectangle_arb_ =
+ CreateProgram(kVertexShader, kFragmentShaderRectangle);
+}
+
+Shader VideoDecodeDemoInstance::CreateProgram(const char* vertex_shader,
+ const char* fragment_shader) {
+ Shader shader;
+
+ // Create shader program.
+ shader.program = gles2_if_->CreateProgram(context_->pp_resource());
+ CreateShader(shader.program, GL_VERTEX_SHADER, vertex_shader,
+ strlen(vertex_shader));
+ CreateShader(shader.program, GL_FRAGMENT_SHADER, fragment_shader,
+ strlen(fragment_shader));
+ gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
+ gles2_if_->UseProgram(context_->pp_resource(), shader.program);
+ gles2_if_->Uniform1i(
+ context_->pp_resource(),
+ gles2_if_->GetUniformLocation(
+ context_->pp_resource(), shader.program, "s_texture"), 0);
+ assertNoGLError();
+
+ shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
+ context_->pp_resource(), shader.program, "v_scale");
+
+ GLint pos_location = gles2_if_->GetAttribLocation(
+ context_->pp_resource(), shader.program, "a_position");
+ GLint tc_location = gles2_if_->GetAttribLocation(
+ context_->pp_resource(), shader.program, "a_texCoord");
+ assertNoGLError();
+
+ gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
+ gles2_if_->VertexAttribPointer(context_->pp_resource(), pos_location, 2,
+ GL_FLOAT, GL_FALSE, 0, 0);
+ gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
+ gles2_if_->VertexAttribPointer(
+ context_->pp_resource(), tc_location, 2, GL_FLOAT, GL_FALSE, 0,
+ static_cast<float*>(0) + 8); // Skip position coordinates.
+
+ gles2_if_->UseProgram(context_->pp_resource(), 0);
+ assertNoGLError();
+ return shader;
+}
+
+void VideoDecodeDemoInstance::CreateShader(
+ GLuint program, GLenum type, const char* source, int size) {
+ GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
+ gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
+ gles2_if_->CompileShader(context_->pp_resource(), shader);
+ gles2_if_->AttachShader(context_->pp_resource(), program, shader);
+ gles2_if_->DeleteShader(context_->pp_resource(), shader);
+}
+} // anonymous namespace
+
+namespace pp {
+// Factory function for your specialization of the Module object.
+Module* CreateModule() {
+ return new VideoDecodeDemoModule();
+}
+} // namespace pp