1 // Copyright 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.
6 #include <GLES2/gl2ext.h>
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/c/ppb_opengles2.h"
13 #include "ppapi/cpp/completion_callback.h"
14 #include "ppapi/cpp/graphics_3d.h"
15 #include "ppapi/cpp/graphics_3d_client.h"
16 #include "ppapi/cpp/instance.h"
17 #include "ppapi/cpp/media_stream_video_track.h"
18 #include "ppapi/cpp/module.h"
19 #include "ppapi/cpp/rect.h"
20 #include "ppapi/cpp/var.h"
21 #include "ppapi/cpp/var_dictionary.h"
22 #include "ppapi/cpp/video_frame.h"
23 #include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
24 #include "ppapi/utility/completion_callback_factory.h"
26 // When compiling natively on Windows, PostMessage can be #define-d to
32 // Assert |context_| isn't holding any GL Errors. Done as a macro instead of a
33 // function to preserve line number information in the failure message.
34 #define AssertNoGLError() \
35 PP_DCHECK(!glGetError());
39 // This object is the global object representing this plugin library as long
41 class MediaStreamVideoModule
: public pp::Module
{
43 MediaStreamVideoModule() : pp::Module() {}
44 virtual ~MediaStreamVideoModule() {}
46 virtual pp::Instance
* CreateInstance(PP_Instance instance
);
49 class MediaStreamVideoDemoInstance
: public pp::Instance
,
50 public pp::Graphics3DClient
{
52 MediaStreamVideoDemoInstance(PP_Instance instance
, pp::Module
* module
);
53 virtual ~MediaStreamVideoDemoInstance();
55 // pp::Instance implementation (see PPP_Instance).
56 virtual void DidChangeView(const pp::Rect
& position
,
57 const pp::Rect
& clip_ignored
);
58 virtual void HandleMessage(const pp::Var
& message_data
);
60 // pp::Graphics3DClient implementation.
61 virtual void Graphics3DContextLost() {
72 // GL-related functions.
74 GLuint
CreateTexture(int32_t width
, int32_t height
, int unit
, bool rgba
);
75 void CreateGLObjects();
76 void CreateShader(GLuint program
, GLenum type
, const char* source
);
77 void PaintFinished(int32_t result
);
78 void CreateTextures();
79 void ConfigureTrack();
82 // MediaStreamVideoTrack callbacks.
83 void OnConfigure(int32_t result
);
84 void OnGetFrame(int32_t result
, pp::VideoFrame frame
);
86 pp::Size position_size_
;
97 pp::MediaStreamVideoTrack video_track_
;
98 pp::CompletionCallbackFactory
<MediaStreamVideoDemoInstance
> callback_factory_
;
99 std::vector
<int32_t> attrib_list_
;
101 // MediaStreamVideoTrack attributes:
103 PP_VideoFrame_Format attrib_format_
;
104 int32_t attrib_width_
;
105 int32_t attrib_height_
;
108 pp::Graphics3D
* context_
;
110 pp::Size frame_size_
;
113 MediaStreamVideoDemoInstance::MediaStreamVideoDemoInstance(
114 PP_Instance instance
, pp::Module
* module
)
115 : pp::Instance(instance
),
116 pp::Graphics3DClient(this),
124 callback_factory_(this),
126 attrib_format_(PP_VIDEOFRAME_FORMAT_I420
),
130 if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) {
131 LogToConsole(PP_LOGLEVEL_ERROR
, pp::Var("Unable to initialize GL PPAPI!"));
136 MediaStreamVideoDemoInstance::~MediaStreamVideoDemoInstance() {
140 void MediaStreamVideoDemoInstance::DidChangeView(
141 const pp::Rect
& position
, const pp::Rect
& clip_ignored
) {
142 if (position
.width() == 0 || position
.height() == 0)
144 if (position
.size() == position_size_
)
147 position_size_
= position
.size();
149 // Initialize graphics.
154 void MediaStreamVideoDemoInstance::HandleMessage(const pp::Var
& var_message
) {
155 if (!var_message
.is_dictionary()) {
156 LogToConsole(PP_LOGLEVEL_ERROR
, pp::Var("Invalid message!"));
160 pp::VarDictionary
var_dictionary_message(var_message
);
161 std::string command
= var_dictionary_message
.Get("command").AsString();
163 if (command
== "init") {
164 pp::Var var_track
= var_dictionary_message
.Get("track");
165 if (!var_track
.is_resource())
167 pp::Resource resource_track
= var_track
.AsResource();
168 video_track_
= pp::MediaStreamVideoTrack(resource_track
);
170 } else if (command
== "format") {
171 std::string str_format
= var_dictionary_message
.Get("format").AsString();
172 if (str_format
== "YV12") {
173 attrib_format_
= PP_VIDEOFRAME_FORMAT_YV12
;
174 } else if (str_format
== "I420") {
175 attrib_format_
= PP_VIDEOFRAME_FORMAT_I420
;
176 } else if (str_format
== "BGRA") {
177 attrib_format_
= PP_VIDEOFRAME_FORMAT_BGRA
;
179 attrib_format_
= PP_VIDEOFRAME_FORMAT_UNKNOWN
;
182 } else if (command
== "size") {
183 attrib_width_
= var_dictionary_message
.Get("width").AsInt();
184 attrib_height_
= var_dictionary_message
.Get("height").AsInt();
187 LogToConsole(PP_LOGLEVEL_ERROR
, pp::Var("Invalid command!"));
191 void MediaStreamVideoDemoInstance::InitGL() {
192 PP_DCHECK(position_size_
.width() && position_size_
.height());
193 is_painting_
= false;
196 int32_t attributes
[] = {
197 PP_GRAPHICS3DATTRIB_ALPHA_SIZE
, 0,
198 PP_GRAPHICS3DATTRIB_BLUE_SIZE
, 8,
199 PP_GRAPHICS3DATTRIB_GREEN_SIZE
, 8,
200 PP_GRAPHICS3DATTRIB_RED_SIZE
, 8,
201 PP_GRAPHICS3DATTRIB_DEPTH_SIZE
, 0,
202 PP_GRAPHICS3DATTRIB_STENCIL_SIZE
, 0,
203 PP_GRAPHICS3DATTRIB_SAMPLES
, 0,
204 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS
, 0,
205 PP_GRAPHICS3DATTRIB_WIDTH
, position_size_
.width(),
206 PP_GRAPHICS3DATTRIB_HEIGHT
, position_size_
.height(),
207 PP_GRAPHICS3DATTRIB_NONE
,
209 context_
= new pp::Graphics3D(this, attributes
);
210 PP_DCHECK(!context_
->is_null());
212 glSetCurrentContextPPAPI(context_
->pp_resource());
214 // Set viewport window size and clear color bit.
215 glClearColor(1, 0, 0, 1);
216 glClear(GL_COLOR_BUFFER_BIT
);
217 glViewport(0, 0, position_size_
.width(), position_size_
.height());
219 BindGraphics(*context_
);
225 void MediaStreamVideoDemoInstance::DrawYUV() {
226 static const float kColorMatrix
[9] = {
227 1.1643828125f
, 1.1643828125f
, 1.1643828125f
,
228 0.0f
, -0.39176171875f
, 2.017234375f
,
229 1.59602734375f
, -0.81296875f
, 0.0f
232 glUseProgram(program_yuv_
);
233 glUniform1i(glGetUniformLocation(program_yuv_
, "y_texture"), 0);
234 glUniform1i(glGetUniformLocation(program_yuv_
, "u_texture"), 1);
235 glUniform1i(glGetUniformLocation(program_yuv_
, "v_texture"), 2);
236 glUniformMatrix3fv(glGetUniformLocation(program_yuv_
, "color_matrix"),
237 1, GL_FALSE
, kColorMatrix
);
240 GLint pos_location
= glGetAttribLocation(program_yuv_
, "a_position");
241 GLint tc_location
= glGetAttribLocation(program_yuv_
, "a_texCoord");
243 glEnableVertexAttribArray(pos_location
);
244 glVertexAttribPointer(pos_location
, 2, GL_FLOAT
, GL_FALSE
, 0, 0);
245 glEnableVertexAttribArray(tc_location
);
246 glVertexAttribPointer(tc_location
, 2, GL_FLOAT
, GL_FALSE
, 0,
247 static_cast<float*>(0) + 16); // Skip position coordinates.
250 glDrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
254 void MediaStreamVideoDemoInstance::DrawRGB() {
255 glUseProgram(program_rgb_
);
256 glUniform1i(glGetUniformLocation(program_rgb_
, "rgb_texture"), 3);
259 GLint pos_location
= glGetAttribLocation(program_rgb_
, "a_position");
260 GLint tc_location
= glGetAttribLocation(program_rgb_
, "a_texCoord");
262 glEnableVertexAttribArray(pos_location
);
263 glVertexAttribPointer(pos_location
, 2, GL_FLOAT
, GL_FALSE
, 0, 0);
264 glEnableVertexAttribArray(tc_location
);
265 glVertexAttribPointer(tc_location
, 2, GL_FLOAT
, GL_FALSE
, 0,
266 static_cast<float*>(0) + 16); // Skip position coordinates.
269 glDrawArrays(GL_TRIANGLE_STRIP
, 4, 4);
272 void MediaStreamVideoDemoInstance::Render() {
273 PP_DCHECK(!is_painting_
);
275 needs_paint_
= false;
281 glClear(GL_COLOR_BUFFER_BIT
);
283 pp::CompletionCallback cb
= callback_factory_
.NewCallback(
284 &MediaStreamVideoDemoInstance::PaintFinished
);
285 context_
->SwapBuffers(cb
);
288 void MediaStreamVideoDemoInstance::PaintFinished(int32_t result
) {
289 is_painting_
= false;
294 GLuint
MediaStreamVideoDemoInstance::CreateTexture(
295 int32_t width
, int32_t height
, int unit
, bool rgba
) {
297 glGenTextures(1, &texture_id
);
300 // Assign parameters.
301 glActiveTexture(GL_TEXTURE0
+ unit
);
302 glBindTexture(GL_TEXTURE_2D
, texture_id
);
303 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
304 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
305 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
306 glTexParameterf(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
308 glTexImage2D(GL_TEXTURE_2D
, 0,
309 rgba
? GL_BGRA_EXT
: GL_LUMINANCE
,
311 rgba
? GL_BGRA_EXT
: GL_LUMINANCE
, GL_UNSIGNED_BYTE
, NULL
);
316 void MediaStreamVideoDemoInstance::CreateGLObjects() {
317 // Code and constants for shader.
318 static const char kVertexShader
[] =
319 "varying vec2 v_texCoord; \n"
320 "attribute vec4 a_position; \n"
321 "attribute vec2 a_texCoord; \n"
324 " v_texCoord = a_texCoord; \n"
325 " gl_Position = a_position; \n"
328 static const char kFragmentShaderYUV
[] =
329 "precision mediump float; \n"
330 "varying vec2 v_texCoord; \n"
331 "uniform sampler2D y_texture; \n"
332 "uniform sampler2D u_texture; \n"
333 "uniform sampler2D v_texture; \n"
334 "uniform mat3 color_matrix; \n"
338 " yuv.x = texture2D(y_texture, v_texCoord).r; \n"
339 " yuv.y = texture2D(u_texture, v_texCoord).r; \n"
340 " yuv.z = texture2D(v_texture, v_texCoord).r; \n"
341 " vec3 rgb = color_matrix * (yuv - vec3(0.0625, 0.5, 0.5));\n"
342 " gl_FragColor = vec4(rgb, 1.0); \n"
345 static const char kFragmentShaderRGB
[] =
346 "precision mediump float; \n"
347 "varying vec2 v_texCoord; \n"
348 "uniform sampler2D rgb_texture; \n"
351 " gl_FragColor = texture2D(rgb_texture, v_texCoord); \n"
354 // Create shader programs.
355 program_yuv_
= glCreateProgram();
356 CreateShader(program_yuv_
, GL_VERTEX_SHADER
, kVertexShader
);
357 CreateShader(program_yuv_
, GL_FRAGMENT_SHADER
, kFragmentShaderYUV
);
358 glLinkProgram(program_yuv_
);
361 program_rgb_
= glCreateProgram();
362 CreateShader(program_rgb_
, GL_VERTEX_SHADER
, kVertexShader
);
363 CreateShader(program_rgb_
, GL_FRAGMENT_SHADER
, kFragmentShaderRGB
);
364 glLinkProgram(program_rgb_
);
367 // Assign vertex positions and texture coordinates to buffers for use in
369 static const float kVertices
[] = {
370 -1, 1, -1, -1, 0, 1, 0, -1, // Position coordinates.
371 0, 1, 0, -1, 1, 1, 1, -1, // Position coordinates.
372 0, 0, 0, 1, 1, 0, 1, 1, // Texture coordinates.
373 0, 0, 0, 1, 1, 0, 1, 1, // Texture coordinates.
376 glGenBuffers(1, &buffer_
);
377 glBindBuffer(GL_ARRAY_BUFFER
, buffer_
);
378 glBufferData(GL_ARRAY_BUFFER
, sizeof(kVertices
), kVertices
, GL_STATIC_DRAW
);
382 void MediaStreamVideoDemoInstance::CreateShader(
383 GLuint program
, GLenum type
, const char* source
) {
384 GLuint shader
= glCreateShader(type
);
385 GLint length
= static_cast<GLint
>(strlen(source
) + 1);
386 glShaderSource(shader
, 1, &source
, &length
);
387 glCompileShader(shader
);
388 glAttachShader(program
, shader
);
389 glDeleteShader(shader
);
392 void MediaStreamVideoDemoInstance::CreateTextures() {
393 int32_t width
= frame_size_
.width();
394 int32_t height
= frame_size_
.height();
395 if (width
== 0 || height
== 0)
398 glDeleteTextures(1, &texture_y_
);
400 glDeleteTextures(1, &texture_u_
);
402 glDeleteTextures(1, &texture_v_
);
404 glDeleteTextures(1, &texture_rgb_
);
405 texture_y_
= CreateTexture(width
, height
, 0, false);
407 texture_u_
= CreateTexture(width
/ 2, height
/ 2, 1, false);
408 texture_v_
= CreateTexture(width
/ 2, height
/ 2, 2, false);
409 texture_rgb_
= CreateTexture(width
, height
, 3, true);
412 void MediaStreamVideoDemoInstance::ConfigureTrack() {
413 const int32_t attrib_list
[] = {
414 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT
, attrib_format_
,
415 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH
, attrib_width_
,
416 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT
, attrib_height_
,
417 PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE
419 video_track_
.Configure(attrib_list
, callback_factory_
.NewCallback(
420 &MediaStreamVideoDemoInstance::OnConfigure
));
423 void MediaStreamVideoDemoInstance::OnConfigure(int32_t result
) {
424 video_track_
.GetFrame(callback_factory_
.NewCallbackWithOutput(
425 &MediaStreamVideoDemoInstance::OnGetFrame
));
428 void MediaStreamVideoDemoInstance::OnGetFrame(
429 int32_t result
, pp::VideoFrame frame
) {
432 const char* data
= static_cast<const char*>(frame
.GetDataBuffer());
434 frame
.GetSize(&size
);
436 if (size
!= frame_size_
) {
441 is_bgra_
= (frame
.GetFormat() == PP_VIDEOFRAME_FORMAT_BGRA
);
443 int32_t width
= frame_size_
.width();
444 int32_t height
= frame_size_
.height();
446 glActiveTexture(GL_TEXTURE0
);
447 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, width
, height
,
448 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, data
);
450 data
+= width
* height
;
454 glActiveTexture(GL_TEXTURE1
);
455 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, width
, height
,
456 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, data
);
458 data
+= width
* height
;
459 glActiveTexture(GL_TEXTURE2
);
460 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, width
, height
,
461 GL_LUMINANCE
, GL_UNSIGNED_BYTE
, data
);
463 glActiveTexture(GL_TEXTURE3
);
464 glTexSubImage2D(GL_TEXTURE_2D
, 0, 0, 0, width
, height
,
465 GL_BGRA_EXT
, GL_UNSIGNED_BYTE
, data
);
473 video_track_
.RecycleFrame(frame
);
476 need_config_
= false;
478 video_track_
.GetFrame(callback_factory_
.NewCallbackWithOutput(
479 &MediaStreamVideoDemoInstance::OnGetFrame
));
483 pp::Instance
* MediaStreamVideoModule::CreateInstance(PP_Instance instance
) {
484 return new MediaStreamVideoDemoInstance(instance
, this);
487 } // anonymous namespace
490 // Factory function for your specialization of the Module object.
491 Module
* CreateModule() {
492 return new MediaStreamVideoModule();