video_decode.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright (c) 2014 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 <GLES2/gl2.h> 6#include <GLES2/gl2ext.h> 7#include <stdio.h> 8#include <string.h> 9 10#include <iostream> 11#include <queue> 12#include <sstream> 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/graphics_3d.h" 18#include "ppapi/cpp/graphics_3d_client.h" 19#include "ppapi/cpp/input_event.h" 20#include "ppapi/cpp/instance.h" 21#include "ppapi/cpp/module.h" 22#include "ppapi/cpp/rect.h" 23#include "ppapi/cpp/var.h" 24#include "ppapi/cpp/video_decoder.h" 25#include "ppapi/utility/completion_callback_factory.h" 26 27// VP8 is more likely to work on different versions of Chrome. Undefine this 28// to decode H264. 29#define USE_VP8_TESTDATA_INSTEAD_OF_H264 30#include "testdata.h" 31 32// Use assert as a poor-man's CHECK, even in non-debug mode. 33// Since <assert.h> redefines assert on every inclusion (it doesn't use 34// include-guards), make sure this is the last file #include'd in this file. 35#undef NDEBUG 36#include <assert.h> 37 38// Assert |context_| isn't holding any GL Errors. Done as a macro instead of a 39// function to preserve line number information in the failure message. 40#define assertNoGLError() assert(!gles2_if_->GetError(context_->pp_resource())); 41 42namespace { 43 44struct Shader { 45 Shader() : program(0), texcoord_scale_location(0) {} 46 ~Shader() {} 47 48 GLuint program; 49 GLint texcoord_scale_location; 50}; 51 52class Decoder; 53class MyInstance; 54 55struct PendingPicture { 56 PendingPicture(Decoder* decoder, const PP_VideoPicture& picture) 57 : decoder(decoder), picture(picture) {} 58 ~PendingPicture() {} 59 60 Decoder* decoder; 61 PP_VideoPicture picture; 62}; 63 64class MyInstance : public pp::Instance, public pp::Graphics3DClient { 65 public: 66 MyInstance(PP_Instance instance, pp::Module* module); 67 virtual ~MyInstance(); 68 69 // pp::Instance implementation. 70 virtual void DidChangeView(const pp::Rect& position, 71 const pp::Rect& clip_ignored); 72 virtual bool HandleInputEvent(const pp::InputEvent& event); 73 74 // pp::Graphics3DClient implementation. 75 virtual void Graphics3DContextLost() { 76 // TODO(vrk/fischman): Properly reset after a lost graphics context. In 77 // particular need to delete context_ and re-create textures. 78 // Probably have to recreate the decoder from scratch, because old textures 79 // can still be outstanding in the decoder! 80 assert(false && "Unexpectedly lost graphics context"); 81 } 82 83 void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture); 84 85 private: 86 // Log an error to the developer console and stderr by creating a temporary 87 // object of this type and streaming to it. Example usage: 88 // LogError(this).s() << "Hello world: " << 42; 89 class LogError { 90 public: 91 LogError(MyInstance* instance) : instance_(instance) {} 92 ~LogError() { 93 const std::string& msg = stream_.str(); 94 instance_->console_if_->Log( 95 instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var()); 96 std::cerr << msg << std::endl; 97 } 98 // Impl note: it would have been nicer to have LogError derive from 99 // std::ostringstream so that it can be streamed to directly, but lookup 100 // rules turn streamed string literals to hex pointers on output. 101 std::ostringstream& s() { return stream_; } 102 103 private: 104 MyInstance* instance_; 105 std::ostringstream stream_; 106 }; 107 108 void InitializeDecoders(); 109 110 // GL-related functions. 111 void InitGL(); 112 void CreateGLObjects(); 113 void Create2DProgramOnce(); 114 void CreateRectangleARBProgramOnce(); 115 void CreateExternalOESProgramOnce(); 116 Shader CreateProgram(const char* vertex_shader, const char* fragment_shader); 117 void CreateShader(GLuint program, GLenum type, const char* source, int size); 118 void PaintNextPicture(); 119 void PaintFinished(int32_t result); 120 121 pp::Size plugin_size_; 122 bool is_painting_; 123 // When decode outpaces render, we queue up decoded pictures for later 124 // painting. 125 typedef std::queue<PendingPicture> PendingPictureQueue; 126 PendingPictureQueue pending_pictures_; 127 128 int num_frames_rendered_; 129 PP_TimeTicks first_frame_delivered_ticks_; 130 PP_TimeTicks last_swap_request_ticks_; 131 PP_TimeTicks swap_ticks_; 132 pp::CompletionCallbackFactory<MyInstance> callback_factory_; 133 134 // Unowned pointers. 135 const PPB_Console* console_if_; 136 const PPB_Core* core_if_; 137 const PPB_OpenGLES2* gles2_if_; 138 139 // Owned data. 140 pp::Graphics3D* context_; 141 typedef std::vector<Decoder*> DecoderList; 142 DecoderList video_decoders_; 143 144 // Shader program to draw GL_TEXTURE_2D target. 145 Shader shader_2d_; 146 // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target. 147 Shader shader_rectangle_arb_; 148 // Shader program to draw GL_TEXTURE_EXTERNAL_OES target. 149 Shader shader_external_oes_; 150}; 151 152class Decoder { 153 public: 154 Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d); 155 ~Decoder(); 156 157 int id() const { return id_; } 158 bool flushing() const { return flushing_; } 159 bool resetting() const { return resetting_; } 160 161 void Reset(); 162 void RecyclePicture(const PP_VideoPicture& picture); 163 164 PP_TimeTicks GetAverageLatency() { 165 return num_pictures_ ? total_latency_ / num_pictures_ : 0; 166 } 167 168 private: 169 void InitializeDone(int32_t result); 170 void Start(); 171 void DecodeNextFrame(); 172 void DecodeDone(int32_t result); 173 void PictureReady(int32_t result, PP_VideoPicture picture); 174 void FlushDone(int32_t result); 175 void ResetDone(int32_t result); 176 177 MyInstance* instance_; 178 int id_; 179 180 pp::VideoDecoder* decoder_; 181 pp::CompletionCallbackFactory<Decoder> callback_factory_; 182 183 size_t encoded_data_next_pos_to_decode_; 184 int next_picture_id_; 185 bool flushing_; 186 bool resetting_; 187 188 const PPB_Core* core_if_; 189 static const int kMaxDecodeDelay = 128; 190 PP_TimeTicks decode_time_[kMaxDecodeDelay]; 191 PP_TimeTicks total_latency_; 192 int num_pictures_; 193}; 194 195#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264 196 197// VP8 is stored in an IVF container. 198// Helpful description: http://wiki.multimedia.cx/index.php?title=IVF 199 200static void GetNextFrame(size_t* start_pos, size_t* end_pos) { 201 size_t current_pos = *start_pos; 202 if (current_pos == 0) 203 current_pos = 32; // Skip stream header. 204 uint32_t frame_size = kData[current_pos] + (kData[current_pos + 1] << 8) + 205 (kData[current_pos + 2] << 16) + 206 (kData[current_pos + 3] << 24); 207 current_pos += 12; // Skip frame header. 208 *start_pos = current_pos; 209 *end_pos = current_pos + frame_size; 210} 211 212#else // !USE_VP8_TESTDATA_INSTEAD_OF_H264 213 214// Returns true if the current position is at the start of a NAL unit. 215static bool LookingAtNAL(const unsigned char* encoded, size_t pos) { 216 // H264 frames start with 0, 0, 0, 1 in our test data. 217 return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 && 218 encoded[pos + 2] == 0 && encoded[pos + 3] == 1; 219} 220 221static void GetNextFrame(size_t* start_pos, size_t* end_pos) { 222 assert(LookingAtNAL(kData, *start_pos)); 223 *end_pos = *start_pos; 224 *end_pos += 4; 225 while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) { 226 ++*end_pos; 227 } 228} 229 230#endif // USE_VP8_TESTDATA_INSTEAD_OF_H264 231 232Decoder::Decoder(MyInstance* instance, 233 int id, 234 const pp::Graphics3D& graphics_3d) 235 : instance_(instance), 236 id_(id), 237 decoder_(new pp::VideoDecoder(instance)), 238 callback_factory_(this), 239 encoded_data_next_pos_to_decode_(0), 240 next_picture_id_(0), 241 flushing_(false), 242 resetting_(false), 243 total_latency_(0.0), 244 num_pictures_(0) { 245 core_if_ = static_cast<const PPB_Core*>( 246 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); 247 248#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264 249 const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8MAIN; 250#else 251 const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN; 252#endif 253 254 assert(!decoder_->is_null()); 255 decoder_->Initialize(graphics_3d, 256 kBitstreamProfile, 257 PP_TRUE /* allow_software_fallback */, 258 callback_factory_.NewCallback(&Decoder::InitializeDone)); 259} 260 261Decoder::~Decoder() { 262 delete decoder_; 263} 264 265void Decoder::InitializeDone(int32_t result) { 266 assert(decoder_); 267 assert(result == PP_OK); 268 Start(); 269} 270 271void Decoder::Start() { 272 assert(decoder_); 273 274 encoded_data_next_pos_to_decode_ = 0; 275 276 // Register callback to get the first picture. We call GetPicture again in 277 // PictureReady to continuously receive pictures as they're decoded. 278 decoder_->GetPicture( 279 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady)); 280 281 // Start the decode loop. 282 DecodeNextFrame(); 283} 284 285void Decoder::Reset() { 286 assert(decoder_); 287 assert(!resetting_); 288 resetting_ = true; 289 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone)); 290} 291 292void Decoder::RecyclePicture(const PP_VideoPicture& picture) { 293 assert(decoder_); 294 decoder_->RecyclePicture(picture); 295} 296 297void Decoder::DecodeNextFrame() { 298 assert(decoder_); 299 if (encoded_data_next_pos_to_decode_ <= kDataLen) { 300 // If we've just reached the end of the bitstream, flush and wait. 301 if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) { 302 flushing_ = true; 303 decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone)); 304 return; 305 } 306 307 // Find the start of the next frame. 308 size_t start_pos = encoded_data_next_pos_to_decode_; 309 size_t end_pos; 310 GetNextFrame(&start_pos, &end_pos); 311 encoded_data_next_pos_to_decode_ = end_pos; 312 // Decode the frame. On completion, DecodeDone will call DecodeNextFrame 313 // to implement a decode loop. 314 uint32_t size = static_cast<uint32_t>(end_pos - start_pos); 315 decode_time_[next_picture_id_ % kMaxDecodeDelay] = core_if_->GetTimeTicks(); 316 decoder_->Decode(next_picture_id_++, 317 size, 318 kData + start_pos, 319 callback_factory_.NewCallback(&Decoder::DecodeDone)); 320 } 321} 322 323void Decoder::DecodeDone(int32_t result) { 324 assert(decoder_); 325 // Break out of the decode loop on abort. 326 if (result == PP_ERROR_ABORTED) 327 return; 328 assert(result == PP_OK); 329 if (!flushing_ && !resetting_) 330 DecodeNextFrame(); 331} 332 333void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) { 334 assert(decoder_); 335 // Break out of the get picture loop on abort. 336 if (result == PP_ERROR_ABORTED) 337 return; 338 assert(result == PP_OK); 339 340 num_pictures_++; 341 PP_TimeTicks latency = core_if_->GetTimeTicks() - 342 decode_time_[picture.decode_id % kMaxDecodeDelay]; 343 total_latency_ += latency; 344 345 decoder_->GetPicture( 346 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady)); 347 instance_->PaintPicture(this, picture); 348} 349 350void Decoder::FlushDone(int32_t result) { 351 assert(decoder_); 352 assert(result == PP_OK || result == PP_ERROR_ABORTED); 353 assert(flushing_); 354 flushing_ = false; 355} 356 357void Decoder::ResetDone(int32_t result) { 358 assert(decoder_); 359 assert(result == PP_OK); 360 assert(resetting_); 361 resetting_ = false; 362 363 Start(); 364} 365 366MyInstance::MyInstance(PP_Instance instance, pp::Module* module) 367 : pp::Instance(instance), 368 pp::Graphics3DClient(this), 369 is_painting_(false), 370 num_frames_rendered_(0), 371 first_frame_delivered_ticks_(-1), 372 last_swap_request_ticks_(-1), 373 swap_ticks_(0), 374 callback_factory_(this), 375 context_(NULL) { 376 console_if_ = static_cast<const PPB_Console*>( 377 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE)); 378 core_if_ = static_cast<const PPB_Core*>( 379 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); 380 gles2_if_ = static_cast<const PPB_OpenGLES2*>( 381 pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE)); 382 383 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); 384} 385 386MyInstance::~MyInstance() { 387 if (!context_) 388 return; 389 390 PP_Resource graphics_3d = context_->pp_resource(); 391 if (shader_2d_.program) 392 gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program); 393 if (shader_rectangle_arb_.program) 394 gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program); 395 if (shader_external_oes_.program) 396 gles2_if_->DeleteProgram(graphics_3d, shader_external_oes_.program); 397 398 for (DecoderList::iterator it = video_decoders_.begin(); 399 it != video_decoders_.end(); 400 ++it) 401 delete *it; 402 403 delete context_; 404} 405 406void MyInstance::DidChangeView(const pp::Rect& position, 407 const pp::Rect& clip_ignored) { 408 if (position.width() == 0 || position.height() == 0) 409 return; 410 if (plugin_size_.width()) { 411 assert(position.size() == plugin_size_); 412 return; 413 } 414 plugin_size_ = position.size(); 415 416 // Initialize graphics. 417 InitGL(); 418 InitializeDecoders(); 419} 420 421bool MyInstance::HandleInputEvent(const pp::InputEvent& event) { 422 switch (event.GetType()) { 423 case PP_INPUTEVENT_TYPE_MOUSEDOWN: { 424 pp::MouseInputEvent mouse_event(event); 425 // Reset all decoders on mouse down. 426 if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT) { 427 // Reset decoders. 428 for (size_t i = 0; i < video_decoders_.size(); i++) { 429 if (!video_decoders_[i]->resetting()) 430 video_decoders_[i]->Reset(); 431 } 432 } 433 return true; 434 } 435 436 default: 437 return false; 438 } 439} 440 441void MyInstance::InitializeDecoders() { 442 assert(video_decoders_.empty()); 443 // Create two decoders with ids 0 and 1. 444 video_decoders_.push_back(new Decoder(this, 0, *context_)); 445 video_decoders_.push_back(new Decoder(this, 1, *context_)); 446} 447 448void MyInstance::PaintPicture(Decoder* decoder, 449 const PP_VideoPicture& picture) { 450 if (first_frame_delivered_ticks_ == -1) 451 assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1); 452 453 pending_pictures_.push(PendingPicture(decoder, picture)); 454 if (!is_painting_) 455 PaintNextPicture(); 456} 457 458void MyInstance::PaintNextPicture() { 459 assert(!is_painting_); 460 is_painting_ = true; 461 462 const PendingPicture& next = pending_pictures_.front(); 463 Decoder* decoder = next.decoder; 464 const PP_VideoPicture& picture = next.picture; 465 466 int x = 0; 467 int y = 0; 468 int half_width = plugin_size_.width() / 2; 469 int half_height = plugin_size_.height() / 2; 470 if (decoder->id() != 0) { 471 x = half_width; 472 y = half_height; 473 } 474 475 PP_Resource graphics_3d = context_->pp_resource(); 476 if (picture.texture_target == GL_TEXTURE_2D) { 477 Create2DProgramOnce(); 478 gles2_if_->UseProgram(graphics_3d, shader_2d_.program); 479 gles2_if_->Uniform2f( 480 graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0); 481 } else if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) { 482 CreateRectangleARBProgramOnce(); 483 gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program); 484 gles2_if_->Uniform2f(graphics_3d, 485 shader_rectangle_arb_.texcoord_scale_location, 486 picture.texture_size.width, 487 picture.texture_size.height); 488 } else { 489 assert(picture.texture_target == GL_TEXTURE_EXTERNAL_OES); 490 CreateExternalOESProgramOnce(); 491 gles2_if_->UseProgram(graphics_3d, shader_external_oes_.program); 492 gles2_if_->Uniform2f( 493 graphics_3d, shader_external_oes_.texcoord_scale_location, 1.0, 1.0); 494 } 495 496 gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height); 497 gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0); 498 gles2_if_->BindTexture( 499 graphics_3d, picture.texture_target, picture.texture_id); 500 gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4); 501 502 gles2_if_->UseProgram(graphics_3d, 0); 503 504 last_swap_request_ticks_ = core_if_->GetTimeTicks(); 505 context_->SwapBuffers( 506 callback_factory_.NewCallback(&MyInstance::PaintFinished)); 507} 508 509void MyInstance::PaintFinished(int32_t result) { 510 assert(result == PP_OK); 511 swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_; 512 is_painting_ = false; 513 ++num_frames_rendered_; 514 if (num_frames_rendered_ % 50 == 0) { 515 double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_; 516 double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000; 517 double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_; 518 double secs_average_latency = 0; 519 for (DecoderList::iterator it = video_decoders_.begin(); 520 it != video_decoders_.end(); 521 ++it) 522 secs_average_latency += (*it)->GetAverageLatency(); 523 secs_average_latency /= video_decoders_.size(); 524 double ms_average_latency = 1000 * secs_average_latency; 525 LogError(this).s() << "Rendered frames: " << num_frames_rendered_ 526 << ", fps: " << fps 527 << ", with average ms/swap of: " << ms_per_swap 528 << ", with average latency (ms) of: " 529 << ms_average_latency; 530 } 531 532 // If the decoders were reset, this will be empty. 533 if (pending_pictures_.empty()) 534 return; 535 536 const PendingPicture& next = pending_pictures_.front(); 537 Decoder* decoder = next.decoder; 538 const PP_VideoPicture& picture = next.picture; 539 decoder->RecyclePicture(picture); 540 pending_pictures_.pop(); 541 542 // Keep painting as long as we have pictures. 543 if (!pending_pictures_.empty()) 544 PaintNextPicture(); 545} 546 547void MyInstance::InitGL() { 548 assert(plugin_size_.width() && plugin_size_.height()); 549 is_painting_ = false; 550 551 assert(!context_); 552 int32_t context_attributes[] = { 553 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, 554 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, 555 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, 556 PP_GRAPHICS3DATTRIB_RED_SIZE, 8, 557 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, 558 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, 559 PP_GRAPHICS3DATTRIB_SAMPLES, 0, 560 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, 561 PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(), 562 PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(), 563 PP_GRAPHICS3DATTRIB_NONE, 564 }; 565 context_ = new pp::Graphics3D(this, context_attributes); 566 assert(!context_->is_null()); 567 assert(BindGraphics(*context_)); 568 569 // Clear color bit. 570 gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1); 571 gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT); 572 573 assertNoGLError(); 574 575 CreateGLObjects(); 576} 577 578void MyInstance::CreateGLObjects() { 579 // Assign vertex positions and texture coordinates to buffers for use in 580 // shader program. 581 static const float kVertices[] = { 582 -1, -1, -1, 1, 1, -1, 1, 1, // Position coordinates. 583 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates. 584 }; 585 586 GLuint buffer; 587 gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer); 588 gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer); 589 590 gles2_if_->BufferData(context_->pp_resource(), 591 GL_ARRAY_BUFFER, 592 sizeof(kVertices), 593 kVertices, 594 GL_STATIC_DRAW); 595 assertNoGLError(); 596} 597 598static const char kVertexShader[] = 599 "varying vec2 v_texCoord; \n" 600 "attribute vec4 a_position; \n" 601 "attribute vec2 a_texCoord; \n" 602 "uniform vec2 v_scale; \n" 603 "void main() \n" 604 "{ \n" 605 " v_texCoord = v_scale * a_texCoord; \n" 606 " gl_Position = a_position; \n" 607 "}"; 608 609void MyInstance::Create2DProgramOnce() { 610 if (shader_2d_.program) 611 return; 612 static const char kFragmentShader2D[] = 613 "precision mediump float; \n" 614 "varying vec2 v_texCoord; \n" 615 "uniform sampler2D s_texture; \n" 616 "void main() \n" 617 "{" 618 " gl_FragColor = texture2D(s_texture, v_texCoord); \n" 619 "}"; 620 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D); 621 assertNoGLError(); 622} 623 624void MyInstance::CreateRectangleARBProgramOnce() { 625 if (shader_rectangle_arb_.program) 626 return; 627 static const char kFragmentShaderRectangle[] = 628 "#extension GL_ARB_texture_rectangle : require\n" 629 "precision mediump float; \n" 630 "varying vec2 v_texCoord; \n" 631 "uniform sampler2DRect s_texture; \n" 632 "void main() \n" 633 "{" 634 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n" 635 "}"; 636 shader_rectangle_arb_ = 637 CreateProgram(kVertexShader, kFragmentShaderRectangle); 638 assertNoGLError(); 639} 640 641void MyInstance::CreateExternalOESProgramOnce() { 642 if (shader_external_oes_.program) 643 return; 644 static const char kFragmentShaderExternal[] = 645 "#extension GL_OES_EGL_image_external : require\n" 646 "precision mediump float; \n" 647 "varying vec2 v_texCoord; \n" 648 "uniform samplerExternalOES s_texture; \n" 649 "void main() \n" 650 "{" 651 " gl_FragColor = texture2D(s_texture, v_texCoord); \n" 652 "}"; 653 shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal); 654 assertNoGLError(); 655} 656 657Shader MyInstance::CreateProgram(const char* vertex_shader, 658 const char* fragment_shader) { 659 Shader shader; 660 661 // Create shader program. 662 shader.program = gles2_if_->CreateProgram(context_->pp_resource()); 663 CreateShader( 664 shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader)); 665 CreateShader(shader.program, 666 GL_FRAGMENT_SHADER, 667 fragment_shader, 668 strlen(fragment_shader)); 669 gles2_if_->LinkProgram(context_->pp_resource(), shader.program); 670 gles2_if_->UseProgram(context_->pp_resource(), shader.program); 671 gles2_if_->Uniform1i( 672 context_->pp_resource(), 673 gles2_if_->GetUniformLocation( 674 context_->pp_resource(), shader.program, "s_texture"), 675 0); 676 assertNoGLError(); 677 678 shader.texcoord_scale_location = gles2_if_->GetUniformLocation( 679 context_->pp_resource(), shader.program, "v_scale"); 680 681 GLint pos_location = gles2_if_->GetAttribLocation( 682 context_->pp_resource(), shader.program, "a_position"); 683 GLint tc_location = gles2_if_->GetAttribLocation( 684 context_->pp_resource(), shader.program, "a_texCoord"); 685 assertNoGLError(); 686 687 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location); 688 gles2_if_->VertexAttribPointer( 689 context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0); 690 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location); 691 gles2_if_->VertexAttribPointer( 692 context_->pp_resource(), 693 tc_location, 694 2, 695 GL_FLOAT, 696 GL_FALSE, 697 0, 698 static_cast<float*>(0) + 8); // Skip position coordinates. 699 700 gles2_if_->UseProgram(context_->pp_resource(), 0); 701 assertNoGLError(); 702 return shader; 703} 704 705void MyInstance::CreateShader(GLuint program, 706 GLenum type, 707 const char* source, 708 int size) { 709 GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type); 710 gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size); 711 gles2_if_->CompileShader(context_->pp_resource(), shader); 712 gles2_if_->AttachShader(context_->pp_resource(), program, shader); 713 gles2_if_->DeleteShader(context_->pp_resource(), shader); 714} 715 716// This object is the global object representing this plugin library as long 717// as it is loaded. 718class MyModule : public pp::Module { 719 public: 720 MyModule() : pp::Module() {} 721 virtual ~MyModule() {} 722 723 virtual pp::Instance* CreateInstance(PP_Instance instance) { 724 return new MyInstance(instance, this); 725 } 726}; 727 728} // anonymous namespace 729 730namespace pp { 731// Factory function for your specialization of the Module object. 732Module* CreateModule() { 733 return new MyModule(); 734} 735} // namespace pp 736