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