1 /*
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "sdk/android/src/jni/video_frame.h"
12
13 #include "api/scoped_refptr.h"
14 #include "common_video/include/video_frame_buffer.h"
15 #include "rtc_base/time_utils.h"
16 #include "sdk/android/generated_video_jni/VideoFrame_jni.h"
17 #include "sdk/android/src/jni/jni_helpers.h"
18 #include "sdk/android/src/jni/wrapped_native_i420_buffer.h"
19
20 namespace webrtc {
21 namespace jni {
22
23 namespace {
24
25 class AndroidVideoBuffer : public VideoFrameBuffer {
26 public:
27 // Creates a native VideoFrameBuffer from a Java VideoFrame.Buffer.
28 static rtc::scoped_refptr<AndroidVideoBuffer> Create(
29 JNIEnv* jni,
30 const JavaRef<jobject>& j_video_frame_buffer);
31
32 // Similar to the Create() above, but adopts and takes ownership of the Java
33 // VideoFrame.Buffer. I.e. retain() will not be called, but release() will be
34 // called when the returned AndroidVideoBuffer is destroyed.
35 static rtc::scoped_refptr<AndroidVideoBuffer> Adopt(
36 JNIEnv* jni,
37 const JavaRef<jobject>& j_video_frame_buffer);
38
39 ~AndroidVideoBuffer() override;
40
41 const ScopedJavaGlobalRef<jobject>& video_frame_buffer() const;
42
43 // Crops a region defined by `crop_x`, `crop_y`, `crop_width` and
44 // `crop_height`. Scales it to size `scale_width` x `scale_height`.
45 rtc::scoped_refptr<VideoFrameBuffer> CropAndScale(int crop_x,
46 int crop_y,
47 int crop_width,
48 int crop_height,
49 int scale_width,
50 int scale_height) override;
51
52 protected:
53 // Should not be called directly. Adopts the Java VideoFrame.Buffer. Use
54 // Create() or Adopt() instead for clarity.
55 AndroidVideoBuffer(JNIEnv* jni, const JavaRef<jobject>& j_video_frame_buffer);
56
57 private:
58 Type type() const override;
59 int width() const override;
60 int height() const override;
61
62 rtc::scoped_refptr<I420BufferInterface> ToI420() override;
63
64 const int width_;
65 const int height_;
66 // Holds a VideoFrame.Buffer.
67 const ScopedJavaGlobalRef<jobject> j_video_frame_buffer_;
68 };
69
70 class AndroidVideoI420Buffer : public I420BufferInterface {
71 public:
72 // Creates a native VideoFrameBuffer from a Java VideoFrame.I420Buffer.
73 static rtc::scoped_refptr<AndroidVideoI420Buffer> Create(
74 JNIEnv* jni,
75 int width,
76 int height,
77 const JavaRef<jobject>& j_video_frame_buffer);
78
79 // Adopts and takes ownership of the Java VideoFrame.Buffer. I.e. retain()
80 // will not be called, but release() will be called when the returned
81 // AndroidVideoBuffer is destroyed.
82 static rtc::scoped_refptr<AndroidVideoI420Buffer> Adopt(
83 JNIEnv* jni,
84 int width,
85 int height,
86 const JavaRef<jobject>& j_video_frame_buffer);
87
88 protected:
89 // Should not be called directly. Adopts the buffer. Use Adopt() instead for
90 // clarity.
91 AndroidVideoI420Buffer(JNIEnv* jni,
92 int width,
93 int height,
94 const JavaRef<jobject>& j_video_frame_buffer);
95 ~AndroidVideoI420Buffer() override;
96
97 private:
DataY() const98 const uint8_t* DataY() const override { return data_y_; }
DataU() const99 const uint8_t* DataU() const override { return data_u_; }
DataV() const100 const uint8_t* DataV() const override { return data_v_; }
101
StrideY() const102 int StrideY() const override { return stride_y_; }
StrideU() const103 int StrideU() const override { return stride_u_; }
StrideV() const104 int StrideV() const override { return stride_v_; }
105
width() const106 int width() const override { return width_; }
height() const107 int height() const override { return height_; }
108
109 const int width_;
110 const int height_;
111 // Holds a VideoFrame.I420Buffer.
112 const ScopedJavaGlobalRef<jobject> j_video_frame_buffer_;
113
114 const uint8_t* data_y_;
115 const uint8_t* data_u_;
116 const uint8_t* data_v_;
117 int stride_y_;
118 int stride_u_;
119 int stride_v_;
120 };
121
Create(JNIEnv * jni,int width,int height,const JavaRef<jobject> & j_video_frame_buffer)122 rtc::scoped_refptr<AndroidVideoI420Buffer> AndroidVideoI420Buffer::Create(
123 JNIEnv* jni,
124 int width,
125 int height,
126 const JavaRef<jobject>& j_video_frame_buffer) {
127 Java_Buffer_retain(jni, j_video_frame_buffer);
128 return AndroidVideoI420Buffer::Adopt(jni, width, height,
129 j_video_frame_buffer);
130 }
131
Adopt(JNIEnv * jni,int width,int height,const JavaRef<jobject> & j_video_frame_buffer)132 rtc::scoped_refptr<AndroidVideoI420Buffer> AndroidVideoI420Buffer::Adopt(
133 JNIEnv* jni,
134 int width,
135 int height,
136 const JavaRef<jobject>& j_video_frame_buffer) {
137 RTC_DCHECK_EQ(
138 static_cast<Type>(Java_Buffer_getBufferType(jni, j_video_frame_buffer)),
139 Type::kI420);
140 return rtc::make_ref_counted<AndroidVideoI420Buffer>(jni, width, height,
141 j_video_frame_buffer);
142 }
143
AndroidVideoI420Buffer(JNIEnv * jni,int width,int height,const JavaRef<jobject> & j_video_frame_buffer)144 AndroidVideoI420Buffer::AndroidVideoI420Buffer(
145 JNIEnv* jni,
146 int width,
147 int height,
148 const JavaRef<jobject>& j_video_frame_buffer)
149 : width_(width),
150 height_(height),
151 j_video_frame_buffer_(jni, j_video_frame_buffer) {
152 ScopedJavaLocalRef<jobject> j_data_y =
153 Java_I420Buffer_getDataY(jni, j_video_frame_buffer);
154 ScopedJavaLocalRef<jobject> j_data_u =
155 Java_I420Buffer_getDataU(jni, j_video_frame_buffer);
156 ScopedJavaLocalRef<jobject> j_data_v =
157 Java_I420Buffer_getDataV(jni, j_video_frame_buffer);
158
159 data_y_ =
160 static_cast<const uint8_t*>(jni->GetDirectBufferAddress(j_data_y.obj()));
161 data_u_ =
162 static_cast<const uint8_t*>(jni->GetDirectBufferAddress(j_data_u.obj()));
163 data_v_ =
164 static_cast<const uint8_t*>(jni->GetDirectBufferAddress(j_data_v.obj()));
165
166 stride_y_ = Java_I420Buffer_getStrideY(jni, j_video_frame_buffer);
167 stride_u_ = Java_I420Buffer_getStrideU(jni, j_video_frame_buffer);
168 stride_v_ = Java_I420Buffer_getStrideV(jni, j_video_frame_buffer);
169 }
170
~AndroidVideoI420Buffer()171 AndroidVideoI420Buffer::~AndroidVideoI420Buffer() {
172 JNIEnv* jni = AttachCurrentThreadIfNeeded();
173 Java_Buffer_release(jni, j_video_frame_buffer_);
174 }
175
176 } // namespace
177
GetJavaVideoFrameTimestampNs(JNIEnv * jni,const JavaRef<jobject> & j_video_frame)178 int64_t GetJavaVideoFrameTimestampNs(JNIEnv* jni,
179 const JavaRef<jobject>& j_video_frame) {
180 return Java_VideoFrame_getTimestampNs(jni, j_video_frame);
181 }
182
Adopt(JNIEnv * jni,const JavaRef<jobject> & j_video_frame_buffer)183 rtc::scoped_refptr<AndroidVideoBuffer> AndroidVideoBuffer::Adopt(
184 JNIEnv* jni,
185 const JavaRef<jobject>& j_video_frame_buffer) {
186 RTC_DCHECK_EQ(
187 static_cast<Type>(Java_Buffer_getBufferType(jni, j_video_frame_buffer)),
188 Type::kNative);
189 return rtc::make_ref_counted<AndroidVideoBuffer>(jni, j_video_frame_buffer);
190 }
191
Create(JNIEnv * jni,const JavaRef<jobject> & j_video_frame_buffer)192 rtc::scoped_refptr<AndroidVideoBuffer> AndroidVideoBuffer::Create(
193 JNIEnv* jni,
194 const JavaRef<jobject>& j_video_frame_buffer) {
195 Java_Buffer_retain(jni, j_video_frame_buffer);
196 return Adopt(jni, j_video_frame_buffer);
197 }
198
AndroidVideoBuffer(JNIEnv * jni,const JavaRef<jobject> & j_video_frame_buffer)199 AndroidVideoBuffer::AndroidVideoBuffer(
200 JNIEnv* jni,
201 const JavaRef<jobject>& j_video_frame_buffer)
202 : width_(Java_Buffer_getWidth(jni, j_video_frame_buffer)),
203 height_(Java_Buffer_getHeight(jni, j_video_frame_buffer)),
204 j_video_frame_buffer_(jni, j_video_frame_buffer) {}
205
~AndroidVideoBuffer()206 AndroidVideoBuffer::~AndroidVideoBuffer() {
207 JNIEnv* jni = AttachCurrentThreadIfNeeded();
208 Java_Buffer_release(jni, j_video_frame_buffer_);
209 }
210
video_frame_buffer() const211 const ScopedJavaGlobalRef<jobject>& AndroidVideoBuffer::video_frame_buffer()
212 const {
213 return j_video_frame_buffer_;
214 }
215
CropAndScale(int crop_x,int crop_y,int crop_width,int crop_height,int scale_width,int scale_height)216 rtc::scoped_refptr<VideoFrameBuffer> AndroidVideoBuffer::CropAndScale(
217 int crop_x,
218 int crop_y,
219 int crop_width,
220 int crop_height,
221 int scale_width,
222 int scale_height) {
223 JNIEnv* jni = AttachCurrentThreadIfNeeded();
224 return Adopt(jni, Java_Buffer_cropAndScale(jni, j_video_frame_buffer_, crop_x,
225 crop_y, crop_width, crop_height,
226 scale_width, scale_height));
227 }
228
type() const229 VideoFrameBuffer::Type AndroidVideoBuffer::type() const {
230 return Type::kNative;
231 }
232
width() const233 int AndroidVideoBuffer::width() const {
234 return width_;
235 }
236
height() const237 int AndroidVideoBuffer::height() const {
238 return height_;
239 }
240
ToI420()241 rtc::scoped_refptr<I420BufferInterface> AndroidVideoBuffer::ToI420() {
242 JNIEnv* jni = AttachCurrentThreadIfNeeded();
243 ScopedJavaLocalRef<jobject> j_i420_buffer =
244 Java_Buffer_toI420(jni, j_video_frame_buffer_);
245 // In case I420 conversion fails, we propagate the nullptr.
246 if (j_i420_buffer.is_null()) {
247 return nullptr;
248 }
249
250 // We don't need to retain the buffer because toI420 returns a new object that
251 // we are assumed to take the ownership of.
252 return AndroidVideoI420Buffer::Adopt(jni, width_, height_, j_i420_buffer);
253 }
254
JavaToNativeFrameBuffer(JNIEnv * jni,const JavaRef<jobject> & j_video_frame_buffer)255 rtc::scoped_refptr<VideoFrameBuffer> JavaToNativeFrameBuffer(
256 JNIEnv* jni,
257 const JavaRef<jobject>& j_video_frame_buffer) {
258 VideoFrameBuffer::Type type = static_cast<VideoFrameBuffer::Type>(
259 Java_Buffer_getBufferType(jni, j_video_frame_buffer));
260 switch (type) {
261 case VideoFrameBuffer::Type::kI420: {
262 const int width = Java_Buffer_getWidth(jni, j_video_frame_buffer);
263 const int height = Java_Buffer_getHeight(jni, j_video_frame_buffer);
264 return AndroidVideoI420Buffer::Create(jni, width, height,
265 j_video_frame_buffer);
266 }
267 case VideoFrameBuffer::Type::kNative:
268 return AndroidVideoBuffer::Create(jni, j_video_frame_buffer);
269 default:
270 RTC_CHECK_NOTREACHED();
271 }
272 }
273
JavaToNativeFrame(JNIEnv * jni,const JavaRef<jobject> & j_video_frame,uint32_t timestamp_rtp)274 VideoFrame JavaToNativeFrame(JNIEnv* jni,
275 const JavaRef<jobject>& j_video_frame,
276 uint32_t timestamp_rtp) {
277 ScopedJavaLocalRef<jobject> j_video_frame_buffer =
278 Java_VideoFrame_getBuffer(jni, j_video_frame);
279 int rotation = Java_VideoFrame_getRotation(jni, j_video_frame);
280 int64_t timestamp_ns = Java_VideoFrame_getTimestampNs(jni, j_video_frame);
281 rtc::scoped_refptr<VideoFrameBuffer> buffer =
282 JavaToNativeFrameBuffer(jni, j_video_frame_buffer);
283 return VideoFrame::Builder()
284 .set_video_frame_buffer(buffer)
285 .set_timestamp_rtp(timestamp_rtp)
286 .set_timestamp_ms(timestamp_ns / rtc::kNumNanosecsPerMillisec)
287 .set_rotation(static_cast<VideoRotation>(rotation))
288 .build();
289 }
290
NativeToJavaVideoFrame(JNIEnv * jni,const VideoFrame & frame)291 ScopedJavaLocalRef<jobject> NativeToJavaVideoFrame(JNIEnv* jni,
292 const VideoFrame& frame) {
293 rtc::scoped_refptr<VideoFrameBuffer> buffer = frame.video_frame_buffer();
294
295 if (buffer->type() == VideoFrameBuffer::Type::kNative) {
296 AndroidVideoBuffer* android_buffer =
297 static_cast<AndroidVideoBuffer*>(buffer.get());
298 ScopedJavaLocalRef<jobject> j_video_frame_buffer(
299 jni, android_buffer->video_frame_buffer());
300 Java_Buffer_retain(jni, j_video_frame_buffer);
301 return Java_VideoFrame_Constructor(
302 jni, j_video_frame_buffer, static_cast<jint>(frame.rotation()),
303 static_cast<jlong>(frame.timestamp_us() *
304 rtc::kNumNanosecsPerMicrosec));
305 } else {
306 return Java_VideoFrame_Constructor(
307 jni, WrapI420Buffer(jni, buffer->ToI420()),
308 static_cast<jint>(frame.rotation()),
309 static_cast<jlong>(frame.timestamp_us() *
310 rtc::kNumNanosecsPerMicrosec));
311 }
312 }
313
ReleaseJavaVideoFrame(JNIEnv * jni,const JavaRef<jobject> & j_video_frame)314 void ReleaseJavaVideoFrame(JNIEnv* jni, const JavaRef<jobject>& j_video_frame) {
315 Java_VideoFrame_release(jni, j_video_frame);
316 }
317
318 } // namespace jni
319 } // namespace webrtc
320