| /* |
| * 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 <android/log.h> |
| #include <stdio.h> |
| |
| #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<jclass> (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<AndroidNativeOpenGl2Channel*>(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<AndroidNativeOpenGl2Channel*> (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 |