xref: /aosp_15_r20/external/webrtc/sdk/android/src/jni/video_frame.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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