- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / examples / video_capture / video_capture.cc
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