| 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..b5af0003f94bf0bba3f12d87eafa56a162b05dcb
|
| --- /dev/null
|
| +++ b/webrtc/sdk/android/src/jni/video_renderer_jni.cc
|
| @@ -0,0 +1,190 @@
|
| +/*
|
| + * 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;
|
| + if (video_frame.video_frame_buffer()->type() ==
|
| + webrtc::VideoFrameBuffer::Type::kNative) {
|
| + AndroidVideoFrameBuffer* android_buffer =
|
| + static_cast<AndroidVideoFrameBuffer*>(
|
| + video_frame.video_frame_buffer().get());
|
| + switch (android_buffer->android_type()) {
|
| + case AndroidVideoFrameBuffer::AndroidType::kTextureBuffer:
|
| + j_frame = ToJavaTextureFrame(&video_frame);
|
| + break;
|
| + case AndroidVideoFrameBuffer::AndroidType::kJavaBuffer:
|
| + j_frame = static_cast<AndroidVideoBuffer*>(android_buffer)
|
| + ->ToJavaI420Frame(jni(), video_frame.width(),
|
| + video_frame.height(),
|
| + video_frame.rotation());
|
| + break;
|
| + default:
|
| + RTC_NOTREACHED();
|
| + }
|
| + } else {
|
| + j_frame = ToJavaI420Frame(&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 ToJavaI420Frame(const webrtc::VideoFrame* frame) {
|
| + jintArray strides = jni()->NewIntArray(3);
|
| + jint* strides_array = jni()->GetIntArrayElements(strides, NULL);
|
| + rtc::scoped_refptr<webrtc::I420BufferInterface> i420_buffer =
|
| + frame->video_frame_buffer()->ToI420();
|
| + strides_array[0] = i420_buffer->StrideY();
|
| + strides_array[1] = i420_buffer->StrideU();
|
| + strides_array[2] = i420_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*>(i420_buffer->DataY()),
|
| + i420_buffer->StrideY() * i420_buffer->height());
|
| + size_t chroma_height = i420_buffer->ChromaHeight();
|
| + jobject u_buffer =
|
| + jni()->NewDirectByteBuffer(const_cast<uint8_t*>(i420_buffer->DataU()),
|
| + i420_buffer->StrideU() * chroma_height);
|
| + jobject v_buffer =
|
| + jni()->NewDirectByteBuffer(const_cast<uint8_t*>(i420_buffer->DataV()),
|
| + i420_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 ToJavaTextureFrame(const webrtc::VideoFrame* frame) {
|
| + NativeHandleImpl handle =
|
| + static_cast<AndroidTextureBuffer*>(frame->video_frame_buffer().get())
|
| + ->native_handle_impl();
|
| + 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
|
|
|