Index: webrtc/sdk/android/src/jni/video_renderer_jni.cc |
diff --git a/webrtc/sdk/android/src/jni/video_renderer_jni.cc b/webrtc/sdk/android/src/jni/video_renderer_jni.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f05facbf0e661a8e7453086375245db6b09753a3 |
--- /dev/null |
+++ b/webrtc/sdk/android/src/jni/video_renderer_jni.cc |
@@ -0,0 +1,169 @@ |
+/* |
+ * Copyright (c) 2017 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 <jni.h> |
+#undef JNIEXPORT |
+#define JNIEXPORT __attribute__((visibility("default"))) |
+ |
+#include "webrtc/api/video/video_frame.h" |
+#include "webrtc/media/base/videosinkinterface.h" |
+#include "webrtc/sdk/android/src/jni/classreferenceholder.h" |
+#include "webrtc/sdk/android/src/jni/jni_helpers.h" |
+#include "webrtc/sdk/android/src/jni/native_handle_impl.h" |
+ |
+namespace webrtc_jni { |
+ |
+// Wrapper dispatching rtc::VideoSinkInterface to a Java VideoRenderer |
+// instance. |
+class JavaVideoRendererWrapper |
+ : public rtc::VideoSinkInterface<webrtc::VideoFrame> { |
+ public: |
+ JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks) |
+ : j_callbacks_(jni, j_callbacks), |
+ j_render_frame_id_( |
+ GetMethodID(jni, |
+ GetObjectClass(jni, j_callbacks), |
+ "renderFrame", |
+ "(Lorg/webrtc/VideoRenderer$I420Frame;)V")), |
+ j_frame_class_(jni, |
+ FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")), |
+ j_i420_frame_ctor_id_(GetMethodID(jni, |
+ *j_frame_class_, |
+ "<init>", |
+ "(III[I[Ljava/nio/ByteBuffer;J)V")), |
+ j_texture_frame_ctor_id_( |
+ GetMethodID(jni, *j_frame_class_, "<init>", "(IIII[FJ)V")), |
+ j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) { |
+ CHECK_EXCEPTION(jni); |
+ } |
+ |
+ virtual ~JavaVideoRendererWrapper() {} |
+ |
+ void OnFrame(const webrtc::VideoFrame& video_frame) override { |
+ ScopedLocalRefFrame local_ref_frame(jni()); |
+ jobject j_frame = |
+ (video_frame.video_frame_buffer()->native_handle() != nullptr) |
+ ? CricketToJavaTextureFrame(&video_frame) |
+ : CricketToJavaI420Frame(&video_frame); |
+ // |j_callbacks_| is responsible for releasing |j_frame| with |
+ // VideoRenderer.renderFrameDone(). |
+ jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, j_frame); |
+ CHECK_EXCEPTION(jni()); |
+ } |
+ |
+ private: |
+ // Make a shallow copy of |frame| to be used with Java. The callee has |
+ // ownership of the frame, and the frame should be released with |
+ // VideoRenderer.releaseNativeFrame(). |
+ static jlong javaShallowCopy(const webrtc::VideoFrame* frame) { |
+ return jlongFromPointer(new webrtc::VideoFrame(*frame)); |
+ } |
+ |
+ // Return a VideoRenderer.I420Frame referring to the data in |frame|. |
+ jobject CricketToJavaI420Frame(const webrtc::VideoFrame* frame) { |
+ jintArray strides = jni()->NewIntArray(3); |
+ jint* strides_array = jni()->GetIntArrayElements(strides, NULL); |
+ strides_array[0] = frame->video_frame_buffer()->StrideY(); |
+ strides_array[1] = frame->video_frame_buffer()->StrideU(); |
+ strides_array[2] = frame->video_frame_buffer()->StrideV(); |
+ jni()->ReleaseIntArrayElements(strides, strides_array, 0); |
+ jobjectArray planes = jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL); |
+ jobject y_buffer = jni()->NewDirectByteBuffer( |
+ const_cast<uint8_t*>(frame->video_frame_buffer()->DataY()), |
+ frame->video_frame_buffer()->StrideY() * |
+ frame->video_frame_buffer()->height()); |
+ size_t chroma_height = (frame->height() + 1) / 2; |
+ jobject u_buffer = jni()->NewDirectByteBuffer( |
+ const_cast<uint8_t*>(frame->video_frame_buffer()->DataU()), |
+ frame->video_frame_buffer()->StrideU() * chroma_height); |
+ jobject v_buffer = jni()->NewDirectByteBuffer( |
+ const_cast<uint8_t*>(frame->video_frame_buffer()->DataV()), |
+ frame->video_frame_buffer()->StrideV() * chroma_height); |
+ |
+ jni()->SetObjectArrayElement(planes, 0, y_buffer); |
+ jni()->SetObjectArrayElement(planes, 1, u_buffer); |
+ jni()->SetObjectArrayElement(planes, 2, v_buffer); |
+ return jni()->NewObject(*j_frame_class_, j_i420_frame_ctor_id_, |
+ frame->width(), frame->height(), |
+ static_cast<int>(frame->rotation()), strides, |
+ planes, javaShallowCopy(frame)); |
+ } |
+ |
+ // Return a VideoRenderer.I420Frame referring texture object in |frame|. |
+ jobject CricketToJavaTextureFrame(const webrtc::VideoFrame* frame) { |
+ NativeHandleImpl* handle = reinterpret_cast<NativeHandleImpl*>( |
+ frame->video_frame_buffer()->native_handle()); |
+ jfloatArray sampling_matrix = handle->sampling_matrix.ToJava(jni()); |
+ |
+ return jni()->NewObject( |
+ *j_frame_class_, j_texture_frame_ctor_id_, frame->width(), |
+ frame->height(), static_cast<int>(frame->rotation()), |
+ handle->oes_texture_id, sampling_matrix, javaShallowCopy(frame)); |
+ } |
+ |
+ JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } |
+ |
+ ScopedGlobalRef<jobject> j_callbacks_; |
+ jmethodID j_render_frame_id_; |
+ ScopedGlobalRef<jclass> j_frame_class_; |
+ jmethodID j_i420_frame_ctor_id_; |
+ jmethodID j_texture_frame_ctor_id_; |
+ ScopedGlobalRef<jclass> j_byte_buffer_class_; |
+}; |
+ |
+JOW(void, VideoRenderer_freeWrappedVideoRenderer)(JNIEnv*, jclass, jlong j_p) { |
+ delete reinterpret_cast<JavaVideoRendererWrapper*>(j_p); |
+} |
+ |
+JOW(void, VideoRenderer_releaseNativeFrame) |
+(JNIEnv* jni, jclass, jlong j_frame_ptr) { |
+ delete reinterpret_cast<const webrtc::VideoFrame*>(j_frame_ptr); |
+} |
+ |
+JOW(jlong, VideoRenderer_nativeWrapVideoRenderer) |
+(JNIEnv* jni, jclass, jobject j_callbacks) { |
+ std::unique_ptr<JavaVideoRendererWrapper> renderer( |
+ new JavaVideoRendererWrapper(jni, j_callbacks)); |
+ return (jlong)renderer.release(); |
+} |
+ |
+JOW(void, VideoRenderer_nativeCopyPlane) |
+(JNIEnv* jni, |
+ jclass, |
+ jobject j_src_buffer, |
+ jint width, |
+ jint height, |
+ jint src_stride, |
+ jobject j_dst_buffer, |
+ jint dst_stride) { |
+ size_t src_size = jni->GetDirectBufferCapacity(j_src_buffer); |
+ size_t dst_size = jni->GetDirectBufferCapacity(j_dst_buffer); |
+ RTC_CHECK(src_stride >= width) << "Wrong source stride " << src_stride; |
+ RTC_CHECK(dst_stride >= width) << "Wrong destination stride " << dst_stride; |
+ RTC_CHECK(src_size >= src_stride * height) |
+ << "Insufficient source buffer capacity " << src_size; |
+ RTC_CHECK(dst_size >= dst_stride * height) |
+ << "Insufficient destination buffer capacity " << dst_size; |
+ uint8_t* src = |
+ reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_src_buffer)); |
+ uint8_t* dst = |
+ reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_dst_buffer)); |
+ if (src_stride == dst_stride) { |
+ memcpy(dst, src, src_stride * height); |
+ } else { |
+ for (int i = 0; i < height; i++) { |
+ memcpy(dst, src, width); |
+ src += src_stride; |
+ dst += dst_stride; |
+ } |
+ } |
+} |
+ |
+} // namespace webrtc_jni |