diff --git a/talk/app/webrtc/legacy_objc_api.gyp b/talk/app/webrtc/legacy_objc_api.gyp index c55e74c29d..f681906c9f 100755 --- a/talk/app/webrtc/legacy_objc_api.gyp +++ b/talk/app/webrtc/legacy_objc_api.gyp @@ -157,8 +157,6 @@ 'OTHER_LDFLAGS': [ '-framework CoreGraphics', '-framework GLKit', - '-framework OpenGLES', - '-framework QuartzCore', ], }, }, diff --git a/webrtc/BUILD.gn b/webrtc/BUILD.gn index 3ae09da2c9..9be2aa0c01 100644 --- a/webrtc/BUILD.gn +++ b/webrtc/BUILD.gn @@ -204,7 +204,10 @@ source_set("webrtc") { ] if (build_with_chromium) { - deps += [ "modules/video_capture" ] + deps += [ + "modules/video_capture", + "modules/video_render", + ] } if (rtc_enable_protobuf) { @@ -219,6 +222,7 @@ if (!build_with_chromium) { deps = [ ":webrtc", "modules/video_capture:video_capture_internal_impl", + "modules/video_render:video_render_internal_impl", "test", ] } diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp index 1267595327..0341741e72 100644 --- a/webrtc/api/api.gyp +++ b/webrtc/api/api.gyp @@ -115,7 +115,7 @@ 'java/android', '<(webrtc_base_dir)/java/src', '<(webrtc_modules_dir)/audio_device/android/java/src', - + '<(webrtc_modules_dir)/video_render/android/java/src', ], }, 'includes': ['../../build/java.gypi'], diff --git a/webrtc/api/java/jni/peerconnection_jni.cc b/webrtc/api/java/jni/peerconnection_jni.cc index 228e5e1ab1..c5154c95d2 100644 --- a/webrtc/api/java/jni/peerconnection_jni.cc +++ b/webrtc/api/java/jni/peerconnection_jni.cc @@ -70,6 +70,7 @@ #include "webrtc/media/devices/videorendererfactory.h" #include "webrtc/media/engine/webrtcvideodecoderfactory.h" #include "webrtc/media/engine/webrtcvideoencoderfactory.h" +#include "webrtc/modules/video_render/video_render_internal.h" #include "webrtc/system_wrappers/include/field_trial_default.h" #include "webrtc/system_wrappers/include/logcat_trace_context.h" #include "webrtc/system_wrappers/include/trace.h" diff --git a/webrtc/media/media.gyp b/webrtc/media/media.gyp index 230d33ab38..5c4a24891f 100644 --- a/webrtc/media/media.gyp +++ b/webrtc/media/media.gyp @@ -15,6 +15,7 @@ 'dependencies': [ '<(webrtc_root)/base/base.gyp:rtc_base_approved', '<(webrtc_root)/common.gyp:webrtc_common', + '<(webrtc_root)/modules/modules.gyp:video_render_module', '<(webrtc_root)/webrtc.gyp:webrtc', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', '<(webrtc_root)/system_wrappers/system_wrappers.gyp:metrics_default', @@ -132,6 +133,7 @@ ['build_with_chromium==1', { 'dependencies': [ '<(webrtc_root)/modules/modules.gyp:video_capture', + '<(webrtc_root)/modules/modules.gyp:video_render', ], }, { 'defines': [ @@ -146,6 +148,7 @@ }, 'dependencies': [ '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', + '<(webrtc_root)/modules/modules.gyp:video_render_module_internal_impl', ], }], ['OS=="linux" and use_gtk==1', { diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index de472720da..502454cef8 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -26,6 +26,7 @@ 'video_coding/video_coding.gypi', 'video_capture/video_capture.gypi', 'video_processing/video_processing.gypi', + 'video_render/video_render.gypi', ], 'conditions': [ ['include_tests==1', { @@ -780,6 +781,19 @@ 'modules_unittests.isolate', ], }, + { + 'target_name': 'video_render_tests_run', + 'type': 'none', + 'dependencies': [ + 'video_render_tests', + ], + 'includes': [ + '../build/isolate.gypi', + ], + 'sources': [ + 'video_render_tests.isolate', + ], + }, ], }], ], diff --git a/webrtc/modules/modules_java.gyp b/webrtc/modules/modules_java.gyp index 2a72fb30a4..060de2a067 100644 --- a/webrtc/modules/modules_java.gyp +++ b/webrtc/modules/modules_java.gyp @@ -18,5 +18,14 @@ 'includes': [ '../../build/java.gypi' ], }, # audio_device_module_java + { + 'target_name': 'video_render_module_java', + 'type': 'none', + 'variables': { + 'java_in_dir': 'video_render/android/java', + 'additional_src_dirs': [ '../base/java/src', ], + }, + 'includes': [ '../../build/java.gypi' ], + }, # video_render_module_java ], } diff --git a/webrtc/modules/modules_java_chromium.gyp b/webrtc/modules/modules_java_chromium.gyp index ebc53d60ff..32d2d8d24e 100644 --- a/webrtc/modules/modules_java_chromium.gyp +++ b/webrtc/modules/modules_java_chromium.gyp @@ -16,5 +16,13 @@ }, 'includes': [ '../../../build/java.gypi' ], }, # audio_device_module_java + { + 'target_name': 'video_render_module_java', + 'type': 'none', + 'variables': { + 'java_in_dir': 'video_render/android/java', + }, + 'includes': [ '../../../build/java.gypi' ], + }, # video_render_module_java ], } diff --git a/webrtc/modules/video_render/BUILD.gn b/webrtc/modules/video_render/BUILD.gn new file mode 100644 index 0000000000..0771bd7080 --- /dev/null +++ b/webrtc/modules/video_render/BUILD.gn @@ -0,0 +1,178 @@ +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../build/webrtc.gni") + +source_set("video_render_module") { + sources = [ + "external/video_render_external_impl.cc", + "external/video_render_external_impl.h", + "i_video_render.h", + "video_render.h", + "video_render_defines.h", + "video_render_impl.h", + ] + + deps = [ + "../..:webrtc_common", + "../../common_video", + "../../system_wrappers", + "../utility", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +source_set("video_render") { + sources = [ + "video_render_impl.cc", + ] + deps = [ + ":video_render_module", + "../../system_wrappers", + ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } +} + +if (!build_with_chromium) { + config("video_render_internal_impl_config") { + if (is_ios) { + libs = [ + "OpenGLES.framework", + "QuartzCore.framework", + ] + } + } + + source_set("video_render_internal_impl") { + libs = [] + sources = [ + "video_render_internal_impl.cc", + ] + deps = [ + ":video_render_module", + "../../system_wrappers", + ] + + if (is_linux) { + sources += [ + "linux/video_render_linux_impl.cc", + "linux/video_render_linux_impl.h", + "linux/video_x11_channel.cc", + "linux/video_x11_channel.h", + "linux/video_x11_render.cc", + "linux/video_x11_render.h", + ] + + deps += [ "../..:webrtc_common" ] + + libs += [ "Xext" ] + } + if (is_mac) { + sources += [ + "mac/cocoa_full_screen_window.h", + "mac/cocoa_full_screen_window.mm", + "mac/cocoa_render_view.h", + "mac/cocoa_render_view.mm", + "mac/video_render_agl.cc", + "mac/video_render_agl.h", + "mac/video_render_mac_carbon_impl.cc", + "mac/video_render_mac_carbon_impl.h", + "mac/video_render_mac_cocoa_impl.h", + "mac/video_render_mac_cocoa_impl.mm", + "mac/video_render_nsopengl.h", + "mac/video_render_nsopengl.mm", + ] + + libs += [ + "CoreVideo.framework", + "QTKit.framework", + ] + } + if (is_win) { + sources += [ + "windows/i_video_render_win.h", + "windows/video_render_direct3d9.cc", + "windows/video_render_direct3d9.h", + "windows/video_render_windows_impl.cc", + "windows/video_render_windows_impl.h", + ] + + directxsdk_exists = + exec_script("//build/dir_exists.py", + [ rebase_path("//third_party/directxsdk/files", + root_build_dir) ], + "trim string") == "True" + if (directxsdk_exists) { + directxsdk_path = "//third_party/directxsdk/files" + } else { + directxsdk_path = + exec_script("../../build/find_directx_sdk.py", [], "trim string") + } + include_dirs = [ directxsdk_path + "/Include" ] + } + if (is_android) { + sources += [ + "android/video_render_android_impl.cc", + "android/video_render_android_impl.h", + "android/video_render_android_native_opengl2.cc", + "android/video_render_android_native_opengl2.h", + "android/video_render_android_surface_view.cc", + "android/video_render_android_surface_view.h", + "android/video_render_opengles20.cc", + "android/video_render_opengles20.h", + ] + + libs += [ "GLESv2" ] + } + if (is_ios) { + sources += [ + "ios/open_gles20.h", + "ios/open_gles20.mm", + "ios/video_render_ios_channel.h", + "ios/video_render_ios_channel.mm", + "ios/video_render_ios_gles20.h", + "ios/video_render_ios_gles20.mm", + "ios/video_render_ios_impl.h", + "ios/video_render_ios_impl.mm", + "ios/video_render_ios_view.h", + "ios/video_render_ios_view.mm", + ] + + deps += [ "../..:webrtc_common" ] + + cflags = [ "-fobjc-arc" ] # CLANG_ENABLE_OBJC_ARC = YES. + } + + all_dependent_configs = [ ":video_render_internal_impl_config" ] + + configs += [ "../..:common_config" ] + public_configs = [ "../..:common_inherited_config" ] + + if (is_clang) { + # Suppress warnings from Chrome's Clang plugins. + # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. + configs -= [ "//build/config/clang:find_bad_constructs" ] + } + } +} diff --git a/webrtc/modules/video_render/DEPS b/webrtc/modules/video_render/DEPS new file mode 100644 index 0000000000..58ae9fe714 --- /dev/null +++ b/webrtc/modules/video_render/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+webrtc/base", + "+webrtc/common_video", + "+webrtc/system_wrappers", +] diff --git a/webrtc/modules/video_render/OWNERS b/webrtc/modules/video_render/OWNERS new file mode 100644 index 0000000000..3aaa5328f5 --- /dev/null +++ b/webrtc/modules/video_render/OWNERS @@ -0,0 +1,12 @@ +mflodman@webrtc.org +perkj@webrtc.org +tkchin@webrtc.org + +per-file *.isolate=kjellander@webrtc.org + +# These are for the common case of adding or renaming files. If you're doing +# structural changes, please get a review from a reviewer in this file. +per-file *.gyp=* +per-file *.gypi=* + +per-file BUILD.gn=kjellander@webrtc.org diff --git a/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java new file mode 100644 index 0000000000..fa756ba67f --- /dev/null +++ b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViEAndroidGLES20.java @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.videoengine; + +import java.util.concurrent.locks.ReentrantLock; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.opengles.GL10; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ConfigurationInfo; +import android.graphics.PixelFormat; +import android.opengl.GLSurfaceView; + +import org.webrtc.Logging; + +public class ViEAndroidGLES20 extends GLSurfaceView + implements GLSurfaceView.Renderer { + private static String TAG = "WEBRTC-JR"; + private static final boolean DEBUG = false; + // True if onSurfaceCreated has been called. + private boolean surfaceCreated = false; + private boolean openGLCreated = false; + // True if NativeFunctionsRegistered has been called. + private boolean nativeFunctionsRegisted = false; + private ReentrantLock nativeFunctionLock = new ReentrantLock(); + // Address of Native object that will do the drawing. + private long nativeObject = 0; + private int viewWidth = 0; + private int viewHeight = 0; + + public static boolean UseOpenGL2(Object renderWindow) { + return ViEAndroidGLES20.class.isInstance(renderWindow); + } + + public ViEAndroidGLES20(Context context) { + super(context); + init(false, 0, 0); + } + + public ViEAndroidGLES20(Context context, boolean translucent, + int depth, int stencil) { + super(context); + init(translucent, depth, stencil); + } + + private void init(boolean translucent, int depth, int stencil) { + + // By default, GLSurfaceView() creates a RGB_565 opaque surface. + // If we want a translucent one, we should change the surface's + // format here, using PixelFormat.TRANSLUCENT for GL Surfaces + // is interpreted as any 32-bit surface with alpha by SurfaceFlinger. + if (translucent) { + this.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + + // Setup the context factory for 2.0 rendering. + // See ContextFactory class definition below + setEGLContextFactory(new ContextFactory()); + + // We need to choose an EGLConfig that matches the format of + // our surface exactly. This is going to be done in our + // custom config chooser. See ConfigChooser class definition + // below. + setEGLConfigChooser( translucent ? + new ConfigChooser(8, 8, 8, 8, depth, stencil) : + new ConfigChooser(5, 6, 5, 0, depth, stencil) ); + + // Set the renderer responsible for frame rendering + this.setRenderer(this); + this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + } + + private static class ContextFactory implements GLSurfaceView.EGLContextFactory { + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + Logging.w(TAG, "creating OpenGL ES 2.0 context"); + checkEglError("Before eglCreateContext", egl); + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + EGLContext context = egl.eglCreateContext(display, eglConfig, + EGL10.EGL_NO_CONTEXT, attrib_list); + checkEglError("After eglCreateContext", egl); + return context; + } + + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } + } + + private static void checkEglError(String prompt, EGL10 egl) { + int error; + while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { + Logging.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + } + } + + private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { + + public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) { + mRedSize = r; + mGreenSize = g; + mBlueSize = b; + mAlphaSize = a; + mDepthSize = depth; + mStencilSize = stencil; + } + + // This EGL config specification is used to specify 2.0 rendering. + // We use a minimum size of 4 bits for red/green/blue, but will + // perform actual matching in chooseConfig() below. + private static int EGL_OPENGL_ES2_BIT = 4; + private static int[] s_configAttribs2 = + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + + // Get the number of minimally matching EGL configurations + int[] num_config = new int[1]; + egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config); + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + // Allocate then read the array of minimally matching EGL configs + EGLConfig[] configs = new EGLConfig[numConfigs]; + egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config); + + if (DEBUG) { + printConfigs(egl, display, configs); + } + // Now return the "best" one + return chooseConfig(egl, display, configs); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + for(EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + + // We need at least mDepthSize and mStencilSize bits + if (d < mDepthSize || s < mStencilSize) + continue; + + // We want an *exact* match for red/green/blue/alpha + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) + return config; + } + return null; + } + + private int findConfigAttrib(EGL10 egl, EGLDisplay display, + EGLConfig config, int attribute, int defaultValue) { + + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { + return mValue[0]; + } + return defaultValue; + } + + private void printConfigs(EGL10 egl, EGLDisplay display, + EGLConfig[] configs) { + int numConfigs = configs.length; + Logging.w(TAG, String.format("%d configurations", numConfigs)); + for (int i = 0; i < numConfigs; i++) { + Logging.w(TAG, String.format("Configuration %d:\n", i)); + printConfig(egl, display, configs[i]); + } + } + + private void printConfig(EGL10 egl, EGLDisplay display, + EGLConfig config) { + int[] attributes = { + EGL10.EGL_BUFFER_SIZE, + EGL10.EGL_ALPHA_SIZE, + EGL10.EGL_BLUE_SIZE, + EGL10.EGL_GREEN_SIZE, + EGL10.EGL_RED_SIZE, + EGL10.EGL_DEPTH_SIZE, + EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_CONFIG_ID, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_HEIGHT, + EGL10.EGL_MAX_PBUFFER_PIXELS, + EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_NATIVE_RENDERABLE, + EGL10.EGL_NATIVE_VISUAL_ID, + EGL10.EGL_NATIVE_VISUAL_TYPE, + 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, + EGL10.EGL_SAMPLES, + EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, + EGL10.EGL_TRANSPARENT_RED_VALUE, + EGL10.EGL_TRANSPARENT_GREEN_VALUE, + EGL10.EGL_TRANSPARENT_BLUE_VALUE, + 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB, + 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA, + 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL, + 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL, + EGL10.EGL_LUMINANCE_SIZE, + EGL10.EGL_ALPHA_MASK_SIZE, + EGL10.EGL_COLOR_BUFFER_TYPE, + EGL10.EGL_RENDERABLE_TYPE, + 0x3042 // EGL10.EGL_CONFORMANT + }; + String[] names = { + "EGL_BUFFER_SIZE", + "EGL_ALPHA_SIZE", + "EGL_BLUE_SIZE", + "EGL_GREEN_SIZE", + "EGL_RED_SIZE", + "EGL_DEPTH_SIZE", + "EGL_STENCIL_SIZE", + "EGL_CONFIG_CAVEAT", + "EGL_CONFIG_ID", + "EGL_LEVEL", + "EGL_MAX_PBUFFER_HEIGHT", + "EGL_MAX_PBUFFER_PIXELS", + "EGL_MAX_PBUFFER_WIDTH", + "EGL_NATIVE_RENDERABLE", + "EGL_NATIVE_VISUAL_ID", + "EGL_NATIVE_VISUAL_TYPE", + "EGL_PRESERVED_RESOURCES", + "EGL_SAMPLES", + "EGL_SAMPLE_BUFFERS", + "EGL_SURFACE_TYPE", + "EGL_TRANSPARENT_TYPE", + "EGL_TRANSPARENT_RED_VALUE", + "EGL_TRANSPARENT_GREEN_VALUE", + "EGL_TRANSPARENT_BLUE_VALUE", + "EGL_BIND_TO_TEXTURE_RGB", + "EGL_BIND_TO_TEXTURE_RGBA", + "EGL_MIN_SWAP_INTERVAL", + "EGL_MAX_SWAP_INTERVAL", + "EGL_LUMINANCE_SIZE", + "EGL_ALPHA_MASK_SIZE", + "EGL_COLOR_BUFFER_TYPE", + "EGL_RENDERABLE_TYPE", + "EGL_CONFORMANT" + }; + int[] value = new int[1]; + for (int i = 0; i < attributes.length; i++) { + int attribute = attributes[i]; + String name = names[i]; + if (egl.eglGetConfigAttrib(display, config, attribute, value)) { + Logging.w(TAG, String.format(" %s: %d\n", name, value[0])); + } else { + // Logging.w(TAG, String.format(" %s: failed\n", name)); + while (egl.eglGetError() != EGL10.EGL_SUCCESS); + } + } + } + + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + private int[] mValue = new int[1]; + } + + // IsSupported + // Return true if this device support Open GL ES 2.0 rendering. + public static boolean IsSupported(Context context) { + ActivityManager am = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + ConfigurationInfo info = am.getDeviceConfigurationInfo(); + if(info.reqGlEsVersion >= 0x20000) { + // Open GL ES 2.0 is supported. + return true; + } + return false; + } + + public void onDrawFrame(GL10 gl) { + nativeFunctionLock.lock(); + if(!nativeFunctionsRegisted || !surfaceCreated) { + nativeFunctionLock.unlock(); + return; + } + + if(!openGLCreated) { + if(0 != CreateOpenGLNative(nativeObject, viewWidth, viewHeight)) { + return; // Failed to create OpenGL + } + openGLCreated = true; // Created OpenGL successfully + } + DrawNative(nativeObject); // Draw the new frame + nativeFunctionLock.unlock(); + } + + public void onSurfaceChanged(GL10 gl, int width, int height) { + surfaceCreated = true; + viewWidth = width; + viewHeight = height; + + nativeFunctionLock.lock(); + if(nativeFunctionsRegisted) { + if(CreateOpenGLNative(nativeObject,width,height) == 0) + openGLCreated = true; + } + nativeFunctionLock.unlock(); + } + + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + } + + public void RegisterNativeObject(long nativeObject) { + nativeFunctionLock.lock(); + this.nativeObject = nativeObject; + nativeFunctionsRegisted = true; + nativeFunctionLock.unlock(); + } + + public void DeRegisterNativeObject() { + nativeFunctionLock.lock(); + nativeFunctionsRegisted = false; + openGLCreated = false; + this.nativeObject = 0; + nativeFunctionLock.unlock(); + } + + public void ReDraw() { + if(surfaceCreated) { + // Request the renderer to redraw using the render thread context. + this.requestRender(); + } + } + + private native int CreateOpenGLNative(long nativeObject, + int width, int height); + private native void DrawNative(long nativeObject); + +} diff --git a/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java new file mode 100644 index 0000000000..50b1a595cc --- /dev/null +++ b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViERenderer.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.videoengine; + +import android.content.Context; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class ViERenderer { + public static SurfaceView CreateRenderer(Context context) { + return CreateRenderer(context, false); + } + + public static SurfaceView CreateRenderer(Context context, + boolean useOpenGLES2) { + if(useOpenGLES2 == true && ViEAndroidGLES20.IsSupported(context)) + return new ViEAndroidGLES20(context); + else + return new SurfaceView(context); + } +} diff --git a/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java new file mode 100644 index 0000000000..71f26c259f --- /dev/null +++ b/webrtc/modules/video_render/android/java/src/org/webrtc/videoengine/ViESurfaceRenderer.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.videoengine; + +// The following four imports are needed saveBitmapToJPEG which +// is for debug only +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.SurfaceHolder.Callback; + +import org.webrtc.Logging; + +public class ViESurfaceRenderer implements Callback { + + private final static String TAG = "WEBRTC"; + + // the bitmap used for drawing. + private Bitmap bitmap = null; + private ByteBuffer byteBuffer = null; + private SurfaceHolder surfaceHolder; + // Rect of the source bitmap to draw + private Rect srcRect = new Rect(); + // Rect of the destination canvas to draw to + private Rect dstRect = new Rect(); + private float dstTopScale = 0; + private float dstBottomScale = 1; + private float dstLeftScale = 0; + private float dstRightScale = 1; + + public ViESurfaceRenderer(SurfaceView view) { + surfaceHolder = view.getHolder(); + if(surfaceHolder == null) + return; + surfaceHolder.addCallback(this); + } + + // surfaceChanged and surfaceCreated share this function + private void changeDestRect(int dstWidth, int dstHeight) { + dstRect.right = (int)(dstRect.left + dstRightScale * dstWidth); + dstRect.bottom = (int)(dstRect.top + dstBottomScale * dstHeight); + } + + public void surfaceChanged(SurfaceHolder holder, int format, + int in_width, int in_height) { + Logging.d(TAG, "ViESurfaceRender::surfaceChanged"); + + changeDestRect(in_width, in_height); + + Logging.d(TAG, "ViESurfaceRender::surfaceChanged" + + " in_width:" + in_width + " in_height:" + in_height + + " srcRect.left:" + srcRect.left + + " srcRect.top:" + srcRect.top + + " srcRect.right:" + srcRect.right + + " srcRect.bottom:" + srcRect.bottom + + " dstRect.left:" + dstRect.left + + " dstRect.top:" + dstRect.top + + " dstRect.right:" + dstRect.right + + " dstRect.bottom:" + dstRect.bottom); + } + + public void surfaceCreated(SurfaceHolder holder) { + Canvas canvas = surfaceHolder.lockCanvas(); + if(canvas != null) { + Rect dst = surfaceHolder.getSurfaceFrame(); + if(dst != null) { + changeDestRect(dst.right - dst.left, dst.bottom - dst.top); + Logging.d(TAG, "ViESurfaceRender::surfaceCreated" + + " dst.left:" + dst.left + + " dst.top:" + dst.top + + " dst.right:" + dst.right + + " dst.bottom:" + dst.bottom + + " srcRect.left:" + srcRect.left + + " srcRect.top:" + srcRect.top + + " srcRect.right:" + srcRect.right + + " srcRect.bottom:" + srcRect.bottom + + " dstRect.left:" + dstRect.left + + " dstRect.top:" + dstRect.top + + " dstRect.right:" + dstRect.right + + " dstRect.bottom:" + dstRect.bottom); + } + surfaceHolder.unlockCanvasAndPost(canvas); + } + } + + public void surfaceDestroyed(SurfaceHolder holder) { + Logging.d(TAG, "ViESurfaceRenderer::surfaceDestroyed"); + bitmap = null; + byteBuffer = null; + } + + public Bitmap CreateBitmap(int width, int height) { + Logging.d(TAG, "CreateByteBitmap " + width + ":" + height); + if (bitmap == null) { + try { + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_DISPLAY); + } + catch (Exception e) { + } + } + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + srcRect.left = 0; + srcRect.top = 0; + srcRect.bottom = height; + srcRect.right = width; + return bitmap; + } + + public ByteBuffer CreateByteBuffer(int width, int height) { + Logging.d(TAG, "CreateByteBuffer " + width + ":" + height); + if (bitmap == null) { + bitmap = CreateBitmap(width, height); + byteBuffer = ByteBuffer.allocateDirect(width * height * 2); + } + return byteBuffer; + } + + public void SetCoordinates(float left, float top, + float right, float bottom) { + Logging.d(TAG, "SetCoordinates " + left + "," + top + ":" + + right + "," + bottom); + dstLeftScale = left; + dstTopScale = top; + dstRightScale = right; + dstBottomScale = bottom; + } + + // It saves bitmap data to a JPEG picture, this function is for debug only. + private void saveBitmapToJPEG(int width, int height) { + ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteOutStream); + + try{ + FileOutputStream output = new FileOutputStream(String.format( + "/sdcard/render_%d.jpg", System.currentTimeMillis())); + output.write(byteOutStream.toByteArray()); + output.flush(); + output.close(); + } + catch (FileNotFoundException e) { + } + catch (IOException e) { + } + } + + public void DrawByteBuffer() { + if(byteBuffer == null) + return; + byteBuffer.rewind(); + bitmap.copyPixelsFromBuffer(byteBuffer); + DrawBitmap(); + } + + public void DrawBitmap() { + if(bitmap == null) + return; + + Canvas canvas = surfaceHolder.lockCanvas(); + if(canvas != null) { + // The follow line is for debug only + // saveBitmapToJPEG(srcRect.right - srcRect.left, + // srcRect.bottom - srcRect.top); + canvas.drawBitmap(bitmap, srcRect, dstRect, null); + surfaceHolder.unlockCanvasAndPost(canvas); + } + } + +} diff --git a/webrtc/modules/video_render/android/video_render_android_impl.cc b/webrtc/modules/video_render/android/video_render_android_impl.cc new file mode 100644 index 0000000000..9affb23d99 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_impl.cc @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/android/video_render_android_impl.h" + +#include "webrtc/modules/video_render/video_render_internal.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/event_wrapper.h" +#include "webrtc/system_wrappers/include/tick_util.h" + +#ifdef ANDROID +#include +#include + +#undef WEBRTC_TRACE +#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) +#else +#include "webrtc/system_wrappers/include/trace.h" +#endif + +namespace webrtc { + +JavaVM* VideoRenderAndroid::g_jvm = NULL; + +int32_t SetRenderAndroidVM(JavaVM* javaVM) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, "%s", __FUNCTION__); + VideoRenderAndroid::g_jvm = javaVM; + return 0; +} + +VideoRenderAndroid::VideoRenderAndroid( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool /*fullscreen*/): + _id(id), + _critSect(*CriticalSectionWrapper::CreateCriticalSection()), + _renderType(videoRenderType), + _ptrWindow((jobject)(window)), + _javaShutDownFlag(false), + _javaShutdownEvent(*EventWrapper::Create()), + _javaRenderEvent(*EventWrapper::Create()), + _lastJavaRenderEvent(0), + _javaRenderJniEnv(NULL) { +} + +VideoRenderAndroid::~VideoRenderAndroid() { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "VideoRenderAndroid dtor"); + + if (_javaRenderThread) + StopRender(); + + for (AndroidStreamMap::iterator it = _streamsMap.begin(); + it != _streamsMap.end(); + ++it) { + delete it->second; + } + delete &_javaShutdownEvent; + delete &_javaRenderEvent; + delete &_critSect; +} + +int32_t VideoRenderAndroid::ChangeWindow(void* /*window*/) { + return -1; +} + +VideoRenderCallback* +VideoRenderAndroid::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, + const float bottom) { + CriticalSectionScoped cs(&_critSect); + + AndroidStream* renderStream = NULL; + AndroidStreamMap::iterator item = _streamsMap.find(streamId); + if (item != _streamsMap.end() && item->second != NULL) { + WEBRTC_TRACE(kTraceInfo, + kTraceVideoRenderer, + -1, + "%s: Render stream already exists", + __FUNCTION__); + return renderStream; + } + + renderStream = CreateAndroidRenderChannel(streamId, zOrder, left, top, + right, bottom, *this); + if (renderStream) { + _streamsMap[streamId] = renderStream; + } + else { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); + return NULL; + } + return renderStream; +} + +int32_t VideoRenderAndroid::DeleteIncomingRenderStream( + const uint32_t streamId) { + CriticalSectionScoped cs(&_critSect); + + AndroidStreamMap::iterator item = _streamsMap.find(streamId); + if (item == _streamsMap.end()) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "(%s:%d): renderStream is NULL", __FUNCTION__, __LINE__); + return -1; + } + delete item->second; + _streamsMap.erase(item); + return 0; +} + +int32_t VideoRenderAndroid::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const { + return -1; +} + +int32_t VideoRenderAndroid::StartRender() { + CriticalSectionScoped cs(&_critSect); + + if (_javaRenderThread) { + // StartRender is called when this stream should start render. + // However StopRender is not called when the streams stop rendering. + // Thus the the thread is only deleted when the renderer is removed. + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s, Render thread already exist", __FUNCTION__); + return 0; + } + + _javaRenderThread.reset(new rtc::PlatformThread(JavaRenderThreadFun, this, + "AndroidRenderThread")); + + _javaRenderThread->Start(); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: thread started", + __FUNCTION__); + _javaRenderThread->SetPriority(rtc::kRealtimePriority); + return 0; +} + +int32_t VideoRenderAndroid::StopRender() { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:", __FUNCTION__); + { + CriticalSectionScoped cs(&_critSect); + if (!_javaRenderThread) + { + return -1; + } + _javaShutDownFlag = true; + _javaRenderEvent.Set(); + } + + _javaShutdownEvent.Wait(3000); + CriticalSectionScoped cs(&_critSect); + _javaRenderThread->Stop(); + _javaRenderThread.reset(); + + return 0; +} + +void VideoRenderAndroid::ReDraw() { + CriticalSectionScoped cs(&_critSect); + // Allow redraw if it was more than 20ms since last. + if (_lastJavaRenderEvent < TickTime::MillisecondTimestamp() - 20) { + _lastJavaRenderEvent = TickTime::MillisecondTimestamp(); + _javaRenderEvent.Set(); + } +} + +bool VideoRenderAndroid::JavaRenderThreadFun(void* obj) { + return static_cast (obj)->JavaRenderThreadProcess(); +} + +bool VideoRenderAndroid::JavaRenderThreadProcess() +{ + _javaRenderEvent.Wait(1000); + + CriticalSectionScoped cs(&_critSect); + if (!_javaRenderJniEnv) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&_javaRenderJniEnv, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !_javaRenderJniEnv) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, res, _javaRenderJniEnv); + return false; + } + } + + for (AndroidStreamMap::iterator it = _streamsMap.begin(); + it != _streamsMap.end(); + ++it) { + it->second->DeliverFrame(_javaRenderJniEnv); + } + + if (_javaShutDownFlag) { + if (g_jvm->DetachCurrentThread() < 0) + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "%s: Could not detach thread from JVM", __FUNCTION__); + else { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "%s: Java thread detached", __FUNCTION__); + } + _javaRenderJniEnv = NULL; + _javaShutDownFlag = false; + _javaShutdownEvent.Set(); + return false; // Do not run this thread again. + } + return true; +} + +VideoRenderType VideoRenderAndroid::RenderType() { + return _renderType; +} + +RawVideoType VideoRenderAndroid::PerferedVideoType() { + return kVideoI420; +} + +bool VideoRenderAndroid::FullScreen() { + return false; +} + +int32_t VideoRenderAndroid::GetGraphicsMemory( + uint64_t& /*totalGraphicsMemory*/, + uint64_t& /*availableGraphicsMemory*/) const { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::GetScreenResolution( + uint32_t& /*screenWidth*/, + uint32_t& /*screenHeight*/) const { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +uint32_t VideoRenderAndroid::RenderFrameRate( + const uint32_t /*streamId*/) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::SetStreamCropping( + const uint32_t /*streamId*/, + const float /*left*/, + const float /*top*/, + const float /*right*/, + const float /*bottom*/) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::SetTransparentBackground(const bool enable) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float rigth, const float bottom) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +int32_t VideoRenderAndroid::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, + const float bottom) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Android", __FUNCTION__); + return -1; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/android/video_render_android_impl.h b/webrtc/modules/video_render/android/video_render_android_impl.h new file mode 100644 index 0000000000..06fd7a1c7c --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_impl.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ + +#include + +#include +#include + +#include "webrtc/base/platform_thread.h" +#include "webrtc/modules/video_render/i_video_render.h" + + +namespace webrtc { + +//#define ANDROID_LOG + +class CriticalSectionWrapper; +class EventWrapper; + +// The object a module user uses to send new frames to the java renderer +// Base class for android render streams. + +class AndroidStream : public VideoRenderCallback { + public: + // DeliverFrame is called from a thread connected to the Java VM. + // Used for Delivering frame for rendering. + virtual void DeliverFrame(JNIEnv* jniEnv)=0; + + virtual ~AndroidStream() {}; +}; + +class VideoRenderAndroid: IVideoRender { + public: + VideoRenderAndroid(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen); + + virtual ~VideoRenderAndroid(); + + virtual int32_t Init()=0; + + virtual int32_t ChangeWindow(void* window); + + virtual VideoRenderCallback* AddIncomingRenderStream( + const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t DeleteIncomingRenderStream( + const uint32_t streamId); + + virtual int32_t GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const; + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + virtual void ReDraw(); + + // Properties + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t GetGraphicsMemory( + uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float rigth, const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, const float left, + const float top, const float right, + const float bottom); + static JavaVM* g_jvm; + + protected: + virtual AndroidStream* CreateAndroidRenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom, + VideoRenderAndroid& renderer) = 0; + + int32_t _id; + CriticalSectionWrapper& _critSect; + VideoRenderType _renderType; + jobject _ptrWindow; + + private: + static bool JavaRenderThreadFun(void* obj); + bool JavaRenderThreadProcess(); + + // Map with streams to render. + typedef std::map AndroidStreamMap; + AndroidStreamMap _streamsMap; + // True if the _javaRenderThread thread shall be detached from the JVM. + bool _javaShutDownFlag; + EventWrapper& _javaShutdownEvent; + EventWrapper& _javaRenderEvent; + int64_t _lastJavaRenderEvent; + JNIEnv* _javaRenderJniEnv; // JNIEnv for the java render thread. + // TODO(pbos): Remove unique_ptr and use the member directly. + std::unique_ptr _javaRenderThread; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_IMPL_H_ diff --git a/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc b/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc new file mode 100644 index 0000000000..286776e317 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_native_opengl2.cc @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/android/video_render_android_native_opengl2.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/tick_util.h" + +#ifdef ANDROID_LOG +#include +#include + +#undef WEBRTC_TRACE +#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTC*", __VA_ARGS__) +#else +#include "webrtc/system_wrappers/include/trace.h" +#endif + +namespace webrtc { + +AndroidNativeOpenGl2Renderer::AndroidNativeOpenGl2Renderer( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + VideoRenderAndroid(id, videoRenderType, window, fullscreen), + _javaRenderObj(NULL), + _javaRenderClass(NULL) { +} + +bool AndroidNativeOpenGl2Renderer::UseOpenGL2(void* window) { + if (!g_jvm) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "RendererAndroid():UseOpenGL No JVM set."); + return false; + } + bool isAttached = false; + JNIEnv* env = NULL; + if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE( + kTraceError, + kTraceVideoRenderer, + -1, + "RendererAndroid(): Could not attach thread to JVM (%d, %p)", + res, env); + return false; + } + isAttached = true; + } + + // get the renderer class + jclass javaRenderClassLocal = + env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); + if (!javaRenderClassLocal) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "%s: could not find ViEAndroidRenderer class", + __FUNCTION__); + return false; + } + + // get the method ID for UseOpenGL + jmethodID cidUseOpenGL = env->GetStaticMethodID(javaRenderClassLocal, + "UseOpenGL2", + "(Ljava/lang/Object;)Z"); + if (cidUseOpenGL == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "%s: could not get UseOpenGL ID", __FUNCTION__); + return false; + } + jboolean res = env->CallStaticBooleanMethod(javaRenderClassLocal, + cidUseOpenGL, (jobject) window); + + // Detach this thread if it was attached + if (isAttached) { + if (g_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, -1, + "%s: Could not detach thread from JVM", __FUNCTION__); + } + } + return res; +} + +AndroidNativeOpenGl2Renderer::~AndroidNativeOpenGl2Renderer() { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "AndroidNativeOpenGl2Renderer dtor"); + if (g_jvm) { + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, res, env); + env = NULL; + } + else { + isAttached = true; + } + } + + env->DeleteGlobalRef(_javaRenderObj); + env->DeleteGlobalRef(_javaRenderClass); + + if (isAttached) { + if (g_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "%s: Could not detach thread from JVM", + __FUNCTION__); + } + } + } +} + +int32_t AndroidNativeOpenGl2Renderer::Init() { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); + if (!g_jvm) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "(%s): Not a valid Java VM pointer.", __FUNCTION__); + return -1; + } + if (!_ptrWindow) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "(%s): No window have been provided.", __FUNCTION__); + return -1; + } + + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, res, env); + return -1; + } + isAttached = true; + } + + // get the ViEAndroidGLES20 class + jclass javaRenderClassLocal = + env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); + if (!javaRenderClassLocal) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not find ViEAndroidGLES20", __FUNCTION__); + return -1; + } + + // create a global reference to the class (to tell JNI that + // we are referencing it after this function has returned) + _javaRenderClass = + reinterpret_cast (env->NewGlobalRef(javaRenderClassLocal)); + if (!_javaRenderClass) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not create Java SurfaceHolder class reference", + __FUNCTION__); + return -1; + } + + // Delete local class ref, we only use the global ref + env->DeleteLocalRef(javaRenderClassLocal); + + // create a reference to the object (to tell JNI that we are referencing it + // after this function has returned) + _javaRenderObj = env->NewGlobalRef(_ptrWindow); + if (!_javaRenderObj) { + WEBRTC_TRACE( + kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not create Java SurfaceRender object reference", + __FUNCTION__); + return -1; + } + + // Detach this thread if it was attached + if (isAttached) { + if (g_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "%s: Could not detach thread from JVM", __FUNCTION__); + } + } + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s done", + __FUNCTION__); + return 0; + +} +AndroidStream* +AndroidNativeOpenGl2Renderer::CreateAndroidRenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom, + VideoRenderAndroid& renderer) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: Id %d", + __FUNCTION__, streamId); + AndroidNativeOpenGl2Channel* stream = + new AndroidNativeOpenGl2Channel(streamId, g_jvm, renderer, + _javaRenderObj); + if (stream && stream->Init(zOrder, left, top, right, bottom) == 0) + return stream; + else { + delete stream; + } + return NULL; +} + +AndroidNativeOpenGl2Channel::AndroidNativeOpenGl2Channel( + uint32_t streamId, + JavaVM* jvm, + VideoRenderAndroid& renderer,jobject javaRenderObj): + _id(streamId), + _renderCritSect(*CriticalSectionWrapper::CreateCriticalSection()), + _renderer(renderer), _jvm(jvm), _javaRenderObj(javaRenderObj), + _registerNativeCID(NULL), _deRegisterNativeCID(NULL), + _openGLRenderer(streamId) { + +} +AndroidNativeOpenGl2Channel::~AndroidNativeOpenGl2Channel() { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "AndroidNativeOpenGl2Channel dtor"); + if (_jvm) { + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = _jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, res, env); + env = NULL; + } else { + isAttached = true; + } + } + if (env && _deRegisterNativeCID) { + env->CallVoidMethod(_javaRenderObj, _deRegisterNativeCID); + } + + if (isAttached) { + if (_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "%s: Could not detach thread from JVM", + __FUNCTION__); + } + } + } + + delete &_renderCritSect; +} + +int32_t AndroidNativeOpenGl2Channel::Init(int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s: AndroidNativeOpenGl2Channel", __FUNCTION__); + if (!_jvm) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Not a valid Java VM pointer", __FUNCTION__); + return -1; + } + + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = _jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, res, env); + return -1; + } + isAttached = true; + } + + jclass javaRenderClass = + env->FindClass("org/webrtc/videoengine/ViEAndroidGLES20"); + if (!javaRenderClass) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not find ViESurfaceRenderer", __FUNCTION__); + return -1; + } + + // get the method ID for the ReDraw function + _redrawCid = env->GetMethodID(javaRenderClass, "ReDraw", "()V"); + if (_redrawCid == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get ReDraw ID", __FUNCTION__); + return -1; + } + + _registerNativeCID = env->GetMethodID(javaRenderClass, + "RegisterNativeObject", "(J)V"); + if (_registerNativeCID == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get RegisterNativeObject ID", __FUNCTION__); + return -1; + } + + _deRegisterNativeCID = env->GetMethodID(javaRenderClass, + "DeRegisterNativeObject", "()V"); + if (_deRegisterNativeCID == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get DeRegisterNativeObject ID", + __FUNCTION__); + return -1; + } + + JNINativeMethod nativeFunctions[2] = { + { "DrawNative", + "(J)V", + (void*) &AndroidNativeOpenGl2Channel::DrawNativeStatic, }, + { "CreateOpenGLNative", + "(JII)I", + (void*) &AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic }, + }; + if (env->RegisterNatives(javaRenderClass, nativeFunctions, 2) == 0) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, -1, + "%s: Registered native functions", __FUNCTION__); + } + else { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "%s: Failed to register native functions", __FUNCTION__); + return -1; + } + + env->CallVoidMethod(_javaRenderObj, _registerNativeCID, (jlong) this); + + // Detach this thread if it was attached + if (isAttached) { + if (_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, + "%s: Could not detach thread from JVM", __FUNCTION__); + } + } + + if (_openGLRenderer.SetCoordinates(zOrder, left, top, right, bottom) != 0) { + return -1; + } + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s: AndroidNativeOpenGl2Channel done", __FUNCTION__); + return 0; +} + +int32_t AndroidNativeOpenGl2Channel::RenderFrame(const uint32_t /*streamId*/, + const VideoFrame& videoFrame) { + // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s:" ,__FUNCTION__); + _renderCritSect.Enter(); + _bufferToRender = videoFrame; + _renderCritSect.Leave(); + _renderer.ReDraw(); + return 0; +} + +/*Implements AndroidStream + * Calls the Java object and render the buffer in _bufferToRender + */ +void AndroidNativeOpenGl2Channel::DeliverFrame(JNIEnv* jniEnv) { + //TickTime timeNow=TickTime::Now(); + + //Draw the Surface + jniEnv->CallVoidMethod(_javaRenderObj, _redrawCid); + + // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, + // "%s: time to deliver %lld" ,__FUNCTION__, + // (TickTime::Now()-timeNow).Milliseconds()); +} + +/* + * JNI callback from Java class. Called when the render + * want to render a frame. Called from the GLRenderThread + * Method: DrawNative + * Signature: (J)V + */ +void JNICALL AndroidNativeOpenGl2Channel::DrawNativeStatic( + JNIEnv * env, jobject, jlong context) { + AndroidNativeOpenGl2Channel* renderChannel = + reinterpret_cast(context); + renderChannel->DrawNative(); +} + +void AndroidNativeOpenGl2Channel::DrawNative() { + _renderCritSect.Enter(); + _openGLRenderer.Render(_bufferToRender); + _renderCritSect.Leave(); +} + +/* + * JNI callback from Java class. Called when the GLSurfaceview + * have created a surface. Called from the GLRenderThread + * Method: CreateOpenGLNativeStatic + * Signature: (JII)I + */ +jint JNICALL AndroidNativeOpenGl2Channel::CreateOpenGLNativeStatic( + JNIEnv * env, + jobject, + jlong context, + jint width, + jint height) { + AndroidNativeOpenGl2Channel* renderChannel = + reinterpret_cast (context); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, "%s:", __FUNCTION__); + return renderChannel->CreateOpenGLNative(width, height); +} + +jint AndroidNativeOpenGl2Channel::CreateOpenGLNative( + int width, int height) { + return _openGLRenderer.Setup(width, height); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/android/video_render_android_native_opengl2.h b/webrtc/modules/video_render/android/video_render_android_native_opengl2.h new file mode 100644 index 0000000000..8be247b834 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_native_opengl2.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ + +#include + +#include "webrtc/modules/video_render/android/video_render_android_impl.h" +#include "webrtc/modules/video_render/android/video_render_opengles20.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +namespace webrtc { + +class CriticalSectionWrapper; + +class AndroidNativeOpenGl2Channel: public AndroidStream { + public: + AndroidNativeOpenGl2Channel( + uint32_t streamId, + JavaVM* jvm, + VideoRenderAndroid& renderer,jobject javaRenderObj); + ~AndroidNativeOpenGl2Channel(); + + int32_t Init(int32_t zOrder, const float left, const float top, + const float right, const float bottom); + + //Implement VideoRenderCallback + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + + //Implements AndroidStream + virtual void DeliverFrame(JNIEnv* jniEnv); + + private: + static jint JNICALL CreateOpenGLNativeStatic( + JNIEnv * env, + jobject, + jlong context, + jint width, + jint height); + jint CreateOpenGLNative(int width, int height); + + static void JNICALL DrawNativeStatic(JNIEnv * env,jobject, jlong context); + void DrawNative(); + uint32_t _id; + CriticalSectionWrapper& _renderCritSect; + + VideoFrame _bufferToRender; + VideoRenderAndroid& _renderer; + JavaVM* _jvm; + jobject _javaRenderObj; + + jmethodID _redrawCid; + jmethodID _registerNativeCID; + jmethodID _deRegisterNativeCID; + VideoRenderOpenGles20 _openGLRenderer; +}; + + +class AndroidNativeOpenGl2Renderer: private VideoRenderAndroid { + public: + AndroidNativeOpenGl2Renderer(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen); + + ~AndroidNativeOpenGl2Renderer(); + static bool UseOpenGL2(void* window); + + int32_t Init(); + virtual AndroidStream* CreateAndroidRenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom, + VideoRenderAndroid& renderer); + + private: + jobject _javaRenderObj; + jclass _javaRenderClass; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_NATIVE_OPENGL2_H_ diff --git a/webrtc/modules/video_render/android/video_render_android_surface_view.cc b/webrtc/modules/video_render/android/video_render_android_surface_view.cc new file mode 100644 index 0000000000..ea3b106b1e --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_surface_view.cc @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_render/android/video_render_android_surface_view.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/tick_util.h" + +#ifdef ANDROID_LOG +#include +#include + +#undef WEBRTC_TRACE +#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTC*", __VA_ARGS__) +#else +#include "webrtc/system_wrappers/include/trace.h" +#endif + +namespace webrtc { + +AndroidSurfaceViewRenderer::AndroidSurfaceViewRenderer( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + VideoRenderAndroid(id,videoRenderType,window,fullscreen), + _javaRenderObj(NULL), + _javaRenderClass(NULL) { +} + +AndroidSurfaceViewRenderer::~AndroidSurfaceViewRenderer() { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "AndroidSurfaceViewRenderer dtor"); + if(g_jvm) { + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, + res, + env); + env=NULL; + } + else { + isAttached = true; + } + } + env->DeleteGlobalRef(_javaRenderObj); + env->DeleteGlobalRef(_javaRenderClass); + + if (isAttached) { + if (g_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + _id, + "%s: Could not detach thread from JVM", + __FUNCTION__); + } + } + } +} + +int32_t AndroidSurfaceViewRenderer::Init() { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); + if (!g_jvm) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "(%s): Not a valid Java VM pointer.", + __FUNCTION__); + return -1; + } + if(!_ptrWindow) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + _id, + "(%s): No window have been provided.", + __FUNCTION__); + return -1; + } + + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = g_jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, + res, + env); + return -1; + } + isAttached = true; + } + + // get the ViESurfaceRender class + jclass javaRenderClassLocal = + env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer"); + if (!javaRenderClassLocal) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not find ViESurfaceRenderer", + __FUNCTION__); + return -1; + } + + // create a global reference to the class (to tell JNI that + // we are referencing it after this function has returned) + _javaRenderClass = + reinterpret_cast(env->NewGlobalRef(javaRenderClassLocal)); + if (!_javaRenderClass) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not create Java ViESurfaceRenderer class reference", + __FUNCTION__); + return -1; + } + + // Delete local class ref, we only use the global ref + env->DeleteLocalRef(javaRenderClassLocal); + + // get the method ID for the constructor + jmethodID cid = env->GetMethodID(_javaRenderClass, + "", + "(Landroid/view/SurfaceView;)V"); + if (cid == NULL) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not get constructor ID", + __FUNCTION__); + return -1; /* exception thrown */ + } + + // construct the object + jobject javaRenderObjLocal = env->NewObject(_javaRenderClass, + cid, + _ptrWindow); + if (!javaRenderObjLocal) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not create Java Render", + __FUNCTION__); + return -1; + } + + // create a reference to the object (to tell JNI that we are referencing it + // after this function has returned) + _javaRenderObj = env->NewGlobalRef(javaRenderObjLocal); + if (!_javaRenderObj) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not create Java SurfaceRender object reference", + __FUNCTION__); + return -1; + } + + // Detach this thread if it was attached + if (isAttached) { + if (g_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + _id, + "%s: Could not detach thread from JVM", __FUNCTION__); + } + } + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s done", __FUNCTION__); + return 0; +} + +AndroidStream* +AndroidSurfaceViewRenderer::CreateAndroidRenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom, + VideoRenderAndroid& renderer) { + WEBRTC_TRACE(kTraceDebug, + kTraceVideoRenderer, + _id, + "%s: Id %d", + __FUNCTION__, + streamId); + AndroidSurfaceViewChannel* stream = + new AndroidSurfaceViewChannel(streamId, g_jvm, renderer, _javaRenderObj); + if(stream && stream->Init(zOrder, left, top, right, bottom) == 0) + return stream; + else + delete stream; + return NULL; +} + +AndroidSurfaceViewChannel::AndroidSurfaceViewChannel( + uint32_t streamId, + JavaVM* jvm, + VideoRenderAndroid& renderer, + jobject javaRenderObj) : + _id(streamId), + _renderCritSect(*CriticalSectionWrapper::CreateCriticalSection()), + _renderer(renderer), + _jvm(jvm), + _javaRenderObj(javaRenderObj), +#ifndef ANDROID_NDK_8_OR_ABOVE + _javaByteBufferObj(NULL), + _directBuffer(NULL), +#endif + _bitmapWidth(0), + _bitmapHeight(0) { +} + +AndroidSurfaceViewChannel::~AndroidSurfaceViewChannel() { + WEBRTC_TRACE(kTraceInfo, + kTraceVideoRenderer, + _id, + "AndroidSurfaceViewChannel dtor"); + delete &_renderCritSect; + if(_jvm) { + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if ( _jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = _jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, + res, + env); + env=NULL; + } + else { + isAttached = true; + } + } + + env->DeleteGlobalRef(_javaByteBufferObj); + if (isAttached) { + if (_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + _id, + "%s: Could not detach thread from JVM", + __FUNCTION__); + } + } + } +} + +int32_t AndroidSurfaceViewChannel::Init( + int32_t /*zOrder*/, + const float left, + const float top, + const float right, + const float bottom) { + + WEBRTC_TRACE(kTraceDebug, + kTraceVideoRenderer, + _id, + "%s: AndroidSurfaceViewChannel", + __FUNCTION__); + if (!_jvm) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: Not a valid Java VM pointer", + __FUNCTION__); + return -1; + } + + if( (top > 1 || top < 0) || + (right > 1 || right < 0) || + (bottom > 1 || bottom < 0) || + (left > 1 || left < 0)) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Wrong coordinates", __FUNCTION__); + return -1; + } + + // get the JNI env for this thread + bool isAttached = false; + JNIEnv* env = NULL; + if (_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { + // try to attach the thread and get the env + // Attach this thread to JVM + jint res = _jvm->AttachCurrentThread(&env, NULL); + + // Get the JNI env for this thread + if ((res < 0) || !env) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: Could not attach thread to JVM (%d, %p)", + __FUNCTION__, + res, + env); + return -1; + } + isAttached = true; + } + + jclass javaRenderClass = + env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer"); + if (!javaRenderClass) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not find ViESurfaceRenderer", + __FUNCTION__); + return -1; + } + + // get the method ID for the CreateIntArray + _createByteBufferCid = + env->GetMethodID(javaRenderClass, + "CreateByteBuffer", + "(II)Ljava/nio/ByteBuffer;"); + if (_createByteBufferCid == NULL) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not get CreateByteBuffer ID", + __FUNCTION__); + return -1; /* exception thrown */ + } + + // get the method ID for the DrawByteBuffer function + _drawByteBufferCid = env->GetMethodID(javaRenderClass, + "DrawByteBuffer", + "()V"); + if (_drawByteBufferCid == NULL) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not get DrawByteBuffer ID", + __FUNCTION__); + return -1; /* exception thrown */ + } + + // get the method ID for the SetCoordinates function + _setCoordinatesCid = env->GetMethodID(javaRenderClass, + "SetCoordinates", + "(FFFF)V"); + if (_setCoordinatesCid == NULL) { + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: could not get SetCoordinates ID", + __FUNCTION__); + return -1; /* exception thrown */ + } + + env->CallVoidMethod(_javaRenderObj, _setCoordinatesCid, + left, top, right, bottom); + + // Detach this thread if it was attached + if (isAttached) { + if (_jvm->DetachCurrentThread() < 0) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + _id, + "%s: Could not detach thread from JVM", + __FUNCTION__); + } + } + + WEBRTC_TRACE(kTraceDebug, + kTraceVideoRenderer, + _id, + "%s: AndroidSurfaceViewChannel done", + __FUNCTION__); + return 0; +} + +int32_t AndroidSurfaceViewChannel::RenderFrame(const uint32_t /*streamId*/, + const VideoFrame& videoFrame) { + // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s:" ,__FUNCTION__); + _renderCritSect.Enter(); + _bufferToRender = videoFrame; + _renderCritSect.Leave(); + _renderer.ReDraw(); + return 0; +} + + +/*Implements AndroidStream + * Calls the Java object and render the buffer in _bufferToRender + */ +void AndroidSurfaceViewChannel::DeliverFrame(JNIEnv* jniEnv) { + _renderCritSect.Enter(); + + if (_bitmapWidth != _bufferToRender.width() || + _bitmapHeight != _bufferToRender.height()) { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: New render size %d " + "%d",__FUNCTION__, + _bufferToRender.width(), _bufferToRender.height()); + if (_javaByteBufferObj) { + jniEnv->DeleteGlobalRef(_javaByteBufferObj); + _javaByteBufferObj = NULL; + _directBuffer = NULL; + } + + jobject javaByteBufferObj = + jniEnv->CallObjectMethod(_javaRenderObj, _createByteBufferCid, + _bufferToRender.width(), + _bufferToRender.height()); + _javaByteBufferObj = jniEnv->NewGlobalRef(javaByteBufferObj); + if (!_javaByteBufferObj) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: could not " + "create Java ByteBuffer object reference", __FUNCTION__); + _renderCritSect.Leave(); + return; + } else { + _directBuffer = static_cast + (jniEnv->GetDirectBufferAddress(_javaByteBufferObj)); + _bitmapWidth = _bufferToRender.width(); + _bitmapHeight = _bufferToRender.height(); + } + } + + if(_javaByteBufferObj && _bitmapWidth && _bitmapHeight) { + const int conversionResult = + ConvertFromI420(_bufferToRender, kRGB565, 0, _directBuffer); + + if (conversionResult < 0) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Color conversion" + " failed.", __FUNCTION__); + _renderCritSect.Leave(); + return; + } + } + _renderCritSect.Leave(); + // Draw the Surface + jniEnv->CallVoidMethod(_javaRenderObj, _drawByteBufferCid); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/android/video_render_android_surface_view.h b/webrtc/modules/video_render/android/video_render_android_surface_view.h new file mode 100644 index 0000000000..0f029b54f3 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_android_surface_view.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ + +#include + +#include "webrtc/modules/video_render/android/video_render_android_impl.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +namespace webrtc { + +class CriticalSectionWrapper; + +class AndroidSurfaceViewChannel : public AndroidStream { + public: + AndroidSurfaceViewChannel(uint32_t streamId, + JavaVM* jvm, + VideoRenderAndroid& renderer, + jobject javaRenderObj); + ~AndroidSurfaceViewChannel(); + + int32_t Init(int32_t zOrder, const float left, const float top, + const float right, const float bottom); + + //Implement VideoRenderCallback + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + + //Implements AndroidStream + virtual void DeliverFrame(JNIEnv* jniEnv); + + private: + uint32_t _id; + CriticalSectionWrapper& _renderCritSect; + + VideoFrame _bufferToRender; + VideoRenderAndroid& _renderer; + JavaVM* _jvm; + jobject _javaRenderObj; + + jobject _javaByteBufferObj; + unsigned char* _directBuffer; + jmethodID _createByteBufferCid; + jmethodID _drawByteBufferCid; + + jmethodID _setCoordinatesCid; + int _bitmapWidth; + int _bitmapHeight; +}; + +class AndroidSurfaceViewRenderer : private VideoRenderAndroid { + public: + AndroidSurfaceViewRenderer(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen); + ~AndroidSurfaceViewRenderer(); + int32_t Init(); + virtual AndroidStream* CreateAndroidRenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom, + VideoRenderAndroid& renderer); + private: + jobject _javaRenderObj; + jclass _javaRenderClass; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_ANDROID_SURFACE_VIEW_H_ diff --git a/webrtc/modules/video_render/android/video_render_opengles20.cc b/webrtc/modules/video_render/android/video_render_opengles20.cc new file mode 100644 index 0000000000..45db56a4f6 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_opengles20.cc @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include + +#include +#include + +#include "webrtc/modules/video_render/android/video_render_opengles20.h" + +//#define ANDROID_LOG + +#ifdef ANDROID_LOG +#include +#include + +#undef WEBRTC_TRACE +#define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) +#else +#include "webrtc/system_wrappers/include/trace.h" +#endif + +namespace webrtc { + +const char VideoRenderOpenGles20::g_indices[] = { 0, 3, 2, 0, 2, 1 }; + +const char VideoRenderOpenGles20::g_vertextShader[] = { + "attribute vec4 aPosition;\n" + "attribute vec2 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTextureCoord = aTextureCoord;\n" + "}\n" }; + +// The fragment shader. +// Do YUV to RGB565 conversion. +const char VideoRenderOpenGles20::g_fragmentShader[] = { + "precision mediump float;\n" + "uniform sampler2D Ytex;\n" + "uniform sampler2D Utex,Vtex;\n" + "varying vec2 vTextureCoord;\n" + "void main(void) {\n" + " float nx,ny,r,g,b,y,u,v;\n" + " mediump vec4 txl,ux,vx;" + " nx=vTextureCoord[0];\n" + " ny=vTextureCoord[1];\n" + " y=texture2D(Ytex,vec2(nx,ny)).r;\n" + " u=texture2D(Utex,vec2(nx,ny)).r;\n" + " v=texture2D(Vtex,vec2(nx,ny)).r;\n" + + //" y = v;\n"+ + " y=1.1643*(y-0.0625);\n" + " u=u-0.5;\n" + " v=v-0.5;\n" + + " r=y+1.5958*v;\n" + " g=y-0.39173*u-0.81290*v;\n" + " b=y+2.017*u;\n" + " gl_FragColor=vec4(r,g,b,1.0);\n" + "}\n" }; + +VideoRenderOpenGles20::VideoRenderOpenGles20(int32_t id) : + _id(id), + _textureWidth(-1), + _textureHeight(-1) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", + __FUNCTION__, (int) _id); + + const GLfloat vertices[20] = { + // X, Y, Z, U, V + -1, -1, 0, 0, 1, // Bottom Left + 1, -1, 0, 1, 1, //Bottom Right + 1, 1, 0, 1, 0, //Top Right + -1, 1, 0, 0, 0 }; //Top Left + + memcpy(_vertices, vertices, sizeof(_vertices)); +} + +VideoRenderOpenGles20::~VideoRenderOpenGles20() { +} + +int32_t VideoRenderOpenGles20::Setup(int32_t width, int32_t height) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s: width %d, height %d", __FUNCTION__, (int) width, + (int) height); + + printGLString("Version", GL_VERSION); + printGLString("Vendor", GL_VENDOR); + printGLString("Renderer", GL_RENDERER); + printGLString("Extensions", GL_EXTENSIONS); + + int maxTextureImageUnits[2]; + int maxTextureSize[2]; + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, maxTextureImageUnits); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s: number of textures %d, size %d", __FUNCTION__, + (int) maxTextureImageUnits[0], (int) maxTextureSize[0]); + + _program = createProgram(g_vertextShader, g_fragmentShader); + if (!_program) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not create program", __FUNCTION__); + return -1; + } + + int positionHandle = glGetAttribLocation(_program, "aPosition"); + checkGlError("glGetAttribLocation aPosition"); + if (positionHandle == -1) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not get aPosition handle", __FUNCTION__); + return -1; + } + + int textureHandle = glGetAttribLocation(_program, "aTextureCoord"); + checkGlError("glGetAttribLocation aTextureCoord"); + if (textureHandle == -1) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not get aTextureCoord handle", __FUNCTION__); + return -1; + } + + // set the vertices array in the shader + // _vertices contains 4 vertices with 5 coordinates. + // 3 for (xyz) for the vertices and 2 for the texture + glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, + 5 * sizeof(GLfloat), _vertices); + checkGlError("glVertexAttribPointer aPosition"); + + glEnableVertexAttribArray(positionHandle); + checkGlError("glEnableVertexAttribArray positionHandle"); + + // set the texture coordinate array in the shader + // _vertices contains 4 vertices with 5 coordinates. + // 3 for (xyz) for the vertices and 2 for the texture + glVertexAttribPointer(textureHandle, 2, GL_FLOAT, false, 5 + * sizeof(GLfloat), &_vertices[3]); + checkGlError("glVertexAttribPointer maTextureHandle"); + glEnableVertexAttribArray(textureHandle); + checkGlError("glEnableVertexAttribArray textureHandle"); + + glUseProgram(_program); + int i = glGetUniformLocation(_program, "Ytex"); + checkGlError("glGetUniformLocation"); + glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */ + checkGlError("glUniform1i Ytex"); + + i = glGetUniformLocation(_program, "Utex"); + checkGlError("glGetUniformLocation Utex"); + glUniform1i(i, 1); /* Bind Utex to texture unit 1 */ + checkGlError("glUniform1i Utex"); + + i = glGetUniformLocation(_program, "Vtex"); + checkGlError("glGetUniformLocation"); + glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */ + checkGlError("glUniform1i"); + + glViewport(0, 0, width, height); + checkGlError("glViewport"); + return 0; +} + +// SetCoordinates +// Sets the coordinates where the stream shall be rendered. +// Values must be between 0 and 1. +int32_t VideoRenderOpenGles20::SetCoordinates(int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) { + if ((top > 1 || top < 0) || (right > 1 || right < 0) || + (bottom > 1 || bottom < 0) || (left > 1 || left < 0)) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Wrong coordinates", __FUNCTION__); + return -1; + } + + // X, Y, Z, U, V + // -1, -1, 0, 0, 1, // Bottom Left + // 1, -1, 0, 1, 1, //Bottom Right + // 1, 1, 0, 1, 0, //Top Right + // -1, 1, 0, 0, 0 //Top Left + + // Bottom Left + _vertices[0] = (left * 2) - 1; + _vertices[1] = -1 * (2 * bottom) + 1; + _vertices[2] = zOrder; + + //Bottom Right + _vertices[5] = (right * 2) - 1; + _vertices[6] = -1 * (2 * bottom) + 1; + _vertices[7] = zOrder; + + //Top Right + _vertices[10] = (right * 2) - 1; + _vertices[11] = -1 * (2 * top) + 1; + _vertices[12] = zOrder; + + //Top Left + _vertices[15] = (left * 2) - 1; + _vertices[16] = -1 * (2 * top) + 1; + _vertices[17] = zOrder; + + return 0; +} + +int32_t VideoRenderOpenGles20::Render(const VideoFrame& frameToRender) { + if (frameToRender.IsZeroSize()) { + return -1; + } + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", + __FUNCTION__, (int) _id); + + glUseProgram(_program); + checkGlError("glUseProgram"); + + if (_textureWidth != (GLsizei) frameToRender.width() || + _textureHeight != (GLsizei) frameToRender.height()) { + SetupTextures(frameToRender); + } + UpdateTextures(frameToRender); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, g_indices); + checkGlError("glDrawArrays"); + + return 0; +} + +GLuint VideoRenderOpenGles20::loadShader(GLenum shaderType, + const char* pSource) { + GLuint shader = glCreateShader(shaderType); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not compile shader %d: %s", + __FUNCTION__, shaderType, buf); + free(buf); + } + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; +} + +GLuint VideoRenderOpenGles20::createProgram(const char* pVertexSource, + const char* pFragmentSource) { + GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); + if (!vertexShader) { + return 0; + } + + GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); + if (!pixelShader) { + return 0; + } + + GLuint program = glCreateProgram(); + if (program) { + glAttachShader(program, vertexShader); + checkGlError("glAttachShader"); + glAttachShader(program, pixelShader); + checkGlError("glAttachShader"); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = (char*) malloc(bufLength); + if (buf) { + glGetProgramInfoLog(program, bufLength, NULL, buf); + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Could not link program: %s", + __FUNCTION__, buf); + free(buf); + } + } + glDeleteProgram(program); + program = 0; + } + } + return program; +} + +void VideoRenderOpenGles20::printGLString(const char *name, GLenum s) { + const char *v = (const char *) glGetString(s); + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "GL %s = %s\n", + name, v); +} + +void VideoRenderOpenGles20::checkGlError(const char* op) { +#ifdef ANDROID_LOG + for (GLint error = glGetError(); error; error = glGetError()) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "after %s() glError (0x%x)\n", op, error); + } +#else + return; +#endif +} + +static void InitializeTexture(int name, int id, int width, int height) { + glActiveTexture(name); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); +} + +void VideoRenderOpenGles20::SetupTextures(const VideoFrame& frameToRender) { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, + "%s: width %d, height %d", __FUNCTION__, + frameToRender.width(), frameToRender.height()); + + const GLsizei width = frameToRender.width(); + const GLsizei height = frameToRender.height(); + + glGenTextures(3, _textureIds); //Generate the Y, U and V texture + InitializeTexture(GL_TEXTURE0, _textureIds[0], width, height); + InitializeTexture(GL_TEXTURE1, _textureIds[1], width / 2, height / 2); + InitializeTexture(GL_TEXTURE2, _textureIds[2], width / 2, height / 2); + + checkGlError("SetupTextures"); + + _textureWidth = width; + _textureHeight = height; +} + +// Uploads a plane of pixel data, accounting for stride != width*bpp. +static void GlTexSubImage2D(GLsizei width, GLsizei height, int stride, + const uint8_t* plane) { + if (stride == width) { + // Yay! We can upload the entire plane in a single GL call. + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, + GL_UNSIGNED_BYTE, + static_cast(plane)); + } else { + // Boo! Since GLES2 doesn't have GL_UNPACK_ROW_LENGTH and Android doesn't + // have GL_EXT_unpack_subimage we have to upload a row at a time. Ick. + for (int row = 0; row < height; ++row) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, row, width, 1, GL_LUMINANCE, + GL_UNSIGNED_BYTE, + static_cast(plane + (row * stride))); + } + } +} + +void VideoRenderOpenGles20::UpdateTextures(const VideoFrame& frameToRender) { + const GLsizei width = frameToRender.width(); + const GLsizei height = frameToRender.height(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _textureIds[0]); + GlTexSubImage2D(width, height, frameToRender.stride(kYPlane), + frameToRender.buffer(kYPlane)); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _textureIds[1]); + GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kUPlane), + frameToRender.buffer(kUPlane)); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, _textureIds[2]); + GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kVPlane), + frameToRender.buffer(kVPlane)); + + checkGlError("UpdateTextures"); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/android/video_render_opengles20.h b/webrtc/modules/video_render/android/video_render_opengles20.h new file mode 100644 index 0000000000..57e2a10d42 --- /dev/null +++ b/webrtc/modules/video_render/android/video_render_opengles20.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ + +#include "webrtc/modules/video_render/video_render_defines.h" + +#include +#include + +namespace webrtc +{ + +class VideoRenderOpenGles20 { + public: + VideoRenderOpenGles20(int32_t id); + ~VideoRenderOpenGles20(); + + int32_t Setup(int32_t widht, int32_t height); + int32_t Render(const VideoFrame& frameToRender); + int32_t SetCoordinates(int32_t zOrder, const float left, const float top, + const float right, const float bottom); + + private: + void printGLString(const char *name, GLenum s); + void checkGlError(const char* op); + GLuint loadShader(GLenum shaderType, const char* pSource); + GLuint createProgram(const char* pVertexSource, + const char* pFragmentSource); + void SetupTextures(const VideoFrame& frameToRender); + void UpdateTextures(const VideoFrame& frameToRender); + + int32_t _id; + GLuint _textureIds[3]; // Texture id of Y,U and V texture. + GLuint _program; + GLsizei _textureWidth; + GLsizei _textureHeight; + + GLfloat _vertices[20]; + static const char g_indices[]; + + static const char g_vertextShader[]; + static const char g_fragmentShader[]; + +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_ANDROID_VIDEO_RENDER_OPENGLES20_H_ diff --git a/webrtc/modules/video_render/external/video_render_external_impl.cc b/webrtc/modules/video_render/external/video_render_external_impl.cc new file mode 100644 index 0000000000..58df07875e --- /dev/null +++ b/webrtc/modules/video_render/external/video_render_external_impl.cc @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/external/video_render_external_impl.h" + +namespace webrtc { + +VideoRenderExternalImpl::VideoRenderExternalImpl( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + _critSect(*CriticalSectionWrapper::CreateCriticalSection()), + _fullscreen(fullscreen) +{ +} + +VideoRenderExternalImpl::~VideoRenderExternalImpl() +{ + delete &_critSect; +} + +int32_t VideoRenderExternalImpl::Init() +{ + return 0; +} + +int32_t VideoRenderExternalImpl::ChangeWindow(void* window) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +VideoRenderCallback* +VideoRenderExternalImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + return this; +} + +int32_t VideoRenderExternalImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const +{ + CriticalSectionScoped cs(&_critSect); + + zOrder = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + + return 0; +} + +int32_t VideoRenderExternalImpl::StartRender() +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::StopRender() +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +VideoRenderType VideoRenderExternalImpl::RenderType() +{ + return kRenderExternal; +} + +RawVideoType VideoRenderExternalImpl::PerferedVideoType() +{ + return kVideoI420; +} + +bool VideoRenderExternalImpl::FullScreen() +{ + CriticalSectionScoped cs(&_critSect); + return _fullscreen; +} + +int32_t VideoRenderExternalImpl::GetGraphicsMemory( + uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const +{ + totalGraphicsMemory = 0; + availableGraphicsMemory = 0; + return -1; +} + +int32_t VideoRenderExternalImpl::GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_critSect); + screenWidth = 0; + screenHeight = 0; + return 0; +} + +uint32_t VideoRenderExternalImpl::RenderFrameRate( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::SetStreamCropping( + const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::SetTransparentBackground( + const bool enable) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +int32_t VideoRenderExternalImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + return 0; +} + +// VideoRenderCallback +int32_t VideoRenderExternalImpl::RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame) { + return 0; +} +} // namespace webrtc diff --git a/webrtc/modules/video_render/external/video_render_external_impl.h b/webrtc/modules/video_render/external/video_render_external_impl.h new file mode 100644 index 0000000000..a8b663fff7 --- /dev/null +++ b/webrtc/modules/video_render/external/video_render_external_impl.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ + +#include "webrtc/modules/include/module_common_types.h" +#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" + +namespace webrtc { + +// Class definitions +class VideoRenderExternalImpl: IVideoRender, public VideoRenderCallback +{ +public: + /* + * Constructor/destructor + */ + + VideoRenderExternalImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, const bool fullscreen); + + virtual ~VideoRenderExternalImpl(); + + virtual int32_t Init(); + + virtual int32_t ChangeWindow(void* window); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId); + + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t + GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, const float left, + const float top, const float right, + const float bottom); + + // VideoRenderCallback + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + +private: + CriticalSectionWrapper& _critSect; + bool _fullscreen; +}; + +} // namespace webrtc + + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_EXTERNAL_VIDEO_RENDER_EXTERNAL_IMPL_H_ diff --git a/webrtc/modules/video_render/i_video_render.h b/webrtc/modules/video_render/i_video_render.h new file mode 100644 index 0000000000..e6ec7a4680 --- /dev/null +++ b/webrtc/modules/video_render/i_video_render.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ + +#include "webrtc/modules/video_render/video_render.h" + +namespace webrtc { + +// Class definitions +class IVideoRender +{ +public: + /* + * Constructor/destructor + */ + + virtual ~IVideoRender() {} + + virtual int32_t Init() = 0; + + virtual int32_t ChangeWindow(void* window) = 0; + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId) = 0; + + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const = 0; + // Implemented in common code? + //virtual uint32_t GetNumIncomingRenderStreams() const = 0; + //virtual bool HasIncomingRenderStream(const uint16_t stramId) const = 0; + + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender() = 0; + + virtual int32_t StopRender() = 0; + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + virtual VideoRenderType RenderType() = 0; + + virtual RawVideoType PerferedVideoType() = 0; + + virtual bool FullScreen() = 0; + + // TODO: This should be treated in platform specific code only + virtual int32_t + GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const = 0; + + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const = 0; + + virtual uint32_t RenderFrameRate(const uint32_t streamId) = 0; + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t SetTransparentBackground(const bool enable) = 0; + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float rigth, + const float bottom) = 0; + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) = 0; + +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_I_VIDEO_RENDER_H_ diff --git a/webrtc/modules/video_render/ios/open_gles20.h b/webrtc/modules/video_render/ios/open_gles20.h new file mode 100644 index 0000000000..880ddb5231 --- /dev/null +++ b/webrtc/modules/video_render/ios/open_gles20.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_IOS_OPEN_GLES20_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_IOS_OPEN_GLES20_H_ + +#include + +#include "webrtc/modules/video_render/video_render_defines.h" + +/* + * This OpenGles20 is the class of renderer for VideoFrame into a GLES 2.0 + * windows used in the VideoRenderIosView class. + */ +namespace webrtc { +class OpenGles20 { + public: + OpenGles20(); + ~OpenGles20(); + + bool Setup(int32_t width, int32_t height); + bool Render(const VideoFrame& frame); + + // SetCoordinates + // Sets the coordinates where the stream shall be rendered. + // Values must be between 0 and 1. + bool SetCoordinates(const float z_order, + const float left, + const float top, + const float right, + const float bottom); + + private: + // Compile and load the vertex and fragment shaders defined at the top of + // open_gles20.mm + GLuint LoadShader(GLenum shader_type, const char* shader_source); + + GLuint CreateProgram(const char* vertex_source, const char* fragment_source); + + // Initialize the textures by the frame width and height + void SetupTextures(const VideoFrame& frame); + + // Update the textures by the YUV data from the frame + void UpdateTextures(const VideoFrame& frame); + + GLuint texture_ids_[3]; // Texture id of Y,U and V texture. + GLuint program_; + GLsizei texture_width_; + GLsizei texture_height_; + + GLfloat vertices_[20]; + static const char indices_[]; + static const char vertext_shader_[]; + static const char fragment_shader_[]; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_OPEN_GLES20_H_ diff --git a/webrtc/modules/video_render/ios/open_gles20.mm b/webrtc/modules/video_render/ios/open_gles20.mm new file mode 100644 index 0000000000..d1735280f2 --- /dev/null +++ b/webrtc/modules/video_render/ios/open_gles20.mm @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// This files is mostly copied from +// webrtc/modules/video_render/android/video_render_opengles20.h + +// TODO(sjlee): unify this copy with the android one. +#include "webrtc/modules/video_render/ios/open_gles20.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +const char OpenGles20::indices_[] = {0, 3, 2, 0, 2, 1}; + +const char OpenGles20::vertext_shader_[] = { + "attribute vec4 aPosition;\n" + "attribute vec2 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTextureCoord = aTextureCoord;\n" + "}\n"}; + +// The fragment shader. +// Do YUV to RGB565 conversion. +const char OpenGles20::fragment_shader_[] = { + "precision mediump float;\n" + "uniform sampler2D Ytex;\n" + "uniform sampler2D Utex,Vtex;\n" + "varying vec2 vTextureCoord;\n" + "void main(void) {\n" + " float nx,ny,r,g,b,y,u,v;\n" + " mediump vec4 txl,ux,vx;" + " nx=vTextureCoord[0];\n" + " ny=vTextureCoord[1];\n" + " y=texture2D(Ytex,vec2(nx,ny)).r;\n" + " u=texture2D(Utex,vec2(nx,ny)).r;\n" + " v=texture2D(Vtex,vec2(nx,ny)).r;\n" + " y=1.1643*(y-0.0625);\n" + " u=u-0.5;\n" + " v=v-0.5;\n" + " r=y+1.5958*v;\n" + " g=y-0.39173*u-0.81290*v;\n" + " b=y+2.017*u;\n" + " gl_FragColor=vec4(r,g,b,1.0);\n" + "}\n"}; + +OpenGles20::OpenGles20() : texture_width_(-1), texture_height_(-1) { + texture_ids_[0] = 0; + texture_ids_[1] = 0; + texture_ids_[2] = 0; + + program_ = 0; + + const GLfloat vertices[20] = { + // X, Y, Z, U, V + -1, -1, 0, 0, 1, // Bottom Left + 1, -1, 0, 1, 1, // Bottom Right + 1, 1, 0, 1, 0, // Top Right + -1, 1, 0, 0, 0}; // Top Left + + memcpy(vertices_, vertices, sizeof(vertices_)); +} + +OpenGles20::~OpenGles20() { + if (program_) { + glDeleteTextures(3, texture_ids_); + glDeleteProgram(program_); + } +} + +bool OpenGles20::Setup(int32_t width, int32_t height) { + program_ = CreateProgram(vertext_shader_, fragment_shader_); + if (!program_) { + return false; + } + + int position_handle = glGetAttribLocation(program_, "aPosition"); + int texture_handle = glGetAttribLocation(program_, "aTextureCoord"); + + // set the vertices array in the shader + // vertices_ contains 4 vertices with 5 coordinates. + // 3 for (xyz) for the vertices and 2 for the texture + glVertexAttribPointer( + position_handle, 3, GL_FLOAT, false, 5 * sizeof(GLfloat), vertices_); + + glEnableVertexAttribArray(position_handle); + + // set the texture coordinate array in the shader + // vertices_ contains 4 vertices with 5 coordinates. + // 3 for (xyz) for the vertices and 2 for the texture + glVertexAttribPointer( + texture_handle, 2, GL_FLOAT, false, 5 * sizeof(GLfloat), &vertices_[3]); + glEnableVertexAttribArray(texture_handle); + + glUseProgram(program_); + int i = glGetUniformLocation(program_, "Ytex"); + glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */ + + i = glGetUniformLocation(program_, "Utex"); + glUniform1i(i, 1); /* Bind Utex to texture unit 1 */ + + i = glGetUniformLocation(program_, "Vtex"); + glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */ + + glViewport(0, 0, width, height); + return true; +} + +bool OpenGles20::SetCoordinates(const float z_order, + const float left, + const float top, + const float right, + const float bottom) { + if (top > 1 || top < 0 || right > 1 || right < 0 || bottom > 1 || + bottom < 0 || left > 1 || left < 0) { + return false; + } + + // Bottom Left + vertices_[0] = (left * 2) - 1; + vertices_[1] = -1 * (2 * bottom) + 1; + vertices_[2] = z_order; + + // Bottom Right + vertices_[5] = (right * 2) - 1; + vertices_[6] = -1 * (2 * bottom) + 1; + vertices_[7] = z_order; + + // Top Right + vertices_[10] = (right * 2) - 1; + vertices_[11] = -1 * (2 * top) + 1; + vertices_[12] = z_order; + + // Top Left + vertices_[15] = (left * 2) - 1; + vertices_[16] = -1 * (2 * top) + 1; + vertices_[17] = z_order; + + return true; +} + +bool OpenGles20::Render(const VideoFrame& frame) { + if (texture_width_ != (GLsizei)frame.width() || + texture_height_ != (GLsizei)frame.height()) { + SetupTextures(frame); + } + UpdateTextures(frame); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices_); + + return true; +} + +GLuint OpenGles20::LoadShader(GLenum shader_type, const char* shader_source) { + GLuint shader = glCreateShader(shader_type); + if (shader) { + glShaderSource(shader, 1, &shader_source, NULL); + glCompileShader(shader); + + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint info_len = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len); + if (info_len) { + char* buf = (char*)malloc(info_len); + glGetShaderInfoLog(shader, info_len, NULL, buf); + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + 0, + "%s: Could not compile shader %d: %s", + __FUNCTION__, + shader_type, + buf); + free(buf); + } + glDeleteShader(shader); + shader = 0; + } + } + return shader; +} + +GLuint OpenGles20::CreateProgram(const char* vertex_source, + const char* fragment_source) { + GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_source); + if (!vertex_shader) { + return -1; + } + + GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER, fragment_source); + if (!fragment_shader) { + return -1; + } + + GLuint program = glCreateProgram(); + if (program) { + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + GLint link_status = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (link_status != GL_TRUE) { + GLint info_len = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len); + if (info_len) { + char* buf = (char*)malloc(info_len); + glGetProgramInfoLog(program, info_len, NULL, buf); + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + 0, + "%s: Could not link program: %s", + __FUNCTION__, + buf); + free(buf); + } + glDeleteProgram(program); + program = 0; + } + } + + if (vertex_shader) { + glDeleteShader(vertex_shader); + } + + if (fragment_shader) { + glDeleteShader(fragment_shader); + } + + return program; +} + +static void InitializeTexture(int name, int id, int width, int height) { + glActiveTexture(name); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_LUMINANCE, + width, + height, + 0, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + NULL); +} + +void OpenGles20::SetupTextures(const VideoFrame& frame) { + const GLsizei width = frame.width(); + const GLsizei height = frame.height(); + + if (!texture_ids_[0]) { + glGenTextures(3, texture_ids_); // Generate the Y, U and V texture + } + + InitializeTexture(GL_TEXTURE0, texture_ids_[0], width, height); + InitializeTexture(GL_TEXTURE1, texture_ids_[1], width / 2, height / 2); + InitializeTexture(GL_TEXTURE2, texture_ids_[2], width / 2, height / 2); + + texture_width_ = width; + texture_height_ = height; +} + +// Uploads a plane of pixel data, accounting for stride != width*bpp. +static void GlTexSubImage2D(GLsizei width, + GLsizei height, + int stride, + const uint8_t* plane) { + if (stride == width) { + // Yay! We can upload the entire plane in a single GL call. + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + width, + height, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + static_cast(plane)); + } else { + // Boo! Since GLES2 doesn't have GL_UNPACK_ROW_LENGTH and iOS doesn't + // have GL_EXT_unpack_subimage we have to upload a row at a time. Ick. + for (int row = 0; row < height; ++row) { + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + row, + width, + 1, + GL_LUMINANCE, + GL_UNSIGNED_BYTE, + static_cast(plane + (row * stride))); + } + } +} + +void OpenGles20::UpdateTextures(const VideoFrame& frame) { + const GLsizei width = frame.width(); + const GLsizei height = frame.height(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_ids_[0]); + GlTexSubImage2D(width, height, frame.stride(kYPlane), frame.buffer(kYPlane)); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture_ids_[1]); + GlTexSubImage2D( + width / 2, height / 2, frame.stride(kUPlane), frame.buffer(kUPlane)); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, texture_ids_[2]); + GlTexSubImage2D( + width / 2, height / 2, frame.stride(kVPlane), frame.buffer(kVPlane)); +} diff --git a/webrtc/modules/video_render/ios/video_render_ios_channel.h b/webrtc/modules/video_render/ios/video_render_ios_channel.h new file mode 100644 index 0000000000..a15ba393dc --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_channel.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_CHANNEL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_CHANNEL_H_ + +#include "webrtc/modules/video_render/video_render_defines.h" +#include "webrtc/modules/video_render/ios/video_render_ios_view.h" + +namespace webrtc { + +class VideoRenderIosGles20; + +class VideoRenderIosChannel : public VideoRenderCallback { + public: + explicit VideoRenderIosChannel(VideoRenderIosView* view); + virtual ~VideoRenderIosChannel(); + + // Implementation of VideoRenderCallback. + int32_t RenderFrame(const uint32_t stream_id, + const VideoFrame& video_frame) override; + + int SetStreamSettings(const float z_order, + const float left, + const float top, + const float right, + const float bottom); + bool IsUpdated(); + bool RenderOffScreenBuffer(); + + private: + VideoRenderIosView* view_; + VideoFrame* current_frame_; + bool buffer_is_updated_; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_CHANNEL_H_ diff --git a/webrtc/modules/video_render/ios/video_render_ios_channel.mm b/webrtc/modules/video_render/ios/video_render_ios_channel.mm new file mode 100644 index 0000000000..b2b15857f9 --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_channel.mm @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#include "webrtc/modules/video_render/ios/video_render_ios_channel.h" + +using namespace webrtc; + +VideoRenderIosChannel::VideoRenderIosChannel(VideoRenderIosView* view) + : view_(view), current_frame_(new VideoFrame()), buffer_is_updated_(false) { +} + +VideoRenderIosChannel::~VideoRenderIosChannel() { delete current_frame_; } + +int32_t VideoRenderIosChannel::RenderFrame(const uint32_t stream_id, + const VideoFrame& video_frame) { + current_frame_->CopyFrame(video_frame); + current_frame_->set_render_time_ms(0); + buffer_is_updated_ = true; + + return 0; +} + +bool VideoRenderIosChannel::RenderOffScreenBuffer() { + if (![view_ renderFrame:current_frame_]) { + return false; + } + + buffer_is_updated_ = false; + + return true; +} + +bool VideoRenderIosChannel::IsUpdated() { return buffer_is_updated_; } + +int VideoRenderIosChannel::SetStreamSettings(const float z_order, + const float left, + const float top, + const float right, + const float bottom) { + if (![view_ setCoordinatesForZOrder:z_order + Left:left + Top:bottom + Right:right + Bottom:top]) { + + return -1; + } + + return 0; +} diff --git a/webrtc/modules/video_render/ios/video_render_ios_gles20.h b/webrtc/modules/video_render/ios/video_render_ios_gles20.h new file mode 100644 index 0000000000..d4e04e79d7 --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_gles20.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_GLES20_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_GLES20_H_ + +#include +#include +#include + +#include "webrtc/base/platform_thread.h" +#include "webrtc/modules/video_render/ios/video_render_ios_channel.h" +#include "webrtc/modules/video_render/ios/video_render_ios_view.h" + +namespace webrtc { + +class CriticalSectionWrapper; +class EventTimerWrapper; + +class VideoRenderIosGles20 { + public: + VideoRenderIosGles20(VideoRenderIosView* view, + bool full_screen, + int render_id); + virtual ~VideoRenderIosGles20(); + + int Init(); + VideoRenderIosChannel* CreateEaglChannel(int channel, + int z_order, + float left, + float top, + float right, + float bottom); + int DeleteEaglChannel(int channel); + bool HasChannel(int channel); + bool ScreenUpdateProcess(); + int GetWindowRect(Rect& rect); // NOLINT + + int GetScreenResolution(uint& screen_width, uint& screen_height); // NOLINT + int SetStreamCropping(const uint stream_id, + const float left, + const float top, + const float right, + const float bottom); + + int ChangeWindow(void* new_window); + int StartRender(); + int StopRender(); + + protected: + static bool ScreenUpdateThreadProc(void* obj); + + private: + bool RenderOffScreenBuffers(); + int SwapAndDisplayBuffers(); + + private: + std::unique_ptr gles_crit_sec_; + EventTimerWrapper* screen_update_event_; + // TODO(pbos): Remove unique_ptr and use member directly. + std::unique_ptr screen_update_thread_; + + VideoRenderIosView* view_; + Rect window_rect_; + int window_width_; + int window_height_; + bool is_full_screen_; + GLint backing_width_; + GLint backing_height_; + GLuint view_renderbuffer_; + GLuint view_framebuffer_; + GLuint depth_renderbuffer_; + std::map agl_channels_; + std::multimap z_order_to_channel_; + EAGLContext* gles_context_; + bool is_rendering_; +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_GLES20_H_ diff --git a/webrtc/modules/video_render/ios/video_render_ios_gles20.mm b/webrtc/modules/video_render/ios/video_render_ios_gles20.mm new file mode 100644 index 0000000000..6ad5db8b8c --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_gles20.mm @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#include "webrtc/modules/video_render/ios/video_render_ios_gles20.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/event_wrapper.h" + +using namespace webrtc; + +VideoRenderIosGles20::VideoRenderIosGles20(VideoRenderIosView* view, + bool full_screen, + int render_id) + : gles_crit_sec_(CriticalSectionWrapper::CreateCriticalSection()), + screen_update_event_(0), + view_(view), + window_rect_(), + window_width_(0), + window_height_(0), + is_full_screen_(full_screen), + agl_channels_(), + z_order_to_channel_(), + gles_context_([view context]), + is_rendering_(true) { + screen_update_thread_.reset(new rtc::PlatformThread( + ScreenUpdateThreadProc, this, "ScreenUpdateGles20")); + screen_update_event_ = EventTimerWrapper::Create(); + GetWindowRect(window_rect_); +} + +VideoRenderIosGles20::~VideoRenderIosGles20() { + // Signal event to exit thread, then delete it + rtc::PlatformThread* thread_wrapper = screen_update_thread_.release(); + + if (thread_wrapper) { + screen_update_event_->Set(); + screen_update_event_->StopTimer(); + + thread_wrapper->Stop(); + delete thread_wrapper; + delete screen_update_event_; + screen_update_event_ = NULL; + is_rendering_ = FALSE; + } + + // Delete all channels + std::map::iterator it = agl_channels_.begin(); + while (it != agl_channels_.end()) { + delete it->second; + agl_channels_.erase(it); + it = agl_channels_.begin(); + } + agl_channels_.clear(); + + // Clean the zOrder map + std::multimap::iterator z_it = z_order_to_channel_.begin(); + while (z_it != z_order_to_channel_.end()) { + z_order_to_channel_.erase(z_it); + z_it = z_order_to_channel_.begin(); + } + z_order_to_channel_.clear(); +} + +int VideoRenderIosGles20::Init() { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + if (!view_) { + view_ = [[VideoRenderIosView alloc] init]; + } + + if (![view_ createContext]) { + return -1; + } + + screen_update_thread_->Start(); + screen_update_thread_->SetPriority(rtc::kRealtimePriority); + + // Start the event triggering the render process + unsigned int monitor_freq = 60; + screen_update_event_->StartTimer(true, 1000 / monitor_freq); + + window_width_ = window_rect_.right - window_rect_.left; + window_height_ = window_rect_.bottom - window_rect_.top; + + return 0; +} + +VideoRenderIosChannel* VideoRenderIosGles20::CreateEaglChannel(int channel, + int z_order, + float left, + float top, + float right, + float bottom) { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + if (HasChannel(channel)) { + return NULL; + } + + VideoRenderIosChannel* new_eagl_channel = new VideoRenderIosChannel(view_); + + if (new_eagl_channel->SetStreamSettings(z_order, left, top, right, bottom) == + -1) { + return NULL; + } + + agl_channels_[channel] = new_eagl_channel; + z_order_to_channel_.insert(std::pair(z_order, channel)); + + return new_eagl_channel; +} + +int VideoRenderIosGles20::DeleteEaglChannel(int channel) { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + std::map::iterator it; + it = agl_channels_.find(channel); + if (it != agl_channels_.end()) { + delete it->second; + agl_channels_.erase(it); + } else { + return -1; + } + + std::multimap::iterator z_it = z_order_to_channel_.begin(); + while (z_it != z_order_to_channel_.end()) { + if (z_it->second == channel) { + z_order_to_channel_.erase(z_it); + break; + } + z_it++; + } + + return 0; +} + +bool VideoRenderIosGles20::HasChannel(int channel) { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + std::map::iterator it = + agl_channels_.find(channel); + + if (it != agl_channels_.end()) { + return true; + } + + return false; +} + +// Rendering process +bool VideoRenderIosGles20::ScreenUpdateThreadProc(void* obj) { + return static_cast(obj)->ScreenUpdateProcess(); +} + +bool VideoRenderIosGles20::ScreenUpdateProcess() { + screen_update_event_->Wait(100); + + CriticalSectionScoped cs(gles_crit_sec_.get()); + + if (!is_rendering_) { + return false; + } + + if (!screen_update_thread_) { + return false; + } + + if (GetWindowRect(window_rect_) == -1) { + return true; + } + + if (window_width_ != (window_rect_.right - window_rect_.left) || + window_height_ != (window_rect_.bottom - window_rect_.top)) { + window_width_ = window_rect_.right - window_rect_.left; + window_height_ = window_rect_.bottom - window_rect_.top; + } + + // Check if there are any updated buffers + bool updated = false; + + std::map::iterator it = agl_channels_.begin(); + while (it != agl_channels_.end()) { + VideoRenderIosChannel* agl_channel = it->second; + + updated = agl_channel->IsUpdated(); + if (updated) { + break; + } + it++; + } + + if (updated) { + // At least one buffer has been updated, we need to repaint the texture + // Loop through all channels starting highest zOrder ending with lowest. + for (std::multimap::reverse_iterator r_it = + z_order_to_channel_.rbegin(); + r_it != z_order_to_channel_.rend(); + r_it++) { + int channel_id = r_it->second; + std::map::iterator it = + agl_channels_.find(channel_id); + + VideoRenderIosChannel* agl_channel = it->second; + + agl_channel->RenderOffScreenBuffer(); + } + + [view_ presentFramebuffer]; + } + + return true; +} + +int VideoRenderIosGles20::GetWindowRect(Rect& rect) { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + if (!view_) { + return -1; + } + + CGRect bounds = [view_ bounds]; + rect.top = bounds.origin.y; + rect.left = bounds.origin.x; + rect.bottom = bounds.size.height + bounds.origin.y; + rect.right = bounds.size.width + bounds.origin.x; + + return 0; +} + +int VideoRenderIosGles20::ChangeWindow(void* new_window) { + CriticalSectionScoped cs(gles_crit_sec_.get()); + + view_ = (__bridge VideoRenderIosView*)new_window; + + return 0; +} + +int VideoRenderIosGles20::StartRender() { + is_rendering_ = true; + return 0; +} + +int VideoRenderIosGles20::StopRender() { + is_rendering_ = false; + return 0; +} + +int VideoRenderIosGles20::GetScreenResolution(uint& screen_width, + uint& screen_height) { + screen_width = [view_ bounds].size.width; + screen_height = [view_ bounds].size.height; + return 0; +} + +int VideoRenderIosGles20::SetStreamCropping(const uint stream_id, + const float left, + const float top, + const float right, + const float bottom) { + // Check if there are any updated buffers + // bool updated = false; + uint counter = 0; + + std::map::iterator it = agl_channels_.begin(); + while (it != agl_channels_.end()) { + if (counter == stream_id) { + VideoRenderIosChannel* agl_channel = it->second; + agl_channel->SetStreamSettings(0, left, top, right, bottom); + } + counter++; + it++; + } + + return 0; +} diff --git a/webrtc/modules/video_render/ios/video_render_ios_impl.h b/webrtc/modules/video_render/ios/video_render_ios_impl.h new file mode 100644 index 0000000000..04a7493300 --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_impl.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_IMPL_H_ + +#include +#include +#include + +#include "webrtc/modules/video_render/i_video_render.h" + +namespace webrtc { + +class VideoRenderIosGles20; +class CriticalSectionWrapper; + +class VideoRenderIosImpl : IVideoRender { + public: + explicit VideoRenderIosImpl(const int32_t id, + void* window, + const bool full_screen); + + ~VideoRenderIosImpl(); + + // Implementation of IVideoRender. + int32_t Init() override; + int32_t ChangeWindow(void* window) override; + + VideoRenderCallback* AddIncomingRenderStream(const uint32_t stream_id, + const uint32_t z_order, + const float left, + const float top, + const float right, + const float bottom) override; + + int32_t DeleteIncomingRenderStream(const uint32_t stream_id) override; + + int32_t GetIncomingRenderStreamProperties(const uint32_t stream_id, + uint32_t& z_order, + float& left, + float& top, + float& right, + float& bottom) const override; + + int32_t StartRender() override; + int32_t StopRender() override; + + VideoRenderType RenderType() override; + RawVideoType PerferedVideoType() override; + bool FullScreen() override; + int32_t GetGraphicsMemory( + uint64_t& total_graphics_memory, + uint64_t& available_graphics_memory) const override; // NOLINT + int32_t GetScreenResolution( + uint32_t& screen_width, + uint32_t& screen_height) const override; // NOLINT + uint32_t RenderFrameRate(const uint32_t stream_id); + int32_t SetStreamCropping(const uint32_t stream_id, + const float left, + const float top, + const float right, + const float bottom) override; + int32_t ConfigureRenderer(const uint32_t stream_id, + const unsigned int z_order, + const float left, + const float top, + const float right, + const float bottom) override; + int32_t SetTransparentBackground(const bool enable) override; + int32_t SetText(const uint8_t text_id, + const uint8_t* text, + const int32_t text_length, + const uint32_t text_color_ref, + const uint32_t background_color_ref, + const float left, + const float top, + const float right, + const float bottom) override; + int32_t SetBitmap(const void* bit_map, + const uint8_t picture_id, + const void* color_key, + const float left, + const float top, + const float right, + const float bottom); + int32_t FullScreenRender(void* window, const bool enable); + + private: + int32_t id_; + void* ptr_window_; + bool full_screen_; + + CriticalSectionWrapper* crit_sec_; + std::unique_ptr ptr_ios_render_; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_VIDEO_RENDER_IOS_IMPL_H_ diff --git a/webrtc/modules/video_render/ios/video_render_ios_impl.mm b/webrtc/modules/video_render/ios/video_render_ios_impl.mm new file mode 100644 index 0000000000..0ef411d56f --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_impl.mm @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#include "webrtc/modules/video_render/ios/video_render_ios_impl.h" +#include "webrtc/modules/video_render/ios/video_render_ios_gles20.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +#define IOS_UNSUPPORTED() \ + WEBRTC_TRACE(kTraceError, \ + kTraceVideoRenderer, \ + id_, \ + "%s is not supported on the iOS platform.", \ + __FUNCTION__); \ + return -1; + +VideoRenderIosImpl::VideoRenderIosImpl(const int32_t id, + void* window, + const bool full_screen) + : id_(id), + ptr_window_(window), + full_screen_(full_screen), + crit_sec_(CriticalSectionWrapper::CreateCriticalSection()) {} + +VideoRenderIosImpl::~VideoRenderIosImpl() { + delete crit_sec_; +} + +int32_t VideoRenderIosImpl::Init() { + CriticalSectionScoped cs(crit_sec_); + + ptr_ios_render_.reset(new VideoRenderIosGles20( + (__bridge VideoRenderIosView*)ptr_window_, full_screen_, id_)); + + return ptr_ios_render_->Init(); + ; +} + +int32_t VideoRenderIosImpl::ChangeWindow(void* window) { + CriticalSectionScoped cs(crit_sec_); + if (window == NULL) { + return -1; + } + + ptr_window_ = window; + + return ptr_ios_render_->ChangeWindow(ptr_window_); +} + +VideoRenderCallback* VideoRenderIosImpl::AddIncomingRenderStream( + const uint32_t stream_id, + const uint32_t z_order, + const float left, + const float top, + const float right, + const float bottom) { + CriticalSectionScoped cs(crit_sec_); + if (!ptr_window_) { + return NULL; + } + + return ptr_ios_render_->CreateEaglChannel( + stream_id, z_order, left, top, right, bottom); +} + +int32_t VideoRenderIosImpl::DeleteIncomingRenderStream( + const uint32_t stream_id) { + CriticalSectionScoped cs(crit_sec_); + + return ptr_ios_render_->DeleteEaglChannel(stream_id); +} + +int32_t VideoRenderIosImpl::GetIncomingRenderStreamProperties( + const uint32_t stream_id, + uint32_t& z_order, + float& left, + float& top, + float& right, + float& bottom) const { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::StartRender() { + return ptr_ios_render_->StartRender(); +} + +int32_t VideoRenderIosImpl::StopRender() { + return ptr_ios_render_->StopRender(); +} + +VideoRenderType VideoRenderIosImpl::RenderType() { return kRenderiOS; } + +RawVideoType VideoRenderIosImpl::PerferedVideoType() { return kVideoI420; } + +bool VideoRenderIosImpl::FullScreen() { IOS_UNSUPPORTED(); } + +int32_t VideoRenderIosImpl::GetGraphicsMemory( + uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const { + return ptr_ios_render_->GetScreenResolution(screenWidth, screenHeight); +} + +uint32_t VideoRenderIosImpl::RenderFrameRate(const uint32_t streamId) { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) { + return ptr_ios_render_->SetStreamCropping(streamId, left, top, right, bottom); +} + +int32_t VideoRenderIosImpl::ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::SetTransparentBackground(const bool enable) { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom) { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) { + IOS_UNSUPPORTED(); +} + +int32_t VideoRenderIosImpl::FullScreenRender(void* window, const bool enable) { + IOS_UNSUPPORTED(); +} diff --git a/webrtc/modules/video_render/ios/video_render_ios_view.h b/webrtc/modules/video_render/ios/video_render_ios_view.h new file mode 100644 index 0000000000..d110bc78bd --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_view.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_IOS_RENDER_VIEW_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_IOS_RENDER_VIEW_H_ + +#import +#import + +#include "webrtc/modules/video_render/ios/open_gles20.h" + +@interface VideoRenderIosView : UIView + +- (BOOL)createContext; +- (BOOL)presentFramebuffer; +- (BOOL)renderFrame:(webrtc::VideoFrame*)frameToRender; +- (BOOL)setCoordinatesForZOrder:(const float)zOrder + Left:(const float)left + Top:(const float)top + Right:(const float)right + Bottom:(const float)bottom; + +@property(nonatomic, retain) EAGLContext* context; + +@end + +#endif // WEBRTC_MODULES_VIDEO_RENDER_IOS_RENDER_VIEW_H_ diff --git a/webrtc/modules/video_render/ios/video_render_ios_view.mm b/webrtc/modules/video_render/ios/video_render_ios_view.mm new file mode 100644 index 0000000000..b106ffa5c4 --- /dev/null +++ b/webrtc/modules/video_render/ios/video_render_ios_view.mm @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#include + +#include "webrtc/modules/video_render/ios/video_render_ios_view.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +@implementation VideoRenderIosView { + EAGLContext* _context; + std::unique_ptr _gles_renderer20; + int _frameBufferWidth; + int _frameBufferHeight; + unsigned int _defaultFrameBuffer; + unsigned int _colorRenderBuffer; +} + +@synthesize context = context_; + ++ (Class)layerClass { + return [CAEAGLLayer class]; +} + +- (id)initWithCoder:(NSCoder*)coder { + // init super class + self = [super initWithCoder:coder]; + if (self) { + _gles_renderer20.reset(new OpenGles20()); + } + return self; +} + +- (id)init { + // init super class + self = [super init]; + if (self) { + _gles_renderer20.reset(new OpenGles20()); + } + return self; +} + +- (id)initWithFrame:(CGRect)frame { + // init super class + self = [super initWithFrame:frame]; + if (self) { + _gles_renderer20.reset(new OpenGles20()); + } + return self; +} + +- (void)dealloc { + if (_defaultFrameBuffer) { + glDeleteFramebuffers(1, &_defaultFrameBuffer); + _defaultFrameBuffer = 0; + } + + if (_colorRenderBuffer) { + glDeleteRenderbuffers(1, &_colorRenderBuffer); + _colorRenderBuffer = 0; + } + + [EAGLContext setCurrentContext:nil]; +} + +- (NSString*)description { + return [NSString stringWithFormat: + @"A WebRTC implemented subclass of UIView." + "+Class method is overwritten, along with custom methods"]; +} + +- (BOOL)createContext { + // create OpenGLES context from self layer class + CAEAGLLayer* eagl_layer = (CAEAGLLayer*)self.layer; + eagl_layer.opaque = YES; + eagl_layer.drawableProperties = + [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], + kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGBA8, + kEAGLDrawablePropertyColorFormat, + nil]; + _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + + if (!_context) { + return NO; + } + + if (![EAGLContext setCurrentContext:_context]) { + return NO; + } + + // generates and binds the OpenGLES buffers + glGenFramebuffers(1, &_defaultFrameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFrameBuffer); + + // Create color render buffer and allocate backing store. + glGenRenderbuffers(1, &_colorRenderBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer); + [_context renderbufferStorage:GL_RENDERBUFFER + fromDrawable:(CAEAGLLayer*)self.layer]; + glGetRenderbufferParameteriv( + GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_frameBufferWidth); + glGetRenderbufferParameteriv( + GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_frameBufferHeight); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + _colorRenderBuffer); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + return NO; + } + + // set the frame buffer + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFrameBuffer); + glViewport(0, 0, self.frame.size.width, self.frame.size.height); + + return _gles_renderer20->Setup([self bounds].size.width, + [self bounds].size.height); +} + +- (BOOL)presentFramebuffer { + if (![_context presentRenderbuffer:GL_RENDERBUFFER]) { + WEBRTC_TRACE(kTraceWarning, + kTraceVideoRenderer, + 0, + "%s:%d [context present_renderbuffer] " + "returned false", + __FUNCTION__, + __LINE__); + } + return YES; +} + +- (BOOL)renderFrame:(VideoFrame*)frameToRender { + if (![EAGLContext setCurrentContext:_context]) { + return NO; + } + + return _gles_renderer20->Render(*frameToRender); +} + +- (BOOL)setCoordinatesForZOrder:(const float)zOrder + Left:(const float)left + Top:(const float)top + Right:(const float)right + Bottom:(const float)bottom { + return _gles_renderer20->SetCoordinates(zOrder, left, top, right, bottom); +} + +@end diff --git a/webrtc/modules/video_render/linux/video_render_linux_impl.cc b/webrtc/modules/video_render/linux/video_render_linux_impl.cc new file mode 100644 index 0000000000..7e53dfdf80 --- /dev/null +++ b/webrtc/modules/video_render/linux/video_render_linux_impl.cc @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/linux/video_render_linux_impl.h" + +#include "webrtc/modules/video_render/linux/video_x11_render.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +#include + +namespace webrtc { + +VideoRenderLinuxImpl::VideoRenderLinuxImpl( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, const bool fullscreen) : + _id(id), + _renderLinuxCritsect( + *CriticalSectionWrapper::CreateCriticalSection()), + _ptrWindow(window), _ptrX11Render(NULL) +{ +} + +VideoRenderLinuxImpl::~VideoRenderLinuxImpl() +{ + if (_ptrX11Render) + delete _ptrX11Render; + + delete &_renderLinuxCritsect; +} + +int32_t VideoRenderLinuxImpl::Init() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + + CriticalSectionScoped cs(&_renderLinuxCritsect); + _ptrX11Render = new VideoX11Render((Window) _ptrWindow); + if (!_ptrX11Render) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s", + "Failed to create instance of VideoX11Render object"); + return -1; + } + int retVal = _ptrX11Render->Init(); + if (retVal == -1) + { + return -1; + } + + return 0; + +} + +int32_t VideoRenderLinuxImpl::ChangeWindow(void* window) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + + CriticalSectionScoped cs(&_renderLinuxCritsect); + _ptrWindow = window; + + if (_ptrX11Render) + { + return _ptrX11Render->ChangeWindow((Window) window); + } + + return -1; +} + +VideoRenderCallback* VideoRenderLinuxImpl::AddIncomingRenderStream( + const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_renderLinuxCritsect); + + VideoRenderCallback* renderCallback = NULL; + if (_ptrX11Render) + { + VideoX11Channel* renderChannel = + _ptrX11Render->CreateX11RenderChannel(streamId, zOrder, left, + top, right, bottom); + if (!renderChannel) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "Render channel creation failed for stream id: %d", + streamId); + return NULL; + } + renderCallback = (VideoRenderCallback *) renderChannel; + } + else + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "_ptrX11Render is NULL"); + return NULL; + } + return renderCallback; +} + +int32_t VideoRenderLinuxImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_renderLinuxCritsect); + + if (_ptrX11Render) + { + return _ptrX11Render->DeleteX11RenderChannel(streamId); + } + return -1; +} + +int32_t VideoRenderLinuxImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_renderLinuxCritsect); + + if (_ptrX11Render) + { + return _ptrX11Render->GetIncomingStreamProperties(streamId, zOrder, + left, top, right, + bottom); + } + return -1; +} + +int32_t VideoRenderLinuxImpl::StartRender() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + return 0; +} + +int32_t VideoRenderLinuxImpl::StopRender() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s", + __FUNCTION__); + return 0; +} + +VideoRenderType VideoRenderLinuxImpl::RenderType() +{ + return kRenderX11; +} + +RawVideoType VideoRenderLinuxImpl::PerferedVideoType() +{ + return kVideoI420; +} + +bool VideoRenderLinuxImpl::FullScreen() +{ + return false; +} + +int32_t VideoRenderLinuxImpl::GetGraphicsMemory( + uint64_t& /*totalGraphicsMemory*/, + uint64_t& /*availableGraphicsMemory*/) const +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +int32_t VideoRenderLinuxImpl::GetScreenResolution( + uint32_t& /*screenWidth*/, + uint32_t& /*screenHeight*/) const +{ + return -1; +} + +uint32_t VideoRenderLinuxImpl::RenderFrameRate(const uint32_t /*streamId*/) +{ + return -1; +} + +int32_t VideoRenderLinuxImpl::SetStreamCropping( + const uint32_t /*streamId*/, + const float /*left*/, + const float /*top*/, + const float /*right*/, + const float /*bottom*/) +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +int32_t VideoRenderLinuxImpl::SetTransparentBackground(const bool /*enable*/) +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +int32_t VideoRenderLinuxImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +int32_t VideoRenderLinuxImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float rigth, + const float bottom) +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +int32_t VideoRenderLinuxImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s - not supported on Linux", __FUNCTION__); + return -1; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/linux/video_render_linux_impl.h b/webrtc/modules/video_render/linux/video_render_linux_impl.h new file mode 100644 index 0000000000..0e9ae54c18 --- /dev/null +++ b/webrtc/modules/video_render/linux/video_render_linux_impl.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ + +#include "webrtc/modules/video_render/i_video_render.h" + +namespace webrtc { +class CriticalSectionWrapper; + +class VideoX11Render; + +// Class definitions +class VideoRenderLinuxImpl: IVideoRender +{ +public: + /* + * Constructor/destructor + */ + + VideoRenderLinuxImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, const bool fullscreen); + + virtual ~VideoRenderLinuxImpl(); + + virtual int32_t Init(); + + virtual int32_t ChangeWindow(void* window); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId); + + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t + GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float rigth, const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom); + +private: + int32_t _id; + CriticalSectionWrapper& _renderLinuxCritsect; + + void* _ptrWindow; + + // X11 Render + VideoX11Render* _ptrX11Render; +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_RENDER_LINUX_IMPL_H_ diff --git a/webrtc/modules/video_render/linux/video_x11_channel.cc b/webrtc/modules/video_render/linux/video_x11_channel.cc new file mode 100644 index 0000000000..8d86b7c72a --- /dev/null +++ b/webrtc/modules/video_render/linux/video_x11_channel.cc @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/linux/video_x11_channel.h" + +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +#define DISP_MAX 128 + +static Display *dispArray[DISP_MAX]; +static int dispCount = 0; + + +VideoX11Channel::VideoX11Channel(int32_t id) : + _crit(*CriticalSectionWrapper::CreateCriticalSection()), _display(NULL), + _shminfo(), _image(NULL), _window(0L), _gc(NULL), + _width(DEFAULT_RENDER_FRAME_WIDTH), + _height(DEFAULT_RENDER_FRAME_HEIGHT), _outWidth(0), _outHeight(0), + _xPos(0), _yPos(0), _prepared(false), _dispCount(0), _buffer(NULL), + _top(0.0), _left(0.0), _right(0.0), _bottom(0.0), + _Id(id) +{ +} + +VideoX11Channel::~VideoX11Channel() +{ + if (_prepared) + { + _crit.Enter(); + ReleaseWindow(); + _crit.Leave(); + } + delete &_crit; +} + +int32_t VideoX11Channel::RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame) { + CriticalSectionScoped cs(&_crit); + if (_width != videoFrame.width() || _height + != videoFrame.height()) { + if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { + return -1; + } + } + return DeliverFrame(videoFrame); +} + +int32_t VideoX11Channel::FrameSizeChange(int32_t width, + int32_t height, + int32_t /*numberOfStreams */) +{ + CriticalSectionScoped cs(&_crit); + if (_prepared) + { + RemoveRenderer(); + } + if (CreateLocalRenderer(width, height) == -1) + { + return -1; + } + + return 0; +} + +int32_t VideoX11Channel::DeliverFrame(const VideoFrame& videoFrame) { + CriticalSectionScoped cs(&_crit); + if (!_prepared) { + return 0; + } + + if (!dispArray[_dispCount]) { + return -1; + } + + ConvertFromI420(videoFrame, kARGB, 0, _buffer); + + // Put image in window. + XShmPutImage(_display, _window, _gc, _image, 0, 0, _xPos, _yPos, _width, + _height, True); + + // Very important for the image to update properly! + XSync(_display, False); + return 0; +} + +int32_t VideoX11Channel::GetFrameSize(int32_t& width, int32_t& height) +{ + width = _width; + height = _height; + + return 0; +} + +int32_t VideoX11Channel::Init(Window window, float left, float top, + float right, float bottom) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_crit); + + _window = window; + _left = left; + _right = right; + _top = top; + _bottom = bottom; + + _display = XOpenDisplay(NULL); // Use default display + if (!_window || !_display) + { + return -1; + } + + if (dispCount < DISP_MAX) + { + dispArray[dispCount] = _display; + _dispCount = dispCount; + dispCount++; + } + else + { + return -1; + } + + if ((1 < left || left < 0) || (1 < top || top < 0) || (1 < right || right + < 0) || (1 < bottom || bottom < 0)) + { + return -1; + } + + // calculate position and size of rendered video + int x, y; + unsigned int winWidth, winHeight, borderwidth, depth; + Window rootret; + if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, + &winHeight, &borderwidth, &depth) == 0) + { + return -1; + } + + _xPos = (int32_t) (winWidth * left); + _yPos = (int32_t) (winHeight * top); + _outWidth = (int32_t) (winWidth * (right - left)); + _outHeight = (int32_t) (winHeight * (bottom - top)); + if (_outWidth % 2) + _outWidth++; // the renderer want's sizes that are multiples of two + if (_outHeight % 2) + _outHeight++; + + _gc = XCreateGC(_display, _window, 0, 0); + if (!_gc) { + // Failed to create the graphics context. + assert(false); + return -1; + } + + if (CreateLocalRenderer(winWidth, winHeight) == -1) + { + return -1; + } + return 0; + +} + +int32_t VideoX11Channel::ChangeWindow(Window window) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_crit); + + // Stop the rendering, if we are rendering... + RemoveRenderer(); + _window = window; + + // calculate position and size of rendered video + int x, y; + unsigned int winWidth, winHeight, borderwidth, depth; + Window rootret; + if (XGetGeometry(_display, _window, &rootret, &x, &y, &winWidth, + &winHeight, &borderwidth, &depth) == -1) + { + return -1; + } + _xPos = (int) (winWidth * _left); + _yPos = (int) (winHeight * _top); + _outWidth = (int) (winWidth * (_right - _left)); + _outHeight = (int) (winHeight * (_bottom - _top)); + if (_outWidth % 2) + _outWidth++; // the renderer want's sizes that are multiples of two + if (_outHeight % 2) + _outHeight++; + + // Prepare rendering using the + if (CreateLocalRenderer(_width, _height) == -1) + { + return -1; + } + return 0; +} + +int32_t VideoX11Channel::ReleaseWindow() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_crit); + + RemoveRenderer(); + if (_gc) { + XFreeGC(_display, _gc); + _gc = NULL; + } + if (_display) + { + XCloseDisplay(_display); + _display = NULL; + } + return 0; +} + +int32_t VideoX11Channel::CreateLocalRenderer(int32_t width, int32_t height) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + CriticalSectionScoped cs(&_crit); + + if (!_window || !_display) + { + return -1; + } + + if (_prepared) + { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _Id, + "Renderer already prepared, exits."); + return -1; + } + + _width = width; + _height = height; + + // create shared memory image + _image = XShmCreateImage(_display, CopyFromParent, 24, ZPixmap, NULL, + &_shminfo, _width, _height); // this parameter needs to be the same for some reason. + _shminfo.shmid = shmget(IPC_PRIVATE, (_image->bytes_per_line + * _image->height), IPC_CREAT | 0777); + _shminfo.shmaddr = _image->data = (char*) shmat(_shminfo.shmid, 0, 0); + if (_image->data == reinterpret_cast(-1)) + { + return -1; + } + _buffer = (unsigned char*) _image->data; + _shminfo.readOnly = False; + + // attach image to display + if (!XShmAttach(_display, &_shminfo)) + { + //printf("XShmAttach failed !\n"); + return -1; + } + XSync(_display, False); + + _prepared = true; + return 0; +} + +int32_t VideoX11Channel::RemoveRenderer() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + + if (!_prepared) + { + return 0; + } + _prepared = false; + + // Free the memory. + XShmDetach(_display, &_shminfo); + XDestroyImage( _image ); + _image = NULL; + shmdt(_shminfo.shmaddr); + _shminfo.shmaddr = NULL; + _buffer = NULL; + shmctl(_shminfo.shmid, IPC_RMID, 0); + _shminfo.shmid = 0; + return 0; +} + +int32_t VideoX11Channel::GetStreamProperties(uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _Id, "%s", + __FUNCTION__); + + zOrder = 0; // no z-order support yet + left = _left; + top = _top; + right = _right; + bottom = _bottom; + + return 0; +} + + +} // namespace webrtc diff --git a/webrtc/modules/video_render/linux/video_x11_channel.h b/webrtc/modules/video_render/linux/video_x11_channel.h new file mode 100644 index 0000000000..6eb402e12e --- /dev/null +++ b/webrtc/modules/video_render/linux/video_x11_channel.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ + +#include +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +#include +#include +#include + +namespace webrtc { +class CriticalSectionWrapper; + +#define DEFAULT_RENDER_FRAME_WIDTH 352 +#define DEFAULT_RENDER_FRAME_HEIGHT 288 + + +class VideoX11Channel: public VideoRenderCallback +{ +public: + VideoX11Channel(int32_t id); + + virtual ~VideoX11Channel(); + + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + + int32_t FrameSizeChange(int32_t width, int32_t height, + int32_t numberOfStreams); + int32_t DeliverFrame(const VideoFrame& videoFrame); + int32_t GetFrameSize(int32_t& width, int32_t& height); + int32_t Init(Window window, float left, float top, float right, + float bottom); + int32_t ChangeWindow(Window window); + int32_t + GetStreamProperties(uint32_t& zOrder, float& left, + float& top, float& right, float& bottom) const; + int32_t ReleaseWindow(); + + bool IsPrepared() + { + return _prepared; + } + +private: + + int32_t + CreateLocalRenderer(int32_t width, int32_t height); + int32_t RemoveRenderer(); + + //FIXME a better place for this method? the GetWidthHeight no longer + // supported by common_video. + int GetWidthHeight(VideoType type, int bufferSize, int& width, + int& height); + + CriticalSectionWrapper& _crit; + + Display* _display; + XShmSegmentInfo _shminfo; + XImage* _image; + Window _window; + GC _gc; + int32_t _width; // incoming frame width + int32_t _height; // incoming frame height + int32_t _outWidth; // render frame width + int32_t _outHeight; // render frame height + int32_t _xPos; // position within window + int32_t _yPos; + bool _prepared; // true if ready to use + int32_t _dispCount; + + unsigned char* _buffer; + float _top; + float _left; + float _right; + float _bottom; + + int32_t _Id; + +}; + + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_CHANNEL_H_ diff --git a/webrtc/modules/video_render/linux/video_x11_render.cc b/webrtc/modules/video_render/linux/video_x11_render.cc new file mode 100644 index 0000000000..5eb4f36f95 --- /dev/null +++ b/webrtc/modules/video_render/linux/video_x11_render.cc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/linux/video_x11_channel.h" +#include "webrtc/modules/video_render/linux/video_x11_render.h" + +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +VideoX11Render::VideoX11Render(Window window) : + _window(window), + _critSect(*CriticalSectionWrapper::CreateCriticalSection()) +{ +} + +VideoX11Render::~VideoX11Render() +{ + delete &_critSect; +} + +int32_t VideoX11Render::Init() +{ + CriticalSectionScoped cs(&_critSect); + + _streamIdToX11ChannelMap.clear(); + + return 0; +} + +int32_t VideoX11Render::ChangeWindow(Window window) +{ + CriticalSectionScoped cs(&_critSect); + VideoX11Channel* renderChannel = NULL; + + std::map::iterator iter = + _streamIdToX11ChannelMap.begin(); + + while (iter != _streamIdToX11ChannelMap.end()) + { + renderChannel = iter->second; + if (renderChannel) + { + renderChannel->ChangeWindow(window); + } + iter++; + } + + _window = window; + + return 0; +} + +VideoX11Channel* VideoX11Render::CreateX11RenderChannel( + int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_critSect); + VideoX11Channel* renderChannel = NULL; + + std::map::iterator iter = + _streamIdToX11ChannelMap.find(streamId); + + if (iter == _streamIdToX11ChannelMap.end()) + { + renderChannel = new VideoX11Channel(streamId); + if (!renderChannel) + { + WEBRTC_TRACE( + kTraceError, + kTraceVideoRenderer, + -1, + "Failed to create VideoX11Channel for streamId : %d", + streamId); + return NULL; + } + renderChannel->Init(_window, left, top, right, bottom); + _streamIdToX11ChannelMap[streamId] = renderChannel; + } + else + { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1, + "Render Channel already exists for streamId: %d", streamId); + renderChannel = iter->second; + } + + return renderChannel; +} + +int32_t VideoX11Render::DeleteX11RenderChannel(int32_t streamId) +{ + CriticalSectionScoped cs(&_critSect); + + std::map::iterator iter = + _streamIdToX11ChannelMap.find(streamId); + if (iter != _streamIdToX11ChannelMap.end()) + { + VideoX11Channel *renderChannel = iter->second; + if (renderChannel) + { + renderChannel->ReleaseWindow(); + delete renderChannel; + renderChannel = NULL; + } + _streamIdToX11ChannelMap.erase(iter); + } + + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "No VideoX11Channel object exists for stream id: %d", + streamId); + return -1; +} + +int32_t VideoX11Render::GetIncomingStreamProperties( + int32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) +{ + CriticalSectionScoped cs(&_critSect); + + std::map::iterator iter = + _streamIdToX11ChannelMap.find(streamId); + if (iter != _streamIdToX11ChannelMap.end()) + { + VideoX11Channel *renderChannel = iter->second; + if (renderChannel) + { + renderChannel->GetStreamProperties(zOrder, left, top, right, bottom); + } + } + + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1, + "No VideoX11Channel object exists for stream id: %d", + streamId); + return -1; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/linux/video_x11_render.h b/webrtc/modules/video_render/linux/video_x11_render.h new file mode 100644 index 0000000000..23b83bd67b --- /dev/null +++ b/webrtc/modules/video_render/linux/video_x11_render.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ + +#include "webrtc/modules/video_render/video_render_defines.h" + +#include +#include + +namespace webrtc { +class CriticalSectionWrapper; + +class VideoX11Channel; + +class VideoX11Render +{ + +public: + VideoX11Render(Window window); + ~VideoX11Render(); + + int32_t Init(); + int32_t ChangeWindow(Window window); + + VideoX11Channel* CreateX11RenderChannel(int32_t streamId, + int32_t zOrder, + const float left, + const float top, + const float right, + const float bottom); + + int32_t DeleteX11RenderChannel(int32_t streamId); + + int32_t GetIncomingStreamProperties(int32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom); + +private: + Window _window; + CriticalSectionWrapper& _critSect; + std::map _streamIdToX11ChannelMap; + +}; + + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_LINUX_VIDEO_X11_RENDER_H_ diff --git a/webrtc/modules/video_render/mac/cocoa_full_screen_window.h b/webrtc/modules/video_render/mac/cocoa_full_screen_window.h new file mode 100644 index 0000000000..c8e98bba67 --- /dev/null +++ b/webrtc/modules/video_render/mac/cocoa_full_screen_window.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// +// cocoa_full_screen_window.h +// +// + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ + +#import +//#define GRAB_ALL_SCREENS 1 + +@interface CocoaFullScreenWindow : NSObject { + NSWindow* _window; +} + +-(id)init; +-(void)grabFullScreen; +-(void)releaseFullScreen; +-(NSWindow*)window; + +@end + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_FULL_SCREEN_WINDOW_H_ diff --git a/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm b/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm new file mode 100644 index 0000000000..b57223b4df --- /dev/null +++ b/webrtc/modules/video_render/mac/cocoa_full_screen_window.mm @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/mac/cocoa_full_screen_window.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +@implementation CocoaFullScreenWindow + +-(id)init{ + + self = [super init]; + if(!self){ + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d COULD NOT CREATE INSTANCE", __FUNCTION__, __LINE__); + return nil; + } + + + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, 0, "%s:%d Created instance", __FUNCTION__, __LINE__); + return self; +} + +-(void)grabFullScreen{ + +#ifdef GRAB_ALL_SCREENS + if(CGCaptureAllDisplays() != kCGErrorSuccess) +#else + if(CGDisplayCapture(kCGDirectMainDisplay) != kCGErrorSuccess) +#endif + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not capture main level", __FUNCTION__, __LINE__); + } + + // get the shielding window level + int windowLevel = CGShieldingWindowLevel(); + + // get the screen rect of main display + NSRect screenRect = [[NSScreen mainScreen]frame]; + + _window = [[NSWindow alloc]initWithContentRect:screenRect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO + screen:[NSScreen mainScreen]]; + + [_window setLevel:windowLevel]; + [_window setBackgroundColor:[NSColor blackColor]]; + [_window makeKeyAndOrderFront:nil]; + +} + +-(void)releaseFullScreen +{ + [_window orderOut:self]; + +#ifdef GRAB_ALL_SCREENS + if(CGReleaseAllDisplays() != kCGErrorSuccess) +#else + if(CGDisplayRelease(kCGDirectMainDisplay) != kCGErrorSuccess) +#endif + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not release the displays", __FUNCTION__, __LINE__); + } +} + +- (NSWindow*)window +{ + return _window; +} + +- (void) dealloc +{ + [self releaseFullScreen]; + [super dealloc]; +} + + + +@end diff --git a/webrtc/modules/video_render/mac/cocoa_render_view.h b/webrtc/modules/video_render/mac/cocoa_render_view.h new file mode 100644 index 0000000000..15a8108dec --- /dev/null +++ b/webrtc/modules/video_render/mac/cocoa_render_view.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// +// cocoa_render_view.h +// + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ + +#import +#import +#import +#import + +@interface CocoaRenderView : NSOpenGLView { + NSOpenGLContext* _nsOpenGLContext; +} + +-(void)initCocoaRenderView:(NSOpenGLPixelFormat*)fmt; +-(void)initCocoaRenderViewFullScreen:(NSOpenGLPixelFormat*)fmt; +-(NSOpenGLContext*)nsOpenGLContext; +@end + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_COCOA_RENDER_VIEW_H_ diff --git a/webrtc/modules/video_render/mac/cocoa_render_view.mm b/webrtc/modules/video_render/mac/cocoa_render_view.mm new file mode 100644 index 0000000000..4631ff31a4 --- /dev/null +++ b/webrtc/modules/video_render/mac/cocoa_render_view.mm @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#import +#import + +#include "webrtc/modules/video_render/mac/cocoa_render_view.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +@implementation CocoaRenderView + +-(void)initCocoaRenderView:(NSOpenGLPixelFormat*)fmt{ + + self = [super initWithFrame:[self frame] pixelFormat:fmt]; + if (self == nil){ + + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not create instance", __FUNCTION__, __LINE__); + } + + + _nsOpenGLContext = [self openGLContext]; + +} + +-(NSOpenGLContext*)nsOpenGLContext { + return _nsOpenGLContext; +} + +-(void)initCocoaRenderViewFullScreen:(NSOpenGLPixelFormat*)fmt{ + + NSRect screenRect = [[NSScreen mainScreen]frame]; +// [_windowRef setFrame:screenRect]; +// [_windowRef setBounds:screenRect]; + self = [super initWithFrame:screenRect pixelFormat:fmt]; + if (self == nil){ + + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, 0, "%s:%d Could not create instance", __FUNCTION__, __LINE__); + } + + _nsOpenGLContext = [self openGLContext]; + +} + +@end + + diff --git a/webrtc/modules/video_render/mac/video_render_agl.cc b/webrtc/modules/video_render/mac/video_render_agl.cc new file mode 100644 index 0000000000..3243563b2b --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_agl.cc @@ -0,0 +1,1987 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" + +#if defined(CARBON_RENDERING) + +#include "webrtc/modules/video_render/mac/video_render_agl.h" + +// includes +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/event_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +/* + * + * VideoChannelAGL + * + */ + +#pragma mark VideoChannelAGL constructor + +VideoChannelAGL::VideoChannelAGL(AGLContext& aglContext, int iId, VideoRenderAGL* owner) : + _aglContext( aglContext), + _id( iId), + _owner( owner), + _width( 0), + _height( 0), + _stretchedWidth( 0), + _stretchedHeight( 0), + _startWidth( 0.0f), + _startHeight( 0.0f), + _stopWidth( 0.0f), + _stopHeight( 0.0f), + _xOldWidth( 0), + _yOldHeight( 0), + _oldStretchedHeight(0), + _oldStretchedWidth( 0), + _buffer( 0), + _bufferSize( 0), + _incomingBufferSize(0), + _bufferIsUpdated( false), + _sizeInitialized( false), + _numberOfStreams( 0), + _bVideoSizeStartedChanging(false), + _pixelFormat( GL_RGBA), + _pixelDataType( GL_UNSIGNED_INT_8_8_8_8), + _texture( 0) + +{ + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Constructor", __FUNCTION__, __LINE__); +} + +VideoChannelAGL::~VideoChannelAGL() +{ + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Destructor", __FUNCTION__, __LINE__); + if (_buffer) + { + delete [] _buffer; + _buffer = NULL; + } + + aglSetCurrentContext(_aglContext); + + if (_texture != 0) + { + glDeleteTextures(1, (const GLuint*) &_texture); + _texture = 0; + } +} + +int32_t VideoChannelAGL::RenderFrame(const uint32_t streamId, + VideoFrame& videoFrame) { + _owner->LockAGLCntx(); + if (_width != videoFrame.width() || + _height != videoFrame.height()) { + if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d FrameSize + Change returned an error", __FUNCTION__, __LINE__); + _owner->UnlockAGLCntx(); + return -1; + } + } + + _owner->UnlockAGLCntx(); + return DeliverFrame(videoFrame); +} + +int VideoChannelAGL::UpdateSize(int /*width*/, int /*height*/) +{ + _owner->LockAGLCntx(); + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelAGL::UpdateStretchSize(int stretchHeight, int stretchWidth) +{ + + _owner->LockAGLCntx(); + _stretchedHeight = stretchHeight; + _stretchedWidth = stretchWidth; + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelAGL::FrameSizeChange(int width, int height, int numberOfStreams) +{ + // We'll get a new frame size from VideoAPI, prepare the buffer + + _owner->LockAGLCntx(); + + if (width == _width && _height == height) + { + // We already have a correct buffer size + _numberOfStreams = numberOfStreams; + _owner->UnlockAGLCntx(); + return 0; + } + + _width = width; + _height = height; + + // Delete the old buffer, create a new one with correct size. + if (_buffer) + { + delete [] _buffer; + _bufferSize = 0; + } + + _incomingBufferSize = CalcBufferSize(kI420, _width, _height); + _bufferSize = CalcBufferSize(kARGB, _width, _height);//_width * _height * bytesPerPixel; + _buffer = new unsigned char [_bufferSize]; + memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); + + if (aglSetCurrentContext(_aglContext) == false) + { + _owner->UnlockAGLCntx(); + return -1; + } + + // Delete a possible old texture + if (_texture != 0) + { + glDeleteTextures(1, (const GLuint*) &_texture); + _texture = 0; + } + + // Create a new texture + glGenTextures(1, (GLuint *) &_texture); + + GLenum glErr = glGetError(); + + if (glErr != GL_NO_ERROR) + { + } + + // Do the setup for both textures + // Note: we setup two textures even if we're not running full screen + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + + // Set texture parameters + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); + + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); + + // Maximum width/height for a texture + GLint texSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); + + if (texSize < _width || texSize < _height) + { + // Image too big for memory + _owner->UnlockAGLCntx(); + return -1; + } + + // Set up th texture type and size + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target + 0, // level + GL_RGBA, // internal format + _width, // width + _height, // height + 0, // border 0/1 = off/on + _pixelFormat, // format, GL_BGRA + _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 + _buffer); // pixel data + + glErr = glGetError(); + if (glErr != GL_NO_ERROR) + { + _owner->UnlockAGLCntx(); + return -1; + } + + _owner->UnlockAGLCntx(); + return 0; +} + +// Called from video engine when a new frame should be rendered. +int VideoChannelAGL::DeliverFrame(const VideoFrame& videoFrame) { + _owner->LockAGLCntx(); + + if (_texture == 0) { + _owner->UnlockAGLCntx(); + return 0; + } + + if (CalcBufferSize(kI420, videoFrame.width(), videoFrame.height()) != + _incomingBufferSize) { + _owner->UnlockAGLCntx(); + return -1; + } + + // Setting stride = width. + int rgbret = ConvertFromYV12(videoFrame, kBGRA, 0, _buffer); + if (rgbret < 0) { + _owner->UnlockAGLCntx(); + return -1; + } + + aglSetCurrentContext(_aglContext); + + // Put the new frame into the graphic card texture. + // Make sure this texture is the active one + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + GLenum glErr = glGetError(); + if (glErr != GL_NO_ERROR) { + _owner->UnlockAGLCntx(); + return -1; + } + + // Copy buffer to texture + glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, + 0, // Level, not use + 0, // start point x, (low left of pic) + 0, // start point y, + _width, // width + _height, // height + _pixelFormat, // pictue format for _buffer + _pixelDataType, // data type of _buffer + (const GLvoid*) _buffer); // the pixel data + + if (glGetError() != GL_NO_ERROR) { + _owner->UnlockAGLCntx(); + return -1; + } + + _bufferIsUpdated = true; + _owner->UnlockAGLCntx(); + + return 0; +} + +int VideoChannelAGL::RenderOffScreenBuffer() +{ + + _owner->LockAGLCntx(); + + if (_texture == 0) + { + _owner->UnlockAGLCntx(); + return 0; + } + + GLfloat xStart = 2.0f * _startWidth - 1.0f; + GLfloat xStop = 2.0f * _stopWidth - 1.0f; + GLfloat yStart = 1.0f - 2.0f * _stopHeight; + GLfloat yStop = 1.0f - 2.0f * _startHeight; + + aglSetCurrentContext(_aglContext); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + + if(_stretchedWidth != _oldStretchedWidth || _stretchedHeight != _oldStretchedHeight) + { + glViewport(0, 0, _stretchedWidth, _stretchedHeight); + } + _oldStretchedHeight = _stretchedHeight; + _oldStretchedWidth = _stretchedWidth; + + // Now really put the texture into the framebuffer + glLoadIdentity(); + + glEnable(GL_TEXTURE_RECTANGLE_EXT); + + glBegin(GL_POLYGON); + { + glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); + glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); + glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); + glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); + } + glEnd(); + + glDisable(GL_TEXTURE_RECTANGLE_EXT); + + _bufferIsUpdated = false; + + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelAGL::IsUpdated(bool& isUpdated) +{ + _owner->LockAGLCntx(); + isUpdated = _bufferIsUpdated; + _owner->UnlockAGLCntx(); + + return 0; +} + +int VideoChannelAGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + + _owner->LockAGLCntx(); + + _startWidth = startWidth; + _stopWidth = stopWidth; + _startHeight = startHeight; + _stopHeight = stopHeight; + + int oldWidth = _width; + int oldHeight = _height; + int oldNumberOfStreams = _numberOfStreams; + + _width = 0; + _height = 0; + + int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); + + _owner->UnlockAGLCntx(); + + return retVal; +} + +int VideoChannelAGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) +{ + return -1; +} + +#pragma mark VideoRenderAGL WindowRef constructor + +VideoRenderAGL::VideoRenderAGL(WindowRef windowRef, bool fullscreen, int iId) : +_hiviewRef( 0), +_windowRef( windowRef), +_fullScreen( fullscreen), +_id( iId), +_renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), +_screenUpdateEvent( 0), +_isHIViewRef( false), +_aglContext( 0), +_windowWidth( 0), +_windowHeight( 0), +_lastWindowWidth( -1), +_lastWindowHeight( -1), +_lastHiViewWidth( -1), +_lastHiViewHeight( -1), +_currentParentWindowHeight( 0), +_currentParentWindowWidth( 0), +_currentParentWindowBounds( ), +_windowHasResized( false), +_lastParentWindowBounds( ), +_currentHIViewBounds( ), +_lastHIViewBounds( ), +_windowRect( ), +_aglChannels( ), +_zOrderToChannel( ), +_hiviewEventHandlerRef( NULL), +_windowEventHandlerRef( NULL), +_currentViewBounds( ), +_lastViewBounds( ), +_renderingIsPaused( false), + +{ + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s"); + + _screenUpdateThread.reset( + new rtc::PlatformThread(ScreenUpdateThreadProc, this, "ScreenUpdate")); + _screenUpdateEvent = EventWrapper::Create(); + + if(!IsValidWindowPtr(_windowRef)) + { + //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Invalid WindowRef:0x%x", __FUNCTION__, __LINE__, _windowRef); + } + else + { + //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d WindowRef 0x%x is valid", __FUNCTION__, __LINE__, _windowRef); + } + + GetWindowRect(_windowRect); + + _lastViewBounds.origin.x = 0; + _lastViewBounds.origin.y = 0; + _lastViewBounds.size.width = 0; + _lastViewBounds.size.height = 0; + +} + +// this is a static function. It has been registered (in class constructor) to be called on various window redrawing or resizing. +// Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. +#pragma mark WindowRef Event Handler +pascal OSStatus VideoRenderAGL::sHandleWindowResized (EventHandlerCallRef /*nextHandler*/, + EventRef theEvent, + void* userData) +{ + WindowRef windowRef = NULL; + + int eventType = GetEventKind(theEvent); + + // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes + GetEventParameter (theEvent, + kEventParamDirectObject, + typeWindowRef, + NULL, + sizeof (WindowRef), + NULL, + &windowRef); + + VideoRenderAGL* obj = (VideoRenderAGL*)(userData); + + bool updateUI = true; + if(kEventWindowBoundsChanged == eventType) + { + } + else if(kEventWindowBoundsChanging == eventType) + { + } + else if(kEventWindowZoomed == eventType) + { + } + else if(kEventWindowExpanding == eventType) + { + } + else if(kEventWindowExpanded == eventType) + { + } + else if(kEventWindowClickResizeRgn == eventType) + { + } + else if(kEventWindowClickDragRgn == eventType) + { + } + else + { + updateUI = false; + } + + if(true == updateUI) + { + obj->ParentWindowResized(windowRef); + obj->UpdateClipping(); + obj->RenderOffScreenBuffers(); + } + + return noErr; +} + +#pragma mark VideoRenderAGL HIViewRef constructor + +VideoRenderAGL::VideoRenderAGL(HIViewRef windowRef, bool fullscreen, int iId) : +_hiviewRef( windowRef), +_windowRef( 0), +_fullScreen( fullscreen), +_id( iId), +_renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), +_screenUpdateEvent( 0), +_isHIViewRef( false), +_aglContext( 0), +_windowWidth( 0), +_windowHeight( 0), +_lastWindowWidth( -1), +_lastWindowHeight( -1), +_lastHiViewWidth( -1), +_lastHiViewHeight( -1), +_currentParentWindowHeight( 0), +_currentParentWindowWidth( 0), +_currentParentWindowBounds( ), +_windowHasResized( false), +_lastParentWindowBounds( ), +_currentHIViewBounds( ), +_lastHIViewBounds( ), +_windowRect( ), +_aglChannels( ), +_zOrderToChannel( ), +_hiviewEventHandlerRef( NULL), +_windowEventHandlerRef( NULL), +_currentViewBounds( ), +_lastViewBounds( ), +_renderingIsPaused( false), +{ + //WEBRTC_TRACE(kTraceDebug, "%s:%d Constructor", __FUNCTION__, __LINE__); + // _renderCritSec = CriticalSectionWrapper::CreateCriticalSection(); + + _screenUpdateThread.reset(new rtc::PlatformThread( + ScreenUpdateThreadProc, this, "ScreenUpdateThread")); + _screenUpdateEvent = EventWrapper::Create(); + + GetWindowRect(_windowRect); + + _lastViewBounds.origin.x = 0; + _lastViewBounds.origin.y = 0; + _lastViewBounds.size.width = 0; + _lastViewBounds.size.height = 0; + +#ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER + // This gets the parent window of the HIViewRef that's passed in and installs a WindowRef event handler on it + // The event handler looks for window resize events and adjusts the offset of the controls. + + //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef's parent window", __FUNCTION__, __LINE__); + + + static const EventTypeSpec windowEventTypes[] = + { + kEventClassWindow, kEventWindowBoundsChanged, + kEventClassWindow, kEventWindowBoundsChanging, + kEventClassWindow, kEventWindowZoomed, + kEventClassWindow, kEventWindowExpanded, + kEventClassWindow, kEventWindowClickResizeRgn, + kEventClassWindow, kEventWindowClickDragRgn + }; + + WindowRef parentWindow = HIViewGetWindow(windowRef); + + InstallWindowEventHandler (parentWindow, + NewEventHandlerUPP (sHandleWindowResized), + GetEventTypeCount(windowEventTypes), + windowEventTypes, + (void *) this, // this is an arbitrary parameter that will be passed on to your event handler when it is called later + &_windowEventHandlerRef); + +#endif + +#ifdef NEW_HIVIEW_EVENT_HANDLER + //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef", __FUNCTION__, __LINE__); + + static const EventTypeSpec hiviewEventTypes[] = + { + kEventClassControl, kEventControlBoundsChanged, + kEventClassControl, kEventControlDraw + // kEventControlDragLeave + // kEventControlDragReceive + // kEventControlGetFocusPart + // kEventControlApplyBackground + // kEventControlDraw + // kEventControlHit + + }; + + HIViewInstallEventHandler(_hiviewRef, + NewEventHandlerUPP(sHandleHiViewResized), + GetEventTypeCount(hiviewEventTypes), + hiviewEventTypes, + (void *) this, + &_hiviewEventHandlerRef); + +#endif +} + +// this is a static function. It has been registered (in constructor) to be called on various window redrawing or resizing. +// Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. +#pragma mark HIViewRef Event Handler +pascal OSStatus VideoRenderAGL::sHandleHiViewResized (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData) +{ + //static int callbackCounter = 1; + HIViewRef hiviewRef = NULL; + + // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes + int eventType = GetEventKind(theEvent); + OSStatus status = noErr; + status = GetEventParameter (theEvent, + kEventParamDirectObject, + typeControlRef, + NULL, + sizeof (ControlRef), + NULL, + &hiviewRef); + + VideoRenderAGL* obj = (VideoRenderAGL*)(userData); + WindowRef parentWindow = HIViewGetWindow(hiviewRef); + bool updateUI = true; + + if(kEventControlBoundsChanged == eventType) + { + } + else if(kEventControlDraw == eventType) + { + } + else + { + updateUI = false; + } + + if(true == updateUI) + { + obj->ParentWindowResized(parentWindow); + obj->UpdateClipping(); + obj->RenderOffScreenBuffers(); + } + + return status; +} + +VideoRenderAGL::~VideoRenderAGL() +{ + + //WEBRTC_TRACE(kTraceDebug, "%s:%d Destructor", __FUNCTION__, __LINE__); + + +#ifdef USE_EVENT_HANDLERS + // remove event handlers + OSStatus status; + if(_isHIViewRef) + { + status = RemoveEventHandler(_hiviewEventHandlerRef); + } + else + { + status = RemoveEventHandler(_windowEventHandlerRef); + } + if(noErr != status) + { + if(_isHIViewRef) + { + + //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); + } + else + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); + } + } + +#endif + + OSStatus status; +#ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER + if(_windowEventHandlerRef) + { + status = RemoveEventHandler(_windowEventHandlerRef); + if(status != noErr) + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); + } + } +#endif + +#ifdef NEW_HIVIEW_EVENT_HANDLER + if(_hiviewEventHandlerRef) + { + status = RemoveEventHandler(_hiviewEventHandlerRef); + if(status != noErr) + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); + } + } +#endif + + // Signal event to exit thread, then delete it + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); + + if (tmpPtr) + { + _screenUpdateEvent->Set(); + _screenUpdateEvent->StopTimer(); + + tmpPtr->Stop(); + delete tmpPtr; + delete _screenUpdateEvent; + _screenUpdateEvent = NULL; + } + + if (_aglContext != 0) + { + aglSetCurrentContext(_aglContext); + aglDestroyContext(_aglContext); + _aglContext = 0; + } + + // Delete all channels + std::map::iterator it = _aglChannels.begin(); + while (it!= _aglChannels.end()) + { + delete it->second; + _aglChannels.erase(it); + it = _aglChannels.begin(); + } + _aglChannels.clear(); + + // Clean the zOrder map + std::multimap::iterator zIt = _zOrderToChannel.begin(); + while(zIt != _zOrderToChannel.end()) + { + _zOrderToChannel.erase(zIt); + zIt = _zOrderToChannel.begin(); + } + _zOrderToChannel.clear(); + + //delete _renderCritSec; + + +} + +int VideoRenderAGL::GetOpenGLVersion(int& aglMajor, int& aglMinor) +{ + aglGetVersion((GLint *) &aglMajor, (GLint *) &aglMinor); + return 0; +} + +int VideoRenderAGL::Init() +{ + LockAGLCntx(); + + // Start rendering thread... + if (!_screenUpdateThread) + { + UnlockAGLCntx(); + //WEBRTC_TRACE(kTraceError, "%s:%d Thread not created", __FUNCTION__, __LINE__); + return -1; + } + _screenUpdateThread->Start(); + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + + // Start the event triggering the render process + unsigned int monitorFreq = 60; + _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); + + // Create mixing textures + if (CreateMixingContext() == -1) + { + //WEBRTC_TRACE(kTraceError, "%s:%d Could not create a mixing context", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return -1; + } + + UnlockAGLCntx(); + return 0; +} + +VideoChannelAGL* VideoRenderAGL::CreateAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + + LockAGLCntx(); + + //WEBRTC_TRACE(kTraceInfo, "%s:%d Creating AGL channel: %d", __FUNCTION__, __LINE__, channel); + + if (HasChannel(channel)) + { + //WEBRTC_TRACE(kTraceError, "%s:%d Channel already exists", __FUNCTION__, __LINE__); + UnlockAGLCntx();k + return NULL; + } + + if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) + { + // There are already one channel using this zOrder + // TODO: Allow multiple channels with same zOrder + } + + VideoChannelAGL* newAGLChannel = new VideoChannelAGL(_aglContext, _id, this); + + if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) + { + if (newAGLChannel) + { + delete newAGLChannel; + newAGLChannel = NULL; + } + //WEBRTC_LOG(kTraceError, "Could not create AGL channel"); + //WEBRTC_TRACE(kTraceError, "%s:%d Could not create AGL channel", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return NULL; + } +k + _aglChannels[channel] = newAGLChannel; + _zOrderToChannel.insert(std::pair(zOrder, channel)); + + UnlockAGLCntx(); + return newAGLChannel; +} + +int VideoRenderAGL::DeleteAllAGLChannels() +{ + CriticalSectionScoped cs(&_renderCritSec); + + //WEBRTC_TRACE(kTraceInfo, "%s:%d Deleting all AGL channels", __FUNCTION__, __LINE__); + //int i = 0 ; + std::map::iterator it; + it = _aglChannels.begin(); + + while (it != _aglChannels.end()) + { + VideoChannelAGL* channel = it->second; + if (channel) + delete channel; + + _aglChannels.erase(it); + it = _aglChannels.begin(); + } + _aglChannels.clear(); + return 0; +} + +int VideoRenderAGL::DeleteAGLChannel(int channel) +{ + CriticalSectionScoped cs(&_renderCritSec); + //WEBRTC_TRACE(kTraceDebug, "%s:%d Deleting AGL channel %d", __FUNCTION__, __LINE__, channel); + + std::map::iterator it; + it = _aglChannels.find(channel); + if (it != _aglChannels.end()) + { + delete it->second; + _aglChannels.erase(it); + } + else + { + //WEBRTC_TRACE(kTraceWarning, "%s:%d Channel not found", __FUNCTION__, __LINE__); + return -1; + } + + std::multimap::iterator zIt = _zOrderToChannel.begin(); + while( zIt != _zOrderToChannel.end()) + { + if (zIt->second == channel) + { + _zOrderToChannel.erase(zIt); + break; + } + zIt++;// = _zOrderToChannel.begin(); + } + + return 0; +} + +int VideoRenderAGL::StopThread() +{ + CriticalSectionScoped cs(&_renderCritSec); + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); + + if (tmpPtr) + { + _screenUpdateEvent->Set(); + _renderCritSec.Leave(); + tmpPtr->Stop(); + delete tmpPtr; + _renderCritSec.Enter(); + } + + delete _screenUpdateEvent; + _screenUpdateEvent = NULL; + + return 0; +} + +bool VideoRenderAGL::IsFullScreen() +{ + CriticalSectionScoped cs(&_renderCritSec); + return _fullScreen; +} + +bool VideoRenderAGL::HasChannels() +{ + + CriticalSectionScoped cs(&_renderCritSec); + + if (_aglChannels.begin() != _aglChannels.end()) + { + return true; + } + + return false; +} + +bool VideoRenderAGL::HasChannel(int channel) +{ + CriticalSectionScoped cs(&_renderCritSec); + + std::map::iterator it = _aglChannels.find(channel); + if (it != _aglChannels.end()) + { + return true; + } + + return false; +} + +int VideoRenderAGL::GetChannels(std::list& channelList) +{ + + CriticalSectionScoped cs(&_renderCritSec); + std::map::iterator it = _aglChannels.begin(); + + while (it != _aglChannels.end()) + { + channelList.push_back(it->first); + it++; + } + + return 0; +} + +VideoChannelAGL* VideoRenderAGL::ConfigureAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + + CriticalSectionScoped cs(&_renderCritSec); + + std::map::iterator it = _aglChannels.find(channel); + + if (it != _aglChannels.end()) + { + VideoChannelAGL* aglChannel = it->second; + if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) + { + return NULL; + } + + std::multimap::iterator it = _zOrderToChannel.begin(); + while(it != _zOrderToChannel.end()) + { + if (it->second == channel) + { + if (it->first != zOrder) + { + _zOrderToChannel.erase(it); + _zOrderToChannel.insert(std::pair(zOrder, channel)); + } + break; + } + it++; + } + return aglChannel; + } + + return NULL; +} + +bool VideoRenderAGL::ScreenUpdateThreadProc(void* obj) +{ + return static_cast(obj)->ScreenUpdateProcess(); +} + +bool VideoRenderAGL::ScreenUpdateProcess() +{ + _screenUpdateEvent->Wait(100); + + LockAGLCntx(); + + if (!_screenUpdateThread) + { + UnlockAGLCntx(); + return false; + } + + if (aglSetCurrentContext(_aglContext) == GL_FALSE) + { + UnlockAGLCntx(); + return true; + } + + if (GetWindowRect(_windowRect) == -1) + { + UnlockAGLCntx(); + return true; + } + + if (_windowWidth != (_windowRect.right - _windowRect.left) + || _windowHeight != (_windowRect.bottom - _windowRect.top)) + { + // We have a new window size, update the context. + if (aglUpdateContext(_aglContext) == GL_FALSE) + { + UnlockAGLCntx(); + return true; + } + _windowWidth = _windowRect.right - _windowRect.left; + _windowHeight = _windowRect.bottom - _windowRect.top; + } + + // this section will poll to see if the window size has changed + // this is causing problem w/invalid windowRef + // this code has been modified and exists now in the window event handler +#ifndef NEW_HIVIEW_PARENT_EVENT_HANDLER + if (_isHIViewRef) + { + + if(FALSE == HIViewIsValid(_hiviewRef)) + { + + //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalid windowRef", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return true; + } + WindowRef window = HIViewGetWindow(_hiviewRef); + + if(FALSE == IsValidWindowPtr(window)) + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalide hiviewRef", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return true; + } + if (window == NULL) + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d WindowRef = NULL", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return true; + } + + if(FALSE == MacIsWindowVisible(window)) + { + //WEBRTC_TRACE(kTraceDebug, "%s:%d MacIsWindowVisible == FALSE. Returning early", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return true; + } + + HIRect viewBounds; // Placement and size for HIView + int windowWidth = 0; // Parent window width + int windowHeight = 0; // Parent window height + + // NOTE: Calling GetWindowBounds with kWindowStructureRgn will crash intermittentaly if the OS decides it needs to push it into the back for a moment. + // To counter this, we get the titlebar height on class construction and then add it to the content region here. Content regions seems not to crash + Rect contentBounds = + { 0, 0, 0, 0}; // The bounds for the parent window + +#if defined(USE_CONTENT_RGN) + GetWindowBounds(window, kWindowContentRgn, &contentBounds); +#elif defined(USE_STRUCT_RGN) + GetWindowBounds(window, kWindowStructureRgn, &contentBounds); +#endif + + Rect globalBounds = + { 0, 0, 0, 0}; // The bounds for the parent window + globalBounds.top = contentBounds.top; + globalBounds.right = contentBounds.right; + globalBounds.bottom = contentBounds.bottom; + globalBounds.left = contentBounds.left; + + windowHeight = globalBounds.bottom - globalBounds.top; + windowWidth = globalBounds.right - globalBounds.left; + + // Get the size of the HIViewRef + HIViewGetBounds(_hiviewRef, &viewBounds); + HIViewConvertRect(&viewBounds, _hiviewRef, NULL); + + // Check if this is the first call.. + if (_lastWindowHeight == -1 && + _lastWindowWidth == -1) + { + _lastWindowWidth = windowWidth; + _lastWindowHeight = windowHeight; + + _lastViewBounds.origin.x = viewBounds.origin.x; + _lastViewBounds.origin.y = viewBounds.origin.y; + _lastViewBounds.size.width = viewBounds.size.width; + _lastViewBounds.size.height = viewBounds.size.height; + } + sfasdfasdf + + bool resized = false; + + // Check if parent window size has changed + if (windowHeight != _lastWindowHeight || + windowWidth != _lastWindowWidth) + { + resized = true; + } + + // Check if the HIView has new size or is moved in the parent window + if (_lastViewBounds.origin.x != viewBounds.origin.x || + _lastViewBounds.origin.y != viewBounds.origin.y || + _lastViewBounds.size.width != viewBounds.size.width || + _lastViewBounds.size.height != viewBounds.size.height) + { + // The HiView is resized or has moved. + resized = true; + } + + if (resized) + { + + //WEBRTC_TRACE(kTraceDebug, "%s:%d Window has resized", __FUNCTION__, __LINE__); + + // Calculate offset between the windows + // {x, y, widht, height}, x,y = lower left corner + const GLint offs[4] = + { (int)(0.5f + viewBounds.origin.x), + (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), + viewBounds.size.width, viewBounds.size.height}; + + //WEBRTC_TRACE(kTraceDebug, "%s:%d contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, __LINE__, + contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); + //WEBRTC_TRACE(kTraceDebug, "%s:%d windowHeight=%d", __FUNCTION__, __LINE__, windowHeight); + //WEBRTC_TRACE(kTraceDebug, "%s:%d offs[4] = %d, %d, %d, %d", __FUNCTION__, __LINE__, offs[0], offs[1], offs[2], offs[3]); + + aglSetDrawable (_aglContext, GetWindowPort(window)); + aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); + aglEnable(_aglContext, AGL_BUFFER_RECT); + + // We need to change the viewport too if the HIView size has changed + glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); + + } + _lastWindowWidth = windowWidth; + _lastWindowHeight = windowHeight; + + _lastViewBounds.origin.x = viewBounds.origin.x; + _lastViewBounds.origin.y = viewBounds.origin.y; + _lastViewBounds.size.width = viewBounds.size.width; + _lastViewBounds.size.height = viewBounds.size.height; + + } +#endif + if (_fullScreen) + { + // TODO + // We use double buffers, must always update + //RenderOffScreenBuffersToBackBuffer(); + } + else + { + // Check if there are any updated buffers + bool updated = false; + + // TODO: check if window size is updated! + // TODO Improvement: Walk through the zOrder Map to only render the ones in need of update + std::map::iterator it = _aglChannels.begin(); + while (it != _aglChannels.end()) + { + + VideoChannelAGL* aglChannel = it->second; + aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); + aglChannel->IsUpdated(updated); + if (updated) + { + break; + } + it++; + } + + if (updated) + { + // At least on buffers is updated, we need to repaint the texture + if (RenderOffScreenBuffers() != -1) + { + // MF + //SwapAndDisplayBuffers(); + } + else + { + // Error updating the mixing texture, don't swap. + } + } + } + + UnlockAGLCntx(); + + //WEBRTC_LOG(kTraceDebug, "Leaving ScreenUpdateProcess()"); + return true; +} + +void VideoRenderAGL::ParentWindowResized(WindowRef window) +{ + //WEBRTC_LOG(kTraceDebug, "%s HIViewRef:%d owner window has resized", __FUNCTION__, (int)_hiviewRef); + + LockAGLCntx(); +k + // set flag + _windowHasResized = false; + + if(FALSE == HIViewIsValid(_hiviewRef)) + { + //WEBRTC_LOG(kTraceDebug, "invalid windowRef"); + UnlockAGLCntx(); + return; + } + + if(FALSE == IsValidWindowPtr(window)) + { + //WEBRTC_LOG(kTraceError, "invalid windowRef"); + UnlockAGLCntx(); + return; + } + + if (window == NULL) + { + //WEBRTC_LOG(kTraceError, "windowRef = NULL"); + UnlockAGLCntx(); + return; + } + + if(FALSE == MacIsWindowVisible(window)) + { + //WEBRTC_LOG(kTraceDebug, "MacIsWindowVisible = FALSE. Returning early."); + UnlockAGLCntx(); + return; + } + + Rect contentBounds = + { 0, 0, 0, 0}; + +#if defined(USE_CONTENT_RGN) + GetWindowBounds(window, kWindowContentRgn, &contentBounds); +#elif defined(USE_STRUCT_RGN) + GetWindowBounds(window, kWindowStructureRgn, &contentBounds); +#endif + + //WEBRTC_LOG(kTraceDebug, "%s contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); + + // update global vars + _currentParentWindowBounds.top = contentBounds.top; + _currentParentWindowBounds.left = contentBounds.left; + _currentParentWindowBounds.bottom = contentBounds.bottom; + _currentParentWindowBounds.right = contentBounds.right; + + _currentParentWindowWidth = _currentParentWindowBounds.right - _currentParentWindowBounds.left; + _currentParentWindowHeight = _currentParentWindowBounds.bottom - _currentParentWindowBounds.top; + + _windowHasResized = true; + + // ********* update AGL offsets + HIRect viewBounds; + HIViewGetBounds(_hiviewRef, &viewBounds); + HIViewConvertRect(&viewBounds, _hiviewRef, NULL); + + const GLint offs[4] = + { (int)(0.5f + viewBounds.origin.x), + (int)(0.5f + _currentParentWindowHeight - (viewBounds.origin.y + viewBounds.size.height)), + viewBounds.size.width, viewBounds.size.height}; + //WEBRTC_LOG(kTraceDebug, "%s _currentParentWindowHeight=%d", __FUNCTION__, _currentParentWindowHeight); + //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); + + aglSetCurrentContext(_aglContext); + aglSetDrawable (_aglContext, GetWindowPort(window)); + aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); + aglEnable(_aglContext, AGL_BUFFER_RECT); + + // We need to change the viewport too if the HIView size has changed + glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); + + UnlockAGLCntx(); + + return; +} + +int VideoRenderAGL::CreateMixingContext() +{ + + LockAGLCntx(); + + //WEBRTC_LOG(kTraceDebug, "Entering CreateMixingContext()"); + + // Use both AGL_ACCELERATED and AGL_NO_RECOVERY to make sure + // a hardware renderer is used and not a software renderer. + + GLint attributes[] = + { + AGL_DOUBLEBUFFER, + AGL_WINDOW, + AGL_RGBA, + AGL_NO_RECOVERY, + AGL_ACCELERATED, + AGL_RED_SIZE, 8, + AGL_GREEN_SIZE, 8, + AGL_BLUE_SIZE, 8, + AGL_ALPHA_SIZE, 8, + AGL_DEPTH_SIZE, 24, + AGL_NONE, + }; + + AGLPixelFormat aglPixelFormat; + + // ***** Set up the OpenGL Context ***** + + // Get a pixel format for the attributes above + aglPixelFormat = aglChoosePixelFormat(NULL, 0, attributes); + if (NULL == aglPixelFormat) + { + //WEBRTC_LOG(kTraceError, "Could not create pixel format"); + UnlockAGLCntx(); + return -1; + } + + // Create an AGL context + _aglContext = aglCreateContext(aglPixelFormat, NULL); + if (_aglContext == NULL) + { + //WEBRTC_LOG(kTraceError, "Could no create AGL context"); + UnlockAGLCntx(); + return -1; + } + + // Release the pixel format memory + aglDestroyPixelFormat(aglPixelFormat); + + // Set the current AGL context for the rest of the settings + if (aglSetCurrentContext(_aglContext) == false) + { + //WEBRTC_LOG(kTraceError, "Could not set current context: %d", aglGetError()); + UnlockAGLCntx(); + return -1; + } + + if (_isHIViewRef) + { + //--------------------------- + // BEGIN: new test code +#if 0 + // Don't use this one! + // There seems to be an OS X bug that can't handle + // movements and resizing of the parent window + // and or the HIView + if (aglSetHIViewRef(_aglContext,_hiviewRef) == false) + { + //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); + UnlockAGLCntx(); + return -1; + } +#else + + // Get the parent window for this control + WindowRef window = GetControlOwner(_hiviewRef); + + Rect globalBounds = + { 0,0,0,0}; // The bounds for the parent window + HIRect viewBounds; // Placemnt in the parent window and size. + int windowHeight = 0; + + // Rect titleBounds = {0,0,0,0}; + // GetWindowBounds(window, kWindowTitleBarRgn, &titleBounds); + // _titleBarHeight = titleBounds.top - titleBounds.bottom; + // if(0 == _titleBarHeight) + // { + // //WEBRTC_LOG(kTraceError, "Titlebar height = 0"); + // //return -1; + // } + + + // Get the bounds for the parent window +#if defined(USE_CONTENT_RGN) + GetWindowBounds(window, kWindowContentRgn, &globalBounds); +#elif defined(USE_STRUCT_RGN) + GetWindowBounds(window, kWindowStructureRgn, &globalBounds); +#endif + windowHeight = globalBounds.bottom - globalBounds.top; + + // Get the bounds for the HIView + HIViewGetBounds(_hiviewRef, &viewBounds); + + HIViewConvertRect(&viewBounds, _hiviewRef, NULL); + + const GLint offs[4] = + { (int)(0.5f + viewBounds.origin.x), + (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), + viewBounds.size.width, viewBounds.size.height}; + + //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); + + + aglSetDrawable (_aglContext, GetWindowPort(window)); + aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); + aglEnable(_aglContext, AGL_BUFFER_RECT); + + GLint surfaceOrder = 1; // 1: above window, -1 below. + //OSStatus status = aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); + aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); + + glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); +#endif + + } + else + { + if(GL_FALSE == aglSetDrawable (_aglContext, GetWindowPort(_windowRef))) + { + //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); + UnlockAGLCntx(); + return -1; + } + } + + _windowWidth = _windowRect.right - _windowRect.left; + _windowHeight = _windowRect.bottom - _windowRect.top; + + // opaque surface + int surfaceOpacity = 1; + if (aglSetInteger(_aglContext, AGL_SURFACE_OPACITY, (const GLint *) &surfaceOpacity) == false) + { + //WEBRTC_LOG(kTraceError, "Could not set surface opacity: %d", aglGetError()); + UnlockAGLCntx(); + return -1; + } + + // 1 -> sync to screen rat, slow... + //int swapInterval = 0; // 0 don't sync with vertical trace + int swapInterval = 0; // 1 sync with vertical trace + if (aglSetInteger(_aglContext, AGL_SWAP_INTERVAL, (const GLint *) &swapInterval) == false) + { + //WEBRTC_LOG(kTraceError, "Could not set swap interval: %d", aglGetError()); + UnlockAGLCntx(); + return -1; + } + + // Update the rect with the current size + if (GetWindowRect(_windowRect) == -1) + { + //WEBRTC_LOG(kTraceError, "Could not get window size"); + UnlockAGLCntx(); + return -1; + } + + // Disable not needed functionality to increase performance + glDisable(GL_DITHER); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_FOG); + glDisable(GL_TEXTURE_2D); + glPixelZoom(1.0, 1.0); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + GLenum glErr = glGetError(); + + if (glErr) + { + } + + UpdateClipping(); + + //WEBRTC_LOG(kTraceDebug, "Leaving CreateMixingContext()"); + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderAGL::RenderOffScreenBuffers() +{ + LockAGLCntx(); + + // Get the current window size, it might have changed since last render. + if (GetWindowRect(_windowRect) == -1) + { + //WEBRTC_LOG(kTraceError, "Could not get window rect"); + UnlockAGLCntx(); + return -1; + } + + if (aglSetCurrentContext(_aglContext) == false) + { + //WEBRTC_LOG(kTraceError, "Could not set current context for rendering"); + UnlockAGLCntx(); + return -1; + } + + // HERE - onl if updated! + glClear(GL_COLOR_BUFFER_BIT); + + // Loop through all channels starting highest zOrder ending with lowest. + for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); + rIt != _zOrderToChannel.rend(); + rIt++) + { + int channelId = rIt->second; + std::map::iterator it = _aglChannels.find(channelId); + + VideoChannelAGL* aglChannel = it->second; + + aglChannel->RenderOffScreenBuffer(); + } + + SwapAndDisplayBuffers(); + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderAGL::SwapAndDisplayBuffers() +{ + + LockAGLCntx(); + if (_fullScreen) + { + // TODO: + // Swap front and back buffers, rendering taking care of in the same call + //aglSwapBuffers(_aglContext); + // Update buffer index to the idx for the next rendering! + //_textureIdx = (_textureIdx + 1) & 1; + } + else + { + // Single buffer rendering, only update context. + glFlush(); + aglSwapBuffers(_aglContext); + HIViewSetNeedsDisplay(_hiviewRef, true); + } + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderAGL::GetWindowRect(Rect& rect) +{ + + LockAGLCntx(); + + if (_isHIViewRef) + { + if (_hiviewRef) + { + HIRect HIViewRect1; + if(FALSE == HIViewIsValid(_hiviewRef)) + { + rect.top = 0; + rect.left = 0; + rect.right = 0; + rect.bottom = 0; + //WEBRTC_LOG(kTraceError,"GetWindowRect() HIViewIsValid() returned false"); + UnlockAGLCntx(); + } + HIViewGetBounds(_hiviewRef,&HIViewRect1); + HIRectConvert(&HIViewRect1, 1, NULL, 2, NULL); + if(HIViewRect1.origin.x < 0) + { + rect.top = 0; + //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.top = 0"); + } + else + { + rect.top = HIViewRect1.origin.x; + } + + if(HIViewRect1.origin.y < 0) + { + rect.left = 0; + //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.left = 0"); + } + else + { + rect.left = HIViewRect1.origin.y; + } + + if(HIViewRect1.size.width < 0) + { + rect.right = 0; + //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.right = 0"); + } + else + { + rect.right = HIViewRect1.size.width; + } + + if(HIViewRect1.size.height < 0) + { + rect.bottom = 0; + //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.bottom = 0"); + } + else + { + rect.bottom = HIViewRect1.size.height; + } + + ////WEBRTC_LOG(kTraceDebug,"GetWindowRect() HIViewRef: rect.top = %d, rect.left = %d, rect.right = %d, rect.bottom =%d in GetWindowRect", rect.top,rect.left,rect.right,rect.bottom); + UnlockAGLCntx(); + } + else + { + //WEBRTC_LOG(kTraceError, "invalid HIViewRef"); + UnlockAGLCntx(); + } + } + else + { + if (_windowRef) + { + GetWindowBounds(_windowRef, kWindowContentRgn, &rect); + UnlockAGLCntx(); + } + else + { + //WEBRTC_LOG(kTraceError, "No WindowRef"); + UnlockAGLCntx(); + } + } +} + +int VideoRenderAGL::UpdateClipping() +{ + //WEBRTC_LOG(kTraceDebug, "Entering UpdateClipping()"); + LockAGLCntx(); + + if(_isHIViewRef) + { + if(FALSE == HIViewIsValid(_hiviewRef)) + { + //WEBRTC_LOG(kTraceError, "UpdateClipping() _isHIViewRef is invalid. Returning -1"); + UnlockAGLCntx(); + return -1; + } + + RgnHandle visibleRgn = NewRgn(); + SetEmptyRgn (visibleRgn); + + if(-1 == CalculateVisibleRegion((ControlRef)_hiviewRef, visibleRgn, true)) + { + } + + if(GL_FALSE == aglSetCurrentContext(_aglContext)) + { + GLenum glErr = aglGetError(); + //WEBRTC_LOG(kTraceError, "aglSetCurrentContext returned FALSE with error code %d at line %d", glErr, __LINE__); + } + + if(GL_FALSE == aglEnable(_aglContext, AGL_CLIP_REGION)) + { + GLenum glErr = aglGetError(); + //WEBRTC_LOG(kTraceError, "aglEnable returned FALSE with error code %d at line %d\n", glErr, __LINE__); + } + + if(GL_FALSE == aglSetInteger(_aglContext, AGL_CLIP_REGION, (const GLint*)visibleRgn)) + { + GLenum glErr = aglGetError(); + //WEBRTC_LOG(kTraceError, "aglSetInteger returned FALSE with error code %d at line %d\n", glErr, __LINE__); + } + + DisposeRgn(visibleRgn); + } + else + { + //WEBRTC_LOG(kTraceDebug, "Not using a hiviewref!\n"); + } + + //WEBRTC_LOG(kTraceDebug, "Leaving UpdateClipping()"); + UnlockAGLCntx(); + return true; +} + +int VideoRenderAGL::CalculateVisibleRegion(ControlRef control, RgnHandle &visibleRgn, bool clipChildren) +{ + + // LockAGLCntx(); + + //WEBRTC_LOG(kTraceDebug, "Entering CalculateVisibleRegion()"); + OSStatus osStatus = 0; + OSErr osErr = 0; + + RgnHandle tempRgn = NewRgn(); + if (IsControlVisible(control)) + { + RgnHandle childRgn = NewRgn(); + WindowRef window = GetControlOwner(control); + ControlRef rootControl; + GetRootControl(window, &rootControl); // 'wvnc' + ControlRef masterControl; + osStatus = GetSuperControl(rootControl, &masterControl); + // //WEBRTC_LOG(kTraceDebug, "IBM GetSuperControl=%d", osStatus); + + if (masterControl != NULL) + { + CheckValidRegion(visibleRgn); + // init visibleRgn with region of 'wvnc' + osStatus = GetControlRegion(rootControl, kControlStructureMetaPart, visibleRgn); + // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); + //GetSuperControl(rootControl, &rootControl); + ControlRef tempControl = control, lastControl = 0; + while (tempControl != masterControl) // current control != master + + { + CheckValidRegion(tempRgn); + + // //WEBRTC_LOG(kTraceDebug, "IBM tempControl=%d masterControl=%d", tempControl, masterControl); + ControlRef subControl; + + osStatus = GetControlRegion(tempControl, kControlStructureMetaPart, tempRgn); // intersect the region of the current control with visibleRgn + // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); + CheckValidRegion(tempRgn); + + osErr = HIViewConvertRegion(tempRgn, tempControl, rootControl); + // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d : %d", osErr, __LINE__); + CheckValidRegion(tempRgn); + + SectRgn(tempRgn, visibleRgn, visibleRgn); + CheckValidRegion(tempRgn); + CheckValidRegion(visibleRgn); + if (EmptyRgn(visibleRgn)) // if the region is empty, bail + break; + + if (clipChildren || tempControl != control) // clip children if true, cut out the tempControl if it's not one passed to this function + + { + UInt16 numChildren; + osStatus = CountSubControls(tempControl, &numChildren); // count the subcontrols + // //WEBRTC_LOG(kTraceDebug, "IBM CountSubControls=%d : %d", osStatus, __LINE__); + + // //WEBRTC_LOG(kTraceDebug, "IBM numChildren=%d", numChildren); + for (int i = 0; i < numChildren; i++) + { + osErr = GetIndexedSubControl(tempControl, numChildren - i, &subControl); // retrieve the subcontrol in order by zorder + // //WEBRTC_LOG(kTraceDebug, "IBM GetIndexedSubControls=%d : %d", osErr, __LINE__); + if ( subControl == lastControl ) // break because of zorder + + { + // //WEBRTC_LOG(kTraceDebug, "IBM breaking because of zorder %d", __LINE__); + break; + } + + if (!IsControlVisible(subControl)) // dont' clip invisible controls + + { + // //WEBRTC_LOG(kTraceDebug, "IBM continue. Control is not visible %d", __LINE__); + continue; + } + + if(!subControl) continue; + + osStatus = GetControlRegion(subControl, kControlStructureMetaPart, tempRgn); //get the region of the current control and union to childrg + // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d %d", osStatus, __LINE__); + CheckValidRegion(tempRgn); + if(osStatus != 0) + { + // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); + continue; + } + if(!tempRgn) + { + // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !tempRgn %d", osStatus, __LINE__); + continue; + } + + osStatus = HIViewConvertRegion(tempRgn, subControl, rootControl); + CheckValidRegion(tempRgn); + // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d %d", osStatus, __LINE__); + if(osStatus != 0) + { + // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); + continue; + } + if(!rootControl) + { + // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !rootControl %d", osStatus, __LINE__); + continue; + } + + UnionRgn(tempRgn, childRgn, childRgn); + CheckValidRegion(tempRgn); + CheckValidRegion(childRgn); + CheckValidRegion(visibleRgn); + if(!childRgn) + { + // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !childRgn %d", osStatus, __LINE__); + continue; + } + + } // next child control + } + lastControl = tempControl; + GetSuperControl(tempControl, &subControl); + tempControl = subControl; + } + + DiffRgn(visibleRgn, childRgn, visibleRgn); + CheckValidRegion(visibleRgn); + CheckValidRegion(childRgn); + DisposeRgn(childRgn); + } + else + { + CopyRgn(tempRgn, visibleRgn); + CheckValidRegion(tempRgn); + CheckValidRegion(visibleRgn); + } + DisposeRgn(tempRgn); + } + + //WEBRTC_LOG(kTraceDebug, "Leaving CalculateVisibleRegion()"); + //_aglCritPtr->Leave(); + return 0; +} + +bool VideoRenderAGL::CheckValidRegion(RgnHandle rHandle) +{ + + Handle hndSize = (Handle)rHandle; + long size = GetHandleSize(hndSize); + if(0 == size) + { + + OSErr memErr = MemError(); + if(noErr != memErr) + { + // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle. MemError() returned %d", memErr); + } + else + { + // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle yet MemError() returned noErr"); + } + + } + else + { + // //WEBRTC_LOG(kTraceDebug, "IBM handleSize = %d", size); + } + + if(false == IsValidRgnHandle(rHandle)) + { + // //WEBRTC_LOG(kTraceError, "IBM ERROR Invalid Region found : $%d", rHandle); + assert(false); + } + + int err = QDError(); + switch(err) + { + case 0: + break; + case -147: + //WEBRTC_LOG(kTraceError, "ERROR region too big"); + assert(false); + break; + + case -149: + //WEBRTC_LOG(kTraceError, "ERROR not enough stack"); + assert(false); + break; + + default: + //WEBRTC_LOG(kTraceError, "ERROR Unknown QDError %d", err); + assert(false); + break; + } + + return true; +} + +int VideoRenderAGL::ChangeWindow(void* newWindowRef) +{ + + LockAGLCntx(); + + UnlockAGLCntx(); + return -1; +} + +int32_t VideoRenderAGL::StartRender() +{ + + LockAGLCntx(); + const unsigned int MONITOR_FREQ = 60; + if(TRUE == _renderingIsPaused) + { + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Rendering is paused. Restarting now", __FUNCTION__, __LINE__); + + // we already have the thread. Most likely StopRender() was called and they were paused + if(FALSE == _screenUpdateThread->Start()) + { + //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return -1; + } + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + if(FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) + { + //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateEvent", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return -1; + } + + return 0; + } + + _screenUpdateThread.reset( + new rtc::PlatformThread(ScreenUpdateThreadProc, this, "ScreenUpdate")); + _screenUpdateEvent = EventWrapper::Create(); + + if (!_screenUpdateThread) + { + //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return -1; + } + + _screenUpdateThread->Start(); + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ); + + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Started screenUpdateThread", __FUNCTION__, __LINE__); + + UnlockAGLCntx(); + return 0; +} + +int32_t VideoRenderAGL::StopRender() +{ + LockAGLCntx(); + + if(!_screenUpdateThread || !_screenUpdateEvent) + { + _renderingIsPaused = TRUE; + UnlockAGLCntx(); + return 0; + } + + if(FALSE == _screenUpdateThread->Stop() || FALSE == _screenUpdateEvent->StopTimer()) + { + _renderingIsPaused = FALSE; + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Could not stop either: screenUpdateThread or screenUpdateEvent", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return -1; + } + + _renderingIsPaused = TRUE; + + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Stopped screenUpdateThread", __FUNCTION__, __LINE__); + UnlockAGLCntx(); + return 0; +} + +int32_t VideoRenderAGL::DeleteAGLChannel(const uint32_t streamID) +{ + + LockAGLCntx(); + + std::map::iterator it; + it = _aglChannels.begin(); + + while (it != _aglChannels.end()) + { + VideoChannelAGL* channel = it->second; + //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Deleting channel %d", __FUNCTION__, __LINE__, streamID); + delete channel; + it++; + } + _aglChannels.clear(); + + UnlockAGLCntx(); + return 0; +} + +int32_t VideoRenderAGL::GetChannelProperties(const uint16_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) +{ + + LockAGLCntx(); + UnlockAGLCntx(); + return -1; + +} + +void VideoRenderAGL::LockAGLCntx() +{ + _renderCritSec.Enter(); +} +void VideoRenderAGL::UnlockAGLCntx() +{ + _renderCritSec.Leave(); +} + +} // namespace webrtc + +#endif // CARBON_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_agl.h b/webrtc/modules/video_render/mac/video_render_agl.h new file mode 100644 index 0000000000..c0a60597e9 --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_agl.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" + +#if defined(CARBON_RENDERING) + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ + +#include "webrtc/base/platform_thread.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +#define NEW_HIVIEW_PARENT_EVENT_HANDLER 1 +#define NEW_HIVIEW_EVENT_HANDLER 1 +#define USE_STRUCT_RGN + +#include +#include +#include +#include +#include +#include +#include +#include + +class VideoRenderAGL; + +namespace webrtc { +class CriticalSectionWrapper; +class EventWrapper; + +class VideoChannelAGL : public VideoRenderCallback { + public: + + VideoChannelAGL(AGLContext& aglContext, int iId, VideoRenderAGL* owner); + virtual ~VideoChannelAGL(); + virtual int FrameSizeChange(int width, int height, int numberOfStreams); + virtual int DeliverFrame(const VideoFrame& videoFrame); + virtual int UpdateSize(int width, int height); + int SetStreamSettings(int streamId, float startWidth, float startHeight, + float stopWidth, float stopHeight); + int SetStreamCropSettings(int streamId, float startWidth, float startHeight, + float stopWidth, float stopHeight); + int RenderOffScreenBuffer(); + int IsUpdated(bool& isUpdated); + virtual int UpdateStretchSize(int stretchHeight, int stretchWidth); + virtual int32_t RenderFrame(const uint32_t streamId, VideoFrame& videoFrame); + + private: + + AGLContext _aglContext; + int _id; + VideoRenderAGL* _owner; + int _width; + int _height; + int _stretchedWidth; + int _stretchedHeight; + float _startHeight; + float _startWidth; + float _stopWidth; + float _stopHeight; + int _xOldWidth; + int _yOldHeight; + int _oldStretchedHeight; + int _oldStretchedWidth; + unsigned char* _buffer; + size_t _bufferSize; + size_t _incomingBufferSize; + bool _bufferIsUpdated; + bool _sizeInitialized; + int _numberOfStreams; + bool _bVideoSizeStartedChanging; + GLenum _pixelFormat; + GLenum _pixelDataType; + unsigned int _texture; +}; + +class VideoRenderAGL { + public: + VideoRenderAGL(WindowRef windowRef, bool fullscreen, int iId); + VideoRenderAGL(HIViewRef windowRef, bool fullscreen, int iId); + ~VideoRenderAGL(); + + int Init(); + VideoChannelAGL* CreateAGLChannel(int channel, int zOrder, float startWidth, + float startHeight, float stopWidth, + float stopHeight); + VideoChannelAGL* ConfigureAGLChannel(int channel, int zOrder, + float startWidth, float startHeight, + float stopWidth, float stopHeight); + int DeleteAGLChannel(int channel); + int DeleteAllAGLChannels(); + int StopThread(); + bool IsFullScreen(); + bool HasChannels(); + bool HasChannel(int channel); + int GetChannels(std::list& channelList); + void LockAGLCntx(); + void UnlockAGLCntx(); + + static int GetOpenGLVersion(int& aglMajor, int& aglMinor); + + // ********** new module functions ************ // + int ChangeWindow(void* newWindowRef); + int32_t StartRender(); + int32_t StopRender(); + int32_t DeleteAGLChannel(const uint32_t streamID); + int32_t GetChannelProperties(const uint16_t streamId, uint32_t& zOrder, + float& left, float& top, float& right, + float& bottom); + + protected: + static bool ScreenUpdateThreadProc(void* obj); + bool ScreenUpdateProcess(); + int GetWindowRect(Rect& rect); + + private: + int CreateMixingContext(); + int RenderOffScreenBuffers(); + int SwapAndDisplayBuffers(); + int UpdateClipping(); + int CalculateVisibleRegion(ControlRef control, RgnHandle& visibleRgn, + bool clipChildren); + bool CheckValidRegion(RgnHandle rHandle); + void ParentWindowResized(WindowRef window); + + // Carbon GUI event handlers + static pascal OSStatus sHandleWindowResized( + EventHandlerCallRef nextHandler, EventRef theEvent, void* userData); + static pascal OSStatus sHandleHiViewResized( + EventHandlerCallRef nextHandler, EventRef theEvent, void* userData); + + HIViewRef _hiviewRef; + WindowRef _windowRef; + bool _fullScreen; + int _id; + webrtc::CriticalSectionWrapper& _renderCritSec; + // TODO(pbos): Remove unique_ptr and use PlatformThread directly. + std::unique_ptr _screenUpdateThread; + webrtc::EventWrapper* _screenUpdateEvent; + bool _isHIViewRef; + AGLContext _aglContext; + int _windowWidth; + int _windowHeight; + int _lastWindowWidth; + int _lastWindowHeight; + int _lastHiViewWidth; + int _lastHiViewHeight; + int _currentParentWindowHeight; + int _currentParentWindowWidth; + Rect _currentParentWindowBounds; + bool _windowHasResized; + Rect _lastParentWindowBounds; + Rect _currentHIViewBounds; + Rect _lastHIViewBounds; + Rect _windowRect; + std::map _aglChannels; + std::multimap _zOrderToChannel; + EventHandlerRef _hiviewEventHandlerRef; + EventHandlerRef _windowEventHandlerRef; + HIRect _currentViewBounds; + HIRect _lastViewBounds; + bool _renderingIsPaused; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_AGL_H_ + +#endif // CARBON_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.cc b/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.cc new file mode 100644 index 0000000000..f85be5fb5e --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.cc @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#if defined(CARBON_RENDERING) + +#include +#include "webrtc/modules/video_render/mac/video_render_agl.h" +#include "webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +VideoRenderMacCarbonImpl::VideoRenderMacCarbonImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : +_id(id), +_renderMacCarbonCritsect(*CriticalSectionWrapper::CreateCriticalSection()), +_fullScreen(fullscreen), +_ptrWindow(window) +{ + + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); + +} + +VideoRenderMacCarbonImpl::~VideoRenderMacCarbonImpl() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Destructor %s:%d", __FUNCTION__, __LINE__); + delete &_renderMacCarbonCritsect; +} + +int32_t +VideoRenderMacCarbonImpl::Init() +{ + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); + + if (!_ptrWindow) + { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); + return -1; + } + + // We don't know if the user passed us a WindowRef or a HIViewRef, so test. + bool referenceIsValid = false; + + // Check if it's a valid WindowRef + //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d _ptrWindowRef before WindowRef cast: %x", __FUNCTION__, __LINE__, _ptrWindowRef); + WindowRef* windowRef = static_cast(_ptrWindow); + //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d _ptrWindowRef after cast: %x", __FUNCTION__, __LINE__, _ptrWindowRef); + if (IsValidWindowPtr(*windowRef)) + { + _ptrCarbonRender = new VideoRenderAGL(*windowRef, _fullScreen, _id); + referenceIsValid = true; + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Successfully initialized CarbonRenderer with WindowRef:%x", __FUNCTION__, __LINE__, *windowRef); + } + else + { + HIViewRef* hiviewRef = static_cast(_ptrWindow); + if (HIViewIsValid(*hiviewRef)) + { + _ptrCarbonRender = new VideoRenderAGL(*hiviewRef, _fullScreen, _id); + referenceIsValid = true; + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Successfully initialized CarbonRenderer with HIViewRef:%x", __FUNCTION__, __LINE__, hiviewRef); + } + } + + if(!referenceIsValid) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Invalid WindowRef/HIViewRef Returning -1", __FUNCTION__, __LINE__); + return -1; + } + + if(!_ptrCarbonRender) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to create an instance of VideoRenderAGL. Returning -1", __FUNCTION__, __LINE__); + } + + int retVal = _ptrCarbonRender->Init(); + if (retVal == -1) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to init CarbonRenderer", __FUNCTION__, __LINE__); + return -1; + } + + return 0; +} + +int32_t +VideoRenderMacCarbonImpl::ChangeWindow(void* window) +{ + return -1; + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s changing ID to ", __FUNCTION__, window); + + if (window == NULL) + { + return -1; + } + _ptrWindow = window; + + + _ptrWindow = window; + + return 0; +} + +VideoRenderCallback* +VideoRenderMacCarbonImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); + VideoChannelAGL* AGLChannel = NULL; + + if(!_ptrWindow) + { + } + + if(!AGLChannel) + { + AGLChannel = _ptrCocoaRender->CreateNSGLChannel(streamId, zOrder, left, top, right, bottom); + } + + return AGLChannel; + +} + +int32_t +VideoRenderMacCarbonImpl::DeleteIncomingRenderStream(const uint32_t streamId) +{ + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + _ptrCarbonRender->DeleteAGLChannel(streamId); + + return 0; +} + +int32_t +VideoRenderMacCarbonImpl::GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const +{ + return -1; + return _ptrCarbonRender->GetChannelProperties(streamId, zOrder, left, top, right, bottom); +} + +int32_t +VideoRenderMacCarbonImpl::StartRender() +{ + return _ptrCarbonRender->StartRender(); +} + +int32_t +VideoRenderMacCarbonImpl::StopRender() +{ + return _ptrCarbonRender->StopRender(); +} + +VideoRenderType +VideoRenderMacCarbonImpl::RenderType() +{ + return kRenderCarbon; +} + +RawVideoType +VideoRenderMacCarbonImpl::PerferedVideoType() +{ + return kVideoI420; +} + +bool +VideoRenderMacCarbonImpl::FullScreen() +{ + return false; +} + +int32_t +VideoRenderMacCarbonImpl::GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const +{ + totalGraphicsMemory = 0; + availableGraphicsMemory = 0; + return 0; +} + +int32_t +VideoRenderMacCarbonImpl::GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + //NSScreen* mainScreen = [NSScreen mainScreen]; + + //NSRect frame = [mainScreen frame]; + + //screenWidth = frame.size.width; + //screenHeight = frame.size.height; + return 0; +} + +uint32_t +VideoRenderMacCarbonImpl::RenderFrameRate(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_renderMacCarbonCritsect); + return 0; +} + +int32_t +VideoRenderMacCarbonImpl::SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t VideoRenderMacCarbonImpl::ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t +VideoRenderMacCarbonImpl::SetTransparentBackground(const bool enable) +{ + return 0; +} + +int32_t VideoRenderMacCarbonImpl::SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t VideoRenderMacCarbonImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + + +} // namespace webrtc + +#endif // CARBON_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h b/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h new file mode 100644 index 0000000000..9ad3a6cdd1 --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#if defined(CARBON_RENDERING) + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ + +#include "webrtc/modules/video_render/i_video_render.h" + +namespace webrtc { + +class CriticalSectionWrapper; +class VideoRenderAGL; + +// Class definitions +class VideoRenderMacCarbonImpl : IVideoRender +{ +public: + /* + * Constructor/destructor + */ + + VideoRenderMacCarbonImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen); + + virtual ~VideoRenderMacCarbonImpl(); + + virtual int32_t Init(); + + virtual int32_t ChangeWindow(void* window); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + virtual VideoRenderCallback* AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t DeleteIncomingRenderStream(const uint32_t streamId); + + virtual int32_t GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t FullScreenRender(void* window, const bool enable) + { + // not supported in Carbon at this time + return -1; + } + +private: + int32_t _id; + CriticalSectionWrapper& _renderMacCarbonCritsect; + bool _fullScreen; + void* _ptrWindow; + VideoRenderAGL* _ptrCarbonRender; + +}; + + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_CARBON_IMPL_H_ +#endif // CARBON_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h b/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h new file mode 100644 index 0000000000..21add272bb --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" + +#if defined(COCOA_RENDERING) + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ + +#include "webrtc/modules/video_render/i_video_render.h" + +namespace webrtc { +class CriticalSectionWrapper; +class VideoRenderNSOpenGL; + +// Class definitions +class VideoRenderMacCocoaImpl : IVideoRender +{ +public: + /* + * Constructor/destructor + */ + + VideoRenderMacCocoaImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen); + + virtual ~VideoRenderMacCocoaImpl(); + + virtual int32_t Init(); + + virtual int32_t ChangeWindow(void* window); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + virtual VideoRenderCallback* AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t DeleteIncomingRenderStream(const uint32_t streamId); + + virtual int32_t GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t FullScreenRender(void* window, const bool enable); + +private: + int32_t _id; + CriticalSectionWrapper& _renderMacCocoaCritsect; + bool _fullScreen; + void* _ptrWindow; + VideoRenderNSOpenGL* _ptrCocoaRender; + +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_MAC_COCOA_IMPL_H_ +#endif // COCOA_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm b/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm new file mode 100644 index 0000000000..5b017fecc0 --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.mm @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#if defined(COCOA_RENDERING) + +#include "webrtc/modules/video_render/mac/cocoa_render_view.h" +#include "webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h" +#include "webrtc/modules/video_render/mac/video_render_nsopengl.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +VideoRenderMacCocoaImpl::VideoRenderMacCocoaImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : +_id(id), +_renderMacCocoaCritsect(*CriticalSectionWrapper::CreateCriticalSection()), +_fullScreen(fullscreen), +_ptrWindow(window) +{ + + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); +} + +VideoRenderMacCocoaImpl::~VideoRenderMacCocoaImpl() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Destructor %s:%d", __FUNCTION__, __LINE__); + delete &_renderMacCocoaCritsect; + if (_ptrCocoaRender) + { + delete _ptrCocoaRender; + _ptrCocoaRender = NULL; + } +} + +int32_t +VideoRenderMacCocoaImpl::Init() +{ + + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); + + // cast ptrWindow from void* to CocoaRenderer. Void* was once NSOpenGLView, and CocoaRenderer is NSOpenGLView. + _ptrCocoaRender = new VideoRenderNSOpenGL((CocoaRenderView*)_ptrWindow, _fullScreen, _id); + if (!_ptrWindow) + { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); + return -1; + } + int retVal = _ptrCocoaRender->Init(); + if (retVal == -1) + { + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "Failed to init %s:%d", __FUNCTION__, __LINE__); + return -1; + } + + return 0; +} + +int32_t +VideoRenderMacCocoaImpl::ChangeWindow(void* window) +{ + + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s changing ID to ", __FUNCTION__, window); + + if (window == NULL) + { + return -1; + } + _ptrWindow = window; + + + _ptrWindow = window; + _ptrCocoaRender->ChangeWindow((CocoaRenderView*)_ptrWindow); + + return 0; +} + +VideoRenderCallback* +VideoRenderMacCocoaImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__); + VideoChannelNSOpenGL* nsOpenGLChannel = NULL; + + if(!_ptrWindow) + { + } + + if(!nsOpenGLChannel) + { + nsOpenGLChannel = _ptrCocoaRender->CreateNSGLChannel(streamId, zOrder, left, top, right, bottom); + } + + return nsOpenGLChannel; + +} + +int32_t +VideoRenderMacCocoaImpl::DeleteIncomingRenderStream(const uint32_t streamId) +{ + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Constructor %s:%d", __FUNCTION__, __LINE__); + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + _ptrCocoaRender->DeleteNSGLChannel(streamId); + + return 0; +} + +int32_t +VideoRenderMacCocoaImpl::GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const +{ + return _ptrCocoaRender->GetChannelProperties(streamId, zOrder, left, top, right, bottom); +} + +int32_t +VideoRenderMacCocoaImpl::StartRender() +{ + return _ptrCocoaRender->StartRender(); +} + +int32_t +VideoRenderMacCocoaImpl::StopRender() +{ + return _ptrCocoaRender->StopRender(); +} + +VideoRenderType +VideoRenderMacCocoaImpl::RenderType() +{ + return kRenderCocoa; +} + +RawVideoType +VideoRenderMacCocoaImpl::PerferedVideoType() +{ + return kVideoI420; +} + +bool +VideoRenderMacCocoaImpl::FullScreen() +{ + return false; +} + +int32_t +VideoRenderMacCocoaImpl::GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const +{ + totalGraphicsMemory = 0; + availableGraphicsMemory = 0; + return 0; +} + +int32_t +VideoRenderMacCocoaImpl::GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + NSScreen* mainScreen = [NSScreen mainScreen]; + + NSRect frame = [mainScreen frame]; + + screenWidth = frame.size.width; + screenHeight = frame.size.height; + return 0; +} + +uint32_t +VideoRenderMacCocoaImpl::RenderFrameRate(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_renderMacCocoaCritsect); + return 0; +} + +int32_t +VideoRenderMacCocoaImpl::SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t VideoRenderMacCocoaImpl::ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t +VideoRenderMacCocoaImpl::SetTransparentBackground(const bool enable) +{ + return 0; +} + +int32_t VideoRenderMacCocoaImpl::SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom) +{ + return _ptrCocoaRender->SetText(textId, text, textLength, textColorRef, backgroundColorRef, left, top, right, bottom); +} + +int32_t VideoRenderMacCocoaImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + return 0; +} + +int32_t VideoRenderMacCocoaImpl::FullScreenRender(void* window, const bool enable) +{ + return -1; +} + +} // namespace webrtc + +#endif // COCOA_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_nsopengl.h b/webrtc/modules/video_render/mac/video_render_nsopengl.h new file mode 100644 index 0000000000..457557dad6 --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_nsopengl.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#if defined(COCOA_RENDERING) + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ + +#import +#import +#import +#import +#include +#include +#include +#include + +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +#import "webrtc/modules/video_render/mac/cocoa_full_screen_window.h" +#import "webrtc/modules/video_render/mac/cocoa_render_view.h" + +class Trace; + +namespace rtc { +class PlatformThread; +} // namespace rtc + +namespace webrtc { +class EventTimerWrapper; +class VideoRenderNSOpenGL; +class CriticalSectionWrapper; + +class VideoChannelNSOpenGL : public VideoRenderCallback { +public: + VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner); + virtual ~VideoChannelNSOpenGL(); + + // A new frame is delivered + virtual int DeliverFrame(const VideoFrame& videoFrame); + + // Called when the incoming frame size and/or number of streams in mix + // changes. + virtual int FrameSizeChange(int width, int height, int numberOfStreams); + + virtual int UpdateSize(int width, int height); + + // Setup + int SetStreamSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); + int SetStreamCropSettings(int streamId, float startWidth, float startHeight, float stopWidth, float stopHeight); + + // Called when it's time to render the last frame for the channel + int RenderOffScreenBuffer(); + + // Returns true if a new buffer has been delivered to the texture + int IsUpdated(bool& isUpdated); + virtual int UpdateStretchSize(int stretchHeight, int stretchWidth); + + // ********** new module functions ************ // + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + + // ********** new module helper functions ***** // + int ChangeContext(NSOpenGLContext *nsglContext); + int32_t GetChannelProperties(float& left, + float& top, + float& right, + float& bottom); + +private: + + NSOpenGLContext* _nsglContext; + const int _id; + VideoRenderNSOpenGL* _owner; + int32_t _width; + int32_t _height; + float _startWidth; + float _startHeight; + float _stopWidth; + float _stopHeight; + int _stretchedWidth; + int _stretchedHeight; + int _oldStretchedHeight; + int _oldStretchedWidth; + unsigned char* _buffer; + size_t _bufferSize; + size_t _incomingBufferSize; + bool _bufferIsUpdated; + int _numberOfStreams; + GLenum _pixelFormat; + GLenum _pixelDataType; + unsigned int _texture; +}; + +class VideoRenderNSOpenGL +{ + +public: // methods + VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId); + ~VideoRenderNSOpenGL(); + + static int GetOpenGLVersion(int& nsglMajor, int& nsglMinor); + + // Allocates textures + int Init(); + VideoChannelNSOpenGL* CreateNSGLChannel(int streamID, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); + VideoChannelNSOpenGL* ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight); + int DeleteNSGLChannel(int channel); + int DeleteAllNSGLChannels(); + int StopThread(); + bool IsFullScreen(); + bool HasChannels(); + bool HasChannel(int channel); + int GetChannels(std::list& channelList); + void LockAGLCntx() EXCLUSIVE_LOCK_FUNCTION(_nsglContextCritSec); + void UnlockAGLCntx() UNLOCK_FUNCTION(_nsglContextCritSec); + + // ********** new module functions ************ // + int ChangeWindow(CocoaRenderView* newWindowRef); + int32_t StartRender(); + int32_t StopRender(); + int32_t DeleteNSGLChannel(const uint32_t streamID); + int32_t GetChannelProperties(const uint16_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom); + + int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom); + + // ********** new module helper functions ***** // + int configureNSOpenGLEngine(); + int configureNSOpenGLView(); + int setRenderTargetWindow(); + int setRenderTargetFullScreen(); + +protected: // methods + static bool ScreenUpdateThreadProc(void* obj); + bool ScreenUpdateProcess(); + int GetWindowRect(Rect& rect); + +private: // methods + + int CreateMixingContext(); + int RenderOffScreenBuffers(); + int DisplayBuffers(); + +private: // variables + + + CocoaRenderView* _windowRef; + bool _fullScreen; + int _id; + CriticalSectionWrapper& _nsglContextCritSec; + // TODO(pbos): Remove unique_ptr and use PlatformThread directly. + std::unique_ptr _screenUpdateThread; + EventTimerWrapper* _screenUpdateEvent; + NSOpenGLContext* _nsglContext; + NSOpenGLContext* _nsglFullScreenContext; + CocoaFullScreenWindow* _fullScreenWindow; + Rect _windowRect; // The size of the window + int _windowWidth; + int _windowHeight; + std::map _nsglChannels; + std::multimap _zOrderToChannel; + bool _renderingIsPaused; + NSView* _windowRefSuperView; + NSRect _windowRefSuperViewFrame; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_MAC_VIDEO_RENDER_NSOPENGL_H_ +#endif // COCOA_RENDERING diff --git a/webrtc/modules/video_render/mac/video_render_nsopengl.mm b/webrtc/modules/video_render/mac/video_render_nsopengl.mm new file mode 100644 index 0000000000..b7683a96af --- /dev/null +++ b/webrtc/modules/video_render/mac/video_render_nsopengl.mm @@ -0,0 +1,1247 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#if defined(COCOA_RENDERING) + +#include "webrtc/base/platform_thread.h" +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_render/mac/video_render_nsopengl.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/event_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +VideoChannelNSOpenGL::VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner) : +_nsglContext( nsglContext), +_id( iId), +_owner( owner), +_width( 0), +_height( 0), +_startWidth( 0.0f), +_startHeight( 0.0f), +_stopWidth( 0.0f), +_stopHeight( 0.0f), +_stretchedWidth( 0), +_stretchedHeight( 0), +_oldStretchedHeight( 0), +_oldStretchedWidth( 0), +_buffer( 0), +_bufferSize( 0), +_incomingBufferSize( 0), +_bufferIsUpdated( false), +_numberOfStreams( 0), +_pixelFormat( GL_RGBA), +_pixelDataType( GL_UNSIGNED_INT_8_8_8_8), +_texture( 0) +{ + +} + +VideoChannelNSOpenGL::~VideoChannelNSOpenGL() +{ + if (_buffer) + { + delete [] _buffer; + _buffer = NULL; + } + + if (_texture != 0) + { + [_nsglContext makeCurrentContext]; + glDeleteTextures(1, (const GLuint*) &_texture); + _texture = 0; + } +} + +int VideoChannelNSOpenGL::ChangeContext(NSOpenGLContext *nsglContext) +{ + _owner->LockAGLCntx(); + + _nsglContext = nsglContext; + [_nsglContext makeCurrentContext]; + + _owner->UnlockAGLCntx(); + return 0; + +} + +int32_t VideoChannelNSOpenGL::GetChannelProperties(float& left, float& top, + float& right, float& bottom) +{ + + _owner->LockAGLCntx(); + + left = _startWidth; + top = _startHeight; + right = _stopWidth; + bottom = _stopHeight; + + _owner->UnlockAGLCntx(); + return 0; +} + +int32_t VideoChannelNSOpenGL::RenderFrame(const uint32_t /*streamId*/, + const VideoFrame& videoFrame) { + _owner->LockAGLCntx(); + + if(_width != videoFrame.width() || + _height != videoFrame.height()) { + if(FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { + _owner->UnlockAGLCntx(); + return -1; + } + } + int ret = DeliverFrame(videoFrame); + + _owner->UnlockAGLCntx(); + return ret; +} + +int VideoChannelNSOpenGL::UpdateSize(int width, int height) +{ + _owner->LockAGLCntx(); + _width = width; + _height = height; + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::UpdateStretchSize(int stretchHeight, int stretchWidth) +{ + + _owner->LockAGLCntx(); + _stretchedHeight = stretchHeight; + _stretchedWidth = stretchWidth; + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::FrameSizeChange(int width, int height, int numberOfStreams) +{ + // We got a new frame size from VideoAPI, prepare the buffer + + _owner->LockAGLCntx(); + + if (width == _width && _height == height) + { + // We already have a correct buffer size + _numberOfStreams = numberOfStreams; + _owner->UnlockAGLCntx(); + return 0; + } + + _width = width; + _height = height; + + // Delete the old buffer, create a new one with correct size. + if (_buffer) + { + delete [] _buffer; + _bufferSize = 0; + } + + _incomingBufferSize = CalcBufferSize(kI420, _width, _height); + _bufferSize = CalcBufferSize(kARGB, _width, _height); + _buffer = new unsigned char [_bufferSize]; + memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); + + [_nsglContext makeCurrentContext]; + + if(glIsTexture(_texture)) + { + glDeleteTextures(1, (const GLuint*) &_texture); + _texture = 0; + } + + // Create a new texture + glGenTextures(1, (GLuint *) &_texture); + + GLenum glErr = glGetError(); + + if (glErr != GL_NO_ERROR) + { + + } + + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + + GLint texSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); + + if (texSize < _width || texSize < _height) + { + _owner->UnlockAGLCntx(); + return -1; + } + + // Set up th texture type and size + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target + 0, // level + GL_RGBA, // internal format + _width, // width + _height, // height + 0, // border 0/1 = off/on + _pixelFormat, // format, GL_RGBA + _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 + _buffer); // pixel data + + glErr = glGetError(); + if (glErr != GL_NO_ERROR) + { + _owner->UnlockAGLCntx(); + return -1; + } + + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::DeliverFrame(const VideoFrame& videoFrame) { + _owner->LockAGLCntx(); + + if (_texture == 0) { + _owner->UnlockAGLCntx(); + return 0; + } + + if (CalcBufferSize(kI420, videoFrame.width(), videoFrame.height()) != + _incomingBufferSize) { + _owner->UnlockAGLCntx(); + return -1; + } + + // Using the VideoFrame for YV12: YV12 is YVU; I420 assumes + // YUV. + // TODO(mikhal) : Use appropriate functionality. + // TODO(wu): See if we are using glTexSubImage2D correctly. + int rgbRet = ConvertFromYV12(videoFrame, kBGRA, 0, _buffer); + if (rgbRet < 0) { + _owner->UnlockAGLCntx(); + return -1; + } + + [_nsglContext makeCurrentContext]; + + // Make sure this texture is the active one + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + GLenum glErr = glGetError(); + if (glErr != GL_NO_ERROR) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "ERROR %d while calling glBindTexture", glErr); + _owner->UnlockAGLCntx(); + return -1; + } + + glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, + 0, // Level, not use + 0, // start point x, (low left of pic) + 0, // start point y, + _width, // width + _height, // height + _pixelFormat, // pictue format for _buffer + _pixelDataType, // data type of _buffer + (const GLvoid*) _buffer); // the pixel data + + glErr = glGetError(); + if (glErr != GL_NO_ERROR) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "ERROR %d while calling glTexSubImage2d", glErr); + _owner->UnlockAGLCntx(); + return -1; + } + + _bufferIsUpdated = true; + + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::RenderOffScreenBuffer() +{ + + _owner->LockAGLCntx(); + + if (_texture == 0) + { + _owner->UnlockAGLCntx(); + return 0; + } + + // if(_fullscreen) + // { + // NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; + // _width = mainDisplayRect.size.width; + // _height = mainDisplayRect.size.height; + // glViewport(0, 0, mainDisplayRect.size.width, mainDisplayRect.size.height); + // float newX = mainDisplayRect.size.width/_width; + // float newY = mainDisplayRect.size.height/_height; + + // convert from 0.0 <= size <= 1.0 to + // open gl world -1.0 < size < 1.0 + GLfloat xStart = 2.0f * _startWidth - 1.0f; + GLfloat xStop = 2.0f * _stopWidth - 1.0f; + GLfloat yStart = 1.0f - 2.0f * _stopHeight; + GLfloat yStop = 1.0f - 2.0f * _startHeight; + + [_nsglContext makeCurrentContext]; + + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); + _oldStretchedHeight = _stretchedHeight; + _oldStretchedWidth = _stretchedWidth; + + glLoadIdentity(); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + glBegin(GL_POLYGON); + { + glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); + glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); + glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); + glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); + } + glEnd(); + + glDisable(GL_TEXTURE_RECTANGLE_EXT); + + _bufferIsUpdated = false; + + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::IsUpdated(bool& isUpdated) +{ + _owner->LockAGLCntx(); + + isUpdated = _bufferIsUpdated; + + _owner->UnlockAGLCntx(); + return 0; +} + +int VideoChannelNSOpenGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + _owner->LockAGLCntx(); + + _startWidth = startWidth; + _stopWidth = stopWidth; + _startHeight = startHeight; + _stopHeight = stopHeight; + + int oldWidth = _width; + int oldHeight = _height; + int oldNumberOfStreams = _numberOfStreams; + + _width = 0; + _height = 0; + + int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); + + _owner->UnlockAGLCntx(); + return retVal; +} + +int VideoChannelNSOpenGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) +{ + return -1; +} + +/* + * + * VideoRenderNSOpenGL + * + */ + +VideoRenderNSOpenGL::VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId) : +_windowRef( (CocoaRenderView*)windowRef), +_fullScreen( fullScreen), +_id( iId), +_nsglContextCritSec( *CriticalSectionWrapper::CreateCriticalSection()), +_screenUpdateEvent(EventTimerWrapper::Create()), +_nsglContext( 0), +_nsglFullScreenContext( 0), +_fullScreenWindow( nil), +_windowRect( ), +_windowWidth( 0), +_windowHeight( 0), +_nsglChannels( ), +_zOrderToChannel( ), +_renderingIsPaused (FALSE), +_windowRefSuperView(NULL), +_windowRefSuperViewFrame(NSMakeRect(0,0,0,0)) +{ + _screenUpdateThread.reset(new rtc::PlatformThread( + ScreenUpdateThreadProc, this, "ScreenUpdateNSOpenGL")); +} + +int VideoRenderNSOpenGL::ChangeWindow(CocoaRenderView* newWindowRef) +{ + + LockAGLCntx(); + + _windowRef = newWindowRef; + + if(CreateMixingContext() == -1) + { + UnlockAGLCntx(); + return -1; + } + + int error = 0; + std::map::iterator it = _nsglChannels.begin(); + while (it!= _nsglChannels.end()) + { + error |= (it->second)->ChangeContext(_nsglContext); + it++; + } + if(error != 0) + { + UnlockAGLCntx(); + return -1; + } + + UnlockAGLCntx(); + return 0; +} + +/* Check if the thread and event already exist. + * If so then they will simply be restarted + * If not then create them and continue + */ +int32_t VideoRenderNSOpenGL::StartRender() +{ + + LockAGLCntx(); + + const unsigned int MONITOR_FREQ = 60; + if(TRUE == _renderingIsPaused) + { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Restarting screenUpdateThread"); + + // we already have the thread. Most likely StopRender() was called and they were paused + _screenUpdateThread->Start(); + if (FALSE == + _screenUpdateEvent->StartTimer(true, 1000 / MONITOR_FREQ)) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); + UnlockAGLCntx(); + return -1; + } + + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + + UnlockAGLCntx(); + return 0; + } + + + if (!_screenUpdateThread) + { + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "failed start screenUpdateThread"); + UnlockAGLCntx(); + return -1; + } + + + UnlockAGLCntx(); + return 0; +} +int32_t VideoRenderNSOpenGL::StopRender() +{ + + LockAGLCntx(); + + /* The code below is functional + * but it pauses for several seconds + */ + + // pause the update thread and the event timer + if(!_screenUpdateThread || !_screenUpdateEvent) + { + _renderingIsPaused = TRUE; + + UnlockAGLCntx(); + return 0; + } + + _screenUpdateThread->Stop(); + if (FALSE == _screenUpdateEvent->StopTimer()) { + _renderingIsPaused = FALSE; + + UnlockAGLCntx(); + return -1; + } + + _renderingIsPaused = TRUE; + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderNSOpenGL::configureNSOpenGLView() +{ + return 0; + +} + +int VideoRenderNSOpenGL::configureNSOpenGLEngine() +{ + + LockAGLCntx(); + + // Disable not needed functionality to increase performance + glDisable(GL_DITHER); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_FOG); + glDisable(GL_TEXTURE_2D); + glPixelZoom(1.0, 1.0); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + + // Set texture parameters + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); + + if (GetWindowRect(_windowRect) == -1) + { + UnlockAGLCntx(); + return true; + } + + if (_windowWidth != (_windowRect.right - _windowRect.left) + || _windowHeight != (_windowRect.bottom - _windowRect.top)) + { + _windowWidth = _windowRect.right - _windowRect.left; + _windowHeight = _windowRect.bottom - _windowRect.top; + } + glViewport(0, 0, _windowWidth, _windowHeight); + + // Synchronize buffer swaps with vertical refresh rate + GLint swapInt = 1; + [_nsglContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderNSOpenGL::setRenderTargetWindow() +{ + LockAGLCntx(); + + + GLuint attribs[] = + { + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 16, + NSOpenGLPFAAccelerated, + 0 + }; + + NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: + (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; + + if(_windowRef) + { + [_windowRef initCocoaRenderView:fmt]; + } + else + { + UnlockAGLCntx(); + return -1; + } + + _nsglContext = [_windowRef nsOpenGLContext]; + [_nsglContext makeCurrentContext]; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + + DisplayBuffers(); + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderNSOpenGL::setRenderTargetFullScreen() +{ + LockAGLCntx(); + + + GLuint attribs[] = + { + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 16, + NSOpenGLPFAAccelerated, + 0 + }; + + NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: + (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; + + // Store original superview and frame for use when exiting full screens + _windowRefSuperViewFrame = [_windowRef frame]; + _windowRefSuperView = [_windowRef superview]; + + + // create new fullscreen window + NSRect screenRect = [[NSScreen mainScreen]frame]; + [_windowRef setFrame:screenRect]; + [_windowRef setBounds:screenRect]; + + + _fullScreenWindow = [[CocoaFullScreenWindow alloc]init]; + [_fullScreenWindow grabFullScreen]; + [[[_fullScreenWindow window] contentView] addSubview:_windowRef]; + + if(_windowRef) + { + [_windowRef initCocoaRenderViewFullScreen:fmt]; + } + else + { + UnlockAGLCntx(); + return -1; + } + + _nsglContext = [_windowRef nsOpenGLContext]; + [_nsglContext makeCurrentContext]; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + DisplayBuffers(); + + UnlockAGLCntx(); + return 0; +} + +VideoRenderNSOpenGL::~VideoRenderNSOpenGL() +{ + + if(_fullScreen) + { + if(_fullScreenWindow) + { + // Detach CocoaRenderView from full screen view back to + // it's original parent. + [_windowRef removeFromSuperview]; + if(_windowRefSuperView) + { + [_windowRefSuperView addSubview:_windowRef]; + [_windowRef setFrame:_windowRefSuperViewFrame]; + } + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, 0, "%s:%d Attempting to release fullscreen window", __FUNCTION__, __LINE__); + [_fullScreenWindow releaseFullScreen]; + + } + } + + // Signal event to exit thread, then delete it + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); + + if (tmpPtr) + { + _screenUpdateEvent->Set(); + _screenUpdateEvent->StopTimer(); + + tmpPtr->Stop(); + delete tmpPtr; + delete _screenUpdateEvent; + _screenUpdateEvent = NULL; + } + + if (_nsglContext != 0) + { + [_nsglContext makeCurrentContext]; + _nsglContext = nil; + } + + // Delete all channels + std::map::iterator it = _nsglChannels.begin(); + while (it!= _nsglChannels.end()) + { + delete it->second; + _nsglChannels.erase(it); + it = _nsglChannels.begin(); + } + _nsglChannels.clear(); + + // Clean the zOrder map + std::multimap::iterator zIt = _zOrderToChannel.begin(); + while(zIt != _zOrderToChannel.end()) + { + _zOrderToChannel.erase(zIt); + zIt = _zOrderToChannel.begin(); + } + _zOrderToChannel.clear(); + +} + +/* static */ +int VideoRenderNSOpenGL::GetOpenGLVersion(int& /*nsglMajor*/, int& /*nsglMinor*/) +{ + return -1; +} + +int VideoRenderNSOpenGL::Init() +{ + + LockAGLCntx(); + if (!_screenUpdateThread) + { + UnlockAGLCntx(); + return -1; + } + + _screenUpdateThread->Start(); + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + + // Start the event triggering the render process + unsigned int monitorFreq = 60; + _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); + + if (CreateMixingContext() == -1) + { + UnlockAGLCntx(); + return -1; + } + + UnlockAGLCntx(); + return 0; +} + +VideoChannelNSOpenGL* VideoRenderNSOpenGL::CreateNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + CriticalSectionScoped cs(&_nsglContextCritSec); + + if (HasChannel(channel)) + { + return NULL; + } + + if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) + { + + } + + VideoChannelNSOpenGL* newAGLChannel = new VideoChannelNSOpenGL(_nsglContext, _id, this); + if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) + { + if (newAGLChannel) + { + delete newAGLChannel; + newAGLChannel = NULL; + } + + return NULL; + } + + _nsglChannels[channel] = newAGLChannel; + _zOrderToChannel.insert(std::pair(zOrder, channel)); + + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s successfully created NSGL channel number %d", __FUNCTION__, channel); + + return newAGLChannel; +} + +int VideoRenderNSOpenGL::DeleteAllNSGLChannels() +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + std::map::iterator it; + it = _nsglChannels.begin(); + + while (it != _nsglChannels.end()) + { + VideoChannelNSOpenGL* channel = it->second; + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Deleting channel %d", __FUNCTION__, channel); + delete channel; + it++; + } + _nsglChannels.clear(); + return 0; +} + +int32_t VideoRenderNSOpenGL::DeleteNSGLChannel(const uint32_t channel) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + std::map::iterator it; + it = _nsglChannels.find(channel); + if (it != _nsglChannels.end()) + { + delete it->second; + _nsglChannels.erase(it); + } + else + { + return -1; + } + + std::multimap::iterator zIt = _zOrderToChannel.begin(); + while( zIt != _zOrderToChannel.end()) + { + if (zIt->second == (int)channel) + { + _zOrderToChannel.erase(zIt); + break; + } + zIt++; + } + + return 0; +} + +int32_t VideoRenderNSOpenGL::GetChannelProperties(const uint16_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + bool channelFound = false; + + // Loop through all channels until we find a match. + // From that, get zorder. + // From that, get T, L, R, B + for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); + rIt != _zOrderToChannel.rend(); + rIt++) + { + if(streamId == rIt->second) + { + channelFound = true; + + zOrder = rIt->second; + + std::map::iterator rIt = _nsglChannels.find(streamId); + VideoChannelNSOpenGL* tempChannel = rIt->second; + + if(-1 == tempChannel->GetChannelProperties(left, top, right, bottom) ) + { + return -1; + } + break; + } + } + + if(false == channelFound) + { + + return -1; + } + + return 0; +} + +int VideoRenderNSOpenGL::StopThread() +{ + + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, + "%s Stopping thread ", __FUNCTION__, tmpPtr); + + if (tmpPtr) + { + _screenUpdateEvent->Set(); + tmpPtr->Stop(); + delete tmpPtr; + } + + delete _screenUpdateEvent; + _screenUpdateEvent = NULL; + + return 0; +} + +bool VideoRenderNSOpenGL::IsFullScreen() +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + return _fullScreen; +} + +bool VideoRenderNSOpenGL::HasChannels() +{ + CriticalSectionScoped cs(&_nsglContextCritSec); + + if (_nsglChannels.begin() != _nsglChannels.end()) + { + return true; + } + return false; +} + +bool VideoRenderNSOpenGL::HasChannel(int channel) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + std::map::iterator it = _nsglChannels.find(channel); + + if (it != _nsglChannels.end()) + { + return true; + } + return false; +} + +int VideoRenderNSOpenGL::GetChannels(std::list& channelList) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + std::map::iterator it = _nsglChannels.begin(); + + while (it != _nsglChannels.end()) + { + channelList.push_back(it->first); + it++; + } + + return 0; +} + +VideoChannelNSOpenGL* VideoRenderNSOpenGL::ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + std::map::iterator it = _nsglChannels.find(channel); + + if (it != _nsglChannels.end()) + { + VideoChannelNSOpenGL* aglChannel = it->second; + if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s failed to set stream settings: channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", + __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); + return NULL; + } + WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Configuring channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", + __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); + + std::multimap::iterator it = _zOrderToChannel.begin(); + while(it != _zOrderToChannel.end()) + { + if (it->second == channel) + { + if (it->first != zOrder) + { + _zOrderToChannel.erase(it); + _zOrderToChannel.insert(std::pair(zOrder, channel)); + } + break; + } + it++; + } + return aglChannel; + } + + return NULL; +} + +/* + * + * Rendering process + * + */ + +bool VideoRenderNSOpenGL::ScreenUpdateThreadProc(void* obj) +{ + return static_cast(obj)->ScreenUpdateProcess(); +} + +bool VideoRenderNSOpenGL::ScreenUpdateProcess() +{ + + _screenUpdateEvent->Wait(10); + LockAGLCntx(); + + if (!_screenUpdateThread) + { + WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s no screen update thread", __FUNCTION__); + UnlockAGLCntx(); + return false; + } + + [_nsglContext makeCurrentContext]; + + if (GetWindowRect(_windowRect) == -1) + { + UnlockAGLCntx(); + return true; + } + + if (_windowWidth != (_windowRect.right - _windowRect.left) + || _windowHeight != (_windowRect.bottom - _windowRect.top)) + { + _windowWidth = _windowRect.right - _windowRect.left; + _windowHeight = _windowRect.bottom - _windowRect.top; + glViewport(0, 0, _windowWidth, _windowHeight); + } + + // Check if there are any updated buffers + bool updated = false; + std::map::iterator it = _nsglChannels.begin(); + while (it != _nsglChannels.end()) + { + + VideoChannelNSOpenGL* aglChannel = it->second; + aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); + aglChannel->IsUpdated(updated); + if (updated) + { + break; + } + it++; + } + + if (updated) + { + + // At least on buffers is updated, we need to repaint the texture + if (RenderOffScreenBuffers() != -1) + { + UnlockAGLCntx(); + return true; + } + } + // } + UnlockAGLCntx(); + return true; +} + +/* + * + * Functions for creating mixing buffers and screen settings + * + */ + +int VideoRenderNSOpenGL::CreateMixingContext() +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + if(_fullScreen) + { + if(-1 == setRenderTargetFullScreen()) + { + return -1; + } + } + else + { + + if(-1 == setRenderTargetWindow()) + { + return -1; + } + } + + configureNSOpenGLEngine(); + + DisplayBuffers(); + + GLenum glErr = glGetError(); + if (glErr) + { + } + + return 0; +} + +/* + * + * Rendering functions + * + */ + +int VideoRenderNSOpenGL::RenderOffScreenBuffers() +{ + LockAGLCntx(); + + // Get the current window size, it might have changed since last render. + if (GetWindowRect(_windowRect) == -1) + { + UnlockAGLCntx(); + return -1; + } + + [_nsglContext makeCurrentContext]; + glClear(GL_COLOR_BUFFER_BIT); + + // Loop through all channels starting highest zOrder ending with lowest. + for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); + rIt != _zOrderToChannel.rend(); + rIt++) + { + int channelId = rIt->second; + std::map::iterator it = _nsglChannels.find(channelId); + + VideoChannelNSOpenGL* aglChannel = it->second; + + aglChannel->RenderOffScreenBuffer(); + } + + DisplayBuffers(); + + UnlockAGLCntx(); + return 0; +} + +/* + * + * Help functions + * + * All help functions assumes external protections + * + */ + +int VideoRenderNSOpenGL::DisplayBuffers() +{ + + LockAGLCntx(); + + glFinish(); + [_nsglContext flushBuffer]; + + WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s glFinish and [_nsglContext flushBuffer]", __FUNCTION__); + + UnlockAGLCntx(); + return 0; +} + +int VideoRenderNSOpenGL::GetWindowRect(Rect& rect) +{ + + CriticalSectionScoped cs(&_nsglContextCritSec); + + if (_windowRef) + { + if(_fullScreen) + { + NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; + rect.bottom = 0; + rect.left = 0; + rect.right = mainDisplayRect.size.width; + rect.top = mainDisplayRect.size.height; + } + else + { + rect.top = [_windowRef frame].origin.y; + rect.left = [_windowRef frame].origin.x; + rect.bottom = [_windowRef frame].origin.y + [_windowRef frame].size.height; + rect.right = [_windowRef frame].origin.x + [_windowRef frame].size.width; + } + + return 0; + } + else + { + return -1; + } +} + +int32_t VideoRenderNSOpenGL::SetText(const uint8_t /*textId*/, + const uint8_t* /*text*/, + const int32_t /*textLength*/, + const uint32_t /*textColorRef*/, + const uint32_t /*backgroundColorRef*/, + const float /*left*/, + const float /*top*/, + const float /*right*/, + const float /*bottom*/) +{ + + return 0; + +} + +void VideoRenderNSOpenGL::LockAGLCntx() +{ + _nsglContextCritSec.Enter(); +} +void VideoRenderNSOpenGL::UnlockAGLCntx() +{ + _nsglContextCritSec.Leave(); +} + +/* + + bool VideoRenderNSOpenGL::SetFullScreen(bool fullscreen) + { + NSRect mainDisplayRect, viewRect; + + // Create a screen-sized window on the display you want to take over + // Note, mainDisplayRect has a non-zero origin if the key window is on a secondary display + mainDisplayRect = [[NSScreen mainScreen] frame]; + fullScreenWindow = [[NSWindow alloc] initWithContentRect:mainDisplayRect styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered defer:YES]; + + // Set the window level to be above the menu bar + [fullScreenWindow setLevel:NSMainMenuWindowLevel+1]; + + // Perform any other window configuration you desire + [fullScreenWindow setOpaque:YES]; + [fullScreenWindow setHidesOnDeactivate:YES]; + + // Create a view with a double-buffered OpenGL context and attach it to the window + // By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined + viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height); + fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect shareContext:[openGLView openGLContext]]; + [fullScreenWindow setContentView:fullScreenView]; + + // Show the window + [fullScreenWindow makeKeyAndOrderFront:self]; + + // Set the scene with the full-screen viewport and viewing transformation + [scene setViewportRect:viewRect]; + + // Assign the view's MainController to self + [fullScreenView setMainController:self]; + + if (!isAnimating) { + // Mark the view as needing drawing to initalize its contents + [fullScreenView setNeedsDisplay:YES]; + } + else { + // Start playing the animation + [fullScreenView startAnimation]; + } + + } + + + + */ + + +} // namespace webrtc + +#endif // COCOA_RENDERING diff --git a/webrtc/modules/video_render/test/testAPI/renderStartImage.bmp b/webrtc/modules/video_render/test/testAPI/renderStartImage.bmp new file mode 100644 index 0000000000..c443a58f6c Binary files /dev/null and b/webrtc/modules/video_render/test/testAPI/renderStartImage.bmp differ diff --git a/webrtc/modules/video_render/test/testAPI/testAPI.cc b/webrtc/modules/video_render/test/testAPI/testAPI.cc new file mode 100644 index 0000000000..cea2f6b56f --- /dev/null +++ b/webrtc/modules/video_render/test/testAPI/testAPI.cc @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/video_render/test/testAPI/testAPI.h" + +#include + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#include + +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + +#include +#include +#include +#include + +#endif + +#include "webrtc/common_types.h" +#include "webrtc/modules/include/module_common_types.h" +#include "webrtc/modules/utility/include/process_thread.h" +#include "webrtc/modules/video_render/video_render.h" +#include "webrtc/modules/video_render/video_render_defines.h" +#include "webrtc/system_wrappers/include/sleep.h" +#include "webrtc/system_wrappers/include/tick_util.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +void GetTestVideoFrame(VideoFrame* frame, uint8_t startColor); +int TestSingleStream(VideoRender* renderModule); +int TestFullscreenStream(VideoRender* &renderModule, + void* window, + const VideoRenderType videoRenderType); +int TestBitmapText(VideoRender* renderModule); +int TestMultipleStreams(VideoRender* renderModule); +int TestExternalRender(VideoRender* renderModule); + +#define TEST_FRAME_RATE 30 +#define TEST_TIME_SECOND 5 +#define TEST_FRAME_NUM (TEST_FRAME_RATE*TEST_TIME_SECOND) +#define TEST_STREAM0_START_COLOR 0 +#define TEST_STREAM1_START_COLOR 64 +#define TEST_STREAM2_START_COLOR 128 +#define TEST_STREAM3_START_COLOR 192 + +#if defined(WEBRTC_LINUX) + +#define GET_TIME_IN_MS timeGetTime() + +unsigned long timeGetTime() +{ + struct timeval tv; + struct timezone tz; + unsigned long val; + + gettimeofday(&tv, &tz); + val= tv.tv_sec*1000+ tv.tv_usec/1000; + return(val); +} + +#elif defined(WEBRTC_MAC) + +#include + +#define GET_TIME_IN_MS timeGetTime() + +unsigned long timeGetTime() +{ + return 0; +} + +#else + +#define GET_TIME_IN_MS ::timeGetTime() + +#endif + +using namespace std; + +#if defined(_WIN32) +LRESULT CALLBACK WebRtcWinProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) +{ + switch(uMsg) + { + case WM_DESTROY: + break; + case WM_COMMAND: + break; + } + return DefWindowProc(hWnd,uMsg,wParam,lParam); +} + +int WebRtcCreateWindow(HWND &hwndMain,int winNum, int width, int height) +{ + HINSTANCE hinst = GetModuleHandle(0); + WNDCLASSEX wcx; + wcx.hInstance = hinst; + wcx.lpszClassName = TEXT("VideoRenderTest"); + wcx.lpfnWndProc = (WNDPROC)WebRtcWinProc; + wcx.style = CS_DBLCLKS; + wcx.hIcon = LoadIcon (NULL, IDI_APPLICATION); + wcx.hIconSm = LoadIcon (NULL, IDI_APPLICATION); + wcx.hCursor = LoadCursor (NULL, IDC_ARROW); + wcx.lpszMenuName = NULL; + wcx.cbSize = sizeof (WNDCLASSEX); + wcx.cbClsExtra = 0; + wcx.cbWndExtra = 0; + wcx.hbrBackground = GetSysColorBrush(COLOR_3DFACE); + + // Register our window class with the operating system. + // If there is an error, exit program. + if ( !RegisterClassEx (&wcx) ) + { + MessageBox( 0, TEXT("Failed to register window class!"),TEXT("Error!"), MB_OK|MB_ICONERROR ); + return 0; + } + + // Create the main window. + hwndMain = CreateWindowEx( + 0, // no extended styles + TEXT("VideoRenderTest"), // class name + TEXT("VideoRenderTest Window"), // window name + WS_OVERLAPPED |WS_THICKFRAME, // overlapped window + 800, // horizontal position + 0, // vertical position + width, // width + height, // height + (HWND) NULL, // no parent or owner window + (HMENU) NULL, // class menu used + hinst, // instance handle + NULL); // no window creation data + + if (!hwndMain) + return -1; + + // Show the window using the flag specified by the program + // that started the application, and send the application + // a WM_PAINT message. + + ShowWindow(hwndMain, SW_SHOWDEFAULT); + UpdateWindow(hwndMain); + return 0; +} + +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) + +int WebRtcCreateWindow(Window *outWindow, Display **outDisplay, int winNum, int width, int height) // unsigned char* title, int titleLength) + +{ + int screen, xpos = 10, ypos = 10; + XEvent evnt; + XSetWindowAttributes xswa; // window attribute struct + XVisualInfo vinfo; // screen visual info struct + unsigned long mask; // attribute mask + + // get connection handle to xserver + Display* _display = XOpenDisplay( NULL ); + + // get screen number + screen = DefaultScreen(_display); + + // put desired visual info for the screen in vinfo + if( XMatchVisualInfo(_display, screen, 24, TrueColor, &vinfo) != 0 ) + { + //printf( "Screen visual info match!\n" ); + } + + // set window attributes + xswa.colormap = XCreateColormap(_display, DefaultRootWindow(_display), vinfo.visual, AllocNone); + xswa.event_mask = StructureNotifyMask | ExposureMask; + xswa.background_pixel = 0; + xswa.border_pixel = 0; + + // value mask for attributes + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + switch( winNum ) + { + case 0: + xpos = 200; + ypos = 200; + break; + case 1: + xpos = 300; + ypos = 200; + break; + default: + break; + } + + // create a subwindow for parent (defroot) + Window _window = XCreateWindow(_display, DefaultRootWindow(_display), + xpos, ypos, + width, + height, + 0, vinfo.depth, + InputOutput, + vinfo.visual, + mask, &xswa); + + // Set window name + if( winNum == 0 ) + { + XStoreName(_display, _window, "VE MM Local Window"); + XSetIconName(_display, _window, "VE MM Local Window"); + } + else if( winNum == 1 ) + { + XStoreName(_display, _window, "VE MM Remote Window"); + XSetIconName(_display, _window, "VE MM Remote Window"); + } + + // make x report events for mask + XSelectInput(_display, _window, StructureNotifyMask); + + // map the window to the display + XMapWindow(_display, _window); + + // wait for map event + do + { + XNextEvent(_display, &evnt); + } + while (evnt.type != MapNotify || evnt.xmap.event != _window); + + *outWindow = _window; + *outDisplay = _display; + + return 0; +} +#endif // WEBRTC_LINUX + +// Note: Mac code is in testApi_mac.mm. + +class MyRenderCallback: public VideoRenderCallback +{ +public: + MyRenderCallback() : + _cnt(0) + { + } + ; + ~MyRenderCallback() + { + } + ; + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame) { + _cnt++; + if (_cnt % 100 == 0) + { + printf("Render callback %d \n",_cnt); + } + return 0; + } + int32_t _cnt; +}; + +void GetTestVideoFrame(VideoFrame* frame, uint8_t startColor) { + // changing color + static uint8_t color = startColor; + + memset(frame->buffer(kYPlane), color, frame->allocated_size(kYPlane)); + memset(frame->buffer(kUPlane), color, frame->allocated_size(kUPlane)); + memset(frame->buffer(kVPlane), color, frame->allocated_size(kVPlane)); + + ++color; +} + +int TestSingleStream(VideoRender* renderModule) { + int error = 0; + // Add settings for a stream to render + printf("Add stream 0 to entire window\n"); + const int streamId0 = 0; + VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); + assert(renderCallback0 != NULL); + + printf("Start render\n"); + error = renderModule->StartRender(streamId0); + if (error != 0) { + // TODO(phoglund): This test will not work if compiled in release mode. + // This rather silly construct here is to avoid compilation errors when + // compiling in release. Release => no asserts => unused 'error' variable. + assert(false); + } + + // Loop through an I420 file and render each frame + const int width = 352; + const int half_width = (width + 1) / 2; + const int height = 288; + + VideoFrame videoFrame0; + videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); + + const uint32_t renderDelayMs = 500; + + for (int i=0; iRenderFrame(streamId0, videoFrame0); + SleepMs(1000/TEST_FRAME_RATE); + } + + + // Shut down + printf("Closing...\n"); + error = renderModule->StopRender(streamId0); + assert(error == 0); + + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); + + return 0; +} + +int TestFullscreenStream(VideoRender* &renderModule, + void* window, + const VideoRenderType videoRenderType) { + VideoRender::DestroyVideoRender(renderModule); + renderModule = VideoRender::CreateVideoRender(12345, window, true, videoRenderType); + + TestSingleStream(renderModule); + + VideoRender::DestroyVideoRender(renderModule); + renderModule = VideoRender::CreateVideoRender(12345, window, false, videoRenderType); + + return 0; +} + +int TestBitmapText(VideoRender* renderModule) { +#if defined(WIN32) + + int error = 0; + // Add settings for a stream to render + printf("Add stream 0 to entire window\n"); + const int streamId0 = 0; + VideoRenderCallback* renderCallback0 = renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 1.0f, 1.0f); + assert(renderCallback0 != NULL); + + printf("Adding Bitmap\n"); + DDCOLORKEY ColorKey; // black + ColorKey.dwColorSpaceHighValue = RGB(0, 0, 0); + ColorKey.dwColorSpaceLowValue = RGB(0, 0, 0); + HBITMAP hbm = (HBITMAP)LoadImage(NULL, + (LPCTSTR)_T("renderStartImage.bmp"), + IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + renderModule->SetBitmap(hbm, 0, &ColorKey, 0.0f, 0.0f, 0.3f, + 0.3f); + + printf("Adding Text\n"); + renderModule->SetText(1, (uint8_t*) "WebRtc Render Demo App", 20, + RGB(255, 0, 0), RGB(0, 0, 0), 0.25f, 0.1f, 1.0f, + 1.0f); + + printf("Start render\n"); + error = renderModule->StartRender(streamId0); + assert(error == 0); + + // Loop through an I420 file and render each frame + const int width = 352; + const int half_width = (width + 1) / 2; + const int height = 288; + + VideoFrame videoFrame0; + videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); + + const uint32_t renderDelayMs = 500; + + for (int i=0; iRenderFrame(streamId0, videoFrame0); + SleepMs(1000/TEST_FRAME_RATE); + } + // Sleep and let all frames be rendered before closing + SleepMs(renderDelayMs*2); + + + // Shut down + printf("Closing...\n"); + ColorKey.dwColorSpaceHighValue = RGB(0,0,0); + ColorKey.dwColorSpaceLowValue = RGB(0,0,0); + renderModule->SetBitmap(NULL, 0, &ColorKey, 0.0f, 0.0f, 0.0f, 0.0f); + renderModule->SetText(1, NULL, 20, RGB(255,255,255), + RGB(0,0,0), 0.0f, 0.0f, 0.0f, 0.0f); + + error = renderModule->StopRender(streamId0); + assert(error == 0); + + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); +#endif + + return 0; +} + +int TestMultipleStreams(VideoRender* renderModule) { + int error = 0; + + // Add settings for a stream to render + printf("Add stream 0\n"); + const int streamId0 = 0; + VideoRenderCallback* renderCallback0 = + renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, 0.45f, 0.45f); + assert(renderCallback0 != NULL); + printf("Add stream 1\n"); + const int streamId1 = 1; + VideoRenderCallback* renderCallback1 = + renderModule->AddIncomingRenderStream(streamId1, 0, 0.55f, 0.0f, 1.0f, 0.45f); + assert(renderCallback1 != NULL); + printf("Add stream 2\n"); + const int streamId2 = 2; + VideoRenderCallback* renderCallback2 = + renderModule->AddIncomingRenderStream(streamId2, 0, 0.0f, 0.55f, 0.45f, 1.0f); + assert(renderCallback2 != NULL); + printf("Add stream 3\n"); + const int streamId3 = 3; + VideoRenderCallback* renderCallback3 = + renderModule->AddIncomingRenderStream(streamId3, 0, 0.55f, 0.55f, 1.0f, 1.0f); + assert(renderCallback3 != NULL); + error = renderModule->StartRender(streamId0); + if (error != 0) { + // TODO(phoglund): This test will not work if compiled in release mode. + // This rather silly construct here is to avoid compilation errors when + // compiling in release. Release => no asserts => unused 'error' variable. + assert(false); + } + error = renderModule->StartRender(streamId1); + assert(error == 0); + error = renderModule->StartRender(streamId2); + assert(error == 0); + error = renderModule->StartRender(streamId3); + assert(error == 0); + + // Loop through an I420 file and render each frame + const int width = 352; + const int half_width = (width + 1) / 2; + const int height = 288; + + VideoFrame videoFrame0; + videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); + VideoFrame videoFrame1; + videoFrame1.CreateEmptyFrame(width, height, width, half_width, half_width); + VideoFrame videoFrame2; + videoFrame2.CreateEmptyFrame(width, height, width, half_width, half_width); + VideoFrame videoFrame3; + videoFrame3.CreateEmptyFrame(width, height, width, half_width, half_width); + + const uint32_t renderDelayMs = 500; + + // Render frames with the specified delay. + for (int i=0; iRenderFrame(streamId0, videoFrame0); + + GetTestVideoFrame(&videoFrame1, TEST_STREAM1_START_COLOR); + videoFrame1.set_render_time_ms(TickTime::MillisecondTimestamp() + + renderDelayMs); + renderCallback1->RenderFrame(streamId1, videoFrame1); + + GetTestVideoFrame(&videoFrame2, TEST_STREAM2_START_COLOR); + videoFrame2.set_render_time_ms(TickTime::MillisecondTimestamp() + + renderDelayMs); + renderCallback2->RenderFrame(streamId2, videoFrame2); + + GetTestVideoFrame(&videoFrame3, TEST_STREAM3_START_COLOR); + videoFrame3.set_render_time_ms(TickTime::MillisecondTimestamp() + + renderDelayMs); + renderCallback3->RenderFrame(streamId3, videoFrame3); + + SleepMs(1000/TEST_FRAME_RATE); + } + + // Shut down + printf("Closing...\n"); + error = renderModule->StopRender(streamId0); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); + error = renderModule->StopRender(streamId1); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId1); + assert(error == 0); + error = renderModule->StopRender(streamId2); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId2); + assert(error == 0); + error = renderModule->StopRender(streamId3); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId3); + assert(error == 0); + + return 0; +} + +int TestExternalRender(VideoRender* renderModule) { + int error = 0; + MyRenderCallback *externalRender = new MyRenderCallback(); + + const int streamId0 = 0; + VideoRenderCallback* renderCallback0 = + renderModule->AddIncomingRenderStream(streamId0, 0, 0.0f, 0.0f, + 1.0f, 1.0f); + assert(renderCallback0 != NULL); + error = renderModule->AddExternalRenderCallback(streamId0, externalRender); + if (error != 0) { + // TODO(phoglund): This test will not work if compiled in release mode. + // This rather silly construct here is to avoid compilation errors when + // compiling in release. Release => no asserts => unused 'error' variable. + assert(false); + } + + error = renderModule->StartRender(streamId0); + assert(error == 0); + + const int width = 352; + const int half_width = (width + 1) / 2; + const int height = 288; + VideoFrame videoFrame0; + videoFrame0.CreateEmptyFrame(width, height, width, half_width, half_width); + + const uint32_t renderDelayMs = 500; + int frameCount = TEST_FRAME_NUM; + for (int i=0; iRenderFrame(streamId0, videoFrame0); + SleepMs(33); + } + + // Sleep and let all frames be rendered before closing + SleepMs(2*renderDelayMs); + + // Shut down + printf("Closing...\n"); + error = renderModule->StopRender(streamId0); + assert(error == 0); + error = renderModule->DeleteIncomingRenderStream(streamId0); + assert(error == 0); + assert(frameCount == externalRender->_cnt); + + delete externalRender; + externalRender = NULL; + + return 0; +} + +void RunVideoRenderTests(void* window, VideoRenderType windowType) { + int myId = 12345; + + // Create the render module + printf("Create render module\n"); + VideoRender* renderModule = NULL; + renderModule = VideoRender::CreateVideoRender(myId, + window, + false, + windowType); + assert(renderModule != NULL); + + // ##### Test single stream rendering #### + printf("#### TestSingleStream ####\n"); + if (TestSingleStream(renderModule) != 0) { + printf ("TestSingleStream failed\n"); + } + + // ##### Test fullscreen rendering #### + printf("#### TestFullscreenStream ####\n"); + if (TestFullscreenStream(renderModule, window, windowType) != 0) { + printf ("TestFullscreenStream failed\n"); + } + + // ##### Test bitmap and text #### + printf("#### TestBitmapText ####\n"); + if (TestBitmapText(renderModule) != 0) { + printf ("TestBitmapText failed\n"); + } + + // ##### Test multiple streams #### + printf("#### TestMultipleStreams ####\n"); + if (TestMultipleStreams(renderModule) != 0) { + printf ("TestMultipleStreams failed\n"); + } + + // ##### Test multiple streams #### + printf("#### TestExternalRender ####\n"); + if (TestExternalRender(renderModule) != 0) { + printf ("TestExternalRender failed\n"); + } + + delete renderModule; + renderModule = NULL; + + printf("VideoRender unit tests passed.\n"); +} + +// Note: The Mac main is implemented in testApi_mac.mm. +#if defined(_WIN32) +int _tmain(int argc, _TCHAR* argv[]) +#elif defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +int main(int argc, char* argv[]) +#endif +#if !defined(WEBRTC_MAC) && !defined(WEBRTC_ANDROID) +{ + // Create a window for testing. + void* window = NULL; +#if defined (_WIN32) + HWND testHwnd; + WebRtcCreateWindow(testHwnd, 0, 352, 288); + window = (void*)testHwnd; + VideoRenderType windowType = kRenderWindows; +#elif defined(WEBRTC_LINUX) + Window testWindow; + Display* display; + WebRtcCreateWindow(&testWindow, &display, 0, 352, 288); + VideoRenderType windowType = kRenderX11; + window = (void*)testWindow; +#endif // WEBRTC_LINUX + + RunVideoRenderTests(window, windowType); + return 0; +} +#endif // !WEBRTC_MAC diff --git a/webrtc/modules/video_render/test/testAPI/testAPI.h b/webrtc/modules/video_render/test/testAPI/testAPI.h new file mode 100644 index 0000000000..0655a5b434 --- /dev/null +++ b/webrtc/modules/video_render/test/testAPI/testAPI.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_TEST_TESTAPI_TESTAPI_H +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_TEST_TESTAPI_TESTAPI_H + +#include "webrtc/modules/video_render/video_render_defines.h" + +void RunVideoRenderTests(void* window, webrtc::VideoRenderType windowType); + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_TEST_TESTAPI_TESTAPI_H diff --git a/webrtc/modules/video_render/test/testAPI/testAPI_android.cc b/webrtc/modules/video_render/test/testAPI/testAPI_android.cc new file mode 100644 index 0000000000..c62a62f39a --- /dev/null +++ b/webrtc/modules/video_render/test/testAPI/testAPI_android.cc @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +int main(int argc, char* argv[]) { + // TODO(leozwang): Video render test app is not ready on android, + // make it dummy test now, will add android specific tests + return 0; +} diff --git a/webrtc/modules/video_render/test/testAPI/testAPI_mac.mm b/webrtc/modules/video_render/test/testAPI/testAPI_mac.mm new file mode 100644 index 0000000000..dfee4c7298 --- /dev/null +++ b/webrtc/modules/video_render/test/testAPI/testAPI_mac.mm @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "testAPI.h" + +#include + +#import +#import +#import +#import +#include + +#import "webrtc/modules/video_render/mac/cocoa_render_view.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/include/module_common_types.h" +#include "webrtc/modules/utility/include/process_thread.h" +#include "webrtc/modules/video_render/video_render.h" +#include "webrtc/modules/video_render/video_render_defines.h" +#include "webrtc/system_wrappers/include/tick_util.h" +#include "webrtc/system_wrappers/include/trace.h" + +using namespace webrtc; + +int WebRtcCreateWindow(CocoaRenderView*& cocoaRenderer, int winNum, int width, int height) +{ + // In Cocoa, rendering is not done directly to a window like in Windows and Linux. + // It is rendererd to a Subclass of NSOpenGLView + + // create cocoa container window + NSRect outWindowFrame = NSMakeRect(200, 800, width + 20, height + 20); + NSWindow* outWindow = [[NSWindow alloc] initWithContentRect:outWindowFrame + styleMask:NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + [outWindow orderOut:nil]; + [outWindow setTitle:@"Cocoa Renderer"]; + [outWindow setBackgroundColor:[NSColor blueColor]]; + + // create renderer and attach to window + NSRect cocoaRendererFrame = NSMakeRect(10, 10, width, height); + cocoaRenderer = [[CocoaRenderView alloc] initWithFrame:cocoaRendererFrame]; + [[outWindow contentView] addSubview:(NSView*)cocoaRenderer]; + + [outWindow makeKeyAndOrderFront:NSApp]; + + return 0; +} + +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + + CocoaRenderView* testWindow; + WebRtcCreateWindow(testWindow, 0, 352, 288); + VideoRenderType windowType = kRenderCocoa; + void* window = (void*)testWindow; + + RunVideoRenderTests(window, windowType); + + [pool release]; +} diff --git a/webrtc/modules/video_render/video_render.gypi b/webrtc/modules/video_render/video_render.gypi new file mode 100644 index 0000000000..e8cc03a4b0 --- /dev/null +++ b/webrtc/modules/video_render/video_render.gypi @@ -0,0 +1,218 @@ +# Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +{ + 'targets': [ + { + # Note this library is missing an implementation for the video render. + # For that targets must link with 'video_render' or + # 'video_render_module_internal_impl' if they want to compile and use + # the internal render as the default renderer. + 'target_name': 'video_render_module', + 'type': 'static_library', + 'dependencies': [ + 'webrtc_utility', + '<(webrtc_root)/common.gyp:webrtc_common', + '<(webrtc_root)/common_video/common_video.gyp:common_video', + '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers', + ], + 'sources': [ + 'external/video_render_external_impl.cc', + 'external/video_render_external_impl.h', + 'i_video_render.h', + 'video_render.h', + 'video_render_defines.h', + 'video_render_impl.h', + ], + }, + { + # Default video_render_module implementation that only supports external + # renders. + 'target_name': 'video_render', + 'type': 'static_library', + 'dependencies': [ + 'video_render_module', + ], + 'sources': [ + 'video_render_impl.cc', + ], + }, + ], # targets + + 'conditions': [ + ['build_with_chromium==0', { + 'targets': [ + { + # video_render_module implementation that supports the internal + # video_render implementation. + 'target_name': 'video_render_module_internal_impl', + 'type': 'static_library', + 'dependencies': [ + '<(webrtc_root)/common.gyp:webrtc_common', + 'video_render_module', + ], + 'sources': [ + 'video_render_internal_impl.cc', + ], + # TODO(andrew): with the proper suffix, these files will be excluded + # automatically. + 'conditions': [ + ['OS=="android"', { + 'sources': [ + 'android/video_render_android_impl.h', + 'android/video_render_android_native_opengl2.h', + 'android/video_render_android_surface_view.h', + 'android/video_render_opengles20.h', + 'android/video_render_android_impl.cc', + 'android/video_render_android_native_opengl2.cc', + 'android/video_render_android_surface_view.cc', + 'android/video_render_opengles20.cc', + ], + 'link_settings': { + 'libraries': [ + '-lGLESv2', + ], + }, + }], + ['OS=="ios"', { + 'sources': [ + # iOS + 'ios/open_gles20.h', + 'ios/open_gles20.mm', + 'ios/video_render_ios_channel.h', + 'ios/video_render_ios_channel.mm', + 'ios/video_render_ios_gles20.h', + 'ios/video_render_ios_gles20.mm', + 'ios/video_render_ios_impl.h', + 'ios/video_render_ios_impl.mm', + 'ios/video_render_ios_view.h', + 'ios/video_render_ios_view.mm', + ], + 'xcode_settings': { + 'CLANG_ENABLE_OBJC_ARC': 'YES', + }, + 'all_dependent_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework OpenGLES', + '-framework QuartzCore', + '-framework UIKit', + ], + }, + }, + }], + ['OS=="linux"', { + 'sources': [ + 'linux/video_render_linux_impl.h', + 'linux/video_x11_channel.h', + 'linux/video_x11_render.h', + 'linux/video_render_linux_impl.cc', + 'linux/video_x11_channel.cc', + 'linux/video_x11_render.cc', + ], + 'link_settings': { + 'libraries': [ + '-lXext', + ], + }, + }], + ['OS=="mac"', { + 'sources': [ + 'mac/cocoa_full_screen_window.h', + 'mac/cocoa_render_view.h', + 'mac/video_render_agl.h', + 'mac/video_render_mac_carbon_impl.h', + 'mac/video_render_mac_cocoa_impl.h', + 'mac/video_render_nsopengl.h', + 'mac/video_render_nsopengl.mm', + 'mac/video_render_mac_cocoa_impl.mm', + 'mac/video_render_agl.cc', + 'mac/video_render_mac_carbon_impl.cc', + 'mac/cocoa_render_view.mm', + 'mac/cocoa_full_screen_window.mm', + ], + }], + ['OS=="win"', { + 'sources': [ + 'windows/i_video_render_win.h', + 'windows/video_render_direct3d9.h', + 'windows/video_render_windows_impl.h', + 'windows/video_render_direct3d9.cc', + 'windows/video_render_windows_impl.cc', + ], + 'include_dirs': [ + '<(directx_sdk_path)/Include', + ], + }], + ['OS=="win" and clang==1', { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'AdditionalOptions': [ + # Disable warnings failing when compiling with Clang on Windows. + # https://bugs.chromium.org/p/webrtc/issues/detail?id=5366 + '-Wno-comment', + '-Wno-reorder', + '-Wno-unused-value', + '-Wno-unused-private-field', + ], + }, + }, + }], + ] # conditions + }, + ], + }], # build_with_chromium==0 + ['include_tests==1 and OS!="ios"', { + 'targets': [ + { + # Does not compile on iOS: webrtc:4755. + 'target_name': 'video_render_tests', + 'type': 'executable', + 'dependencies': [ + 'video_render_module_internal_impl', + 'webrtc_utility', + '<(webrtc_root)/common.gyp:webrtc_common', + '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers', + '<(webrtc_root)/common_video/common_video.gyp:common_video', + ], + 'sources': [ + 'test/testAPI/testAPI.cc', + 'test/testAPI/testAPI.h', + 'test/testAPI/testAPI_android.cc', + 'test/testAPI/testAPI_mac.mm', + ], + 'conditions': [ + ['OS=="mac" or OS=="linux"', { + 'cflags': [ + '-Wno-write-strings', + ], + 'ldflags': [ + '-lpthread -lm', + ], + }], + ['OS=="linux"', { + 'link_settings': { + 'libraries': [ + '-lX11', + ], + }, + }], + ['OS=="mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Foundation -framework AppKit -framework Cocoa -framework OpenGL', + ], + }, + }], + ] # conditions + }, # video_render_module_test + ], # targets + }], # include_tests==1 and OS!=ios + ], # conditions +} + diff --git a/webrtc/modules/video_render/video_render.h b/webrtc/modules/video_render/video_render.h new file mode 100644 index 0000000000..a24acb9e7a --- /dev/null +++ b/webrtc/modules/video_render/video_render.h @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_H_ + +/* + * video_render.h + * + * This header file together with module.h and module_common_types.h + * contains all of the APIs that are needed for using the video render + * module class. + * + */ + +#include "webrtc/modules/include/module.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +namespace webrtc { + +// Class definitions +class VideoRender: public Module +{ +public: + /* + * Create a video render module object + * + * id - unique identifier of this video render module object + * window - pointer to the window to render to + * fullscreen - true if this is a fullscreen renderer + * videoRenderType - type of renderer to create + */ + static VideoRender + * CreateVideoRender( + const int32_t id, + void* window, + const bool fullscreen, + const VideoRenderType videoRenderType = + kRenderDefault); + + /* + * Destroy a video render module object + * + * module - object to destroy + */ + static void DestroyVideoRender(VideoRender* module); + + int64_t TimeUntilNextProcess() override = 0; + void Process() override = 0; + + /************************************************************************** + * + * Window functions + * + ***************************************************************************/ + + /* + * Get window for this renderer + */ + virtual void* Window() = 0; + + /* + * Change render window + * + * window - the new render window, assuming same type as originally created. + */ + virtual int32_t ChangeWindow(void* window) = 0; + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + /* + * Add incoming render stream + * + * streamID - id of the stream to add + * zOrder - relative render order for the streams, 0 = on top + * left - position of the stream in the window, [0.0f, 1.0f] + * top - position of the stream in the window, [0.0f, 1.0f] + * right - position of the stream in the window, [0.0f, 1.0f] + * bottom - position of the stream in the window, [0.0f, 1.0f] + * + * Return - callback class to use for delivering new frames to render. + */ + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom) = 0; + /* + * Delete incoming render stream + * + * streamID - id of the stream to add + */ + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId) = 0; + + /* + * Add incoming render callback, used for external rendering + * + * streamID - id of the stream the callback is used for + * renderObject - the VideoRenderCallback to use for this stream, NULL to remove + * + * Return - callback class to use for delivering new frames to render. + */ + virtual int32_t + AddExternalRenderCallback(const uint32_t streamId, + VideoRenderCallback* renderObject) = 0; + + /* + * Get the porperties for an incoming render stream + * + * streamID - [in] id of the stream to get properties for + * zOrder - [out] relative render order for the streams, 0 = on top + * left - [out] position of the stream in the window, [0.0f, 1.0f] + * top - [out] position of the stream in the window, [0.0f, 1.0f] + * right - [out] position of the stream in the window, [0.0f, 1.0f] + * bottom - [out] position of the stream in the window, [0.0f, 1.0f] + */ + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const = 0; + /* + * The incoming frame rate to the module, not the rate rendered in the window. + */ + virtual uint32_t + GetIncomingFrameRate(const uint32_t streamId) = 0; + + /* + * Returns the number of incoming streams added to this render module + */ + virtual uint32_t GetNumIncomingRenderStreams() const = 0; + + /* + * Returns true if this render module has the streamId added, false otherwise. + */ + virtual bool + HasIncomingRenderStream(const uint32_t streamId) const = 0; + + /* + * Registers a callback to get raw images in the same time as sent + * to the renderer. To be used for external rendering. + */ + virtual int32_t + RegisterRawFrameCallback(const uint32_t streamId, + VideoRenderCallback* callbackObj) = 0; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + /* + * Starts rendering the specified stream + */ + virtual int32_t StartRender(const uint32_t streamId) = 0; + + /* + * Stops the renderer + */ + virtual int32_t StopRender(const uint32_t streamId) = 0; + + /* + * Resets the renderer + * No streams are removed. The state should be as after AddStream was called. + */ + virtual int32_t ResetRender() = 0; + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + /* + * Returns the preferred render video type + */ + virtual RawVideoType PreferredVideoType() const = 0; + + /* + * Returns true if the renderer is in fullscreen mode, otherwise false. + */ + virtual bool IsFullScreen() = 0; + + /* + * Gets screen resolution in pixels + */ + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const = 0; + + /* + * Get the actual render rate for this stream. I.e rendered frame rate, + * not frames delivered to the renderer. + */ + virtual uint32_t RenderFrameRate(const uint32_t streamId) = 0; + + /* + * Set cropping of incoming stream + */ + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) = 0; + + /* + * re-configure renderer + */ + + // Set the expected time needed by the graphics card or external renderer, + // i.e. frames will be released for rendering |delay_ms| before set render + // time in the video frame. + virtual int32_t SetExpectedRenderDelay(uint32_t stream_id, + int32_t delay_ms) = 0; + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t SetTransparentBackground(const bool enable) = 0; + + virtual int32_t FullScreenRender(void* window, const bool enable) = 0; + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom) = 0; + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, const float bottom) = 0; +}; +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_H_ diff --git a/webrtc/modules/video_render/video_render_defines.h b/webrtc/modules/video_render/video_render_defines.h new file mode 100644 index 0000000000..999707cb6e --- /dev/null +++ b/webrtc/modules/video_render/video_render_defines.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_DEFINES_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_DEFINES_H_ + +#include "webrtc/common_types.h" +#include "webrtc/common_video/include/incoming_video_stream.h" +#include "webrtc/modules/include/module_common_types.h" + +namespace webrtc +{ +// Defines +#ifndef NULL +#define NULL 0 +#endif + +// Enums +enum VideoRenderType +{ + kRenderExternal = 0, // External + kRenderWindows = 1, // Windows + kRenderCocoa = 2, // Mac + kRenderCarbon = 3, + kRenderiOS = 4, // iPhone + kRenderAndroid = 5, // Android + kRenderX11 = 6, // Linux + kRenderDefault +}; + +// Runtime errors +enum VideoRenderError +{ + kRenderShutDown = 0, + kRenderPerformanceAlarm = 1 +}; + +// Feedback class to be implemented by module user +class VideoRenderFeedback +{ +public: + virtual void OnRenderError(const int32_t streamId, + const VideoRenderError error) = 0; + +protected: + virtual ~VideoRenderFeedback() + { + } +}; + +// Mobile enums +enum StretchMode +{ + kStretchToInsideEdge = 1, + kStretchToOutsideEdge = 2, + kStretchMatchWidth = 3, + kStretchMatchHeight = 4, + kStretchNone = 5 +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_DEFINES_H_ diff --git a/webrtc/modules/video_render/video_render_impl.cc b/webrtc/modules/video_render/video_render_impl.cc new file mode 100644 index 0000000000..f3d12dce8f --- /dev/null +++ b/webrtc/modules/video_render/video_render_impl.cc @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/common_video/include/incoming_video_stream.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/video_render/external/video_render_external_impl.h" +#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/modules/video_render/video_render_defines.h" +#include "webrtc/modules/video_render/video_render_impl.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +VideoRender* +VideoRender::CreateVideoRender(const int32_t id, + void* window, + const bool fullscreen, + const VideoRenderType videoRenderType/*=kRenderDefault*/) +{ + VideoRenderType resultVideoRenderType = videoRenderType; + if (videoRenderType == kRenderDefault) + { + resultVideoRenderType = kRenderExternal; + } + return new ModuleVideoRenderImpl(id, resultVideoRenderType, window, + fullscreen); +} + +void VideoRender::DestroyVideoRender( + VideoRender* module) +{ + if (module) + { + delete module; + } +} + +ModuleVideoRenderImpl::ModuleVideoRenderImpl( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + _id(id), _moduleCrit(*CriticalSectionWrapper::CreateCriticalSection()), + _ptrWindow(window), _fullScreen(fullscreen), _ptrRenderer(NULL) +{ + + // Create platform specific renderer + switch (videoRenderType) + { + case kRenderExternal: + { + VideoRenderExternalImpl* ptrRenderer(NULL); + ptrRenderer = new VideoRenderExternalImpl(_id, videoRenderType, + window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + break; + default: + // Error... + break; + } + if (_ptrRenderer) + { + if (_ptrRenderer->Init() == -1) + { + } + } +} + +ModuleVideoRenderImpl::~ModuleVideoRenderImpl() +{ + delete &_moduleCrit; + + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + delete it->second; + } + + // Delete platform specific renderer + if (_ptrRenderer) + { + VideoRenderType videoRenderType = _ptrRenderer->RenderType(); + + switch (videoRenderType) + { + case kRenderExternal: + { + VideoRenderExternalImpl + * ptrRenderer = + reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; + + default: + // Error... + break; + } + } +} + +int64_t ModuleVideoRenderImpl::TimeUntilNextProcess() +{ + // Not used + return 50; +} +void ModuleVideoRenderImpl::Process() {} + +void* +ModuleVideoRenderImpl::Window() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _ptrWindow; +} + +int32_t ModuleVideoRenderImpl::ChangeWindow(void* window) +{ + return -1; +} + +int32_t ModuleVideoRenderImpl::Id() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _id; +} + +uint32_t ModuleVideoRenderImpl::GetIncomingFrameRate(const uint32_t streamId) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator it = _streamRenderMap.find(streamId); + + if (it == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: stream doesn't exist", + __FUNCTION__); + return 0; + } + assert(it->second != NULL); + return it->second->IncomingRate(); +} + +VideoRenderCallback* +ModuleVideoRenderImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return NULL; + } + + if (_streamRenderMap.find(streamId) != _streamRenderMap.end()) { + // The stream already exists... + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream already exists", __FUNCTION__); + return NULL; + } + + VideoRenderCallback* ptrRenderCallback = + _ptrRenderer->AddIncomingRenderStream(streamId, zOrder, left, top, + right, bottom); + if (ptrRenderCallback == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Can't create incoming stream in renderer", + __FUNCTION__); + return NULL; + } + + // Create platform independant code + IncomingVideoStream* ptrIncomingStream = + new IncomingVideoStream(streamId, false); + ptrIncomingStream->SetRenderCallback(ptrRenderCallback); + VideoRenderCallback* moduleCallback = ptrIncomingStream->ModuleCallback(); + + // Store the stream + _streamRenderMap[streamId] = ptrIncomingStream; + + return moduleCallback; +} + +int32_t ModuleVideoRenderImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + delete item->second; + + _ptrRenderer->DeleteIncomingRenderStream(streamId); + + _streamRenderMap.erase(item); + + return 0; +} + +int32_t ModuleVideoRenderImpl::AddExternalRenderCallback( + const uint32_t streamId, + VideoRenderCallback* renderObject) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + if (item->second == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get stream", __FUNCTION__); + return -1; + } + item->second->SetExternalCallback(renderObject); + return 0; +} + +int32_t ModuleVideoRenderImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + return _ptrRenderer->GetIncomingRenderStreamProperties(streamId, zOrder, + left, top, right, + bottom); +} + +uint32_t ModuleVideoRenderImpl::GetNumIncomingRenderStreams() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + return static_cast(_streamRenderMap.size()); +} + +bool ModuleVideoRenderImpl::HasIncomingRenderStream( + const uint32_t streamId) const { + CriticalSectionScoped cs(&_moduleCrit); + + return _streamRenderMap.find(streamId) != _streamRenderMap.end(); +} + +int32_t ModuleVideoRenderImpl::RegisterRawFrameCallback( + const uint32_t streamId, + VideoRenderCallback* callbackObj) { + return -1; +} + +int32_t ModuleVideoRenderImpl::StartRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + // Start the stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Start() == -1) + { + return -1; + } + + // Start the HW renderer + if (_ptrRenderer->StartRender() == -1) + { + return -1; + } + return 0; +} + +int32_t ModuleVideoRenderImpl::StopRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%d): No renderer", __FUNCTION__, streamId); + return -1; + } + + // Stop the incoming stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Stop() == -1) + { + return -1; + } + + return 0; +} + +int32_t ModuleVideoRenderImpl::ResetRender() +{ + CriticalSectionScoped cs(&_moduleCrit); + + int32_t ret = 0; + // Loop through all incoming streams and reset them + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + if (it->second->Reset() == -1) + ret = -1; + } + return ret; +} + +RawVideoType ModuleVideoRenderImpl::PreferredVideoType() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (_ptrRenderer == NULL) + { + return kVideoI420; + } + + return _ptrRenderer->PerferedVideoType(); +} + +bool ModuleVideoRenderImpl::IsFullScreen() +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->FullScreen(); +} + +int32_t ModuleVideoRenderImpl::GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->GetScreenResolution(screenWidth, screenHeight); +} + +uint32_t ModuleVideoRenderImpl::RenderFrameRate( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->RenderFrameRate(streamId); +} + +int32_t ModuleVideoRenderImpl::SetStreamCropping( + const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetStreamCropping(streamId, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetTransparentBackground(const bool enable) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetTransparentBackground(enable); +} + +int32_t ModuleVideoRenderImpl::FullScreenRender(void* window, const bool enable) +{ + return -1; +} + +int32_t ModuleVideoRenderImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetText(textId, text, textLength, textColorRef, + backgroundColorRef, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetBitmap(bitMap, pictureId, colorKey, left, top, + right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetExpectedRenderDelay( + uint32_t stream_id, int32_t delay_ms) { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(stream_id); + if (item == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%u, %d): stream doesn't exist", __FUNCTION__, stream_id, + delay_ms); + return -1; + } + + assert(item->second != NULL); + return item->second->SetExpectedRenderDelay(delay_ms); +} + +int32_t ModuleVideoRenderImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->ConfigureRenderer(streamId, zOrder, left, top, right, + bottom); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/video_render_impl.h b/webrtc/modules/video_render/video_render_impl.h new file mode 100644 index 0000000000..8dfa57d25b --- /dev/null +++ b/webrtc/modules/video_render/video_render_impl.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ + +#include + +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/video_render/video_render.h" + +namespace webrtc { +class CriticalSectionWrapper; +class IncomingVideoStream; +class IVideoRender; + +// Class definitions +class ModuleVideoRenderImpl: public VideoRender +{ +public: + /* + * VideoRenderer constructor/destructor + */ + ModuleVideoRenderImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, const bool fullscreen); + + virtual ~ModuleVideoRenderImpl(); + + virtual int64_t TimeUntilNextProcess(); + virtual void Process(); + + /* + * Returns the render window + */ + virtual void* Window(); + + /* + * Change render window + */ + virtual int32_t ChangeWindow(void* window); + + /* + * Returns module id + */ + int32_t Id(); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + /* + * Add incoming render stream + */ + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom); + /* + * Delete incoming render stream + */ + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId); + + /* + * Add incoming render callback, used for external rendering + */ + virtual int32_t + AddExternalRenderCallback(const uint32_t streamId, + VideoRenderCallback* renderObject); + + /* + * Get the porperties for an incoming render stream + */ + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const; + /* + * Incoming frame rate for the specified stream. + */ + virtual uint32_t GetIncomingFrameRate(const uint32_t streamId); + + /* + * Returns the number of incoming streams added to this render module + */ + virtual uint32_t GetNumIncomingRenderStreams() const; + + /* + * Returns true if this render module has the streamId added, false otherwise. + */ + virtual bool HasIncomingRenderStream(const uint32_t streamId) const; + + /* + * + */ + virtual int32_t + RegisterRawFrameCallback(const uint32_t streamId, + VideoRenderCallback* callbackObj); + + virtual int32_t SetExpectedRenderDelay(uint32_t stream_id, + int32_t delay_ms); + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + /* + * Starts rendering the specified stream + */ + virtual int32_t StartRender(const uint32_t streamId); + + /* + * Stops the renderer + */ + virtual int32_t StopRender(const uint32_t streamId); + + /* + * Sets the renderer in start state, no streams removed. + */ + virtual int32_t ResetRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + /* + * Returns the prefered render video type + */ + virtual RawVideoType PreferredVideoType() const; + + /* + * Returns true if the renderer is in fullscreen mode, otherwise false. + */ + virtual bool IsFullScreen(); + + /* + * Gets screen resolution in pixels + */ + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + /* + * Get the actual render rate for this stream. I.e rendered frame rate, + * not frames delivered to the renderer. + */ + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + /* + * Set cropping of incoming stream + */ + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t FullScreenRender(void* window, const bool enable); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, const float bottom); + +private: + int32_t _id; + CriticalSectionWrapper& _moduleCrit; + void* _ptrWindow; + bool _fullScreen; + + IVideoRender* _ptrRenderer; + typedef std::map IncomingVideoStreamMap; + IncomingVideoStreamMap _streamRenderMap; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_VIDEO_RENDER_IMPL_H_ diff --git a/webrtc/modules/video_render/video_render_internal.h b/webrtc/modules/video_render/video_render_internal.h new file mode 100644 index 0000000000..0508c1a708 --- /dev/null +++ b/webrtc/modules/video_render/video_render_internal.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ + +#ifdef ANDROID +#include + +namespace webrtc { + +// In order to be able to use the internal webrtc video render +// for android, the jvm objects must be set via this method. +int32_t SetRenderAndroidVM(JavaVM* javaVM); + +} // namespace webrtc + +#endif // ANDROID + +#endif // WEBRTC_MODULES_VIDEO_RENDER_VIDEO_RENDER_INTERNAL_H_ diff --git a/webrtc/modules/video_render/video_render_internal_impl.cc b/webrtc/modules/video_render/video_render_internal_impl.cc new file mode 100644 index 0000000000..ac89e7f2c9 --- /dev/null +++ b/webrtc/modules/video_render/video_render_internal_impl.cc @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "webrtc/common_video/include/incoming_video_stream.h" +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/modules/video_render/video_render_defines.h" +#include "webrtc/modules/video_render/video_render_impl.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +#if defined (_WIN32) +#include "webrtc/modules/video_render/windows/video_render_windows_impl.h" +#define STANDARD_RENDERING kRenderWindows + +// WEBRTC_IOS should go before WEBRTC_MAC because WEBRTC_MAC +// gets defined if WEBRTC_IOS is defined +#elif defined(WEBRTC_IOS) +#define STANDARD_RENDERING kRenderiOS +#include "webrtc/modules/video_render/ios/video_render_ios_impl.h" +#elif defined(WEBRTC_MAC) +#if defined(COCOA_RENDERING) +#define STANDARD_RENDERING kRenderCocoa +#include "webrtc/modules/video_render/mac/video_render_mac_cocoa_impl.h" +#elif defined(CARBON_RENDERING) +#define STANDARD_RENDERING kRenderCarbon +#include "webrtc/modules/video_render/mac/video_render_mac_carbon_impl.h" +#endif + +#elif defined(WEBRTC_ANDROID) +#include "webrtc/modules/video_render/android/video_render_android_impl.h" +#include "webrtc/modules/video_render/android/video_render_android_native_opengl2.h" +#include "webrtc/modules/video_render/android/video_render_android_surface_view.h" +#define STANDARD_RENDERING kRenderAndroid + +#elif defined(WEBRTC_LINUX) +#include "webrtc/modules/video_render/linux/video_render_linux_impl.h" +#define STANDARD_RENDERING kRenderX11 + +#else +//Other platforms +#endif + +// For external rendering +#include "webrtc/modules/video_render/external/video_render_external_impl.h" +#ifndef STANDARD_RENDERING +#define STANDARD_RENDERING kRenderExternal +#endif // STANDARD_RENDERING + +namespace webrtc { + +VideoRender* +VideoRender::CreateVideoRender(const int32_t id, + void* window, + const bool fullscreen, + const VideoRenderType videoRenderType/*=kRenderDefault*/) +{ + VideoRenderType resultVideoRenderType = videoRenderType; + if (videoRenderType == kRenderDefault) + { + resultVideoRenderType = STANDARD_RENDERING; + } + return new ModuleVideoRenderImpl(id, resultVideoRenderType, window, + fullscreen); +} + +void VideoRender::DestroyVideoRender( + VideoRender* module) +{ + if (module) + { + delete module; + } +} + +ModuleVideoRenderImpl::ModuleVideoRenderImpl( + const int32_t id, + const VideoRenderType videoRenderType, + void* window, + const bool fullscreen) : + _id(id), _moduleCrit(*CriticalSectionWrapper::CreateCriticalSection()), + _ptrWindow(window), _fullScreen(fullscreen), _ptrRenderer(NULL) +{ + + // Create platform specific renderer + switch (videoRenderType) + { +#if defined(_WIN32) + case kRenderWindows: + { + VideoRenderWindowsImpl* ptrRenderer; + ptrRenderer = new VideoRenderWindowsImpl(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; + +#elif defined(WEBRTC_IOS) + case kRenderiOS: + { + VideoRenderIosImpl* ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; + +#elif defined(WEBRTC_MAC) + +#if defined(COCOA_RENDERING) + case kRenderCocoa: + { + VideoRenderMacCocoaImpl* ptrRenderer = new VideoRenderMacCocoaImpl(_id, videoRenderType, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + + break; +#elif defined(CARBON_RENDERING) + case kRenderCarbon: + { + VideoRenderMacCarbonImpl* ptrRenderer = new VideoRenderMacCarbonImpl(_id, videoRenderType, window, _fullScreen); + if(ptrRenderer) + { + _ptrRenderer = reinterpret_cast(ptrRenderer); + } + } + break; +#endif + +#elif defined(WEBRTC_ANDROID) + case kRenderAndroid: + { + if(AndroidNativeOpenGl2Renderer::UseOpenGL2(window)) + { + AndroidNativeOpenGl2Renderer* ptrRenderer = NULL; + ptrRenderer = new AndroidNativeOpenGl2Renderer(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + else + { + AndroidSurfaceViewRenderer* ptrRenderer = NULL; + ptrRenderer = new AndroidSurfaceViewRenderer(_id, videoRenderType, window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + + } + break; +#elif defined(WEBRTC_LINUX) + case kRenderX11: + { + VideoRenderLinuxImpl* ptrRenderer = NULL; + ptrRenderer = new VideoRenderLinuxImpl(_id, videoRenderType, window, _fullScreen); + if ( ptrRenderer ) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + break; + +#else + // Other platforms +#endif + case kRenderExternal: + { + VideoRenderExternalImpl* ptrRenderer(NULL); + ptrRenderer = new VideoRenderExternalImpl(_id, videoRenderType, + window, _fullScreen); + if (ptrRenderer) + { + _ptrRenderer = reinterpret_cast (ptrRenderer); + } + } + break; + default: + // Error... + break; + } + if (_ptrRenderer) + { + if (_ptrRenderer->Init() == -1) + { + } + } +} + +ModuleVideoRenderImpl::~ModuleVideoRenderImpl() +{ + delete &_moduleCrit; + + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + delete it->second; + } + + // Delete platform specific renderer + if (_ptrRenderer) + { + VideoRenderType videoRenderType = _ptrRenderer->RenderType(); + + switch (videoRenderType) + { + case kRenderExternal: + { + VideoRenderExternalImpl + * ptrRenderer = + reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#if defined(_WIN32) + case kRenderWindows: + { + VideoRenderWindowsImpl* ptrRenderer = reinterpret_cast(_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(WEBRTC_IOS) + case kRenderiOS: + { + VideoRenderIosImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(WEBRTC_MAC) + +#if defined(COCOA_RENDERING) + case kRenderCocoa: + { + VideoRenderMacCocoaImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#elif defined(CARBON_RENDERING) + case kRenderCarbon: + { + VideoRenderMacCarbonImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#endif + +#elif defined(WEBRTC_ANDROID) + case kRenderAndroid: + { + VideoRenderAndroid* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; + +#elif defined(WEBRTC_LINUX) + case kRenderX11: + { + VideoRenderLinuxImpl* ptrRenderer = reinterpret_cast (_ptrRenderer); + _ptrRenderer = NULL; + delete ptrRenderer; + } + break; +#else + //other platforms +#endif + + default: + // Error... + break; + } + } +} + +int64_t ModuleVideoRenderImpl::TimeUntilNextProcess() +{ + // Not used + return 50; +} +void ModuleVideoRenderImpl::Process() {} + +void* +ModuleVideoRenderImpl::Window() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _ptrWindow; +} + +int32_t ModuleVideoRenderImpl::ChangeWindow(void* window) +{ + + CriticalSectionScoped cs(&_moduleCrit); + +#if defined(WEBRTC_IOS) // WEBRTC_IOS must go before WEBRTC_MAC + _ptrRenderer = NULL; + delete _ptrRenderer; + + VideoRenderIosImpl* ptrRenderer; + ptrRenderer = new VideoRenderIosImpl(_id, window, _fullScreen); + if (!ptrRenderer) + { + return -1; + } + _ptrRenderer = reinterpret_cast(ptrRenderer); + return _ptrRenderer->ChangeWindow(window); +#elif defined(WEBRTC_MAC) + + _ptrRenderer = NULL; + delete _ptrRenderer; + +#if defined(COCOA_RENDERING) + VideoRenderMacCocoaImpl* ptrRenderer; + ptrRenderer = new VideoRenderMacCocoaImpl(_id, kRenderCocoa, window, _fullScreen); +#elif defined(CARBON_RENDERING) + VideoRenderMacCarbonImpl* ptrRenderer; + ptrRenderer = new VideoRenderMacCarbonImpl(_id, kRenderCarbon, window, _fullScreen); +#endif + if (!ptrRenderer) + { + return -1; + } + _ptrRenderer = reinterpret_cast(ptrRenderer); + return _ptrRenderer->ChangeWindow(window); + +#else + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->ChangeWindow(window); + +#endif +} + +int32_t ModuleVideoRenderImpl::Id() +{ + CriticalSectionScoped cs(&_moduleCrit); + return _id; +} + +uint32_t ModuleVideoRenderImpl::GetIncomingFrameRate(const uint32_t streamId) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator it = _streamRenderMap.find(streamId); + + if (it == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, + kTraceVideoRenderer, + _id, + "%s: stream doesn't exist", + __FUNCTION__); + return 0; + } + assert(it->second != NULL); + return it->second->IncomingRate(); +} + +VideoRenderCallback* +ModuleVideoRenderImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return NULL; + } + + if (_streamRenderMap.find(streamId) != _streamRenderMap.end()) { + // The stream already exists... + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream already exists", __FUNCTION__); + return NULL; + } + + VideoRenderCallback* ptrRenderCallback = + _ptrRenderer->AddIncomingRenderStream(streamId, zOrder, left, top, + right, bottom); + if (ptrRenderCallback == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: Can't create incoming stream in renderer", + __FUNCTION__); + return NULL; + } + + // Create platform independant code + IncomingVideoStream* ptrIncomingStream = + new IncomingVideoStream(streamId, false); + ptrIncomingStream->SetRenderCallback(ptrRenderCallback); + VideoRenderCallback* moduleCallback = ptrIncomingStream->ModuleCallback(); + + // Store the stream + _streamRenderMap[streamId] = ptrIncomingStream; + + return moduleCallback; +} + +int32_t ModuleVideoRenderImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + delete item->second; + + _ptrRenderer->DeleteIncomingRenderStream(streamId); + + _streamRenderMap.erase(item); + + return 0; +} + +int32_t ModuleVideoRenderImpl::AddExternalRenderCallback( + const uint32_t streamId, + VideoRenderCallback* renderObject) { + CriticalSectionScoped cs(&_moduleCrit); + + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: stream doesn't exist", __FUNCTION__); + return -1; + } + + if (item->second == NULL) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: could not get stream", __FUNCTION__); + return -1; + } + item->second->SetExternalCallback(renderObject); + return 0; +} + +int32_t ModuleVideoRenderImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + return _ptrRenderer->GetIncomingRenderStreamProperties(streamId, zOrder, + left, top, right, + bottom); +} + +uint32_t ModuleVideoRenderImpl::GetNumIncomingRenderStreams() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + return static_cast(_streamRenderMap.size()); +} + +bool ModuleVideoRenderImpl::HasIncomingRenderStream( + const uint32_t streamId) const { + CriticalSectionScoped cs(&_moduleCrit); + + return _streamRenderMap.find(streamId) != _streamRenderMap.end(); +} + +int32_t ModuleVideoRenderImpl::RegisterRawFrameCallback( + const uint32_t streamId, + VideoRenderCallback* callbackObj) { + return -1; +} + +int32_t ModuleVideoRenderImpl::StartRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + + // Start the stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Start() == -1) + { + return -1; + } + + // Start the HW renderer + if (_ptrRenderer->StartRender() == -1) + { + return -1; + } + return 0; +} + +int32_t ModuleVideoRenderImpl::StopRender(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%d): No renderer", __FUNCTION__, streamId); + return -1; + } + + // Stop the incoming stream + IncomingVideoStreamMap::iterator item = _streamRenderMap.find(streamId); + + if (item == _streamRenderMap.end()) + { + return -1; + } + + if (item->second->Stop() == -1) + { + return -1; + } + + return 0; +} + +int32_t ModuleVideoRenderImpl::ResetRender() +{ + CriticalSectionScoped cs(&_moduleCrit); + + int32_t ret = 0; + // Loop through all incoming streams and reset them + for (IncomingVideoStreamMap::iterator it = _streamRenderMap.begin(); + it != _streamRenderMap.end(); + ++it) { + if (it->second->Reset() == -1) + ret = -1; + } + return ret; +} + +RawVideoType ModuleVideoRenderImpl::PreferredVideoType() const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (_ptrRenderer == NULL) + { + return kVideoI420; + } + + return _ptrRenderer->PerferedVideoType(); +} + +bool ModuleVideoRenderImpl::IsFullScreen() +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->FullScreen(); +} + +int32_t ModuleVideoRenderImpl::GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->GetScreenResolution(screenWidth, screenHeight); +} + +uint32_t ModuleVideoRenderImpl::RenderFrameRate( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->RenderFrameRate(streamId); +} + +int32_t ModuleVideoRenderImpl::SetStreamCropping( + const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetStreamCropping(streamId, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetTransparentBackground(const bool enable) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->SetTransparentBackground(enable); +} + +int32_t ModuleVideoRenderImpl::FullScreenRender(void* window, const bool enable) +{ + return -1; +} + +int32_t ModuleVideoRenderImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetText(textId, text, textLength, textColorRef, + backgroundColorRef, left, top, right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return -1; + } + return _ptrRenderer->SetBitmap(bitMap, pictureId, colorKey, left, top, + right, bottom); +} + +int32_t ModuleVideoRenderImpl::SetExpectedRenderDelay( + uint32_t stream_id, int32_t delay_ms) { + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + + IncomingVideoStreamMap::const_iterator item = + _streamRenderMap.find(stream_id); + if (item == _streamRenderMap.end()) { + // This stream doesn't exist + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s(%u, %d): stream doesn't exist", __FUNCTION__, stream_id, + delay_ms); + return -1; + } + + assert(item->second != NULL); + return item->second->SetExpectedRenderDelay(delay_ms); +} + +int32_t ModuleVideoRenderImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_moduleCrit); + + if (!_ptrRenderer) + { + WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, + "%s: No renderer", __FUNCTION__); + return false; + } + return _ptrRenderer->ConfigureRenderer(streamId, zOrder, left, top, right, + bottom); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/windows/i_video_render_win.h b/webrtc/modules/video_render/windows/i_video_render_win.h new file mode 100644 index 0000000000..6dbb4fd3cb --- /dev/null +++ b/webrtc/modules/video_render/windows/i_video_render_win.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ + +#include "webrtc/modules/video_render/video_render.h" + +namespace webrtc { + +// Class definitions +class IVideoRenderWin +{ +public: + /************************************************************************** + * + * Constructor/destructor + * + ***************************************************************************/ + virtual ~IVideoRenderWin() + { + }; + + virtual int32_t Init() = 0; + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + virtual VideoRenderCallback + * CreateChannel(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t DeleteChannel(const uint32_t streamId) = 0; + + virtual int32_t GetStreamSettings(const uint32_t channel, + const uint16_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) = 0; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender() = 0; + + virtual int32_t StopRender() = 0; + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual bool IsFullScreen() = 0; + + virtual int32_t SetCropping(const uint32_t channel, + const uint16_t streamId, + const float left, const float top, + const float right, const float bottom) = 0; + + virtual int32_t ConfigureRenderer(const uint32_t channel, + const uint16_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) = 0; + + virtual int32_t SetTransparentBackground(const bool enable) = 0; + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t colorText, + const uint32_t colorBg, + const float left, const float top, + const float rigth, const float bottom) = 0; + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom) = 0; + + virtual int32_t ChangeWindow(void* window) = 0; + + virtual int32_t GetGraphicsMemory(uint64_t& totalMemory, + uint64_t& availableMemory) = 0; + +}; + +} // namespace webrtc +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_I_VIDEO_RENDER_WIN_H_ diff --git a/webrtc/modules/video_render/windows/video_render_direct3d9.cc b/webrtc/modules/video_render/windows/video_render_direct3d9.cc new file mode 100644 index 0000000000..b59b944e48 --- /dev/null +++ b/webrtc/modules/video_render/windows/video_render_direct3d9.cc @@ -0,0 +1,1160 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Own include file +#include "webrtc/modules/video_render/windows/video_render_direct3d9.h" + +// System include files +#include + +// WebRtc include files +#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/event_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" + +namespace webrtc { + +// A structure for our custom vertex type +struct CUSTOMVERTEX +{ + FLOAT x, y, z; + DWORD color; // The vertex color + FLOAT u, v; +}; + +// Our custom FVF, which describes our custom vertex structure +#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) + +/* + * + * D3D9Channel + * + */ +D3D9Channel::D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice, + CriticalSectionWrapper* critSect, + Trace* trace) : + _width(0), + _height(0), + _pd3dDevice(pd3DDevice), + _pTexture(NULL), + _bufferIsUpdated(false), + _critSect(critSect), + _streamId(0), + _zOrder(0), + _startWidth(0), + _startHeight(0), + _stopWidth(0), + _stopHeight(0) +{ + +} + +D3D9Channel::~D3D9Channel() +{ + //release the texture + if (_pTexture != NULL) + { + _pTexture->Release(); + _pTexture = NULL; + } +} + +void D3D9Channel::SetStreamSettings(uint16_t streamId, + uint32_t zOrder, + float startWidth, + float startHeight, + float stopWidth, + float stopHeight) +{ + _streamId = streamId; + _zOrder = zOrder; + _startWidth = startWidth; + _startHeight = startHeight; + _stopWidth = stopWidth; + _stopHeight = stopHeight; +} + +int D3D9Channel::GetStreamSettings(uint16_t streamId, + uint32_t& zOrder, + float& startWidth, + float& startHeight, + float& stopWidth, + float& stopHeight) +{ + streamId = _streamId; + zOrder = _zOrder; + startWidth = _startWidth; + startHeight = _startHeight; + stopWidth = _stopWidth; + stopHeight = _stopHeight; + return 0; +} + +int D3D9Channel::GetTextureWidth() +{ + return _width; +} + +int D3D9Channel::GetTextureHeight() +{ + return _height; +} + +// Called from video engine when a the frame size changed +int D3D9Channel::FrameSizeChange(int width, int height, int numberOfStreams) +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, + "FrameSizeChange, wifth: %d, height: %d, streams: %d", width, + height, numberOfStreams); + + CriticalSectionScoped cs(_critSect); + _width = width; + _height = height; + + //clean the previous texture + if (_pTexture != NULL) + { + _pTexture->Release(); + _pTexture = NULL; + } + + HRESULT ret = E_POINTER; + + if (_pd3dDevice) + ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, &_pTexture, NULL); + + if (FAILED(ret)) + { + _pTexture = NULL; + return -1; + } + + return 0; +} + +int32_t D3D9Channel::RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame) { + CriticalSectionScoped cs(_critSect); + if (_width != videoFrame.width() || _height != videoFrame.height()) + { + if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) + { + return -1; + } + } + return DeliverFrame(videoFrame); +} + +// Called from video engine when a new frame should be rendered. +int D3D9Channel::DeliverFrame(const VideoFrame& videoFrame) { + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, + "DeliverFrame to D3D9Channel"); + + CriticalSectionScoped cs(_critSect); + + // FIXME if _bufferIsUpdated is still true (not be renderred), do we want to + // update the texture? probably not + if (_bufferIsUpdated) { + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, + "Last frame hasn't been rendered yet. Drop this frame."); + return -1; + } + + if (!_pd3dDevice) { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "D3D for rendering not initialized."); + return -1; + } + + if (!_pTexture) { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Texture for rendering not initialized."); + return -1; + } + + D3DLOCKED_RECT lr; + + if (FAILED(_pTexture->LockRect(0, &lr, NULL, 0))) { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Failed to lock a texture in D3D9 Channel."); + return -1; + } + UCHAR* pRect = (UCHAR*) lr.pBits; + + ConvertFromI420(videoFrame, kARGB, 0, pRect); + + if (FAILED(_pTexture->UnlockRect(0))) { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Failed to unlock a texture in D3D9 Channel."); + return -1; + } + + _bufferIsUpdated = true; + return 0; +} + +// Called by d3d channel owner to indicate the frame/texture has been rendered off +int D3D9Channel::RenderOffFrame() +{ + WEBRTC_TRACE(kTraceStream, kTraceVideo, -1, + "Frame has been rendered to the screen."); + CriticalSectionScoped cs(_critSect); + _bufferIsUpdated = false; + return 0; +} + +// Called by d3d channel owner to check if the texture is updated +int D3D9Channel::IsUpdated(bool& isUpdated) +{ + CriticalSectionScoped cs(_critSect); + isUpdated = _bufferIsUpdated; + return 0; +} + +// Called by d3d channel owner to get the texture +LPDIRECT3DTEXTURE9 D3D9Channel::GetTexture() +{ + CriticalSectionScoped cs(_critSect); + return _pTexture; +} + +int D3D9Channel::ReleaseTexture() +{ + CriticalSectionScoped cs(_critSect); + + //release the texture + if (_pTexture != NULL) + { + _pTexture->Release(); + _pTexture = NULL; + } + _pd3dDevice = NULL; + return 0; +} + +int D3D9Channel::RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice) +{ + CriticalSectionScoped cs(_critSect); + + _pd3dDevice = pd3DDevice; + + if (_pTexture != NULL) + { + _pTexture->Release(); + _pTexture = NULL; + } + + HRESULT ret; + + ret = _pd3dDevice->CreateTexture(_width, _height, 1, 0, D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, &_pTexture, NULL); + + if (FAILED(ret)) + { + _pTexture = NULL; + return -1; + } + + return 0; +} + +/* + * + * VideoRenderDirect3D9 + * + */ +VideoRenderDirect3D9::VideoRenderDirect3D9(Trace* trace, + HWND hWnd, + bool fullScreen) : + _refD3DCritsect(*CriticalSectionWrapper::CreateCriticalSection()), + _trace(trace), + _hWnd(hWnd), + _fullScreen(fullScreen), + _pTextureLogo(NULL), + _pVB(NULL), + _pd3dDevice(NULL), + _pD3D(NULL), + _d3dChannels(), + _d3dZorder(), + _screenUpdateEvent(NULL), + _logoLeft(0), + _logoTop(0), + _logoRight(0), + _logoBottom(0), + _pd3dSurface(NULL), + _totalMemory(0), + _availableMemory(0) +{ + _screenUpdateThread.reset(new rtc::PlatformThread( + ScreenUpdateThreadProc, this, "ScreenUpdateThread")); + _screenUpdateEvent = EventTimerWrapper::Create(); + SetRect(&_originalHwndRect, 0, 0, 0, 0); +} + +VideoRenderDirect3D9::~VideoRenderDirect3D9() +{ + //NOTE: we should not enter CriticalSection in here! + + // Signal event to exit thread, then delete it + rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); + if (tmpPtr) + { + _screenUpdateEvent->Set(); + _screenUpdateEvent->StopTimer(); + + tmpPtr->Stop(); + delete tmpPtr; + } + delete _screenUpdateEvent; + + //close d3d device + CloseDevice(); + + // Delete all channels + std::map::iterator it = _d3dChannels.begin(); + while (it != _d3dChannels.end()) + { + delete it->second; + it = _d3dChannels.erase(it); + } + // Clean the zOrder map + _d3dZorder.clear(); + + if (_fullScreen) + { + // restore hwnd to original size and position + ::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left, + _originalHwndRect.top, _originalHwndRect.right + - _originalHwndRect.left, + _originalHwndRect.bottom - _originalHwndRect.top, + SWP_FRAMECHANGED); + ::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW + | RDW_ERASE); + ::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW + | RDW_ERASE); + } + + delete &_refD3DCritsect; +} + +DWORD VideoRenderDirect3D9::GetVertexProcessingCaps() +{ + D3DCAPS9 caps; + DWORD dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING; + if (SUCCEEDED(_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + &caps))) + { + if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) + == D3DDEVCAPS_HWTRANSFORMANDLIGHT) + { + dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING; + } + } + return dwVertexProcessing; +} + +int VideoRenderDirect3D9::InitializeD3D(HWND hWnd, + D3DPRESENT_PARAMETERS* pd3dpp) +{ + // initialize Direct3D + if (NULL == (_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) + { + return -1; + } + + // determine what type of vertex processing to use based on the device capabilities + DWORD dwVertexProcessing = GetVertexProcessingCaps(); + + // get the display mode + D3DDISPLAYMODE d3ddm; + _pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); + pd3dpp->BackBufferFormat = d3ddm.Format; + + // create the D3D device + if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, + dwVertexProcessing | D3DCREATE_MULTITHREADED + | D3DCREATE_FPU_PRESERVE, pd3dpp, + &_pd3dDevice))) + { + //try the ref device + if (FAILED(_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, + hWnd, dwVertexProcessing + | D3DCREATE_MULTITHREADED + | D3DCREATE_FPU_PRESERVE, + pd3dpp, &_pd3dDevice))) + { + return -1; + } + } + + return 0; +} + +int VideoRenderDirect3D9::ResetDevice() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, + "VideoRenderDirect3D9::ResetDevice"); + + CriticalSectionScoped cs(&_refD3DCritsect); + + //release the channel texture + std::map::iterator it; + it = _d3dChannels.begin(); + while (it != _d3dChannels.end()) + { + if (it->second) + { + it->second->ReleaseTexture(); + } + it++; + } + + //close d3d device + if (CloseDevice() != 0) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "VideoRenderDirect3D9::ResetDevice failed to CloseDevice"); + return -1; + } + + //reinit d3d device + if (InitDevice() != 0) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "VideoRenderDirect3D9::ResetDevice failed to InitDevice"); + return -1; + } + + //recreate channel texture + it = _d3dChannels.begin(); + while (it != _d3dChannels.end()) + { + if (it->second) + { + it->second->RecreateTexture(_pd3dDevice); + } + it++; + } + + return 0; +} + +int VideoRenderDirect3D9::InitDevice() +{ + // Set up the structure used to create the D3DDevice + ZeroMemory(&_d3dpp, sizeof(_d3dpp)); + _d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + _d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; + if (GetWindowRect(_hWnd, &_originalHwndRect) == 0) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "VideoRenderDirect3D9::InitDevice Could not get window size"); + return -1; + } + if (!_fullScreen) + { + _winWidth = _originalHwndRect.right - _originalHwndRect.left; + _winHeight = _originalHwndRect.bottom - _originalHwndRect.top; + _d3dpp.Windowed = TRUE; + _d3dpp.BackBufferHeight = 0; + _d3dpp.BackBufferWidth = 0; + } + else + { + _winWidth = (LONG) ::GetSystemMetrics(SM_CXSCREEN); + _winHeight = (LONG) ::GetSystemMetrics(SM_CYSCREEN); + _d3dpp.Windowed = FALSE; + _d3dpp.BackBufferWidth = _winWidth; + _d3dpp.BackBufferHeight = _winHeight; + _d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + } + + if (InitializeD3D(_hWnd, &_d3dpp) == -1) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "VideoRenderDirect3D9::InitDevice failed in InitializeD3D"); + return -1; + } + + // Turn off culling, so we see the front and back of the triangle + _pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + + // Turn off D3D lighting, since we are providing our own vertex colors + _pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + + // Settings for alpha blending + _pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + _pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + _pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + _pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR ); + + // Initialize Vertices + CUSTOMVERTEX Vertices[] = { + //front + { -1.0f, -1.0f, 0.0f, 0xffffffff, 0, 1 }, { -1.0f, 1.0f, 0.0f, + 0xffffffff, 0, 0 }, + { 1.0f, -1.0f, 0.0f, 0xffffffff, 1, 1 }, { 1.0f, 1.0f, 0.0f, + 0xffffffff, 1, 0 } }; + + // Create the vertex buffer. + if (FAILED(_pd3dDevice->CreateVertexBuffer(sizeof(Vertices), 0, + D3DFVF_CUSTOMVERTEX, + D3DPOOL_DEFAULT, &_pVB, NULL ))) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Failed to create the vertex buffer."); + return -1; + } + + // Now we fill the vertex buffer. + VOID* pVertices; + if (FAILED(_pVB->Lock(0, sizeof(Vertices), (void**) &pVertices, 0))) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Failed to lock the vertex buffer."); + return -1; + } + memcpy(pVertices, Vertices, sizeof(Vertices)); + _pVB->Unlock(); + + return 0; +} + +int32_t VideoRenderDirect3D9::Init() +{ + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, + "VideoRenderDirect3D9::Init"); + + CriticalSectionScoped cs(&_refD3DCritsect); + + // Start rendering thread... + if (!_screenUpdateThread) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Thread not created"); + return -1; + } + _screenUpdateThread->Start(); + _screenUpdateThread->SetPriority(rtc::kRealtimePriority); + + // Start the event triggering the render process + unsigned int monitorFreq = 60; + DEVMODE dm; + // initialize the DEVMODE structure + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm)) + { + monitorFreq = dm.dmDisplayFrequency; + } + _screenUpdateEvent->StartTimer(true, 1000 / monitorFreq); + + return InitDevice(); +} + +int32_t VideoRenderDirect3D9::ChangeWindow(void* window) +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return -1; +} + +int VideoRenderDirect3D9::UpdateRenderSurface() +{ + CriticalSectionScoped cs(&_refD3DCritsect); + + // Check if there are any updated buffers + bool updated = false; + std::map::iterator it; + it = _d3dChannels.begin(); + while (it != _d3dChannels.end()) + { + + D3D9Channel* channel = it->second; + channel->IsUpdated(updated); + if (updated) + { + break; + } + it++; + } + //nothing is updated, continue + if (!updated) + return -1; + + // Clear the backbuffer to a black color + _pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, + 0); + + // Begin the scene + if (SUCCEEDED(_pd3dDevice->BeginScene())) + { + _pd3dDevice->SetStreamSource(0, _pVB, 0, sizeof(CUSTOMVERTEX)); + _pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); + + //draw all the channels + //get texture from the channels + LPDIRECT3DTEXTURE9 textureFromChannel = NULL; + DWORD textureWidth, textureHeight; + + std::multimap::reverse_iterator it; + it = _d3dZorder.rbegin(); + while (it != _d3dZorder.rend()) + { + // loop through all channels and streams in Z order + int channel = it->second & 0x0000ffff; + + std::map::iterator ddIt; + ddIt = _d3dChannels.find(channel); + if (ddIt != _d3dChannels.end()) + { + // found the channel + D3D9Channel* channelObj = ddIt->second; + if (channelObj) + { + textureFromChannel = channelObj->GetTexture(); + textureWidth = channelObj->GetTextureWidth(); + textureHeight = channelObj->GetTextureHeight(); + + uint32_t zOrder; + float startWidth, startHeight, stopWidth, stopHeight; + channelObj->GetStreamSettings(0, zOrder, startWidth, + startHeight, stopWidth, + stopHeight); + + //draw the video stream + UpdateVerticeBuffer(_pVB, 0, startWidth, startHeight, + stopWidth, stopHeight); + _pd3dDevice->SetTexture(0, textureFromChannel); + _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + + //Notice channel that this frame as been rendered + channelObj->RenderOffFrame(); + } + } + it++; + } + + //draw the logo + if (_pTextureLogo) + { + UpdateVerticeBuffer(_pVB, 0, _logoLeft, _logoTop, _logoRight, + _logoBottom); + _pd3dDevice->SetTexture(0, _pTextureLogo); + _pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + } + + // End the scene + _pd3dDevice->EndScene(); + } + + // Present the backbuffer contents to the display + _pd3dDevice->Present(NULL, NULL, NULL, NULL ); + + return 0; +} + +//set the alpha value of the pixal with a particular colorkey as 0 +int VideoRenderDirect3D9::SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture, + DDCOLORKEY* transparentColorKey, + DWORD width, + DWORD height) +{ + D3DLOCKED_RECT lr; + if (!pTexture) + return -1; + + CriticalSectionScoped cs(&_refD3DCritsect); + if (SUCCEEDED(pTexture->LockRect(0, &lr, NULL, D3DLOCK_DISCARD))) + { + for (DWORD y = 0; y < height; y++) + { + DWORD dwOffset = y * width; + + for (DWORD x = 0; x < width; x) + { + DWORD temp = ((DWORD*) lr.pBits)[dwOffset + x]; + if ((temp & 0x00FFFFFF) + == transparentColorKey->dwColorSpaceLowValue) + { + temp &= 0x00FFFFFF; + } + else + { + temp |= 0xFF000000; + } + ((DWORD*) lr.pBits)[dwOffset + x] = temp; + x++; + } + } + pTexture->UnlockRect(0); + return 0; + } + return -1; +} + +/* + * + * Rendering process + * + */ +bool VideoRenderDirect3D9::ScreenUpdateThreadProc(void* obj) +{ + return static_cast (obj)->ScreenUpdateProcess(); +} + +bool VideoRenderDirect3D9::ScreenUpdateProcess() +{ + _screenUpdateEvent->Wait(100); + + if (!_screenUpdateThread) + { + //stop the thread + return false; + } + if (!_pd3dDevice) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "d3dDevice not created."); + return true; + } + + HRESULT hr = _pd3dDevice->TestCooperativeLevel(); + + if (SUCCEEDED(hr)) + { + UpdateRenderSurface(); + } + + if (hr == D3DERR_DEVICELOST) + { + //Device is lost and cannot be reset yet + + } + else if (hr == D3DERR_DEVICENOTRESET) + { + //Lost but we can reset it now + //Note: the standard way is to call Reset, however for some reason doesn't work here. + //so we will release the device and create it again. + ResetDevice(); + } + + return true; +} + +int VideoRenderDirect3D9::CloseDevice() +{ + CriticalSectionScoped cs(&_refD3DCritsect); + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, + "VideoRenderDirect3D9::CloseDevice"); + + if (_pTextureLogo != NULL) + { + _pTextureLogo->Release(); + _pTextureLogo = NULL; + } + + if (_pVB != NULL) + { + _pVB->Release(); + _pVB = NULL; + } + + if (_pd3dDevice != NULL) + { + _pd3dDevice->Release(); + _pd3dDevice = NULL; + } + + if (_pD3D != NULL) + { + _pD3D->Release(); + _pD3D = NULL; + } + + if (_pd3dSurface != NULL) + _pd3dSurface->Release(); + return 0; +} + +D3D9Channel* VideoRenderDirect3D9::GetD3DChannel(int channel) +{ + std::map::iterator ddIt; + ddIt = _d3dChannels.find(channel & 0x0000ffff); + D3D9Channel* ddobj = NULL; + if (ddIt != _d3dChannels.end()) + { + ddobj = ddIt->second; + } + if (ddobj == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D render failed to find channel"); + return NULL; + } + return ddobj; +} + +int32_t VideoRenderDirect3D9::DeleteChannel(const uint32_t streamId) +{ + CriticalSectionScoped cs(&_refD3DCritsect); + + + std::multimap::iterator it; + it = _d3dZorder.begin(); + while (it != _d3dZorder.end()) + { + if ((streamId & 0x0000ffff) == (it->second & 0x0000ffff)) + { + it = _d3dZorder.erase(it); + break; + } + it++; + } + + std::map::iterator ddIt; + ddIt = _d3dChannels.find(streamId & 0x0000ffff); + if (ddIt != _d3dChannels.end()) + { + delete ddIt->second; + _d3dChannels.erase(ddIt); + return 0; + } + return -1; +} + +VideoRenderCallback* VideoRenderDirect3D9::CreateChannel(const uint32_t channel, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_refD3DCritsect); + + //FIXME this should be done in VideoAPIWindows? stop the frame deliver first + //remove the old channel + DeleteChannel(channel); + + D3D9Channel* d3dChannel = new D3D9Channel(_pd3dDevice, + &_refD3DCritsect, _trace); + d3dChannel->SetStreamSettings(0, zOrder, left, top, right, bottom); + + // store channel + _d3dChannels[channel & 0x0000ffff] = d3dChannel; + + // store Z order + // default streamID is 0 + _d3dZorder.insert( + std::pair(zOrder, channel & 0x0000ffff)); + + return d3dChannel; +} + +int32_t VideoRenderDirect3D9::GetStreamSettings(const uint32_t channel, + const uint16_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) +{ + std::map::iterator ddIt; + ddIt = _d3dChannels.find(channel & 0x0000ffff); + D3D9Channel* ddobj = NULL; + if (ddIt != _d3dChannels.end()) + { + ddobj = ddIt->second; + } + if (ddobj == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D render failed to find channel"); + return -1; + } + // Only allow one stream per channel, demuxing is + return ddobj->GetStreamSettings(0, zOrder, left, top, right, bottom); +} + +int VideoRenderDirect3D9::UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB, + int offset, + float startWidth, + float startHeight, + float stopWidth, + float stopHeight) +{ + if (pVB == NULL) + return -1; + + float left, right, top, bottom; + + //update the vertice buffer + //0,1 => -1,1 + left = startWidth * 2 - 1; + right = stopWidth * 2 - 1; + + //0,1 => 1,-1 + top = 1 - startHeight * 2; + bottom = 1 - stopHeight * 2; + + CUSTOMVERTEX newVertices[] = { + //logo + { left, bottom, 0.0f, 0xffffffff, 0, 1 }, { left, top, 0.0f, + 0xffffffff, 0, 0 }, + { right, bottom, 0.0f, 0xffffffff, 1, 1 }, { right, top, 0.0f, + 0xffffffff, 1, 0 }, }; + // Now we fill the vertex buffer. + VOID* pVertices; + if (FAILED(pVB->Lock(sizeof(CUSTOMVERTEX) * offset, sizeof(newVertices), + (void**) &pVertices, 0))) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Failed to lock the vertex buffer."); + return -1; + } + memcpy(pVertices, newVertices, sizeof(newVertices)); + pVB->Unlock(); + + return 0; +} + +int32_t VideoRenderDirect3D9::StartRender() +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return 0; +} + +int32_t VideoRenderDirect3D9::StopRender() +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return 0; +} + +bool VideoRenderDirect3D9::IsFullScreen() +{ + return _fullScreen; +} + +int32_t VideoRenderDirect3D9::SetCropping(const uint32_t channel, + const uint16_t streamId, + const float left, const float top, + const float right, const float bottom) +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return 0; +} + +int32_t VideoRenderDirect3D9::SetTransparentBackground( + const bool enable) +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return 0; +} + +int32_t VideoRenderDirect3D9::SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t colorText, + const uint32_t colorBg, + const float left, const float top, + const float rigth, const float bottom) +{ + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, "Not supported."); + return 0; +} + +int32_t VideoRenderDirect3D9::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom) +{ + if (!bitMap) + { + if (_pTextureLogo != NULL) + { + _pTextureLogo->Release(); + _pTextureLogo = NULL; + } + WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "Remove bitmap."); + return 0; + } + + // sanity + if (left > 1.0f || left < 0.0f || + top > 1.0f || top < 0.0f || + right > 1.0f || right < 0.0f || + bottom > 1.0f || bottom < 0.0f) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D SetBitmap invalid parameter"); + return -1; + } + + if ((bottom <= top) || (right <= left)) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D SetBitmap invalid parameter"); + return -1; + } + + CriticalSectionScoped cs(&_refD3DCritsect); + + unsigned char* srcPtr; + HGDIOBJ oldhand; + BITMAPINFO pbi; + BITMAP bmap; + HDC hdcNew; + hdcNew = CreateCompatibleDC(0); + // Fill out the BITMAP structure. + GetObject((HBITMAP)bitMap, sizeof(bmap), &bmap); + //Select the bitmap handle into the new device context. + oldhand = SelectObject(hdcNew, (HGDIOBJ) bitMap); + // we are done with this object + DeleteObject(oldhand); + pbi.bmiHeader.biSize = 40; + pbi.bmiHeader.biWidth = bmap.bmWidth; + pbi.bmiHeader.biHeight = bmap.bmHeight; + pbi.bmiHeader.biPlanes = 1; + pbi.bmiHeader.biBitCount = bmap.bmBitsPixel; + pbi.bmiHeader.biCompression = BI_RGB; + pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3; + srcPtr = new unsigned char[bmap.bmWidth * bmap.bmHeight * 4]; + // the original un-stretched image in RGB24 + int pixelHeight = GetDIBits(hdcNew, (HBITMAP)bitMap, 0, bmap.bmHeight, srcPtr, &pbi, + DIB_RGB_COLORS); + if (pixelHeight == 0) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D failed to GetDIBits in SetBitmap"); + delete[] srcPtr; + return -1; + } + DeleteDC(hdcNew); + if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D failed to SetBitmap invalid bit depth"); + delete[] srcPtr; + return -1; + } + + HRESULT ret; + //release the previous logo texture + if (_pTextureLogo != NULL) + { + _pTextureLogo->Release(); + _pTextureLogo = NULL; + } + ret = _pd3dDevice->CreateTexture(bmap.bmWidth, bmap.bmHeight, 1, 0, + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + &_pTextureLogo, NULL); + if (FAILED(ret)) + { + _pTextureLogo = NULL; + delete[] srcPtr; + return -1; + } + if (!_pTextureLogo) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Texture for rendering not initialized."); + delete[] srcPtr; + return -1; + } + + D3DLOCKED_RECT lr; + if (FAILED(_pTextureLogo->LockRect(0, &lr, NULL, 0))) + { + delete[] srcPtr; + return -1; + } + unsigned char* dstPtr = (UCHAR*) lr.pBits; + int pitch = bmap.bmWidth * 4; + + if (pbi.bmiHeader.biBitCount == 24) + { + ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight, 0); + } + else + { + unsigned char* srcTmp = srcPtr + (bmap.bmWidth * 4) * (bmap.bmHeight - 1); + for (int i = 0; i < bmap.bmHeight; ++i) + { + memcpy(dstPtr, srcTmp, bmap.bmWidth * 4); + srcTmp -= bmap.bmWidth * 4; + dstPtr += pitch; + } + } + + delete[] srcPtr; + if (FAILED(_pTextureLogo->UnlockRect(0))) + { + return -1; + } + + if (colorKey) + { + DDCOLORKEY* ddColorKey = + static_cast (const_cast (colorKey)); + SetTransparentColor(_pTextureLogo, ddColorKey, bmap.bmWidth, + bmap.bmHeight); + } + + //update the vertice buffer + //0,1 => -1,1 + _logoLeft = left; + _logoRight = right; + + //0,1 => 1,-1 + _logoTop = top; + _logoBottom = bottom; + + return 0; + +} + +int32_t VideoRenderDirect3D9::GetGraphicsMemory(uint64_t& totalMemory, + uint64_t& availableMemory) +{ + totalMemory = _totalMemory; + availableMemory = _availableMemory; + return 0; +} + +int32_t VideoRenderDirect3D9::ConfigureRenderer(const uint32_t channel, + const uint16_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + std::map::iterator ddIt; + ddIt = _d3dChannels.find(channel & 0x0000ffff); + D3D9Channel* ddobj = NULL; + if (ddIt != _d3dChannels.end()) + { + ddobj = ddIt->second; + } + if (ddobj == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceVideo, -1, + "Direct3D render failed to find channel"); + return -1; + } + // Only allow one stream per channel, demuxing is + ddobj->SetStreamSettings(0, zOrder, left, top, right, bottom); + + return 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/windows/video_render_direct3d9.h b/webrtc/modules/video_render/windows/video_render_direct3d9.h new file mode 100644 index 0000000000..eaa8c147e2 --- /dev/null +++ b/webrtc/modules/video_render/windows/video_render_direct3d9.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ + +#include + +#include "webrtc/modules/video_render/windows/i_video_render_win.h" + +#include +#include + +#include + +// Added +#include "webrtc/base/platform_thread.h" +#include "webrtc/modules/video_render/video_render_defines.h" + +#pragma comment(lib, "d3d9.lib") // located in DirectX SDK + +namespace webrtc { +class CriticalSectionWrapper; +class EventTimerWrapper; +class Trace; + +class D3D9Channel: public VideoRenderCallback +{ +public: + D3D9Channel(LPDIRECT3DDEVICE9 pd3DDevice, + CriticalSectionWrapper* critSect, Trace* trace); + + virtual ~D3D9Channel(); + + // Inherited from VideoRencerCallback, called from VideoAPI class. + // Called when the incomming frame size and/or number of streams in mix changes + virtual int FrameSizeChange(int width, int height, int numberOfStreams); + + // A new frame is delivered. + virtual int DeliverFrame(const VideoFrame& videoFrame); + virtual int32_t RenderFrame(const uint32_t streamId, + const VideoFrame& videoFrame); + + // Called to check if the video frame is updated. + int IsUpdated(bool& isUpdated); + // Called after the video frame has been render to the screen + int RenderOffFrame(); + // Called to get the texture that contains the video frame + LPDIRECT3DTEXTURE9 GetTexture(); + // Called to get the texture(video frame) size + int GetTextureWidth(); + int GetTextureHeight(); + // + void SetStreamSettings(uint16_t streamId, + uint32_t zOrder, + float startWidth, + float startHeight, + float stopWidth, + float stopHeight); + int GetStreamSettings(uint16_t streamId, + uint32_t& zOrder, + float& startWidth, + float& startHeight, + float& stopWidth, + float& stopHeight); + + int ReleaseTexture(); + int RecreateTexture(LPDIRECT3DDEVICE9 pd3DDevice); + +protected: + +private: + //critical section passed from the owner + CriticalSectionWrapper* _critSect; + LPDIRECT3DDEVICE9 _pd3dDevice; + LPDIRECT3DTEXTURE9 _pTexture; + + bool _bufferIsUpdated; + // the frame size + int _width; + int _height; + //sream settings + //TODO support multiple streams in one channel + uint16_t _streamId; + uint32_t _zOrder; + float _startWidth; + float _startHeight; + float _stopWidth; + float _stopHeight; +}; + +class VideoRenderDirect3D9: IVideoRenderWin +{ +public: + VideoRenderDirect3D9(Trace* trace, HWND hWnd, bool fullScreen); + ~VideoRenderDirect3D9(); + +public: + //IVideoRenderWin + + /************************************************************************** + * + * Init + * + ***************************************************************************/ + virtual int32_t Init(); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + virtual VideoRenderCallback + * CreateChannel(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom); + + virtual int32_t DeleteChannel(const uint32_t streamId); + + virtual int32_t GetStreamSettings(const uint32_t channel, + const uint16_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom); + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual bool IsFullScreen(); + + virtual int32_t SetCropping(const uint32_t channel, + const uint16_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t channel, + const uint16_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t ChangeWindow(void* window); + + virtual int32_t GetGraphicsMemory(uint64_t& totalMemory, + uint64_t& availableMemory); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t colorText, + const uint32_t colorBg, + const float left, const float top, + const float rigth, const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom); + +public: + // Get a channel by channel id + D3D9Channel* GetD3DChannel(int channel); + int UpdateRenderSurface(); + +protected: + // The thread rendering the screen + static bool ScreenUpdateThreadProc(void* obj); + bool ScreenUpdateProcess(); + +private: + // Init/close the d3d device + int InitDevice(); + int CloseDevice(); + + // Transparent related functions + int SetTransparentColor(LPDIRECT3DTEXTURE9 pTexture, + DDCOLORKEY* transparentColorKey, + DWORD width, + DWORD height); + + CriticalSectionWrapper& _refD3DCritsect; + Trace* _trace; + // TODO(pbos): Remove unique_ptr and use PlatformThread directly. + std::unique_ptr _screenUpdateThread; + EventTimerWrapper* _screenUpdateEvent; + + HWND _hWnd; + bool _fullScreen; + RECT _originalHwndRect; + //FIXME we probably don't need this since all the information can be get from _d3dChannels + int _channel; + //Window size + UINT _winWidth; + UINT _winHeight; + + // Device + LPDIRECT3D9 _pD3D; // Used to create the D3DDevice + LPDIRECT3DDEVICE9 _pd3dDevice; // Our rendering device + LPDIRECT3DVERTEXBUFFER9 _pVB; // Buffer to hold Vertices + LPDIRECT3DTEXTURE9 _pTextureLogo; + + std::map _d3dChannels; + std::multimap _d3dZorder; + + // The position where the logo will be placed + float _logoLeft; + float _logoTop; + float _logoRight; + float _logoBottom; + + typedef HRESULT (WINAPI *DIRECT3DCREATE9EX)(UINT SDKVersion, IDirect3D9Ex**); + LPDIRECT3DSURFACE9 _pd3dSurface; + + DWORD GetVertexProcessingCaps(); + int InitializeD3D(HWND hWnd, D3DPRESENT_PARAMETERS* pd3dpp); + + D3DPRESENT_PARAMETERS _d3dpp; + int ResetDevice(); + + int UpdateVerticeBuffer(LPDIRECT3DVERTEXBUFFER9 pVB, int offset, + float startWidth, float startHeight, + float stopWidth, float stopHeight); + + //code for providing graphics settings + DWORD _totalMemory; + DWORD _availableMemory; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_DIRECT3D9_H_ diff --git a/webrtc/modules/video_render/windows/video_render_windows_impl.cc b/webrtc/modules/video_render/windows/video_render_windows_impl.cc new file mode 100644 index 0000000000..042d7fdfa3 --- /dev/null +++ b/webrtc/modules/video_render/windows/video_render_windows_impl.cc @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/engine_configurations.h" +#include "webrtc/modules/video_render/windows/video_render_windows_impl.h" + +#include "webrtc/system_wrappers/include/critical_section_wrapper.h" +#include "webrtc/system_wrappers/include/trace.h" +#ifdef DIRECT3D9_RENDERING +#include "webrtc/modules/video_render/windows/video_render_direct3d9.h" +#endif + +#include + +namespace webrtc { + +VideoRenderWindowsImpl::VideoRenderWindowsImpl(const int32_t id, + const VideoRenderType videoRenderType, void* window, const bool fullscreen) + : _renderWindowsCritsect(*CriticalSectionWrapper::CreateCriticalSection()), + _prtWindow(window), + _fullscreen(fullscreen), + _renderMethod(kVideoRenderWinD3D9), + _ptrRendererWin(NULL) { +} + +VideoRenderWindowsImpl::~VideoRenderWindowsImpl() +{ + delete &_renderWindowsCritsect; + if (_ptrRendererWin) + { + delete _ptrRendererWin; + _ptrRendererWin = NULL; + } +} + +int32_t VideoRenderWindowsImpl::Init() +{ + // Create the win renderer + switch (_renderMethod) + { + case kVideoRenderWinD3D9: + { +#ifdef DIRECT3D9_RENDERING + VideoRenderDirect3D9* ptrRenderer; + ptrRenderer = new VideoRenderDirect3D9(NULL, (HWND) _prtWindow, _fullscreen); + if (ptrRenderer == NULL) + { + break; + } + _ptrRendererWin = reinterpret_cast(ptrRenderer); +#else + return NULL; +#endif //DIRECT3D9_RENDERING + } + break; + default: + break; + } + + //Init renderer + if (_ptrRendererWin) + return _ptrRendererWin->Init(); + else + return -1; +} + +int32_t VideoRenderWindowsImpl::ChangeWindow(void* window) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + if (!_ptrRendererWin) + { + return -1; + } + else + { + return _ptrRendererWin->ChangeWindow(window); + } +} + +VideoRenderCallback* +VideoRenderWindowsImpl::AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + VideoRenderCallback* renderCallback = NULL; + + if (!_ptrRendererWin) + { + } + else + { + renderCallback = _ptrRendererWin->CreateChannel(streamId, zOrder, left, + top, right, bottom); + } + + return renderCallback; +} + +int32_t VideoRenderWindowsImpl::DeleteIncomingRenderStream( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->DeleteChannel(streamId); + } + return error; +} + +int32_t VideoRenderWindowsImpl::GetIncomingRenderStreamProperties( + const uint32_t streamId, + uint32_t& zOrder, + float& left, + float& top, + float& right, + float& bottom) const +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + zOrder = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->GetStreamSettings(streamId, 0, zOrder, left, + top, right, bottom); + } + return error; +} + +int32_t VideoRenderWindowsImpl::StartRender() +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->StartRender(); + } + return error; +} + +int32_t VideoRenderWindowsImpl::StopRender() +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->StopRender(); + } + return error; +} + +VideoRenderType VideoRenderWindowsImpl::RenderType() +{ + return kRenderWindows; +} + +RawVideoType VideoRenderWindowsImpl::PerferedVideoType() +{ + return kVideoI420; +} + +bool VideoRenderWindowsImpl::FullScreen() +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + bool fullscreen = false; + if (!_ptrRendererWin) + { + } + else + { + fullscreen = _ptrRendererWin->IsFullScreen(); + } + return fullscreen; +} + +int32_t VideoRenderWindowsImpl::GetGraphicsMemory( + uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const +{ + if (_ptrRendererWin) + { + return _ptrRendererWin->GetGraphicsMemory(totalGraphicsMemory, + availableGraphicsMemory); + } + + totalGraphicsMemory = 0; + availableGraphicsMemory = 0; + return -1; +} + +int32_t VideoRenderWindowsImpl::GetScreenResolution( + uint32_t& screenWidth, + uint32_t& screenHeight) const +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + screenWidth = 0; + screenHeight = 0; + return 0; +} + +uint32_t VideoRenderWindowsImpl::RenderFrameRate( + const uint32_t streamId) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + return 0; +} + +int32_t VideoRenderWindowsImpl::SetStreamCropping( + const uint32_t streamId, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->SetCropping(streamId, 0, left, top, right, + bottom); + } + return error; +} + +int32_t VideoRenderWindowsImpl::ConfigureRenderer( + const uint32_t streamId, + const unsigned int zOrder, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->ConfigureRenderer(streamId, 0, zOrder, left, + top, right, bottom); + } + + return error; +} + +int32_t VideoRenderWindowsImpl::SetTransparentBackground( + const bool enable) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->SetTransparentBackground(enable); + } + return error; +} + +int32_t VideoRenderWindowsImpl::SetText( + const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, + const float top, + const float right, + const float bottom) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->SetText(textId, text, textLength, + textColorRef, backgroundColorRef, + left, top, right, bottom); + } + return error; +} + +int32_t VideoRenderWindowsImpl::SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom) +{ + CriticalSectionScoped cs(&_renderWindowsCritsect); + int32_t error = -1; + if (!_ptrRendererWin) + { + } + else + { + error = _ptrRendererWin->SetBitmap(bitMap, pictureId, colorKey, left, + top, right, bottom); + } + return error; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_render/windows/video_render_windows_impl.h b/webrtc/modules/video_render/windows/video_render_windows_impl.h new file mode 100644 index 0000000000..aaa3f81fc7 --- /dev/null +++ b/webrtc/modules/video_render/windows/video_render_windows_impl.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ +#define WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ + +#include +#include + +#include "webrtc/modules/video_render/i_video_render.h" +#include "webrtc/modules/video_render/windows/i_video_render_win.h" + +namespace webrtc { +class CriticalSectionWrapper; + +#define EXPAND(x) x, sizeof(x)/sizeof(TCHAR) + +enum VideoRenderWinMethod { + kVideoRenderWinD3D9 = 0, +}; + +// Class definitions +class VideoRenderWindowsImpl: IVideoRender +{ +public: + /* + * Constructor/destructor + */ + + VideoRenderWindowsImpl(const int32_t id, + const VideoRenderType videoRenderType, + void* window, const bool fullscreen); + + virtual ~VideoRenderWindowsImpl(); + + virtual int32_t Init(); + + virtual int32_t ChangeWindow(void* window); + + /************************************************************************** + * + * Incoming Streams + * + ***************************************************************************/ + + virtual VideoRenderCallback + * AddIncomingRenderStream(const uint32_t streamId, + const uint32_t zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t + DeleteIncomingRenderStream(const uint32_t streamId); + + virtual int32_t + GetIncomingRenderStreamProperties(const uint32_t streamId, + uint32_t& zOrder, + float& left, float& top, + float& right, float& bottom) const; + + /************************************************************************** + * + * Start/Stop + * + ***************************************************************************/ + + virtual int32_t StartRender(); + + virtual int32_t StopRender(); + + /************************************************************************** + * + * Properties + * + ***************************************************************************/ + + virtual VideoRenderType RenderType(); + + virtual RawVideoType PerferedVideoType(); + + virtual bool FullScreen(); + + virtual int32_t + GetGraphicsMemory(uint64_t& totalGraphicsMemory, + uint64_t& availableGraphicsMemory) const; + + virtual int32_t + GetScreenResolution(uint32_t& screenWidth, + uint32_t& screenHeight) const; + + virtual uint32_t RenderFrameRate(const uint32_t streamId); + + virtual int32_t SetStreamCropping(const uint32_t streamId, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t ConfigureRenderer(const uint32_t streamId, + const unsigned int zOrder, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetTransparentBackground(const bool enable); + + virtual int32_t SetText(const uint8_t textId, + const uint8_t* text, + const int32_t textLength, + const uint32_t textColorRef, + const uint32_t backgroundColorRef, + const float left, const float top, + const float right, const float bottom); + + virtual int32_t SetBitmap(const void* bitMap, + const uint8_t pictureId, + const void* colorKey, + const float left, const float top, + const float right, const float bottom); + +private: + CriticalSectionWrapper& _renderWindowsCritsect; + + void* _prtWindow; + bool _fullscreen; + + VideoRenderWinMethod _renderMethod; + IVideoRenderWin* _ptrRendererWin; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_RENDER_MAIN_SOURCE_WINDOWS_VIDEO_RENDER_WINDOWS_IMPL_H_ diff --git a/webrtc/test/test.gyp b/webrtc/test/test.gyp index a04a128a0e..65349ccff4 100644 --- a/webrtc/test/test.gyp +++ b/webrtc/test/test.gyp @@ -304,6 +304,7 @@ '<(webrtc_root)/base/base.gyp:rtc_base_approved', '<(webrtc_root)/common.gyp:webrtc_common', '<(webrtc_root)/modules/modules.gyp:media_file', + '<(webrtc_root)/modules/modules.gyp:video_render', '<(webrtc_root)/webrtc.gyp:webrtc', 'rtp_test_utils', 'test_support', diff --git a/webrtc/video/BUILD.gn b/webrtc/video/BUILD.gn index da5ffc3b01..4f1b7ae197 100644 --- a/webrtc/video/BUILD.gn +++ b/webrtc/video/BUILD.gn @@ -71,6 +71,7 @@ source_set("video") { "../modules/video_capture:video_capture_module", "../modules/video_coding", "../modules/video_processing", + "../modules/video_render:video_render_module", "../system_wrappers", "../voice_engine", ] diff --git a/webrtc/video/DEPS b/webrtc/video/DEPS index 7e53144d33..8c54066fd9 100644 --- a/webrtc/video/DEPS +++ b/webrtc/video/DEPS @@ -12,6 +12,7 @@ include_rules = [ "+webrtc/modules/video_coding", "+webrtc/modules/video_capture", "+webrtc/modules/video_processing", + "+webrtc/modules/video_render", "+webrtc/system_wrappers", "+webrtc/voice_engine", ] diff --git a/webrtc/video/video_capture_input.cc b/webrtc/video/video_capture_input.cc index 8f574e2115..54b41b5006 100644 --- a/webrtc/video/video_capture_input.cc +++ b/webrtc/video/video_capture_input.cc @@ -16,6 +16,7 @@ #include "webrtc/modules/include/module_common_types.h" #include "webrtc/modules/video_capture/video_capture_factory.h" #include "webrtc/modules/video_processing/include/video_processing.h" +#include "webrtc/modules/video_render/video_render_defines.h" #include "webrtc/video/overuse_frame_detector.h" #include "webrtc/video/send_statistics_proxy.h" #include "webrtc/video/vie_encoder.h" diff --git a/webrtc/video/video_receive_stream.h b/webrtc/video/video_receive_stream.h index a0edcac8d4..316e281fcc 100644 --- a/webrtc/video/video_receive_stream.h +++ b/webrtc/video/video_receive_stream.h @@ -18,6 +18,7 @@ #include "webrtc/call/transport_adapter.h" #include "webrtc/common_video/include/incoming_video_stream.h" #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "webrtc/modules/video_render/video_render_defines.h" #include "webrtc/modules/video_coding/video_coding_impl.h" #include "webrtc/system_wrappers/include/clock.h" #include "webrtc/video/encoded_frame_callback_adapter.h" diff --git a/webrtc/video/vie_channel.cc b/webrtc/video/vie_channel.cc index a1bdfb0333..a8ee4bcad0 100644 --- a/webrtc/video/vie_channel.cc +++ b/webrtc/video/vie_channel.cc @@ -25,6 +25,7 @@ #include "webrtc/modules/utility/include/process_thread.h" #include "webrtc/modules/video_coding/video_coding_impl.h" #include "webrtc/modules/video_processing/include/video_processing.h" +#include "webrtc/modules/video_render/video_render_defines.h" #include "webrtc/system_wrappers/include/metrics.h" #include "webrtc/video/call_stats.h" #include "webrtc/video/payload_router.h" diff --git a/webrtc/video/webrtc_video.gypi b/webrtc/video/webrtc_video.gypi index f7e1fd937b..f11ce95727 100644 --- a/webrtc/video/webrtc_video.gypi +++ b/webrtc/video/webrtc_video.gypi @@ -17,6 +17,7 @@ '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', '<(webrtc_root)/modules/modules.gyp:video_capture_module', '<(webrtc_root)/modules/modules.gyp:video_processing', + '<(webrtc_root)/modules/modules.gyp:video_render_module', '<(webrtc_root)/modules/modules.gyp:webrtc_utility', '<(webrtc_root)/modules/modules.gyp:webrtc_video_coding', '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers', diff --git a/webrtc/webrtc.gyp b/webrtc/webrtc.gyp index abc48e981e..8e7c99bc2b 100644 --- a/webrtc/webrtc.gyp +++ b/webrtc/webrtc.gyp @@ -148,6 +148,7 @@ ['build_with_chromium==1', { 'dependencies': [ '<(webrtc_root)/modules/modules.gyp:video_capture', + '<(webrtc_root)/modules/modules.gyp:video_render', ], }], ], diff --git a/webrtc/webrtc_tests.gypi b/webrtc/webrtc_tests.gypi index e10aec119d..57996f29b1 100644 --- a/webrtc/webrtc_tests.gypi +++ b/webrtc/webrtc_tests.gypi @@ -57,6 +57,7 @@ ], 'dependencies': [ '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/modules/modules.gyp:video_render', '<(webrtc_root)/modules/modules.gyp:video_capture_module_internal_impl', '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers', 'webrtc', @@ -143,6 +144,7 @@ 'test/test.gyp:test_common', 'test/test.gyp:test_renderer', '<(webrtc_root)/modules/modules.gyp:video_capture', + '<(webrtc_root)/modules/modules.gyp:video_render', '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers_default', 'webrtc', ], @@ -182,6 +184,7 @@ '<(webrtc_root)/common.gyp:webrtc_common', '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', '<(webrtc_root)/modules/modules.gyp:video_capture', + '<(webrtc_root)/modules/modules.gyp:video_render', '<(webrtc_root)/test/test.gyp:channel_transport', '<(webrtc_root)/voice_engine/voice_engine.gyp:voice_engine', 'test/metrics.gyp:metrics',