Use the underlying type of Java Video Buffer on Java -> C++ Frame Buffer
Just like the C++ API, add a method in Java VideoFrame.Buffer that describes the underlying implementation. Use this method to properly select AndroidVideoBuffer or AndroidVideoI420Buffer in Java -> C++ Video Frame Conversion. Also, add a test case for WrappedNativeI420Buffer in VideoFrameBufferTest for consistency. Bug: webrtc:12602 Change-Id: I4c0444e8af6f6a1109bc514e7ab6c2214f1f6d60 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/223080 Commit-Queue: Byoungchan Lee <daniel.l@hpcnt.com> Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Xavier Lepaul <xalep@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34545}
This commit is contained in:
committed by
WebRTC LUCI CQ
parent
d2b885fd91
commit
f740c252e7
@ -69,6 +69,12 @@ rtc_library("video_frame") {
|
|||||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_android) {
|
||||||
|
java_cpp_enum("video_frame_enums") {
|
||||||
|
sources = [ "video_frame_buffer.h" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rtc_library("video_frame_i010") {
|
rtc_library("video_frame_i010") {
|
||||||
visibility = [ "*" ]
|
visibility = [ "*" ]
|
||||||
sources = [
|
sources = [
|
||||||
|
|||||||
@ -46,6 +46,8 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface {
|
|||||||
// New frame buffer types will be added conservatively when there is an
|
// New frame buffer types will be added conservatively when there is an
|
||||||
// opportunity to optimize the path between some pair of video source and
|
// opportunity to optimize the path between some pair of video source and
|
||||||
// video sink.
|
// video sink.
|
||||||
|
// GENERATED_JAVA_ENUM_PACKAGE: org.webrtc
|
||||||
|
// GENERATED_JAVA_CLASS_NAME_OVERRIDE: VideoFrameBufferType
|
||||||
enum class Type {
|
enum class Type {
|
||||||
kNative,
|
kNative,
|
||||||
kI420,
|
kI420,
|
||||||
|
|||||||
@ -202,7 +202,9 @@ if (is_android) {
|
|||||||
":base_java",
|
":base_java",
|
||||||
"//rtc_base:base_java",
|
"//rtc_base:base_java",
|
||||||
"//third_party/android_deps:com_android_support_support_annotations_java",
|
"//third_party/android_deps:com_android_support_support_annotations_java",
|
||||||
|
"//third_party/androidx:androidx_annotation_annotation_java",
|
||||||
]
|
]
|
||||||
|
srcjar_deps = [ "//api/video:video_frame_enums" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_android_library("video_java") {
|
rtc_android_library("video_java") {
|
||||||
@ -1486,11 +1488,16 @@ if (is_android) {
|
|||||||
|
|
||||||
rtc_library("instrumentationtests_jni") {
|
rtc_library("instrumentationtests_jni") {
|
||||||
testonly = true
|
testonly = true
|
||||||
sources = [ "instrumentationtests/loggable_test.cc" ]
|
sources = [
|
||||||
|
"instrumentationtests/loggable_test.cc",
|
||||||
|
"instrumentationtests/video_frame_buffer_test.cc",
|
||||||
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":base_jni",
|
":base_jni",
|
||||||
":native_api_jni",
|
":native_api_jni",
|
||||||
|
":videoframe_jni",
|
||||||
|
"../../api/video:video_frame",
|
||||||
"../../rtc_base:rtc_base_approved",
|
"../../rtc_base:rtc_base_approved",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,15 @@ public class VideoFrame implements RefCounted {
|
|||||||
* and the buffer needs to be returned to the VideoSource as soon as all references are gone.
|
* and the buffer needs to be returned to the VideoSource as soon as all references are gone.
|
||||||
*/
|
*/
|
||||||
public interface Buffer extends RefCounted {
|
public interface Buffer extends RefCounted {
|
||||||
|
/**
|
||||||
|
* Representation of the underlying buffer. Currently, only NATIVE and I420 are supported.
|
||||||
|
*/
|
||||||
|
@CalledByNative("Buffer")
|
||||||
|
@VideoFrameBufferType
|
||||||
|
default int getBufferType() {
|
||||||
|
return VideoFrameBufferType.NATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolution of the buffer in pixels.
|
* Resolution of the buffer in pixels.
|
||||||
*/
|
*/
|
||||||
@ -63,6 +72,11 @@ public class VideoFrame implements RefCounted {
|
|||||||
* Interface for I420 buffers.
|
* Interface for I420 buffers.
|
||||||
*/
|
*/
|
||||||
public interface I420Buffer extends Buffer {
|
public interface I420Buffer extends Buffer {
|
||||||
|
@Override
|
||||||
|
default int getBufferType() {
|
||||||
|
return VideoFrameBufferType.I420;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
|
* Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
|
||||||
* getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
|
* getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import org.chromium.base.test.params.ParameterizedRunner;
|
|||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.webrtc.VideoFrame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test VideoFrame buffers of different kind of formats: I420, RGB, OES, NV12, NV21, and verify
|
* Test VideoFrame buffers of different kind of formats: I420, RGB, OES, NV12, NV21, and verify
|
||||||
@ -45,7 +46,7 @@ public class VideoFrameBufferTest {
|
|||||||
/**
|
/**
|
||||||
* These tests are parameterized on this enum which represents the different VideoFrame.Buffers.
|
* These tests are parameterized on this enum which represents the different VideoFrame.Buffers.
|
||||||
*/
|
*/
|
||||||
private static enum BufferType { I420, RGB_TEXTURE, OES_TEXTURE, NV21, NV12 }
|
private static enum BufferType { I420_JAVA, I420_NATIVE, RGB_TEXTURE, OES_TEXTURE, NV21, NV12 }
|
||||||
|
|
||||||
@ClassParameter private static List<ParameterSet> CLASS_PARAMS = new ArrayList<>();
|
@ClassParameter private static List<ParameterSet> CLASS_PARAMS = new ArrayList<>();
|
||||||
|
|
||||||
@ -75,17 +76,39 @@ public class VideoFrameBufferTest {
|
|||||||
*/
|
*/
|
||||||
private static VideoFrame.Buffer createBufferWithType(
|
private static VideoFrame.Buffer createBufferWithType(
|
||||||
BufferType bufferType, VideoFrame.I420Buffer i420Buffer) {
|
BufferType bufferType, VideoFrame.I420Buffer i420Buffer) {
|
||||||
|
VideoFrame.Buffer buffer;
|
||||||
switch (bufferType) {
|
switch (bufferType) {
|
||||||
case I420:
|
case I420_JAVA:
|
||||||
return i420Buffer.toI420();
|
buffer = i420Buffer;
|
||||||
|
buffer.retain();
|
||||||
|
assertEquals(VideoFrameBufferType.I420, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.I420, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
|
case I420_NATIVE:
|
||||||
|
buffer = nativeGetNativeI420Buffer(i420Buffer);
|
||||||
|
assertEquals(VideoFrameBufferType.I420, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.I420, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
case RGB_TEXTURE:
|
case RGB_TEXTURE:
|
||||||
return createRgbTextureBuffer(/* eglContext= */ null, i420Buffer);
|
buffer = createRgbTextureBuffer(/* eglContext= */ null, i420Buffer);
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
case OES_TEXTURE:
|
case OES_TEXTURE:
|
||||||
return createOesTextureBuffer(/* eglContext= */ null, i420Buffer);
|
buffer = createOesTextureBuffer(/* eglContext= */ null, i420Buffer);
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
case NV21:
|
case NV21:
|
||||||
return createNV21Buffer(i420Buffer);
|
buffer = createNV21Buffer(i420Buffer);
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
case NV12:
|
case NV12:
|
||||||
return createNV12Buffer(i420Buffer);
|
buffer = createNV12Buffer(i420Buffer);
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, buffer.getBufferType());
|
||||||
|
assertEquals(VideoFrameBufferType.NATIVE, nativeGetBufferType(buffer));
|
||||||
|
return buffer;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown buffer type: " + bufferType);
|
throw new IllegalArgumentException("Unknown buffer type: " + bufferType);
|
||||||
}
|
}
|
||||||
@ -452,6 +475,7 @@ public class VideoFrameBufferTest {
|
|||||||
final VideoFrame.I420Buffer outputI420Buffer = bufferToTest.toI420();
|
final VideoFrame.I420Buffer outputI420Buffer = bufferToTest.toI420();
|
||||||
bufferToTest.release();
|
bufferToTest.release();
|
||||||
|
|
||||||
|
assertEquals(VideoFrameBufferType.I420, nativeGetBufferType(outputI420Buffer));
|
||||||
assertAlmostEqualI420Buffers(referenceI420Buffer, outputI420Buffer);
|
assertAlmostEqualI420Buffers(referenceI420Buffer, outputI420Buffer);
|
||||||
referenceI420Buffer.release();
|
referenceI420Buffer.release();
|
||||||
outputI420Buffer.release();
|
outputI420Buffer.release();
|
||||||
@ -504,4 +528,10 @@ public class VideoFrameBufferTest {
|
|||||||
testCropAndScale(4 /* cropX= */, 4 /* cropY= */, /* cropWidth= */ 12, /* cropHeight= */ 12,
|
testCropAndScale(4 /* cropX= */, 4 /* cropY= */, /* cropWidth= */ 12, /* cropHeight= */ 12,
|
||||||
/* scaleWidth= */ 8, /* scaleHeight= */ 8);
|
/* scaleWidth= */ 8, /* scaleHeight= */ 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VideoFrameBufferType private static native int nativeGetBufferType(VideoFrame.Buffer buffer);
|
||||||
|
|
||||||
|
/** Returns the copy of I420Buffer using WrappedNativeI420Buffer. */
|
||||||
|
private static native VideoFrame.Buffer nativeGetNativeI420Buffer(
|
||||||
|
VideoFrame.I420Buffer i420Buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
45
sdk/android/instrumentationtests/video_frame_buffer_test.cc
Normal file
45
sdk/android/instrumentationtests/video_frame_buffer_test.cc
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 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 "api/video/i420_buffer.h"
|
||||||
|
#include "sdk/android/src/jni/jni_helpers.h"
|
||||||
|
#include "sdk/android/src/jni/video_frame.h"
|
||||||
|
#include "sdk/android/src/jni/wrapped_native_i420_buffer.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
JNI_FUNCTION_DECLARATION(jint,
|
||||||
|
VideoFrameBufferTest_nativeGetBufferType,
|
||||||
|
JNIEnv* jni,
|
||||||
|
jclass,
|
||||||
|
jobject video_frame_buffer) {
|
||||||
|
const JavaParamRef<jobject> j_video_frame_buffer(video_frame_buffer);
|
||||||
|
rtc::scoped_refptr<VideoFrameBuffer> buffer =
|
||||||
|
JavaToNativeFrameBuffer(jni, j_video_frame_buffer);
|
||||||
|
return static_cast<jint>(buffer->type());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNI_FUNCTION_DECLARATION(jobject,
|
||||||
|
VideoFrameBufferTest_nativeGetNativeI420Buffer,
|
||||||
|
JNIEnv* jni,
|
||||||
|
jclass,
|
||||||
|
jobject i420_buffer) {
|
||||||
|
const JavaParamRef<jobject> j_i420_buffer(i420_buffer);
|
||||||
|
rtc::scoped_refptr<VideoFrameBuffer> buffer =
|
||||||
|
JavaToNativeFrameBuffer(jni, j_i420_buffer);
|
||||||
|
const I420BufferInterface* inputBuffer = buffer->GetI420();
|
||||||
|
RTC_DCHECK(inputBuffer != nullptr);
|
||||||
|
rtc::scoped_refptr<I420Buffer> outputBuffer = I420Buffer::Copy(*inputBuffer);
|
||||||
|
return WrapI420Buffer(jni, outputBuffer).Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jni
|
||||||
|
} // namespace webrtc
|
||||||
@ -70,6 +70,13 @@ class AndroidVideoBuffer : public VideoFrameBuffer {
|
|||||||
|
|
||||||
class AndroidVideoI420Buffer : public I420BufferInterface {
|
class AndroidVideoI420Buffer : public I420BufferInterface {
|
||||||
public:
|
public:
|
||||||
|
// Creates a native VideoFrameBuffer from a Java VideoFrame.I420Buffer.
|
||||||
|
static rtc::scoped_refptr<AndroidVideoI420Buffer> Create(
|
||||||
|
JNIEnv* jni,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const JavaRef<jobject>& j_video_frame_buffer);
|
||||||
|
|
||||||
// Adopts and takes ownership of the Java VideoFrame.Buffer. I.e. retain()
|
// Adopts and takes ownership of the Java VideoFrame.Buffer. I.e. retain()
|
||||||
// will not be called, but release() will be called when the returned
|
// will not be called, but release() will be called when the returned
|
||||||
// AndroidVideoBuffer is destroyed.
|
// AndroidVideoBuffer is destroyed.
|
||||||
@ -113,11 +120,24 @@ class AndroidVideoI420Buffer : public I420BufferInterface {
|
|||||||
int stride_v_;
|
int stride_v_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rtc::scoped_refptr<AndroidVideoI420Buffer> AndroidVideoI420Buffer::Create(
|
||||||
|
JNIEnv* jni,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
const JavaRef<jobject>& j_video_frame_buffer) {
|
||||||
|
Java_Buffer_retain(jni, j_video_frame_buffer);
|
||||||
|
return AndroidVideoI420Buffer::Adopt(jni, width, height,
|
||||||
|
j_video_frame_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
rtc::scoped_refptr<AndroidVideoI420Buffer> AndroidVideoI420Buffer::Adopt(
|
rtc::scoped_refptr<AndroidVideoI420Buffer> AndroidVideoI420Buffer::Adopt(
|
||||||
JNIEnv* jni,
|
JNIEnv* jni,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
const JavaRef<jobject>& j_video_frame_buffer) {
|
const JavaRef<jobject>& j_video_frame_buffer) {
|
||||||
|
RTC_DCHECK_EQ(
|
||||||
|
static_cast<Type>(Java_Buffer_getBufferType(jni, j_video_frame_buffer)),
|
||||||
|
Type::kI420);
|
||||||
return rtc::make_ref_counted<AndroidVideoI420Buffer>(jni, width, height,
|
return rtc::make_ref_counted<AndroidVideoI420Buffer>(jni, width, height,
|
||||||
j_video_frame_buffer);
|
j_video_frame_buffer);
|
||||||
}
|
}
|
||||||
@ -164,6 +184,9 @@ int64_t GetJavaVideoFrameTimestampNs(JNIEnv* jni,
|
|||||||
rtc::scoped_refptr<AndroidVideoBuffer> AndroidVideoBuffer::Adopt(
|
rtc::scoped_refptr<AndroidVideoBuffer> AndroidVideoBuffer::Adopt(
|
||||||
JNIEnv* jni,
|
JNIEnv* jni,
|
||||||
const JavaRef<jobject>& j_video_frame_buffer) {
|
const JavaRef<jobject>& j_video_frame_buffer) {
|
||||||
|
RTC_DCHECK_EQ(
|
||||||
|
static_cast<Type>(Java_Buffer_getBufferType(jni, j_video_frame_buffer)),
|
||||||
|
Type::kNative);
|
||||||
return rtc::make_ref_counted<AndroidVideoBuffer>(jni, j_video_frame_buffer);
|
return rtc::make_ref_counted<AndroidVideoBuffer>(jni, j_video_frame_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +256,20 @@ rtc::scoped_refptr<I420BufferInterface> AndroidVideoBuffer::ToI420() {
|
|||||||
rtc::scoped_refptr<VideoFrameBuffer> JavaToNativeFrameBuffer(
|
rtc::scoped_refptr<VideoFrameBuffer> JavaToNativeFrameBuffer(
|
||||||
JNIEnv* jni,
|
JNIEnv* jni,
|
||||||
const JavaRef<jobject>& j_video_frame_buffer) {
|
const JavaRef<jobject>& j_video_frame_buffer) {
|
||||||
return AndroidVideoBuffer::Create(jni, j_video_frame_buffer);
|
VideoFrameBuffer::Type type = static_cast<VideoFrameBuffer::Type>(
|
||||||
|
Java_Buffer_getBufferType(jni, j_video_frame_buffer));
|
||||||
|
switch (type) {
|
||||||
|
case VideoFrameBuffer::Type::kI420: {
|
||||||
|
const int width = Java_Buffer_getWidth(jni, j_video_frame_buffer);
|
||||||
|
const int height = Java_Buffer_getHeight(jni, j_video_frame_buffer);
|
||||||
|
return AndroidVideoI420Buffer::Create(jni, width, height,
|
||||||
|
j_video_frame_buffer);
|
||||||
|
}
|
||||||
|
case VideoFrameBuffer::Type::kNative:
|
||||||
|
return AndroidVideoBuffer::Create(jni, j_video_frame_buffer);
|
||||||
|
default:
|
||||||
|
RTC_CHECK_NOTREACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoFrame JavaToNativeFrame(JNIEnv* jni,
|
VideoFrame JavaToNativeFrame(JNIEnv* jni,
|
||||||
@ -243,8 +279,8 @@ VideoFrame JavaToNativeFrame(JNIEnv* jni,
|
|||||||
Java_VideoFrame_getBuffer(jni, j_video_frame);
|
Java_VideoFrame_getBuffer(jni, j_video_frame);
|
||||||
int rotation = Java_VideoFrame_getRotation(jni, j_video_frame);
|
int rotation = Java_VideoFrame_getRotation(jni, j_video_frame);
|
||||||
int64_t timestamp_ns = Java_VideoFrame_getTimestampNs(jni, j_video_frame);
|
int64_t timestamp_ns = Java_VideoFrame_getTimestampNs(jni, j_video_frame);
|
||||||
rtc::scoped_refptr<AndroidVideoBuffer> buffer =
|
rtc::scoped_refptr<VideoFrameBuffer> buffer =
|
||||||
AndroidVideoBuffer::Create(jni, j_video_frame_buffer);
|
JavaToNativeFrameBuffer(jni, j_video_frame_buffer);
|
||||||
return VideoFrame::Builder()
|
return VideoFrame::Builder()
|
||||||
.set_video_frame_buffer(buffer)
|
.set_video_frame_buffer(buffer)
|
||||||
.set_timestamp_rtp(timestamp_rtp)
|
.set_timestamp_rtp(timestamp_rtp)
|
||||||
|
|||||||
Reference in New Issue
Block a user