OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2017 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/sdk/android/src/jni/videoencoderwrapper.h" | |
12 | |
13 #include "webrtc/common_video/h264/h264_common.h" | |
14 #include "webrtc/modules/include/module_common_types.h" | |
15 #include "webrtc/modules/video_coding/include/video_codec_interface.h" | |
16 #include "webrtc/modules/video_coding/include/video_error_codes.h" | |
17 #include "webrtc/modules/video_coding/utility/vp8_header_parser.h" | |
18 #include "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h" | |
19 #include "webrtc/rtc_base/logging.h" | |
20 #include "webrtc/rtc_base/random.h" | |
21 #include "webrtc/rtc_base/timeutils.h" | |
22 #include "webrtc/sdk/android/src/jni/classreferenceholder.h" | |
23 | |
24 namespace webrtc_jni { | |
magjed_webrtc
2017/08/28 16:54:22
Nest the namespaces instead.
sakal
2017/08/29 12:59:38
Done.
| |
25 | |
26 static const int kMaxJavaEncoderResets = 3; | |
27 | |
28 VideoEncoderWrapper::VideoEncoderWrapper(JNIEnv* jni, jobject j_encoder) | |
29 : encoder_(jni, j_encoder), | |
30 settings_class_(jni, FindClass(jni, "org/webrtc/VideoEncoder$Settings")), | |
31 encode_info_class_(jni, | |
32 FindClass(jni, "org/webrtc/VideoEncoder$EncodeInfo")), | |
33 frame_type_class_(jni, | |
34 FindClass(jni, "org/webrtc/EncodedImage$FrameType")), | |
35 bitrate_allocation_class_( | |
36 jni, | |
37 FindClass(jni, "org/webrtc/VideoEncoder$BitrateAllocation")), | |
38 int_array_class_(jni, jni->FindClass("[I")), | |
39 video_frame_factory_(jni) { | |
40 jclass encoder_class = FindClass(jni, "org/webrtc/VideoEncoder"); | |
41 | |
42 init_encode_method_ = | |
43 jni->GetMethodID(encoder_class, "initEncode", | |
44 "(Lorg/webrtc/VideoEncoder$Settings;Lorg/webrtc/" | |
45 "VideoEncoder$Callback;)Lorg/webrtc/VideoCodecStatus;"); | |
46 release_method_ = jni->GetMethodID(encoder_class, "release", | |
47 "()Lorg/webrtc/VideoCodecStatus;"); | |
48 encode_method_ = jni->GetMethodID( | |
49 encoder_class, "encode", | |
50 "(Lorg/webrtc/VideoFrame;Lorg/webrtc/" | |
51 "VideoEncoder$EncodeInfo;)Lorg/webrtc/VideoCodecStatus;"); | |
52 set_channel_parameters_method_ = | |
53 jni->GetMethodID(encoder_class, "setChannelParameters", | |
54 "(SJ)Lorg/webrtc/VideoCodecStatus;"); | |
55 set_rate_allocation_method_ = | |
56 jni->GetMethodID(encoder_class, "setRateAllocation", | |
57 "(Lorg/webrtc/VideoEncoder$BitrateAllocation;I)Lorg/" | |
58 "webrtc/VideoCodecStatus;"); | |
59 get_scaling_settings_method_ = | |
60 jni->GetMethodID(encoder_class, "getScalingSettings", | |
61 "()Lorg/webrtc/VideoEncoder$ScalingSettings;"); | |
62 get_implementation_name_method_ = jni->GetMethodID( | |
63 encoder_class, "getImplementationName", "()Ljava/lang/String;"); | |
64 | |
65 settings_constructor_ = | |
66 jni->GetMethodID(*settings_class_, "<init>", "(IIIIIZ)V"); | |
67 | |
68 encode_info_constructor_ = jni->GetMethodID( | |
69 *encode_info_class_, "<init>", "([Lorg/webrtc/EncodedImage$FrameType;)V"); | |
70 | |
71 frame_type_from_native_method_ = | |
72 jni->GetStaticMethodID(*frame_type_class_, "fromNative", | |
73 "(I)Lorg/webrtc/EncodedImage$FrameType;"); | |
74 | |
75 bitrate_allocation_constructor_ = | |
76 jni->GetMethodID(*bitrate_allocation_class_, "<init>", "([[I)V"); | |
77 | |
78 jclass video_codec_status_class = | |
79 FindClass(jni, "org/webrtc/VideoCodecStatus"); | |
80 get_number_method_ = | |
81 jni->GetMethodID(video_codec_status_class, "getNumber", "()I"); | |
82 | |
83 jclass integer_class = jni->FindClass("java/lang/Integer"); | |
84 int_value_method_ = jni->GetMethodID(integer_class, "intValue", "()I"); | |
85 | |
86 jclass scaling_settings_class = | |
87 FindClass(jni, "org/webrtc/VideoEncoder$ScalingSettings"); | |
88 scaling_settings_on_field_ = | |
89 jni->GetFieldID(scaling_settings_class, "on", "Z"); | |
90 scaling_settings_low_field_ = | |
91 jni->GetFieldID(scaling_settings_class, "low", "Ljava/lang/Integer;"); | |
92 scaling_settings_high_field_ = | |
93 jni->GetFieldID(scaling_settings_class, "high", "Ljava/lang/Integer;"); | |
94 | |
95 implementation_name_ = GetImplementationName(jni); | |
96 | |
97 initialized_ = false; | |
98 num_resets_ = 0; | |
99 | |
100 webrtc::Random random(rtc::TimeMicros()); | |
101 picture_id_ = random.Rand<uint16_t>() & 0x7FFF; | |
102 tl0_pic_idx_ = random.Rand<uint8_t>(); | |
103 } | |
104 | |
105 int32_t VideoEncoderWrapper::InitEncode( | |
106 const webrtc::VideoCodec* codec_settings, | |
107 int32_t number_of_cores, | |
108 size_t max_payload_size) { | |
109 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
110 ScopedLocalRefFrame local_ref_frame(jni); | |
111 | |
112 number_of_cores_ = number_of_cores; | |
113 codec_settings_ = *codec_settings; | |
114 num_resets_ = 0; | |
115 | |
116 return InitEncodeInternal(jni); | |
117 } | |
118 | |
119 int32_t VideoEncoderWrapper::InitEncodeInternal(JNIEnv* jni) { | |
120 bool automatic_resize_on; | |
121 switch (codec_settings_.codecType) { | |
122 case webrtc::kVideoCodecVP8: | |
123 automatic_resize_on = codec_settings_.VP8()->automaticResizeOn; | |
124 break; | |
125 case webrtc::kVideoCodecVP9: | |
126 automatic_resize_on = codec_settings_.VP9()->automaticResizeOn; | |
127 break; | |
128 default: | |
129 automatic_resize_on = true; | |
130 } | |
131 | |
132 jobject settings = | |
133 jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_, | |
134 codec_settings_.width, codec_settings_.height, | |
135 codec_settings_.startBitrate, codec_settings_.maxFramerate, | |
136 automatic_resize_on); | |
137 | |
138 jclass callback_class = | |
139 FindClass(jni, "org/webrtc/VideoEncoderWrapperCallback"); | |
140 jmethodID callback_constructor = | |
141 jni->GetMethodID(callback_class, "<init>", "(J)V"); | |
142 jobject callback = jni->NewObject(callback_class, callback_constructor, | |
143 jlongFromPointer(this)); | |
144 | |
145 jobject ret = | |
146 jni->CallObjectMethod(*encoder_, init_encode_method_, settings, callback); | |
147 if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) { | |
148 initialized_ = true; | |
149 } | |
150 | |
151 return HandleReturnCode(jni, ret); | |
152 } | |
153 | |
154 int32_t VideoEncoderWrapper::RegisterEncodeCompleteCallback( | |
155 webrtc::EncodedImageCallback* callback) { | |
156 callback_ = callback; | |
157 return WEBRTC_VIDEO_CODEC_OK; | |
158 } | |
159 | |
160 int32_t VideoEncoderWrapper::Release() { | |
161 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
162 ScopedLocalRefFrame local_ref_frame(jni); | |
163 jobject ret = jni->CallObjectMethod(*encoder_, release_method_); | |
164 initialized_ = false; | |
165 return HandleReturnCode(jni, ret); | |
166 } | |
167 | |
168 int32_t VideoEncoderWrapper::Encode( | |
169 const webrtc::VideoFrame& frame, | |
170 const webrtc::CodecSpecificInfo* /* codec_specific_info */, | |
171 const std::vector<webrtc::FrameType>* frame_types) { | |
172 if (!initialized_) { | |
173 // Most likely initializing the codec failed. | |
174 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
175 } | |
176 | |
177 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
178 ScopedLocalRefFrame local_ref_frame(jni); | |
magjed_webrtc
2017/08/28 16:54:22
Remove this if it's not needed since this is calle
sakal
2017/08/29 12:59:38
This is needed because we call methods that return
magjed_webrtc
2017/08/30 10:34:43
Acknowledged.
| |
179 | |
180 // Construct encode info. | |
181 jobjectArray j_frame_types = | |
182 jni->NewObjectArray(frame_types->size(), *frame_type_class_, nullptr); | |
183 for (size_t i = 0; i < frame_types->size(); ++i) { | |
184 jobject j_frame_type = jni->CallStaticObjectMethod( | |
185 *frame_type_class_, frame_type_from_native_method_, | |
186 static_cast<jint>((*frame_types)[i])); | |
187 jni->SetObjectArrayElement(j_frame_types, i, j_frame_type); | |
188 } | |
189 jobject encode_info = jni->NewObject(*encode_info_class_, | |
190 encode_info_constructor_, j_frame_types); | |
191 | |
192 jobject ret = jni->CallObjectMethod( | |
193 *encoder_, encode_method_, video_frame_factory_.ToJavaFrame(jni, frame), | |
194 encode_info); | |
195 return HandleReturnCode(jni, ret); | |
196 } | |
197 | |
198 int32_t VideoEncoderWrapper::SetChannelParameters(uint32_t packet_loss, | |
199 int64_t rtt) { | |
200 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
201 ScopedLocalRefFrame local_ref_frame(jni); | |
202 jobject ret = jni->CallObjectMethod(*encoder_, set_channel_parameters_method_, | |
203 (jshort)packet_loss, (jlong)rtt); | |
204 return HandleReturnCode(jni, ret); | |
205 } | |
206 | |
207 int32_t VideoEncoderWrapper::SetRateAllocation( | |
208 const webrtc::BitrateAllocation& allocation, | |
209 uint32_t framerate) { | |
210 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
211 ScopedLocalRefFrame local_ref_frame(jni); | |
212 | |
213 jobject j_bitrate_allocation = ToJavaBitrateAllocation(jni, allocation); | |
214 jobject ret = jni->CallObjectMethod(*encoder_, set_rate_allocation_method_, | |
215 j_bitrate_allocation, (jint)framerate); | |
216 return HandleReturnCode(jni, ret); | |
217 } | |
218 | |
219 VideoEncoderWrapper::ScalingSettings VideoEncoderWrapper::GetScalingSettings() | |
220 const { | |
221 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | |
222 ScopedLocalRefFrame local_ref_frame(jni); | |
223 jobject j_scaling_settings = | |
224 jni->CallObjectMethod(*encoder_, get_scaling_settings_method_); | |
225 bool on = | |
226 jni->GetBooleanField(j_scaling_settings, scaling_settings_on_field_); | |
227 jobject j_low = | |
228 jni->GetObjectField(j_scaling_settings, scaling_settings_low_field_); | |
229 jobject j_high = | |
230 jni->GetObjectField(j_scaling_settings, scaling_settings_high_field_); | |
231 | |
232 if (j_low != nullptr || j_high != nullptr) { | |
233 RTC_DCHECK(j_low != nullptr); | |
234 RTC_DCHECK(j_high != nullptr); | |
235 int low = jni->CallIntMethod(j_low, int_value_method_); | |
236 int high = jni->CallIntMethod(j_high, int_value_method_); | |
237 return ScalingSettings(on, low, high); | |
238 } else { | |
239 return ScalingSettings(on); | |
240 } | |
241 } | |
242 | |
243 const char* VideoEncoderWrapper::ImplementationName() const { | |
244 return implementation_name_.c_str(); | |
245 } | |
246 | |
247 void VideoEncoderWrapper::OnEncodedFrame(JNIEnv* jni, | |
248 jobject j_buffer, | |
249 jint encoded_width, | |
250 jint encoded_height, | |
251 jlong capture_time_ms, | |
252 jint frame_type, | |
253 jint rotation, | |
254 jboolean complete_frame, | |
255 jobject j_qp) { | |
256 uint8_t* buffer = | |
257 static_cast<uint8_t*>(jni->GetDirectBufferAddress(j_buffer)); | |
258 size_t buffer_size = jni->GetDirectBufferCapacity(j_buffer); | |
259 | |
260 webrtc::RTPFragmentationHeader header = | |
261 ParseFragmentationHeader(buffer, buffer_size); | |
262 | |
263 webrtc::EncodedImage frame(buffer, buffer_size, buffer_size); | |
264 frame._encodedWidth = encoded_width; | |
265 frame._encodedHeight = encoded_height; | |
266 // TOOD(sakal): Maybe this should be a different timestamp? | |
magjed_webrtc
2017/08/28 16:54:22
Sounds like something we should figure out. Is it
sakal
2017/08/29 12:59:38
Done.
| |
267 frame._timeStamp = capture_time_ms; | |
268 frame.capture_time_ms_ = capture_time_ms; | |
269 frame._frameType = (webrtc::FrameType)frame_type; | |
270 frame.rotation_ = (webrtc::VideoRotation)rotation; | |
271 frame._completeFrame = complete_frame; | |
272 | |
273 if (j_qp != nullptr) { | |
274 frame.qp_ = jni->CallIntMethod(j_qp, int_value_method_); | |
275 } else { | |
276 frame.qp_ = ParseQp(buffer, buffer_size); | |
277 } | |
278 | |
279 webrtc::CodecSpecificInfo info(ParseCodecSpecificInfo(frame)); | |
280 callback_->OnEncodedImage(frame, &info, &header); | |
281 } | |
282 | |
283 int32_t VideoEncoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) { | |
284 int32_t value = jni->CallIntMethod(code, get_number_method_); | |
285 if (value < 0) { // Any errors are represented by negative values. | |
286 // Try resetting the codec. | |
287 if (++num_resets_ <= kMaxJavaEncoderResets && | |
288 Release() == WEBRTC_VIDEO_CODEC_OK) { | |
289 LOG(LS_WARNING) << "Reset Java encoder: " << num_resets_; | |
290 return InitEncodeInternal(jni); | |
291 } | |
292 | |
293 LOG(LS_WARNING) << "Falling back to software decoder."; | |
294 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
295 } else { | |
296 return value; | |
297 } | |
298 } | |
299 | |
300 webrtc::RTPFragmentationHeader VideoEncoderWrapper::ParseFragmentationHeader( | |
301 uint8_t* buffer, | |
302 size_t buffer_size) { | |
303 webrtc::RTPFragmentationHeader header; | |
304 if (codec_settings_.codecType == webrtc::kVideoCodecH264) { | |
305 h264_bitstream_parser_.ParseBitstream(buffer, buffer_size); | |
306 | |
307 // For H.264 search for start codes. | |
308 const std::vector<webrtc::H264::NaluIndex> nalu_idxs = | |
309 webrtc::H264::FindNaluIndices(buffer, buffer_size); | |
310 if (nalu_idxs.empty()) { | |
311 LOG(LS_ERROR) << "Start code is not found!"; | |
312 LOG(LS_ERROR) << "Data:" << buffer[0] << " " << buffer[1] << " " | |
313 << buffer[2] << " " << buffer[3] << " " << buffer[4] << " " | |
314 << buffer[5]; | |
315 } | |
316 header.VerifyAndAllocateFragmentationHeader(nalu_idxs.size()); | |
317 for (size_t i = 0; i < nalu_idxs.size(); i++) { | |
318 header.fragmentationOffset[i] = nalu_idxs[i].payload_start_offset; | |
319 header.fragmentationLength[i] = nalu_idxs[i].payload_size; | |
320 header.fragmentationPlType[i] = 0; | |
321 header.fragmentationTimeDiff[i] = 0; | |
322 } | |
323 } else { | |
324 // Generate a header describing a single fragment. | |
325 header.VerifyAndAllocateFragmentationHeader(1); | |
326 header.fragmentationOffset[0] = 0; | |
327 header.fragmentationLength[0] = buffer_size; | |
328 header.fragmentationPlType[0] = 0; | |
329 header.fragmentationTimeDiff[0] = 0; | |
330 } | |
331 return header; | |
332 } | |
333 | |
334 int VideoEncoderWrapper::ParseQp(uint8_t* buffer, size_t buffer_size) { | |
335 switch (codec_settings_.codecType) { | |
336 case webrtc::kVideoCodecVP8: { | |
337 int qp; | |
magjed_webrtc
2017/08/28 16:54:22
I would like to write it like this instead:
int qp
sakal
2017/08/29 12:59:38
Done.
| |
338 if (webrtc::vp8::GetQp(buffer, buffer_size, &qp)) { | |
339 return qp; | |
340 } | |
341 break; | |
342 } | |
343 case webrtc::kVideoCodecVP9: { | |
344 int qp; | |
345 if (webrtc::vp9::GetQp(buffer, buffer_size, &qp)) { | |
346 return qp; | |
347 } | |
348 break; | |
349 } | |
350 case webrtc::kVideoCodecH264: { | |
351 int qp; | |
352 if (h264_bitstream_parser_.GetLastSliceQp(&qp)) { | |
353 return qp; | |
354 } | |
355 break; | |
356 } | |
357 default: // Default is to not provide QP. | |
358 break; | |
359 } | |
360 return -1; // -1 means unknown QP. | |
361 } | |
362 | |
363 webrtc::CodecSpecificInfo VideoEncoderWrapper::ParseCodecSpecificInfo( | |
364 const webrtc::EncodedImage& frame) { | |
365 const bool key_frame = frame._frameType == webrtc::kVideoFrameKey; | |
366 | |
367 webrtc::CodecSpecificInfo info; | |
368 memset(&info, 0, sizeof(info)); | |
369 info.codecType = codec_settings_.codecType; | |
370 info.codec_name = implementation_name_.c_str(); | |
371 | |
372 switch (codec_settings_.codecType) { | |
373 case webrtc::kVideoCodecVP8: | |
374 info.codecSpecific.VP8.pictureId = picture_id_; | |
375 info.codecSpecific.VP8.nonReference = false; | |
376 info.codecSpecific.VP8.simulcastIdx = 0; | |
377 info.codecSpecific.VP8.temporalIdx = webrtc::kNoTemporalIdx; | |
378 info.codecSpecific.VP8.layerSync = false; | |
379 info.codecSpecific.VP8.tl0PicIdx = webrtc::kNoTl0PicIdx; | |
380 info.codecSpecific.VP8.keyIdx = webrtc::kNoKeyIdx; | |
381 break; | |
382 case webrtc::kVideoCodecVP9: | |
383 if (key_frame) { | |
384 gof_idx_ = 0; | |
385 } | |
386 info.codecSpecific.VP9.picture_id = picture_id_; | |
387 info.codecSpecific.VP9.inter_pic_predicted = key_frame ? false : true; | |
388 info.codecSpecific.VP9.flexible_mode = false; | |
389 info.codecSpecific.VP9.ss_data_available = key_frame ? true : false; | |
390 info.codecSpecific.VP9.tl0_pic_idx = tl0_pic_idx_++; | |
391 info.codecSpecific.VP9.temporal_idx = webrtc::kNoTemporalIdx; | |
392 info.codecSpecific.VP9.spatial_idx = webrtc::kNoSpatialIdx; | |
393 info.codecSpecific.VP9.temporal_up_switch = true; | |
394 info.codecSpecific.VP9.inter_layer_predicted = false; | |
395 info.codecSpecific.VP9.gof_idx = | |
396 static_cast<uint8_t>(gof_idx_++ % gof_.num_frames_in_gof); | |
397 info.codecSpecific.VP9.num_spatial_layers = 1; | |
398 info.codecSpecific.VP9.spatial_layer_resolution_present = false; | |
399 if (info.codecSpecific.VP9.ss_data_available) { | |
400 info.codecSpecific.VP9.spatial_layer_resolution_present = true; | |
401 info.codecSpecific.VP9.width[0] = frame._encodedWidth; | |
402 info.codecSpecific.VP9.height[0] = frame._encodedHeight; | |
403 info.codecSpecific.VP9.gof.CopyGofInfoVP9(gof_); | |
404 } | |
405 break; | |
406 default: | |
407 break; | |
408 } | |
409 | |
410 picture_id_ = (picture_id_ + 1) & 0x7FFF; | |
411 | |
412 return info; | |
413 } | |
414 | |
415 jobject VideoEncoderWrapper::ToJavaBitrateAllocation( | |
416 JNIEnv* jni, | |
417 const webrtc::BitrateAllocation& allocation) { | |
418 jobjectArray j_allocation_array = jni->NewObjectArray( | |
419 webrtc::kMaxSpatialLayers, *int_array_class_, nullptr /* initial */); | |
420 for (int spatial_i = 0; spatial_i < webrtc::kMaxSpatialLayers; ++spatial_i) { | |
421 jintArray j_array_spatial_layer = | |
422 jni->NewIntArray(webrtc::kMaxTemporalStreams); | |
423 jint* array_spatial_layer = | |
424 jni->GetIntArrayElements(j_array_spatial_layer, nullptr /* isCopy */); | |
425 for (int temporal_i = 0; temporal_i < webrtc::kMaxTemporalStreams; | |
426 ++temporal_i) { | |
427 array_spatial_layer[temporal_i] = | |
428 allocation.GetBitrate(spatial_i, temporal_i); | |
429 } | |
430 jni->ReleaseIntArrayElements(j_array_spatial_layer, array_spatial_layer, | |
431 JNI_COMMIT); | |
432 | |
433 jni->SetObjectArrayElement(j_allocation_array, spatial_i, | |
434 j_array_spatial_layer); | |
435 } | |
436 return jni->NewObject(*bitrate_allocation_class_, | |
437 bitrate_allocation_constructor_, j_allocation_array); | |
438 } | |
439 | |
440 std::string VideoEncoderWrapper::GetImplementationName(JNIEnv* jni) const { | |
441 jstring jname = reinterpret_cast<jstring>( | |
442 jni->CallObjectMethod(*encoder_, get_implementation_name_method_)); | |
443 return JavaToStdString(jni, jname); | |
444 } | |
445 | |
446 JNI_FUNCTION_DECLARATION(void, | |
447 VideoEncoderWrapperCallback_nativeOnEncodedFrame, | |
448 JNIEnv* jni, | |
449 jclass, | |
450 jlong j_native_encoder, | |
451 jobject buffer, | |
452 jint encoded_width, | |
453 jint encoded_height, | |
454 jlong capture_time_ms, | |
455 jint frame_type, | |
456 jint rotation, | |
457 jboolean complete_frame, | |
458 jobject qp) { | |
459 VideoEncoderWrapper* native_encoder = | |
460 reinterpret_cast<VideoEncoderWrapper*>(j_native_encoder); | |
461 native_encoder->OnEncodedFrame(jni, buffer, encoded_width, encoded_height, | |
462 capture_time_ms, frame_type, rotation, | |
463 complete_frame, qp); | |
464 } | |
465 | |
466 } // namespace webrtc_jni | |
OLD | NEW |