Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Side by Side Diff: webrtc/sdk/android/src/jni/videoencoderwrapper.cc

Issue 3003873002: Bindings for injectable Java video encoders. (Closed)
Patch Set: Rebase Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698