| 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
|
|
|