Home | History | Annotate | Download | only in video_capture
      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 <stdlib.h>
      6 #include <string.h>
      7 
      8 #include <map>
      9 #include <vector>
     10 
     11 #include "ppapi/c/dev/ppb_video_capture_dev.h"
     12 #include "ppapi/c/pp_errors.h"
     13 #include "ppapi/c/ppb_opengles2.h"
     14 #include "ppapi/cpp/dev/buffer_dev.h"
     15 #include "ppapi/cpp/dev/device_ref_dev.h"
     16 #include "ppapi/cpp/dev/video_capture_dev.h"
     17 #include "ppapi/cpp/dev/video_capture_client_dev.h"
     18 #include "ppapi/cpp/completion_callback.h"
     19 #include "ppapi/cpp/graphics_3d_client.h"
     20 #include "ppapi/cpp/graphics_3d.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/lib/gl/include/GLES2/gl2.h"
     26 #include "ppapi/utility/completion_callback_factory.h"
     27 
     28 // When compiling natively on Windows, PostMessage can be #define-d to
     29 // something else.
     30 #ifdef PostMessage
     31 #undef PostMessage
     32 #endif
     33 
     34 // Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
     35 // function to preserve line number information in the failure message.
     36 #define AssertNoGLError() \
     37   PP_DCHECK(!gles2_if_->GetError(context_->pp_resource()));
     38 
     39 namespace {
     40 
     41 const char* const kDelimiter = "#__#";
     42 
     43 // This object is the global object representing this plugin library as long
     44 // as it is loaded.
     45 class VCDemoModule : public pp::Module {
     46  public:
     47   VCDemoModule() : pp::Module() {}
     48   virtual ~VCDemoModule() {}
     49 
     50   virtual pp::Instance* CreateInstance(PP_Instance instance);
     51 };
     52 
     53 class VCDemoInstance : public pp::Instance,
     54                        public pp::Graphics3DClient,
     55                        public pp::VideoCaptureClient_Dev {
     56  public:
     57   VCDemoInstance(PP_Instance instance, pp::Module* module);
     58   virtual ~VCDemoInstance();
     59 
     60   // pp::Instance implementation (see PPP_Instance).
     61   virtual void DidChangeView(const pp::Rect& position,
     62                              const pp::Rect& clip_ignored);
     63   virtual void HandleMessage(const pp::Var& message_data);
     64 
     65   // pp::Graphics3DClient implementation.
     66   virtual void Graphics3DContextLost() {
     67     InitGL();
     68     CreateYUVTextures();
     69     Render();
     70   }
     71 
     72   virtual void OnDeviceInfo(PP_Resource resource,
     73                             const PP_VideoCaptureDeviceInfo_Dev& info,
     74                             const std::vector<pp::Buffer_Dev>& buffers) {
     75     capture_info_ = info;
     76     buffers_ = buffers;
     77     CreateYUVTextures();
     78   }
     79 
     80   virtual void OnStatus(PP_Resource resource, uint32_t status) {
     81   }
     82 
     83   virtual void OnError(PP_Resource resource, uint32_t error) {
     84   }
     85 
     86   virtual void OnBufferReady(PP_Resource resource, uint32_t buffer) {
     87     const char* data = static_cast<const char*>(buffers_[buffer].data());
     88     int32_t width = capture_info_.width;
     89     int32_t height = capture_info_.height;
     90     gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0);
     91     gles2_if_->TexSubImage2D(
     92         context_->pp_resource(), GL_TEXTURE_2D, 0, 0, 0, width, height,
     93         GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
     94 
     95     data += width * height;
     96     width /= 2;
     97     height /= 2;
     98 
     99     gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE1);
    100     gles2_if_->TexSubImage2D(
    101         context_->pp_resource(), GL_TEXTURE_2D, 0, 0, 0, width, height,
    102         GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
    103 
    104     data += width * height;
    105     gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE2);
    106     gles2_if_->TexSubImage2D(
    107         context_->pp_resource(), GL_TEXTURE_2D, 0, 0, 0, width, height,
    108         GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
    109 
    110     video_capture_.ReuseBuffer(buffer);
    111     if (is_painting_)
    112       needs_paint_ = true;
    113     else
    114       Render();
    115   }
    116 
    117  private:
    118   void Render();
    119 
    120   // GL-related functions.
    121   void InitGL();
    122   GLuint CreateTexture(int32_t width, int32_t height, int unit);
    123   void CreateGLObjects();
    124   void CreateShader(GLuint program, GLenum type, const char* source, int size);
    125   void PaintFinished(int32_t result);
    126   void CreateYUVTextures();
    127 
    128   void Open(const pp::DeviceRef_Dev& device);
    129   void Stop();
    130   void Start();
    131   void EnumerateDevicesFinished(int32_t result,
    132                                 std::vector<pp::DeviceRef_Dev>& devices);
    133   void OpenFinished(int32_t result);
    134 
    135   static void MonitorDeviceChangeCallback(void* user_data,
    136                                           uint32_t device_count,
    137                                           const PP_Resource devices[]);
    138 
    139   pp::Size position_size_;
    140   bool is_painting_;
    141   bool needs_paint_;
    142   GLuint texture_y_;
    143   GLuint texture_u_;
    144   GLuint texture_v_;
    145   pp::VideoCapture_Dev video_capture_;
    146   PP_VideoCaptureDeviceInfo_Dev capture_info_;
    147   std::vector<pp::Buffer_Dev> buffers_;
    148   pp::CompletionCallbackFactory<VCDemoInstance> callback_factory_;
    149 
    150   // Unowned pointers.
    151   const struct PPB_OpenGLES2* gles2_if_;
    152 
    153   // Owned data.
    154   pp::Graphics3D* context_;
    155 
    156   std::vector<pp::DeviceRef_Dev> enumerate_devices_;
    157   std::vector<pp::DeviceRef_Dev> monitor_devices_;
    158 };
    159 
    160 VCDemoInstance::VCDemoInstance(PP_Instance instance, pp::Module* module)
    161     : pp::Instance(instance),
    162       pp::Graphics3DClient(this),
    163       pp::VideoCaptureClient_Dev(this),
    164       is_painting_(false),
    165       needs_paint_(false),
    166       texture_y_(0),
    167       texture_u_(0),
    168       texture_v_(0),
    169       video_capture_(this),
    170       callback_factory_(this),
    171       context_(NULL) {
    172   gles2_if_ = static_cast<const struct PPB_OpenGLES2*>(
    173       module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
    174   PP_DCHECK(gles2_if_);
    175 
    176   capture_info_.width = 320;
    177   capture_info_.height = 240;
    178   capture_info_.frames_per_second = 30;
    179 }
    180 
    181 VCDemoInstance::~VCDemoInstance() {
    182   video_capture_.MonitorDeviceChange(NULL, NULL);
    183   delete context_;
    184 }
    185 
    186 void VCDemoInstance::DidChangeView(
    187     const pp::Rect& position, const pp::Rect& clip_ignored) {
    188   if (position.width() == 0 || position.height() == 0)
    189     return;
    190   if (position.size() == position_size_)
    191     return;
    192 
    193   position_size_ = position.size();
    194 
    195   // Initialize graphics.
    196   InitGL();
    197 
    198   Render();
    199 }
    200 
    201 void VCDemoInstance::HandleMessage(const pp::Var& message_data) {
    202   if (message_data.is_string()) {
    203     std::string event = message_data.AsString();
    204     if (event == "PageInitialized") {
    205       int32_t result = video_capture_.MonitorDeviceChange(
    206           &VCDemoInstance::MonitorDeviceChangeCallback, this);
    207       if (result != PP_OK)
    208         PostMessage(pp::Var("MonitorDeviceChangeFailed"));
    209 
    210       pp::CompletionCallbackWithOutput<std::vector<pp::DeviceRef_Dev> >
    211           callback = callback_factory_.NewCallbackWithOutput(
    212               &VCDemoInstance::EnumerateDevicesFinished);
    213       result = video_capture_.EnumerateDevices(callback);
    214       if (result != PP_OK_COMPLETIONPENDING)
    215         PostMessage(pp::Var("EnumerationFailed"));
    216     } else if (event == "UseDefault") {
    217       Open(pp::DeviceRef_Dev());
    218     } else if (event == "Stop") {
    219       Stop();
    220     } else if (event == "Start") {
    221       Start();
    222     } else if (event.find("Monitor:") == 0) {
    223       std::string index_str = event.substr(strlen("Monitor:"));
    224       int index = atoi(index_str.c_str());
    225       if (index >= 0 && index < static_cast<int>(monitor_devices_.size()))
    226         Open(monitor_devices_[index]);
    227       else
    228         PP_NOTREACHED();
    229     } else if (event.find("Enumerate:") == 0) {
    230       std::string index_str = event.substr(strlen("Enumerate:"));
    231       int index = atoi(index_str.c_str());
    232       if (index >= 0 && index < static_cast<int>(enumerate_devices_.size()))
    233         Open(enumerate_devices_[index]);
    234       else
    235         PP_NOTREACHED();
    236     }
    237   }
    238 }
    239 
    240 void VCDemoInstance::InitGL() {
    241   PP_DCHECK(position_size_.width() && position_size_.height());
    242   is_painting_ = false;
    243 
    244   delete context_;
    245   int32_t attributes[] = {
    246     PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 0,
    247     PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
    248     PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
    249     PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
    250     PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
    251     PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
    252     PP_GRAPHICS3DATTRIB_SAMPLES, 0,
    253     PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
    254     PP_GRAPHICS3DATTRIB_WIDTH, position_size_.width(),
    255     PP_GRAPHICS3DATTRIB_HEIGHT, position_size_.height(),
    256     PP_GRAPHICS3DATTRIB_NONE,
    257   };
    258   context_ = new pp::Graphics3D(this, attributes);
    259   PP_DCHECK(!context_->is_null());
    260 
    261   // Set viewport window size and clear color bit.
    262   gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
    263   gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
    264   gles2_if_->Viewport(context_->pp_resource(), 0, 0,
    265                       position_size_.width(), position_size_.height());
    266 
    267   BindGraphics(*context_);
    268   AssertNoGLError();
    269 
    270   CreateGLObjects();
    271 }
    272 
    273 void VCDemoInstance::Render() {
    274   PP_DCHECK(!is_painting_);
    275   is_painting_ = true;
    276   needs_paint_ = false;
    277   if (texture_y_) {
    278     gles2_if_->DrawArrays(context_->pp_resource(), GL_TRIANGLE_STRIP, 0, 4);
    279   } else {
    280     gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
    281   }
    282   pp::CompletionCallback cb = callback_factory_.NewCallback(
    283       &VCDemoInstance::PaintFinished);
    284   context_->SwapBuffers(cb);
    285 }
    286 
    287 void VCDemoInstance::PaintFinished(int32_t result) {
    288   is_painting_ = false;
    289   if (needs_paint_)
    290     Render();
    291 }
    292 
    293 GLuint VCDemoInstance::CreateTexture(int32_t width, int32_t height, int unit) {
    294   GLuint texture_id;
    295   gles2_if_->GenTextures(context_->pp_resource(), 1, &texture_id);
    296   AssertNoGLError();
    297   // Assign parameters.
    298   gles2_if_->ActiveTexture(context_->pp_resource(), GL_TEXTURE0 + unit);
    299   gles2_if_->BindTexture(context_->pp_resource(), GL_TEXTURE_2D, texture_id);
    300   gles2_if_->TexParameteri(
    301       context_->pp_resource(), GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    302       GL_NEAREST);
    303   gles2_if_->TexParameteri(
    304       context_->pp_resource(), GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
    305       GL_NEAREST);
    306   gles2_if_->TexParameterf(
    307       context_->pp_resource(), GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
    308       GL_CLAMP_TO_EDGE);
    309   gles2_if_->TexParameterf(
    310       context_->pp_resource(), GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
    311       GL_CLAMP_TO_EDGE);
    312 
    313   // Allocate texture.
    314   gles2_if_->TexImage2D(
    315       context_->pp_resource(), GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0,
    316       GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
    317   AssertNoGLError();
    318   return texture_id;
    319 }
    320 
    321 void VCDemoInstance::CreateGLObjects() {
    322   // Code and constants for shader.
    323   static const char kVertexShader[] =
    324       "varying vec2 v_texCoord;            \n"
    325       "attribute vec4 a_position;          \n"
    326       "attribute vec2 a_texCoord;          \n"
    327       "void main()                         \n"
    328       "{                                   \n"
    329       "    v_texCoord = a_texCoord;        \n"
    330       "    gl_Position = a_position;       \n"
    331       "}";
    332 
    333   static const char kFragmentShader[] =
    334       "precision mediump float;                                   \n"
    335       "varying vec2 v_texCoord;                                   \n"
    336       "uniform sampler2D y_texture;                               \n"
    337       "uniform sampler2D u_texture;                               \n"
    338       "uniform sampler2D v_texture;                               \n"
    339       "uniform mat3 color_matrix;                                 \n"
    340       "void main()                                                \n"
    341       "{                                                          \n"
    342       "  vec3 yuv;                                                \n"
    343       "  yuv.x = texture2D(y_texture, v_texCoord).r;              \n"
    344       "  yuv.y = texture2D(u_texture, v_texCoord).r;              \n"
    345       "  yuv.z = texture2D(v_texture, v_texCoord).r;              \n"
    346       "  vec3 rgb = color_matrix * (yuv - vec3(0.0625, 0.5, 0.5));\n"
    347       "  gl_FragColor = vec4(rgb, 1.0);                           \n"
    348       "}";
    349 
    350   static const float kColorMatrix[9] = {
    351     1.1643828125f, 1.1643828125f, 1.1643828125f,
    352     0.0f, -0.39176171875f, 2.017234375f,
    353     1.59602734375f, -0.81296875f, 0.0f
    354   };
    355 
    356   PP_Resource context = context_->pp_resource();
    357 
    358   // Create shader program.
    359   GLuint program = gles2_if_->CreateProgram(context);
    360   CreateShader(program, GL_VERTEX_SHADER, kVertexShader, sizeof(kVertexShader));
    361   CreateShader(
    362       program, GL_FRAGMENT_SHADER, kFragmentShader, sizeof(kFragmentShader));
    363   gles2_if_->LinkProgram(context, program);
    364   gles2_if_->UseProgram(context, program);
    365   gles2_if_->DeleteProgram(context, program);
    366   gles2_if_->Uniform1i(
    367       context, gles2_if_->GetUniformLocation(context, program, "y_texture"), 0);
    368   gles2_if_->Uniform1i(
    369       context, gles2_if_->GetUniformLocation(context, program, "u_texture"), 1);
    370   gles2_if_->Uniform1i(
    371       context, gles2_if_->GetUniformLocation(context, program, "v_texture"), 2);
    372   gles2_if_->UniformMatrix3fv(
    373       context,
    374       gles2_if_->GetUniformLocation(context, program, "color_matrix"),
    375       1, GL_FALSE, kColorMatrix);
    376   AssertNoGLError();
    377 
    378   // Assign vertex positions and texture coordinates to buffers for use in
    379   // shader program.
    380   static const float kVertices[] = {
    381     -1, 1, -1, -1, 1, 1, 1, -1,  // Position coordinates.
    382     0, 0, 0, 1, 1, 0, 1, 1,  // Texture coordinates.
    383   };
    384 
    385   GLuint buffer;
    386   gles2_if_->GenBuffers(context, 1, &buffer);
    387   gles2_if_->BindBuffer(context, GL_ARRAY_BUFFER, buffer);
    388   gles2_if_->BufferData(context, GL_ARRAY_BUFFER,
    389                         sizeof(kVertices), kVertices, GL_STATIC_DRAW);
    390   AssertNoGLError();
    391   GLint pos_location = gles2_if_->GetAttribLocation(
    392       context, program, "a_position");
    393   GLint tc_location = gles2_if_->GetAttribLocation(
    394       context, program, "a_texCoord");
    395   AssertNoGLError();
    396   gles2_if_->EnableVertexAttribArray(context, pos_location);
    397   gles2_if_->VertexAttribPointer(context, pos_location, 2,
    398                                  GL_FLOAT, GL_FALSE, 0, 0);
    399   gles2_if_->EnableVertexAttribArray(context, tc_location);
    400   gles2_if_->VertexAttribPointer(
    401       context, tc_location, 2, GL_FLOAT, GL_FALSE, 0,
    402       static_cast<float*>(0) + 8);  // Skip position coordinates.
    403   AssertNoGLError();
    404 }
    405 
    406 void VCDemoInstance::CreateShader(
    407     GLuint program, GLenum type, const char* source, int size) {
    408   PP_Resource context = context_->pp_resource();
    409   GLuint shader = gles2_if_->CreateShader(context, type);
    410   gles2_if_->ShaderSource(context, shader, 1, &source, &size);
    411   gles2_if_->CompileShader(context, shader);
    412   gles2_if_->AttachShader(context, program, shader);
    413   gles2_if_->DeleteShader(context, shader);
    414 }
    415 
    416 void VCDemoInstance::CreateYUVTextures() {
    417   int32_t width = capture_info_.width;
    418   int32_t height = capture_info_.height;
    419   texture_y_ = CreateTexture(width, height, 0);
    420 
    421   width /= 2;
    422   height /= 2;
    423   texture_u_ = CreateTexture(width, height, 1);
    424   texture_v_ = CreateTexture(width, height, 2);
    425 }
    426 
    427 void VCDemoInstance::Open(const pp::DeviceRef_Dev& device) {
    428   pp::CompletionCallback callback = callback_factory_.NewCallback(
    429       &VCDemoInstance::OpenFinished);
    430   int32_t result = video_capture_.Open(device, capture_info_, 4, callback);
    431   if (result != PP_OK_COMPLETIONPENDING)
    432     PostMessage(pp::Var("OpenFailed"));
    433 }
    434 
    435 void VCDemoInstance::Stop() {
    436   if (video_capture_.StopCapture() != PP_OK)
    437     PostMessage(pp::Var("StopFailed"));
    438 }
    439 
    440 void VCDemoInstance::Start() {
    441   if (video_capture_.StartCapture() != PP_OK)
    442     PostMessage(pp::Var("StartFailed"));
    443 }
    444 
    445 void VCDemoInstance::EnumerateDevicesFinished(
    446     int32_t result,
    447   std::vector<pp::DeviceRef_Dev>& devices) {
    448   if (result == PP_OK) {
    449     enumerate_devices_.swap(devices);
    450     std::string device_names = "Enumerate:";
    451     for (size_t index = 0; index < enumerate_devices_.size(); ++index) {
    452       pp::Var name = enumerate_devices_[index].GetName();
    453       PP_DCHECK(name.is_string());
    454 
    455       if (index != 0)
    456         device_names += kDelimiter;
    457       device_names += name.AsString();
    458     }
    459     PostMessage(pp::Var(device_names));
    460   } else {
    461     PostMessage(pp::Var("EnumerationFailed"));
    462   }
    463 }
    464 
    465 void VCDemoInstance::OpenFinished(int32_t result) {
    466   if (result == PP_OK)
    467     Start();
    468   else
    469     PostMessage(pp::Var("OpenFailed"));
    470 }
    471 
    472 // static
    473 void VCDemoInstance::MonitorDeviceChangeCallback(void* user_data,
    474                                                  uint32_t device_count,
    475                                                  const PP_Resource devices[]) {
    476   VCDemoInstance* thiz = static_cast<VCDemoInstance*>(user_data);
    477 
    478   std::string device_names = "Monitor:";
    479   thiz->monitor_devices_.clear();
    480   thiz->monitor_devices_.reserve(device_count);
    481   for (size_t index = 0; index < device_count; ++index) {
    482     thiz->monitor_devices_.push_back(pp::DeviceRef_Dev(devices[index]));
    483     pp::Var name = thiz->monitor_devices_.back().GetName();
    484     PP_DCHECK(name.is_string());
    485 
    486     if (index != 0)
    487       device_names += kDelimiter;
    488     device_names += name.AsString();
    489   }
    490   thiz->PostMessage(pp::Var(device_names));
    491 }
    492 
    493 pp::Instance* VCDemoModule::CreateInstance(PP_Instance instance) {
    494   return new VCDemoInstance(instance, this);
    495 }
    496 
    497 }  // anonymous namespace
    498 
    499 namespace pp {
    500 // Factory function for your specialization of the Module object.
    501 Module* CreateModule() {
    502   return new VCDemoModule();
    503 }
    504 }  // namespace pp
    505