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.
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"
28 // When compiling natively on Windows, PostMessage can be #define-d to
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()));
41 const char* const kDelimiter
= "#__#";
43 // This object is the global object representing this plugin library as long
45 class VCDemoModule
: public pp::Module
{
47 VCDemoModule() : pp::Module() {}
48 virtual ~VCDemoModule() {}
50 virtual pp::Instance
* CreateInstance(PP_Instance instance
);
53 class VCDemoInstance
: public pp::Instance
,
54 public pp::Graphics3DClient
,
55 public pp::VideoCaptureClient_Dev
{
57 VCDemoInstance(PP_Instance instance
, pp::Module
* module
);
58 virtual ~VCDemoInstance();
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
);
65 // pp::Graphics3DClient implementation.
66 virtual void Graphics3DContextLost() {
72 virtual void OnDeviceInfo(PP_Resource resource
,
73 const PP_VideoCaptureDeviceInfo_Dev
& info
,
74 const std::vector
<pp::Buffer_Dev
>& buffers
) {
80 virtual void OnStatus(PP_Resource resource
, uint32_t status
) {
83 virtual void OnError(PP_Resource resource
, uint32_t error
) {
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
);
95 data
+= width
* height
;
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
);
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
);
110 video_capture_
.ReuseBuffer(buffer
);
120 // GL-related functions.
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();
128 void Open(const pp::DeviceRef_Dev
& device
);
131 void EnumerateDevicesFinished(int32_t result
,
132 std::vector
<pp::DeviceRef_Dev
>& devices
);
133 void OpenFinished(int32_t result
);
135 static void MonitorDeviceChangeCallback(void* user_data
,
136 uint32_t device_count
,
137 const PP_Resource devices
[]);
139 pp::Size position_size_
;
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_
;
151 const struct PPB_OpenGLES2
* gles2_if_
;
154 pp::Graphics3D
* context_
;
156 std::vector
<pp::DeviceRef_Dev
> enumerate_devices_
;
157 std::vector
<pp::DeviceRef_Dev
> monitor_devices_
;
160 VCDemoInstance::VCDemoInstance(PP_Instance instance
, pp::Module
* module
)
161 : pp::Instance(instance
),
162 pp::Graphics3DClient(this),
163 pp::VideoCaptureClient_Dev(this),
169 video_capture_(this),
170 callback_factory_(this),
172 gles2_if_
= static_cast<const struct PPB_OpenGLES2
*>(
173 module
->GetBrowserInterface(PPB_OPENGLES2_INTERFACE
));
174 PP_DCHECK(gles2_if_
);
176 capture_info_
.width
= 320;
177 capture_info_
.height
= 240;
178 capture_info_
.frames_per_second
= 30;
181 VCDemoInstance::~VCDemoInstance() {
182 video_capture_
.MonitorDeviceChange(NULL
, NULL
);
186 void VCDemoInstance::DidChangeView(
187 const pp::Rect
& position
, const pp::Rect
& clip_ignored
) {
188 if (position
.width() == 0 || position
.height() == 0)
190 if (position
.size() == position_size_
)
193 position_size_
= position
.size();
195 // Initialize graphics.
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);
208 PostMessage(pp::Var("MonitorDeviceChangeFailed"));
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") {
220 } else if (event
== "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
]);
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
]);
240 void VCDemoInstance::InitGL() {
241 PP_DCHECK(position_size_
.width() && position_size_
.height());
242 is_painting_
= false;
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
,
258 context_
= new pp::Graphics3D(this, attributes
);
259 PP_DCHECK(!context_
->is_null());
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());
267 BindGraphics(*context_
);
273 void VCDemoInstance::Render() {
274 PP_DCHECK(!is_painting_
);
276 needs_paint_
= false;
278 gles2_if_
->DrawArrays(context_
->pp_resource(), GL_TRIANGLE_STRIP
, 0, 4);
280 gles2_if_
->Clear(context_
->pp_resource(), GL_COLOR_BUFFER_BIT
);
282 pp::CompletionCallback cb
= callback_factory_
.NewCallback(
283 &VCDemoInstance::PaintFinished
);
284 context_
->SwapBuffers(cb
);
287 void VCDemoInstance::PaintFinished(int32_t result
) {
288 is_painting_
= false;
293 GLuint
VCDemoInstance::CreateTexture(int32_t width
, int32_t height
, int unit
) {
295 gles2_if_
->GenTextures(context_
->pp_resource(), 1, &texture_id
);
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
,
303 gles2_if_
->TexParameteri(
304 context_
->pp_resource(), GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
,
306 gles2_if_
->TexParameterf(
307 context_
->pp_resource(), GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
,
309 gles2_if_
->TexParameterf(
310 context_
->pp_resource(), GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
,
314 gles2_if_
->TexImage2D(
315 context_
->pp_resource(), GL_TEXTURE_2D
, 0, GL_LUMINANCE
, width
, height
, 0,
316 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, NULL
);
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"
329 " v_texCoord = a_texCoord; \n"
330 " gl_Position = a_position; \n"
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"
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"
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
356 PP_Resource context
= context_
->pp_resource();
358 // Create shader program.
359 GLuint program
= gles2_if_
->CreateProgram(context
);
360 CreateShader(program
, GL_VERTEX_SHADER
, kVertexShader
, sizeof(kVertexShader
));
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(
374 gles2_if_
->GetUniformLocation(context
, program
, "color_matrix"),
375 1, GL_FALSE
, kColorMatrix
);
378 // Assign vertex positions and texture coordinates to buffers for use in
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.
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
);
391 GLint pos_location
= gles2_if_
->GetAttribLocation(
392 context
, program
, "a_position");
393 GLint tc_location
= gles2_if_
->GetAttribLocation(
394 context
, program
, "a_texCoord");
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.
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
);
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);
423 texture_u_
= CreateTexture(width
, height
, 1);
424 texture_v_
= CreateTexture(width
, height
, 2);
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"));
435 void VCDemoInstance::Stop() {
436 if (video_capture_
.StopCapture() != PP_OK
)
437 PostMessage(pp::Var("StopFailed"));
440 void VCDemoInstance::Start() {
441 if (video_capture_
.StartCapture() != PP_OK
)
442 PostMessage(pp::Var("StartFailed"));
445 void VCDemoInstance::EnumerateDevicesFinished(
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());
456 device_names
+= kDelimiter
;
457 device_names
+= name
.AsString();
459 PostMessage(pp::Var(device_names
));
461 PostMessage(pp::Var("EnumerationFailed"));
465 void VCDemoInstance::OpenFinished(int32_t result
) {
469 PostMessage(pp::Var("OpenFailed"));
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
);
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());
487 device_names
+= kDelimiter
;
488 device_names
+= name
.AsString();
490 thiz
->PostMessage(pp::Var(device_names
));
493 pp::Instance
* VCDemoModule::CreateInstance(PP_Instance instance
) {
494 return new VCDemoInstance(instance
, this);
497 } // anonymous namespace
500 // Factory function for your specialization of the Module object.
501 Module
* CreateModule() {
502 return new VCDemoModule();