OLD | NEW |
| (Empty) |
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 "webrtc/api/java/jni/androidvideocapturer_jni.h" | |
12 #include "webrtc/api/java/jni/classreferenceholder.h" | |
13 #include "webrtc/api/java/jni/native_handle_impl.h" | |
14 #include "webrtc/api/java/jni/surfacetexturehelper_jni.h" | |
15 #include "third_party/libyuv/include/libyuv/convert.h" | |
16 #include "webrtc/base/bind.h" | |
17 | |
18 namespace webrtc_jni { | |
19 | |
20 jobject AndroidVideoCapturerJni::application_context_ = nullptr; | |
21 | |
22 // static | |
23 int AndroidVideoCapturerJni::SetAndroidObjects(JNIEnv* jni, | |
24 jobject appliction_context) { | |
25 if (application_context_) { | |
26 jni->DeleteGlobalRef(application_context_); | |
27 } | |
28 application_context_ = NewGlobalRef(jni, appliction_context); | |
29 | |
30 return 0; | |
31 } | |
32 | |
33 AndroidVideoCapturerJni::AndroidVideoCapturerJni(JNIEnv* jni, | |
34 jobject j_video_capturer, | |
35 jobject j_egl_context) | |
36 : j_video_capturer_(jni, j_video_capturer), | |
37 j_video_capturer_class_(jni, FindClass(jni, "org/webrtc/VideoCapturer")), | |
38 j_observer_class_( | |
39 jni, | |
40 FindClass(jni, | |
41 "org/webrtc/VideoCapturer$NativeObserver")), | |
42 surface_texture_helper_(SurfaceTextureHelper::create( | |
43 jni, "Camera SurfaceTextureHelper", j_egl_context)), | |
44 capturer_(nullptr) { | |
45 LOG(LS_INFO) << "AndroidVideoCapturerJni ctor"; | |
46 thread_checker_.DetachFromThread(); | |
47 } | |
48 | |
49 AndroidVideoCapturerJni::~AndroidVideoCapturerJni() { | |
50 LOG(LS_INFO) << "AndroidVideoCapturerJni dtor"; | |
51 jni()->CallVoidMethod( | |
52 *j_video_capturer_, | |
53 GetMethodID(jni(), *j_video_capturer_class_, "dispose", "()V")); | |
54 CHECK_EXCEPTION(jni()) << "error during VideoCapturer.dispose()"; | |
55 } | |
56 | |
57 void AndroidVideoCapturerJni::Start(int width, int height, int framerate, | |
58 webrtc::AndroidVideoCapturer* capturer) { | |
59 LOG(LS_INFO) << "AndroidVideoCapturerJni start"; | |
60 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
61 { | |
62 rtc::CritScope cs(&capturer_lock_); | |
63 RTC_CHECK(capturer_ == nullptr); | |
64 RTC_CHECK(invoker_.get() == nullptr); | |
65 capturer_ = capturer; | |
66 invoker_.reset(new rtc::GuardedAsyncInvoker()); | |
67 } | |
68 jobject j_frame_observer = | |
69 jni()->NewObject(*j_observer_class_, | |
70 GetMethodID(jni(), *j_observer_class_, "<init>", "(J)V"), | |
71 jlongFromPointer(this)); | |
72 CHECK_EXCEPTION(jni()) << "error during NewObject"; | |
73 | |
74 jmethodID m = GetMethodID( | |
75 jni(), *j_video_capturer_class_, "startCapture", | |
76 "(IIILorg/webrtc/SurfaceTextureHelper;Landroid/content/Context;" | |
77 "Lorg/webrtc/VideoCapturer$CapturerObserver;)V"); | |
78 jni()->CallVoidMethod( | |
79 *j_video_capturer_, m, width, height, framerate, | |
80 surface_texture_helper_ | |
81 ? surface_texture_helper_->GetJavaSurfaceTextureHelper() | |
82 : nullptr, | |
83 application_context_, j_frame_observer); | |
84 CHECK_EXCEPTION(jni()) << "error during VideoCapturer.startCapture"; | |
85 } | |
86 | |
87 void AndroidVideoCapturerJni::Stop() { | |
88 LOG(LS_INFO) << "AndroidVideoCapturerJni stop"; | |
89 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
90 { | |
91 // TODO(nisse): Consider moving this block until *after* the call to | |
92 // stopCapturer. stopCapturer should ensure that we get no | |
93 // more frames, and then we shouldn't need the if (!capturer_) | |
94 // checks in OnMemoryBufferFrame and OnTextureFrame. | |
95 rtc::CritScope cs(&capturer_lock_); | |
96 // Destroying |invoker_| will cancel all pending calls to |capturer_|. | |
97 invoker_ = nullptr; | |
98 capturer_ = nullptr; | |
99 } | |
100 jmethodID m = GetMethodID(jni(), *j_video_capturer_class_, | |
101 "stopCapture", "()V"); | |
102 jni()->CallVoidMethod(*j_video_capturer_, m); | |
103 CHECK_EXCEPTION(jni()) << "error during VideoCapturer.stopCapture"; | |
104 LOG(LS_INFO) << "AndroidVideoCapturerJni stop done"; | |
105 } | |
106 | |
107 template <typename... Args> | |
108 void AndroidVideoCapturerJni::AsyncCapturerInvoke( | |
109 const rtc::Location& posted_from, | |
110 void (webrtc::AndroidVideoCapturer::*method)(Args...), | |
111 typename Identity<Args>::type... args) { | |
112 rtc::CritScope cs(&capturer_lock_); | |
113 if (!invoker_) { | |
114 LOG(LS_WARNING) << posted_from.function_name() | |
115 << "() called for closed capturer."; | |
116 return; | |
117 } | |
118 invoker_->AsyncInvoke<void>(posted_from, | |
119 rtc::Bind(method, capturer_, args...)); | |
120 } | |
121 | |
122 std::vector<cricket::VideoFormat> | |
123 AndroidVideoCapturerJni::GetSupportedFormats() { | |
124 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
125 jobject j_list_of_formats = jni->CallObjectMethod( | |
126 *j_video_capturer_, | |
127 GetMethodID(jni, *j_video_capturer_class_, "getSupportedFormats", | |
128 "()Ljava/util/List;")); | |
129 CHECK_EXCEPTION(jni) << "error during getSupportedFormats"; | |
130 | |
131 // Extract Java List<CaptureFormat> to std::vector<cricket::VideoFormat>. | |
132 jclass j_list_class = jni->FindClass("java/util/List"); | |
133 jclass j_format_class = | |
134 jni->FindClass("org/webrtc/CameraEnumerationAndroid$CaptureFormat"); | |
135 jclass j_framerate_class = jni->FindClass( | |
136 "org/webrtc/CameraEnumerationAndroid$CaptureFormat$FramerateRange"); | |
137 const int size = jni->CallIntMethod( | |
138 j_list_of_formats, GetMethodID(jni, j_list_class, "size", "()I")); | |
139 jmethodID j_get = | |
140 GetMethodID(jni, j_list_class, "get", "(I)Ljava/lang/Object;"); | |
141 jfieldID j_framerate_field = GetFieldID( | |
142 jni, j_format_class, "framerate", | |
143 "Lorg/webrtc/CameraEnumerationAndroid$CaptureFormat$FramerateRange;"); | |
144 jfieldID j_width_field = GetFieldID(jni, j_format_class, "width", "I"); | |
145 jfieldID j_height_field = GetFieldID(jni, j_format_class, "height", "I"); | |
146 jfieldID j_max_framerate_field = | |
147 GetFieldID(jni, j_framerate_class, "max", "I"); | |
148 | |
149 std::vector<cricket::VideoFormat> formats; | |
150 formats.reserve(size); | |
151 for (int i = 0; i < size; ++i) { | |
152 jobject j_format = jni->CallObjectMethod(j_list_of_formats, j_get, i); | |
153 jobject j_framerate = GetObjectField(jni, j_format, j_framerate_field); | |
154 const int frame_interval = cricket::VideoFormat::FpsToInterval( | |
155 (GetIntField(jni, j_framerate, j_max_framerate_field) + 999) / 1000); | |
156 formats.emplace_back(GetIntField(jni, j_format, j_width_field), | |
157 GetIntField(jni, j_format, j_height_field), | |
158 frame_interval, cricket::FOURCC_NV21); | |
159 } | |
160 CHECK_EXCEPTION(jni) << "error while extracting formats"; | |
161 return formats; | |
162 } | |
163 | |
164 void AndroidVideoCapturerJni::OnCapturerStarted(bool success) { | |
165 LOG(LS_INFO) << "AndroidVideoCapturerJni capture started: " << success; | |
166 AsyncCapturerInvoke( | |
167 RTC_FROM_HERE, &webrtc::AndroidVideoCapturer::OnCapturerStarted, success); | |
168 } | |
169 | |
170 void AndroidVideoCapturerJni::OnMemoryBufferFrame(void* video_frame, | |
171 int length, | |
172 int width, | |
173 int height, | |
174 int rotation, | |
175 int64_t timestamp_ns) { | |
176 RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 || | |
177 rotation == 270); | |
178 rtc::CritScope cs(&capturer_lock_); | |
179 if (!capturer_) { | |
180 LOG(LS_WARNING) << "OnMemoryBufferFrame() called for closed capturer."; | |
181 return; | |
182 } | |
183 int adapted_width; | |
184 int adapted_height; | |
185 int crop_width; | |
186 int crop_height; | |
187 int crop_x; | |
188 int crop_y; | |
189 int64_t translated_camera_time_us; | |
190 | |
191 if (!capturer_->AdaptFrame(width, height, | |
192 timestamp_ns / rtc::kNumNanosecsPerMicrosec, | |
193 rtc::TimeMicros(), | |
194 &adapted_width, &adapted_height, | |
195 &crop_width, &crop_height, &crop_x, &crop_y, | |
196 &translated_camera_time_us)) { | |
197 return; | |
198 } | |
199 | |
200 int rotated_width = crop_width; | |
201 int rotated_height = crop_height; | |
202 | |
203 if (capturer_->apply_rotation() && (rotation == 90 || rotation == 270)) { | |
204 std::swap(adapted_width, adapted_height); | |
205 std::swap(rotated_width, rotated_height); | |
206 } | |
207 | |
208 rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer = | |
209 pre_scale_pool_.CreateBuffer(rotated_width, rotated_height); | |
210 | |
211 const uint8_t* y_plane = static_cast<const uint8_t*>(video_frame); | |
212 const uint8_t* uv_plane = y_plane + width * height; | |
213 | |
214 // Can only crop at even pixels. | |
215 crop_x &= ~1; | |
216 crop_y &= ~1; | |
217 int uv_width = (width + 1) / 2; | |
218 | |
219 libyuv::NV12ToI420Rotate( | |
220 y_plane + width * crop_y + crop_x, width, | |
221 uv_plane + uv_width * crop_y + crop_x, width, | |
222 buffer->MutableDataY(), buffer->StrideY(), | |
223 // Swap U and V, since we have NV21, not NV12. | |
224 buffer->MutableDataV(), buffer->StrideV(), | |
225 buffer->MutableDataU(), buffer->StrideU(), | |
226 crop_width, crop_height, static_cast<libyuv::RotationMode>( | |
227 capturer_->apply_rotation() ? rotation : 0)); | |
228 | |
229 if (adapted_width != buffer->width() || adapted_height != buffer->height()) { | |
230 rtc::scoped_refptr<webrtc::I420Buffer> scaled_buffer( | |
231 post_scale_pool_.CreateBuffer(adapted_width, adapted_height)); | |
232 scaled_buffer->ScaleFrom(buffer); | |
233 buffer = scaled_buffer; | |
234 } | |
235 capturer_->OnFrame(cricket::WebRtcVideoFrame( | |
236 buffer, | |
237 capturer_->apply_rotation() | |
238 ? webrtc::kVideoRotation_0 | |
239 : static_cast<webrtc::VideoRotation>(rotation), | |
240 translated_camera_time_us), | |
241 width, height); | |
242 } | |
243 | |
244 void AndroidVideoCapturerJni::OnTextureFrame(int width, | |
245 int height, | |
246 int rotation, | |
247 int64_t timestamp_ns, | |
248 const NativeHandleImpl& handle) { | |
249 RTC_DCHECK(rotation == 0 || rotation == 90 || rotation == 180 || | |
250 rotation == 270); | |
251 rtc::CritScope cs(&capturer_lock_); | |
252 if (!capturer_) { | |
253 LOG(LS_WARNING) << "OnTextureFrame() called for closed capturer."; | |
254 surface_texture_helper_->ReturnTextureFrame(); | |
255 return; | |
256 } | |
257 int adapted_width; | |
258 int adapted_height; | |
259 int crop_width; | |
260 int crop_height; | |
261 int crop_x; | |
262 int crop_y; | |
263 int64_t translated_camera_time_us; | |
264 | |
265 if (!capturer_->AdaptFrame(width, height, | |
266 timestamp_ns / rtc::kNumNanosecsPerMicrosec, | |
267 rtc::TimeMicros(), | |
268 &adapted_width, &adapted_height, | |
269 &crop_width, &crop_height, &crop_x, &crop_y, | |
270 &translated_camera_time_us)) { | |
271 surface_texture_helper_->ReturnTextureFrame(); | |
272 return; | |
273 } | |
274 | |
275 Matrix matrix = handle.sampling_matrix; | |
276 | |
277 matrix.Crop(crop_width / static_cast<float>(width), | |
278 crop_height / static_cast<float>(height), | |
279 crop_x / static_cast<float>(width), | |
280 crop_y / static_cast<float>(height)); | |
281 | |
282 if (capturer_->apply_rotation()) { | |
283 if (rotation == webrtc::kVideoRotation_90 || | |
284 rotation == webrtc::kVideoRotation_270) { | |
285 std::swap(adapted_width, adapted_height); | |
286 } | |
287 matrix.Rotate(static_cast<webrtc::VideoRotation>(rotation)); | |
288 } | |
289 | |
290 capturer_->OnFrame( | |
291 cricket::WebRtcVideoFrame( | |
292 surface_texture_helper_->CreateTextureFrame( | |
293 adapted_width, adapted_height, | |
294 NativeHandleImpl(handle.oes_texture_id, matrix)), | |
295 capturer_->apply_rotation() | |
296 ? webrtc::kVideoRotation_0 | |
297 : static_cast<webrtc::VideoRotation>(rotation), | |
298 translated_camera_time_us), | |
299 width, height); | |
300 } | |
301 | |
302 void AndroidVideoCapturerJni::OnOutputFormatRequest(int width, | |
303 int height, | |
304 int fps) { | |
305 AsyncCapturerInvoke(RTC_FROM_HERE, | |
306 &webrtc::AndroidVideoCapturer::OnOutputFormatRequest, | |
307 width, height, fps); | |
308 } | |
309 | |
310 JNIEnv* AndroidVideoCapturerJni::jni() { return AttachCurrentThreadIfNeeded(); } | |
311 | |
312 JOW(void, | |
313 VideoCapturer_00024NativeObserver_nativeOnByteBufferFrameCaptured) | |
314 (JNIEnv* jni, jclass, jlong j_capturer, jbyteArray j_frame, jint length, | |
315 jint width, jint height, jint rotation, jlong timestamp) { | |
316 jboolean is_copy = true; | |
317 jbyte* bytes = jni->GetByteArrayElements(j_frame, &is_copy); | |
318 reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer) | |
319 ->OnMemoryBufferFrame(bytes, length, width, height, rotation, timestamp); | |
320 jni->ReleaseByteArrayElements(j_frame, bytes, JNI_ABORT); | |
321 } | |
322 | |
323 JOW(void, VideoCapturer_00024NativeObserver_nativeOnTextureFrameCaptured) | |
324 (JNIEnv* jni, jclass, jlong j_capturer, jint j_width, jint j_height, | |
325 jint j_oes_texture_id, jfloatArray j_transform_matrix, | |
326 jint j_rotation, jlong j_timestamp) { | |
327 reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer) | |
328 ->OnTextureFrame(j_width, j_height, j_rotation, j_timestamp, | |
329 NativeHandleImpl(jni, j_oes_texture_id, | |
330 j_transform_matrix)); | |
331 } | |
332 | |
333 JOW(void, VideoCapturer_00024NativeObserver_nativeCapturerStarted) | |
334 (JNIEnv* jni, jclass, jlong j_capturer, jboolean j_success) { | |
335 LOG(LS_INFO) << "NativeObserver_nativeCapturerStarted"; | |
336 reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer)->OnCapturerStarted( | |
337 j_success); | |
338 } | |
339 | |
340 JOW(void, VideoCapturer_00024NativeObserver_nativeOnOutputFormatRequest) | |
341 (JNIEnv* jni, jclass, jlong j_capturer, jint j_width, jint j_height, | |
342 jint j_fps) { | |
343 LOG(LS_INFO) << "NativeObserver_nativeOnOutputFormatRequest"; | |
344 reinterpret_cast<AndroidVideoCapturerJni*>(j_capturer)->OnOutputFormatRequest( | |
345 j_width, j_height, j_fps); | |
346 } | |
347 | |
348 } // namespace webrtc_jni | |
OLD | NEW |