diff options
Diffstat (limited to 'chromium/ppapi/examples/media_stream_video/media_stream_video.cc')
-rw-r--r-- | chromium/ppapi/examples/media_stream_video/media_stream_video.cc | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/chromium/ppapi/examples/media_stream_video/media_stream_video.cc b/chromium/ppapi/examples/media_stream_video/media_stream_video.cc new file mode 100644 index 00000000000..c0b60a3ece9 --- /dev/null +++ b/chromium/ppapi/examples/media_stream_video/media_stream_video.cc @@ -0,0 +1,494 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <string.h> + +#include <vector> + +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/ppb_opengles2.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/graphics_3d.h" +#include "ppapi/cpp/graphics_3d_client.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/media_stream_video_track.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/rect.h" +#include "ppapi/cpp/var.h" +#include "ppapi/cpp/var_dictionary.h" +#include "ppapi/cpp/video_frame.h" +#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" +#include "ppapi/utility/completion_callback_factory.h" + +// When compiling natively on Windows, PostMessage can be #define-d to +// something else. +#ifdef PostMessage +#undef PostMessage +#endif + +// Assert |context_| isn't holding any GL Errors. Done as a macro instead of a +// function to preserve line number information in the failure message. +#define AssertNoGLError() \ + PP_DCHECK(!glGetError()); + +namespace { + +// This object is the global object representing this plugin library as long +// as it is loaded. +class MediaStreamVideoModule : public pp::Module { + public: + MediaStreamVideoModule() : pp::Module() {} + virtual ~MediaStreamVideoModule() {} + + virtual pp::Instance* CreateInstance(PP_Instance instance); +}; + +class MediaStreamVideoDemoInstance : public pp::Instance, + public pp::Graphics3DClient { + public: + MediaStreamVideoDemoInstance(PP_Instance instance, pp::Module* module); + virtual ~MediaStreamVideoDemoInstance(); + + // pp::Instance implementation (see PPP_Instance). + virtual void DidChangeView(const pp::Rect& position, + const pp::Rect& clip_ignored); + virtual void HandleMessage(const pp::Var& message_data); + + // pp::Graphics3DClient implementation. + virtual void Graphics3DContextLost() { + InitGL(); + CreateTextures(); + Render(); + } + + private: + void DrawYUV(); + void DrawRGB(); + void Render(); + + // GL-related functions. + void InitGL(); + GLuint CreateTexture(int32_t width, int32_t height, int unit, bool rgba); + void CreateGLObjects(); + void CreateShader(GLuint program, GLenum type, const char* source); + void PaintFinished(int32_t result); + void CreateTextures(); + void ConfigureTrack(); + + + // MediaStreamVideoTrack callbacks. + void OnConfigure(int32_t result); + void OnGetFrame(int32_t result, pp::VideoFrame frame); + + pp::Size position_size_; + bool is_painting_; + bool needs_paint_; + bool is_bgra_; + GLuint program_yuv_; + GLuint program_rgb_; + GLuint buffer_; + GLuint texture_y_; + GLuint texture_u_; + GLuint texture_v_; + GLuint texture_rgb_; + pp::MediaStreamVideoTrack video_track_; + pp::CompletionCallbackFactory<MediaStreamVideoDemoInstance> callback_factory_; + std::vector<int32_t> attrib_list_; + + // MediaStreamVideoTrack attributes: + bool need_config_; + PP_VideoFrame_Format attrib_format_; + int32_t attrib_width_; + int32_t attrib_height_; + + // Owned data. + pp::Graphics3D* context_; + + pp::Size frame_size_; +}; + +MediaStreamVideoDemoInstance::MediaStreamVideoDemoInstance( + PP_Instance instance, pp::Module* module) + : pp::Instance(instance), + pp::Graphics3DClient(this), + is_painting_(false), + needs_paint_(false), + is_bgra_(false), + texture_y_(0), + texture_u_(0), + texture_v_(0), + texture_rgb_(0), + callback_factory_(this), + need_config_(false), + attrib_format_(PP_VIDEOFRAME_FORMAT_I420), + attrib_width_(0), + attrib_height_(0), + context_(NULL) { + if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) { + LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Unable to initialize GL PPAPI!")); + assert(false); + } +} + +MediaStreamVideoDemoInstance::~MediaStreamVideoDemoInstance() { + delete context_; +} + +void MediaStreamVideoDemoInstance::DidChangeView( + const pp::Rect& position, const pp::Rect& clip_ignored) { + if (position.width() == 0 || position.height() == 0) + return; + if (position.size() == position_size_) + return; + + position_size_ = position.size(); + + // Initialize graphics. + InitGL(); + Render(); +} + +void MediaStreamVideoDemoInstance::HandleMessage(const pp::Var& var_message) { + if (!var_message.is_dictionary()) { + LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid message!")); + return; + } + + pp::VarDictionary var_dictionary_message(var_message); + std::string command = var_dictionary_message.Get("command").AsString(); + + if (command == "init") { + pp::Var var_track = var_dictionary_message.Get("track"); + if (!var_track.is_resource()) + return; + pp::Resource resource_track = var_track.AsResource(); + video_track_ = pp::MediaStreamVideoTrack(resource_track); + ConfigureTrack(); + } else if (command == "format") { + std::string str_format = var_dictionary_message.Get("format").AsString(); + if (str_format == "YV12") { + attrib_format_ = PP_VIDEOFRAME_FORMAT_YV12; + } else if (str_format == "I420") { + attrib_format_ = PP_VIDEOFRAME_FORMAT_I420; + } else if (str_format == "BGRA") { + attrib_format_ = PP_VIDEOFRAME_FORMAT_BGRA; + } else { + attrib_format_ = PP_VIDEOFRAME_FORMAT_UNKNOWN; + } + need_config_ = true; + } else if (command == "size") { + attrib_width_ = var_dictionary_message.Get("width").AsInt(); + attrib_height_ = var_dictionary_message.Get("height").AsInt(); + need_config_ = true; + } else { + LogToConsole(PP_LOGLEVEL_ERROR, pp::Var("Invalid command!")); + } +} + +void MediaStreamVideoDemoInstance::InitGL() { + PP_DCHECK(position_size_.width() && position_size_.height()); + is_painting_ = false; + + delete context_; + int32_t attributes[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 0, + PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, + PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, + PP_GRAPHICS3DATTRIB_RED_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, + PP_GRAPHICS3DATTRIB_SAMPLES, 0, + PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, + PP_GRAPHICS3DATTRIB_WIDTH, position_size_.width(), + PP_GRAPHICS3DATTRIB_HEIGHT, position_size_.height(), + PP_GRAPHICS3DATTRIB_NONE, + }; + context_ = new pp::Graphics3D(this, attributes); + PP_DCHECK(!context_->is_null()); + + glSetCurrentContextPPAPI(context_->pp_resource()); + + // Set viewport window size and clear color bit. + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, position_size_.width(), position_size_.height()); + + BindGraphics(*context_); + AssertNoGLError(); + + CreateGLObjects(); +} + +void MediaStreamVideoDemoInstance::DrawYUV() { + static const float kColorMatrix[9] = { + 1.1643828125f, 1.1643828125f, 1.1643828125f, + 0.0f, -0.39176171875f, 2.017234375f, + 1.59602734375f, -0.81296875f, 0.0f + }; + + glUseProgram(program_yuv_); + glUniform1i(glGetUniformLocation(program_yuv_, "y_texture"), 0); + glUniform1i(glGetUniformLocation(program_yuv_, "u_texture"), 1); + glUniform1i(glGetUniformLocation(program_yuv_, "v_texture"), 2); + glUniformMatrix3fv(glGetUniformLocation(program_yuv_, "color_matrix"), + 1, GL_FALSE, kColorMatrix); + AssertNoGLError(); + + GLint pos_location = glGetAttribLocation(program_yuv_, "a_position"); + GLint tc_location = glGetAttribLocation(program_yuv_, "a_texCoord"); + AssertNoGLError(); + glEnableVertexAttribArray(pos_location); + glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(tc_location); + glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, + static_cast<float*>(0) + 16); // Skip position coordinates. + AssertNoGLError(); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + AssertNoGLError(); +} + +void MediaStreamVideoDemoInstance::DrawRGB() { + glUseProgram(program_rgb_); + glUniform1i(glGetUniformLocation(program_rgb_, "rgb_texture"), 3); + AssertNoGLError(); + + GLint pos_location = glGetAttribLocation(program_rgb_, "a_position"); + GLint tc_location = glGetAttribLocation(program_rgb_, "a_texCoord"); + AssertNoGLError(); + glEnableVertexAttribArray(pos_location); + glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(tc_location); + glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, + static_cast<float*>(0) + 16); // Skip position coordinates. + AssertNoGLError(); + + glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); +} + +void MediaStreamVideoDemoInstance::Render() { + PP_DCHECK(!is_painting_); + is_painting_ = true; + needs_paint_ = false; + + if (texture_y_) { + DrawRGB(); + DrawYUV(); + } else { + glClear(GL_COLOR_BUFFER_BIT); + } + pp::CompletionCallback cb = callback_factory_.NewCallback( + &MediaStreamVideoDemoInstance::PaintFinished); + context_->SwapBuffers(cb); +} + +void MediaStreamVideoDemoInstance::PaintFinished(int32_t result) { + is_painting_ = false; + if (needs_paint_) + Render(); +} + +GLuint MediaStreamVideoDemoInstance::CreateTexture( + int32_t width, int32_t height, int unit, bool rgba) { + GLuint texture_id; + glGenTextures(1, &texture_id); + AssertNoGLError(); + + // Assign parameters. + glActiveTexture(GL_TEXTURE0 + unit); + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Allocate texture. + glTexImage2D(GL_TEXTURE_2D, 0, + rgba ? GL_BGRA_EXT : GL_LUMINANCE, + width, height, 0, + rgba ? GL_BGRA_EXT : GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); + AssertNoGLError(); + return texture_id; +} + +void MediaStreamVideoDemoInstance::CreateGLObjects() { + // Code and constants for shader. + static const char kVertexShader[] = + "varying vec2 v_texCoord; \n" + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "void main() \n" + "{ \n" + " v_texCoord = a_texCoord; \n" + " gl_Position = a_position; \n" + "}"; + + static const char kFragmentShaderYUV[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D y_texture; \n" + "uniform sampler2D u_texture; \n" + "uniform sampler2D v_texture; \n" + "uniform mat3 color_matrix; \n" + "void main() \n" + "{ \n" + " vec3 yuv; \n" + " yuv.x = texture2D(y_texture, v_texCoord).r; \n" + " yuv.y = texture2D(u_texture, v_texCoord).r; \n" + " yuv.z = texture2D(v_texture, v_texCoord).r; \n" + " vec3 rgb = color_matrix * (yuv - vec3(0.0625, 0.5, 0.5));\n" + " gl_FragColor = vec4(rgb, 1.0); \n" + "}"; + + static const char kFragmentShaderRGB[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D rgb_texture; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(rgb_texture, v_texCoord); \n" + "}"; + + // Create shader programs. + program_yuv_ = glCreateProgram(); + CreateShader(program_yuv_, GL_VERTEX_SHADER, kVertexShader); + CreateShader(program_yuv_, GL_FRAGMENT_SHADER, kFragmentShaderYUV); + glLinkProgram(program_yuv_); + AssertNoGLError(); + + program_rgb_ = glCreateProgram(); + CreateShader(program_rgb_, GL_VERTEX_SHADER, kVertexShader); + CreateShader(program_rgb_, GL_FRAGMENT_SHADER, kFragmentShaderRGB); + glLinkProgram(program_rgb_); + AssertNoGLError(); + + // Assign vertex positions and texture coordinates to buffers for use in + // shader program. + static const float kVertices[] = { + -1, 1, -1, -1, 0, 1, 0, -1, // Position coordinates. + 0, 1, 0, -1, 1, 1, 1, -1, // Position coordinates. + 0, 0, 0, 1, 1, 0, 1, 1, // Texture coordinates. + 0, 0, 0, 1, 1, 0, 1, 1, // Texture coordinates. + }; + + glGenBuffers(1, &buffer_); + glBindBuffer(GL_ARRAY_BUFFER, buffer_); + glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); + AssertNoGLError(); +} + +void MediaStreamVideoDemoInstance::CreateShader( + GLuint program, GLenum type, const char* source) { + GLuint shader = glCreateShader(type); + GLint length = static_cast<GLint>(strlen(source) + 1); + glShaderSource(shader, 1, &source, &length); + glCompileShader(shader); + glAttachShader(program, shader); + glDeleteShader(shader); +} + +void MediaStreamVideoDemoInstance::CreateTextures() { + int32_t width = frame_size_.width(); + int32_t height = frame_size_.height(); + if (width == 0 || height == 0) + return; + if (texture_y_) + glDeleteTextures(1, &texture_y_); + if (texture_u_) + glDeleteTextures(1, &texture_u_); + if (texture_v_) + glDeleteTextures(1, &texture_v_); + if (texture_rgb_) + glDeleteTextures(1, &texture_rgb_); + texture_y_ = CreateTexture(width, height, 0, false); + + texture_u_ = CreateTexture(width / 2, height / 2, 1, false); + texture_v_ = CreateTexture(width / 2, height / 2, 2, false); + texture_rgb_ = CreateTexture(width, height, 3, true); +} + +void MediaStreamVideoDemoInstance::ConfigureTrack() { + const int32_t attrib_list[] = { + PP_MEDIASTREAMVIDEOTRACK_ATTRIB_FORMAT, attrib_format_, + PP_MEDIASTREAMVIDEOTRACK_ATTRIB_WIDTH, attrib_width_, + PP_MEDIASTREAMVIDEOTRACK_ATTRIB_HEIGHT, attrib_height_, + PP_MEDIASTREAMVIDEOTRACK_ATTRIB_NONE + }; + video_track_.Configure(attrib_list, callback_factory_.NewCallback( + &MediaStreamVideoDemoInstance::OnConfigure)); +} + +void MediaStreamVideoDemoInstance::OnConfigure(int32_t result) { + video_track_.GetFrame(callback_factory_.NewCallbackWithOutput( + &MediaStreamVideoDemoInstance::OnGetFrame)); +} + +void MediaStreamVideoDemoInstance::OnGetFrame( + int32_t result, pp::VideoFrame frame) { + if (result != PP_OK) + return; + const char* data = static_cast<const char*>(frame.GetDataBuffer()); + pp::Size size; + frame.GetSize(&size); + + if (size != frame_size_) { + frame_size_ = size; + CreateTextures(); + } + + is_bgra_ = (frame.GetFormat() == PP_VIDEOFRAME_FORMAT_BGRA); + + int32_t width = frame_size_.width(); + int32_t height = frame_size_.height(); + if (!is_bgra_) { + glActiveTexture(GL_TEXTURE0); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + + data += width * height; + width /= 2; + height /= 2; + + glActiveTexture(GL_TEXTURE1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + + data += width * height; + glActiveTexture(GL_TEXTURE2); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + glActiveTexture(GL_TEXTURE3); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + GL_BGRA_EXT, GL_UNSIGNED_BYTE, data); + } + + if (is_painting_) + needs_paint_ = true; + else + Render(); + + video_track_.RecycleFrame(frame); + if (need_config_) { + ConfigureTrack(); + need_config_ = false; + } else { + video_track_.GetFrame(callback_factory_.NewCallbackWithOutput( + &MediaStreamVideoDemoInstance::OnGetFrame)); + } +} + +pp::Instance* MediaStreamVideoModule::CreateInstance(PP_Instance instance) { + return new MediaStreamVideoDemoInstance(instance, this); +} + +} // anonymous namespace + +namespace pp { +// Factory function for your specialization of the Module object. +Module* CreateModule() { + return new MediaStreamVideoModule(); +} +} // namespace pp |