blob: 02ea1b31c06785fc9dd607dbd7542a63e971584c [file] [log] [blame]
/*
* 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>
#include "api/video/video_frame.h"
#include "api/videosinkinterface.h"
#include "sdk/android/generated_video_jni/jni/VideoRenderer_jni.h"
#include "sdk/android/src/jni/jni_helpers.h"
#include "sdk/android/src/jni/videoframe.h"
namespace webrtc {
namespace jni {
// Wrapper dispatching rtc::VideoSinkInterface to a Java VideoRenderer
// instance.
class JavaVideoRendererWrapper : public rtc::VideoSinkInterface<VideoFrame> {
public:
JavaVideoRendererWrapper(JNIEnv* jni, const JavaRef<jobject>& j_callbacks)
: j_callbacks_(jni, j_callbacks) {}
~JavaVideoRendererWrapper() override {}
void OnFrame(const VideoFrame& video_frame) override {
JNIEnv* env = AttachCurrentThreadIfNeeded();
ScopedJavaLocalRef<jobject> j_frame;
if (video_frame.video_frame_buffer()->type() ==
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(env, video_frame);
break;
case AndroidVideoFrameBuffer::AndroidType::kJavaBuffer:
j_frame = FromWrappedJavaBuffer(env, video_frame);
break;
default:
RTC_NOTREACHED();
}
} else {
j_frame = ToJavaI420Frame(env, video_frame);
}
// |j_callbacks_| is responsible for releasing |j_frame| with
// VideoRenderer.renderFrameDone().
Java_Callbacks_renderFrame(env, j_callbacks_, j_frame);
}
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 VideoFrame& frame) {
return jlongFromPointer(new VideoFrame(frame));
}
// Return a VideoRenderer.I420Frame referring to the data in |frame|.
ScopedJavaLocalRef<jobject> FromWrappedJavaBuffer(JNIEnv* env,
const VideoFrame& frame) {
return Java_I420Frame_Constructor(
env, frame.rotation(),
static_cast<AndroidVideoBuffer*>(frame.video_frame_buffer().get())
->video_frame_buffer(),
javaShallowCopy(frame));
}
// Return a VideoRenderer.I420Frame referring to the data in |frame|.
ScopedJavaLocalRef<jobject> ToJavaI420Frame(JNIEnv* env,
const VideoFrame& frame) {
rtc::scoped_refptr<I420BufferInterface> i420_buffer =
frame.video_frame_buffer()->ToI420();
ScopedJavaLocalRef<jobject> y_buffer =
NewDirectByteBuffer(env, const_cast<uint8_t*>(i420_buffer->DataY()),
i420_buffer->StrideY() * i420_buffer->height());
size_t chroma_height = i420_buffer->ChromaHeight();
ScopedJavaLocalRef<jobject> u_buffer =
NewDirectByteBuffer(env, const_cast<uint8_t*>(i420_buffer->DataU()),
i420_buffer->StrideU() * chroma_height);
ScopedJavaLocalRef<jobject> v_buffer =
NewDirectByteBuffer(env, const_cast<uint8_t*>(i420_buffer->DataV()),
i420_buffer->StrideV() * chroma_height);
return Java_I420Frame_createI420Frame(
env, frame.width(), frame.height(), static_cast<int>(frame.rotation()),
i420_buffer->StrideY(), y_buffer, i420_buffer->StrideU(), u_buffer,
i420_buffer->StrideV(), v_buffer, javaShallowCopy(frame));
}
// Return a VideoRenderer.I420Frame referring texture object in |frame|.
ScopedJavaLocalRef<jobject> ToJavaTextureFrame(JNIEnv* env,
const VideoFrame& frame) {
NativeHandleImpl handle =
static_cast<AndroidTextureBuffer*>(frame.video_frame_buffer().get())
->native_handle_impl();
return Java_I420Frame_createTextureFrame(
env, frame.width(), frame.height(), static_cast<int>(frame.rotation()),
handle.oes_texture_id, handle.sampling_matrix.ToJava(env),
javaShallowCopy(frame));
}
ScopedJavaGlobalRef<jobject> j_callbacks_;
};
static void JNI_VideoRenderer_FreeWrappedVideoRenderer(
JNIEnv*,
const JavaParamRef<jclass>&,
jlong j_p) {
delete reinterpret_cast<JavaVideoRendererWrapper*>(j_p);
}
static void JNI_VideoRenderer_ReleaseFrame(JNIEnv* jni,
const JavaParamRef<jclass>&,
jlong j_frame_ptr) {
delete reinterpret_cast<const VideoFrame*>(j_frame_ptr);
}
static jlong JNI_VideoRenderer_CreateVideoRenderer(
JNIEnv* jni,
const JavaParamRef<jclass>&,
const JavaParamRef<jobject>& j_callbacks) {
std::unique_ptr<JavaVideoRendererWrapper> renderer(
new JavaVideoRendererWrapper(jni, j_callbacks));
return jlongFromPointer(renderer.release());
}
static void JNI_VideoRenderer_CopyPlane(
JNIEnv* jni,
const JavaParamRef<jclass>&,
const JavaParamRef<jobject>& j_src_buffer,
jint width,
jint height,
jint src_stride,
const JavaParamRef<jobject>& j_dst_buffer,
jint dst_stride) {
size_t src_size = jni->GetDirectBufferCapacity(j_src_buffer.obj());
size_t dst_size = jni->GetDirectBufferCapacity(j_dst_buffer.obj());
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.obj()));
uint8_t* dst = reinterpret_cast<uint8_t*>(
jni->GetDirectBufferAddress(j_dst_buffer.obj()));
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 jni
} // namespace webrtc