diff options
Diffstat (limited to 'ppapi/examples/video_decode/video_decode_dev.cc')
-rw-r--r-- | ppapi/examples/video_decode/video_decode_dev.cc | 685 |
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 |