| /* |
| * 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 <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 { |
| |
| 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<jclass>(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, |
| "<init>", |
| "(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<unsigned char*> |
| (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 |