blob: afa1f2eed19839999fe8ed7217ae9d6c085e52f4 [file] [log] [blame]
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +02001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "sdk/android/src/jni/videodecoderwrapper.h"
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "api/video/video_frame.h"
14#include "modules/video_coding/include/video_codec_interface.h"
15#include "modules/video_coding/utility/vp8_header_parser.h"
16#include "modules/video_coding/utility/vp9_uncompressed_header_parser.h"
17#include "rtc_base/logging.h"
18#include "rtc_base/timeutils.h"
19#include "sdk/android/src/jni/classreferenceholder.h"
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020020
magjeda3d4f682017-08-28 16:24:06 -070021namespace webrtc {
22namespace jni {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020023
24VideoDecoderWrapper::VideoDecoderWrapper(JNIEnv* jni, jobject decoder)
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010025 : decoder_(jni, decoder),
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020026 encoded_image_class_(jni, FindClass(jni, "org/webrtc/EncodedImage")),
27 frame_type_class_(jni,
28 FindClass(jni, "org/webrtc/EncodedImage$FrameType")),
29 settings_class_(jni, FindClass(jni, "org/webrtc/VideoDecoder$Settings")),
30 video_frame_class_(jni, FindClass(jni, "org/webrtc/VideoFrame")),
31 video_codec_status_class_(jni,
32 FindClass(jni, "org/webrtc/VideoCodecStatus")),
33 integer_class_(jni, jni->FindClass("java/lang/Integer")) {
34 encoded_image_constructor_ =
35 jni->GetMethodID(*encoded_image_class_, "<init>",
36 "(Ljava/nio/ByteBuffer;IIJLorg/webrtc/"
37 "EncodedImage$FrameType;IZLjava/lang/Integer;)V");
38 settings_constructor_ =
39 jni->GetMethodID(*settings_class_, "<init>", "(III)V");
40
41 empty_frame_field_ = jni->GetStaticFieldID(
42 *frame_type_class_, "EmptyFrame", "Lorg/webrtc/EncodedImage$FrameType;");
43 video_frame_key_field_ =
44 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameKey",
45 "Lorg/webrtc/EncodedImage$FrameType;");
46 video_frame_delta_field_ =
47 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameDelta",
48 "Lorg/webrtc/EncodedImage$FrameType;");
49
50 video_frame_get_timestamp_ns_method_ =
51 jni->GetMethodID(*video_frame_class_, "getTimestampNs", "()J");
52
53 jclass decoder_class = jni->GetObjectClass(decoder);
54 init_decode_method_ =
55 jni->GetMethodID(decoder_class, "initDecode",
56 "(Lorg/webrtc/VideoDecoder$Settings;Lorg/webrtc/"
57 "VideoDecoder$Callback;)Lorg/webrtc/VideoCodecStatus;");
58 release_method_ = jni->GetMethodID(decoder_class, "release",
59 "()Lorg/webrtc/VideoCodecStatus;");
60 decode_method_ = jni->GetMethodID(decoder_class, "decode",
61 "(Lorg/webrtc/EncodedImage;Lorg/webrtc/"
62 "VideoDecoder$DecodeInfo;)Lorg/webrtc/"
63 "VideoCodecStatus;");
64 get_prefers_late_decoding_method_ =
65 jni->GetMethodID(decoder_class, "getPrefersLateDecoding", "()Z");
66 get_implementation_name_method_ = jni->GetMethodID(
67 decoder_class, "getImplementationName", "()Ljava/lang/String;");
68
69 get_number_method_ =
70 jni->GetMethodID(*video_codec_status_class_, "getNumber", "()I");
71
72 integer_constructor_ = jni->GetMethodID(*integer_class_, "<init>", "(I)V");
73 int_value_method_ = jni->GetMethodID(*integer_class_, "intValue", "()I");
74
75 initialized_ = false;
sakal4dee3442017-08-17 02:18:04 -070076 // QP parsing starts enabled and we disable it if the decoder provides frames.
77 qp_parsing_enabled_ = true;
Sami Kalliomäkicee3e532017-10-13 15:20:32 +020078
79 implementation_name_ = GetImplementationName(jni);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020080}
81
magjeda3d4f682017-08-28 16:24:06 -070082int32_t VideoDecoderWrapper::InitDecode(const VideoCodec* codec_settings,
83 int32_t number_of_cores) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020084 JNIEnv* jni = AttachCurrentThreadIfNeeded();
85 ScopedLocalRefFrame local_ref_frame(jni);
86
87 codec_settings_ = *codec_settings;
88 number_of_cores_ = number_of_cores;
89 return InitDecodeInternal(jni);
90}
91
92int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
93 jobject settings =
94 jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_,
95 codec_settings_.width, codec_settings_.height);
96
97 jclass callback_class =
98 FindClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
99 jmethodID callback_constructor =
100 jni->GetMethodID(callback_class, "<init>", "(J)V");
101 jobject callback = jni->NewObject(callback_class, callback_constructor,
102 jlongFromPointer(this));
103
104 jobject ret =
105 jni->CallObjectMethod(*decoder_, init_decode_method_, settings, callback);
106 if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) {
107 initialized_ = true;
108 }
sakal4dee3442017-08-17 02:18:04 -0700109
110 // The decoder was reinitialized so re-enable the QP parsing in case it stops
111 // providing QP values.
112 qp_parsing_enabled_ = true;
113
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200114 return HandleReturnCode(jni, ret);
115}
116
117int32_t VideoDecoderWrapper::Decode(
magjeda3d4f682017-08-28 16:24:06 -0700118 const EncodedImage& input_image,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200119 bool missing_frames,
magjeda3d4f682017-08-28 16:24:06 -0700120 const RTPFragmentationHeader* fragmentation,
121 const CodecSpecificInfo* codec_specific_info,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200122 int64_t render_time_ms) {
123 if (!initialized_) {
124 // Most likely initializing the codec failed.
125 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
126 }
127
128 JNIEnv* jni = AttachCurrentThreadIfNeeded();
129 ScopedLocalRefFrame local_ref_frame(jni);
130
131 FrameExtraInfo frame_extra_info;
sakale172d892017-08-31 02:37:28 -0700132 frame_extra_info.capture_time_ns =
133 input_image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200134 frame_extra_info.timestamp_rtp = input_image._timeStamp;
sakal4dee3442017-08-17 02:18:04 -0700135 frame_extra_info.qp =
136 qp_parsing_enabled_ ? ParseQP(input_image) : rtc::Optional<uint8_t>();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200137 frame_extra_infos_.push_back(frame_extra_info);
138
139 jobject jinput_image =
140 ConvertEncodedImageToJavaEncodedImage(jni, input_image);
141 jobject ret =
142 jni->CallObjectMethod(*decoder_, decode_method_, jinput_image, nullptr);
143 return HandleReturnCode(jni, ret);
144}
145
146int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
magjeda3d4f682017-08-28 16:24:06 -0700147 DecodedImageCallback* callback) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200148 callback_ = callback;
149 return WEBRTC_VIDEO_CODEC_OK;
150}
151
152int32_t VideoDecoderWrapper::Release() {
153 JNIEnv* jni = AttachCurrentThreadIfNeeded();
154 ScopedLocalRefFrame local_ref_frame(jni);
155 jobject ret = jni->CallObjectMethod(*decoder_, release_method_);
156 frame_extra_infos_.clear();
157 initialized_ = false;
158 return HandleReturnCode(jni, ret);
159}
160
161bool VideoDecoderWrapper::PrefersLateDecoding() const {
162 JNIEnv* jni = AttachCurrentThreadIfNeeded();
163 return jni->CallBooleanMethod(*decoder_, get_prefers_late_decoding_method_);
164}
165
166const char* VideoDecoderWrapper::ImplementationName() const {
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200167 return implementation_name_.c_str();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200168}
169
170void VideoDecoderWrapper::OnDecodedFrame(JNIEnv* jni,
171 jobject jframe,
172 jobject jdecode_time_ms,
173 jobject jqp) {
174 const jlong capture_time_ns =
175 jni->CallLongMethod(jframe, video_frame_get_timestamp_ns_method_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200176 FrameExtraInfo frame_extra_info;
177 do {
178 if (frame_extra_infos_.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100179 RTC_LOG(LS_WARNING) << "Java decoder produced an unexpected frame.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200180 return;
181 }
182
183 frame_extra_info = frame_extra_infos_.front();
184 frame_extra_infos_.pop_front();
185 // If the decoder might drop frames so iterate through the queue until we
186 // find a matching timestamp.
sakale172d892017-08-31 02:37:28 -0700187 } while (frame_extra_info.capture_time_ns != capture_time_ns);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200188
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100189 VideoFrame frame =
190 JavaToNativeFrame(jni, jframe, frame_extra_info.timestamp_rtp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200191
192 rtc::Optional<int32_t> decoding_time_ms;
193 if (jdecode_time_ms != nullptr) {
194 decoding_time_ms = rtc::Optional<int32_t>(
195 jni->CallIntMethod(jdecode_time_ms, int_value_method_));
196 }
197
198 rtc::Optional<uint8_t> qp;
199 if (jqp != nullptr) {
200 qp = rtc::Optional<uint8_t>(jni->CallIntMethod(jqp, int_value_method_));
sakal4dee3442017-08-17 02:18:04 -0700201 // The decoder provides QP values itself, no need to parse the bitstream.
202 qp_parsing_enabled_ = false;
203 } else {
204 qp = frame_extra_info.qp;
205 // The decoder doesn't provide QP values, ensure bitstream parsing is
206 // enabled.
207 qp_parsing_enabled_ = true;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200208 }
209
sakal4dee3442017-08-17 02:18:04 -0700210 callback_->Decoded(frame, decoding_time_ms, qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200211}
212
213jobject VideoDecoderWrapper::ConvertEncodedImageToJavaEncodedImage(
214 JNIEnv* jni,
magjeda3d4f682017-08-28 16:24:06 -0700215 const EncodedImage& image) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200216 jobject buffer = jni->NewDirectByteBuffer(image._buffer, image._length);
217 jfieldID frame_type_field;
218 switch (image._frameType) {
magjeda3d4f682017-08-28 16:24:06 -0700219 case kEmptyFrame:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200220 frame_type_field = empty_frame_field_;
221 break;
magjeda3d4f682017-08-28 16:24:06 -0700222 case kVideoFrameKey:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200223 frame_type_field = video_frame_key_field_;
224 break;
magjeda3d4f682017-08-28 16:24:06 -0700225 case kVideoFrameDelta:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200226 frame_type_field = video_frame_delta_field_;
227 break;
228 default:
229 RTC_NOTREACHED();
230 return nullptr;
231 }
232 jobject frame_type =
233 jni->GetStaticObjectField(*frame_type_class_, frame_type_field);
234 jobject qp = nullptr;
235 if (image.qp_ != -1) {
236 qp = jni->NewObject(*integer_class_, integer_constructor_, image.qp_);
237 }
sakale172d892017-08-31 02:37:28 -0700238 return jni->NewObject(
239 *encoded_image_class_, encoded_image_constructor_, buffer,
240 static_cast<jint>(image._encodedWidth),
241 static_cast<jint>(image._encodedHeight),
242 static_cast<jlong>(image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec),
243 frame_type, static_cast<jint>(image.rotation_), image._completeFrame, qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200244}
245
246int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) {
247 int32_t value = jni->CallIntMethod(code, get_number_method_);
248 if (value < 0) { // Any errors are represented by negative values.
249 // Reset the codec.
250 if (Release() == WEBRTC_VIDEO_CODEC_OK) {
251 InitDecodeInternal(jni);
252 }
253
Mirko Bonadei675513b2017-11-09 11:09:25 +0100254 RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200255 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
256 } else {
257 return value;
258 }
259}
260
sakal4dee3442017-08-17 02:18:04 -0700261rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(
magjeda3d4f682017-08-28 16:24:06 -0700262 const EncodedImage& input_image) {
sakal4dee3442017-08-17 02:18:04 -0700263 if (input_image.qp_ != -1) {
264 return rtc::Optional<uint8_t>(input_image.qp_);
265 }
266
267 rtc::Optional<uint8_t> qp;
268 switch (codec_settings_.codecType) {
magjeda3d4f682017-08-28 16:24:06 -0700269 case kVideoCodecVP8: {
sakal4dee3442017-08-17 02:18:04 -0700270 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700271 if (vp8::GetQp(input_image._buffer, input_image._length, &qp_int)) {
sakal4dee3442017-08-17 02:18:04 -0700272 qp = rtc::Optional<uint8_t>(qp_int);
273 }
274 break;
275 }
magjeda3d4f682017-08-28 16:24:06 -0700276 case kVideoCodecVP9: {
sakal4dee3442017-08-17 02:18:04 -0700277 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700278 if (vp9::GetQp(input_image._buffer, input_image._length, &qp_int)) {
sakal4dee3442017-08-17 02:18:04 -0700279 qp = rtc::Optional<uint8_t>(qp_int);
280 }
281 break;
282 }
magjeda3d4f682017-08-28 16:24:06 -0700283 case kVideoCodecH264: {
sakal4dee3442017-08-17 02:18:04 -0700284 h264_bitstream_parser_.ParseBitstream(input_image._buffer,
285 input_image._length);
286 int qp_int;
287 if (h264_bitstream_parser_.GetLastSliceQp(&qp_int)) {
288 qp = rtc::Optional<uint8_t>(qp_int);
289 }
290 break;
291 }
292 default:
293 break; // Default is to not provide QP.
294 }
295 return qp;
296}
297
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200298std::string VideoDecoderWrapper::GetImplementationName(JNIEnv* jni) const {
299 jstring jname = reinterpret_cast<jstring>(
300 jni->CallObjectMethod(*decoder_, get_implementation_name_method_));
301 return JavaToStdString(jni, jname);
302}
303
magjed09f3f6e2017-08-26 03:25:43 -0700304JNI_FUNCTION_DECLARATION(void,
305 VideoDecoderWrapperCallback_nativeOnDecodedFrame,
306 JNIEnv* jni,
307 jclass,
308 jlong jnative_decoder,
309 jobject jframe,
310 jobject jdecode_time_ms,
311 jobject jqp) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200312 VideoDecoderWrapper* native_decoder =
313 reinterpret_cast<VideoDecoderWrapper*>(jnative_decoder);
314 native_decoder->OnDecodedFrame(jni, jframe, jdecode_time_ms, jqp);
315}
316
magjeda3d4f682017-08-28 16:24:06 -0700317} // namespace jni
318} // namespace webrtc