blob: 1bc75b0c3ba34b680cbbb3f8a13a7c7ab08c5699 [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"
Magnus Jedvertcdda0d92017-11-18 14:02:59 +010018#include "rtc_base/safe_conversions.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "rtc_base/timeutils.h"
20#include "sdk/android/src/jni/classreferenceholder.h"
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020021
magjeda3d4f682017-08-28 16:24:06 -070022namespace webrtc {
23namespace jni {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020024
Magnus Jedvertcdda0d92017-11-18 14:02:59 +010025namespace {
26template <typename Dst, typename Src>
27inline rtc::Optional<Dst> cast_optional(const rtc::Optional<Src>& value) {
28 return value ? rtc::Optional<Dst>(rtc::dchecked_cast<Dst, Src>(*value))
29 : rtc::nullopt;
30}
31} // namespace
32
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020033VideoDecoderWrapper::VideoDecoderWrapper(JNIEnv* jni, jobject decoder)
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010034 : decoder_(jni, decoder),
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020035 encoded_image_class_(jni, FindClass(jni, "org/webrtc/EncodedImage")),
36 frame_type_class_(jni,
37 FindClass(jni, "org/webrtc/EncodedImage$FrameType")),
38 settings_class_(jni, FindClass(jni, "org/webrtc/VideoDecoder$Settings")),
39 video_frame_class_(jni, FindClass(jni, "org/webrtc/VideoFrame")),
40 video_codec_status_class_(jni,
41 FindClass(jni, "org/webrtc/VideoCodecStatus")),
42 integer_class_(jni, jni->FindClass("java/lang/Integer")) {
43 encoded_image_constructor_ =
44 jni->GetMethodID(*encoded_image_class_, "<init>",
45 "(Ljava/nio/ByteBuffer;IIJLorg/webrtc/"
46 "EncodedImage$FrameType;IZLjava/lang/Integer;)V");
47 settings_constructor_ =
48 jni->GetMethodID(*settings_class_, "<init>", "(III)V");
49
50 empty_frame_field_ = jni->GetStaticFieldID(
51 *frame_type_class_, "EmptyFrame", "Lorg/webrtc/EncodedImage$FrameType;");
52 video_frame_key_field_ =
53 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameKey",
54 "Lorg/webrtc/EncodedImage$FrameType;");
55 video_frame_delta_field_ =
56 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameDelta",
57 "Lorg/webrtc/EncodedImage$FrameType;");
58
59 video_frame_get_timestamp_ns_method_ =
60 jni->GetMethodID(*video_frame_class_, "getTimestampNs", "()J");
61
62 jclass decoder_class = jni->GetObjectClass(decoder);
63 init_decode_method_ =
64 jni->GetMethodID(decoder_class, "initDecode",
65 "(Lorg/webrtc/VideoDecoder$Settings;Lorg/webrtc/"
66 "VideoDecoder$Callback;)Lorg/webrtc/VideoCodecStatus;");
67 release_method_ = jni->GetMethodID(decoder_class, "release",
68 "()Lorg/webrtc/VideoCodecStatus;");
69 decode_method_ = jni->GetMethodID(decoder_class, "decode",
70 "(Lorg/webrtc/EncodedImage;Lorg/webrtc/"
71 "VideoDecoder$DecodeInfo;)Lorg/webrtc/"
72 "VideoCodecStatus;");
73 get_prefers_late_decoding_method_ =
74 jni->GetMethodID(decoder_class, "getPrefersLateDecoding", "()Z");
75 get_implementation_name_method_ = jni->GetMethodID(
76 decoder_class, "getImplementationName", "()Ljava/lang/String;");
77
78 get_number_method_ =
79 jni->GetMethodID(*video_codec_status_class_, "getNumber", "()I");
80
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020081 initialized_ = false;
sakal4dee3442017-08-17 02:18:04 -070082 // QP parsing starts enabled and we disable it if the decoder provides frames.
83 qp_parsing_enabled_ = true;
Sami Kalliomäkicee3e532017-10-13 15:20:32 +020084
85 implementation_name_ = GetImplementationName(jni);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020086}
87
magjeda3d4f682017-08-28 16:24:06 -070088int32_t VideoDecoderWrapper::InitDecode(const VideoCodec* codec_settings,
89 int32_t number_of_cores) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020090 JNIEnv* jni = AttachCurrentThreadIfNeeded();
91 ScopedLocalRefFrame local_ref_frame(jni);
92
93 codec_settings_ = *codec_settings;
94 number_of_cores_ = number_of_cores;
95 return InitDecodeInternal(jni);
96}
97
98int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
99 jobject settings =
100 jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_,
101 codec_settings_.width, codec_settings_.height);
102
103 jclass callback_class =
104 FindClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
105 jmethodID callback_constructor =
106 jni->GetMethodID(callback_class, "<init>", "(J)V");
107 jobject callback = jni->NewObject(callback_class, callback_constructor,
108 jlongFromPointer(this));
109
110 jobject ret =
111 jni->CallObjectMethod(*decoder_, init_decode_method_, settings, callback);
112 if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) {
113 initialized_ = true;
114 }
sakal4dee3442017-08-17 02:18:04 -0700115
116 // The decoder was reinitialized so re-enable the QP parsing in case it stops
117 // providing QP values.
118 qp_parsing_enabled_ = true;
119
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200120 return HandleReturnCode(jni, ret);
121}
122
123int32_t VideoDecoderWrapper::Decode(
magjeda3d4f682017-08-28 16:24:06 -0700124 const EncodedImage& input_image,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200125 bool missing_frames,
magjeda3d4f682017-08-28 16:24:06 -0700126 const RTPFragmentationHeader* fragmentation,
127 const CodecSpecificInfo* codec_specific_info,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200128 int64_t render_time_ms) {
129 if (!initialized_) {
130 // Most likely initializing the codec failed.
131 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
132 }
133
134 JNIEnv* jni = AttachCurrentThreadIfNeeded();
135 ScopedLocalRefFrame local_ref_frame(jni);
136
137 FrameExtraInfo frame_extra_info;
sakale172d892017-08-31 02:37:28 -0700138 frame_extra_info.capture_time_ns =
139 input_image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200140 frame_extra_info.timestamp_rtp = input_image._timeStamp;
sakal4dee3442017-08-17 02:18:04 -0700141 frame_extra_info.qp =
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100142 qp_parsing_enabled_ ? ParseQP(input_image) : rtc::nullopt;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200143 frame_extra_infos_.push_back(frame_extra_info);
144
145 jobject jinput_image =
146 ConvertEncodedImageToJavaEncodedImage(jni, input_image);
147 jobject ret =
148 jni->CallObjectMethod(*decoder_, decode_method_, jinput_image, nullptr);
149 return HandleReturnCode(jni, ret);
150}
151
152int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
magjeda3d4f682017-08-28 16:24:06 -0700153 DecodedImageCallback* callback) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200154 callback_ = callback;
155 return WEBRTC_VIDEO_CODEC_OK;
156}
157
158int32_t VideoDecoderWrapper::Release() {
159 JNIEnv* jni = AttachCurrentThreadIfNeeded();
160 ScopedLocalRefFrame local_ref_frame(jni);
161 jobject ret = jni->CallObjectMethod(*decoder_, release_method_);
162 frame_extra_infos_.clear();
163 initialized_ = false;
164 return HandleReturnCode(jni, ret);
165}
166
167bool VideoDecoderWrapper::PrefersLateDecoding() const {
168 JNIEnv* jni = AttachCurrentThreadIfNeeded();
169 return jni->CallBooleanMethod(*decoder_, get_prefers_late_decoding_method_);
170}
171
172const char* VideoDecoderWrapper::ImplementationName() const {
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200173 return implementation_name_.c_str();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200174}
175
176void VideoDecoderWrapper::OnDecodedFrame(JNIEnv* jni,
177 jobject jframe,
178 jobject jdecode_time_ms,
179 jobject jqp) {
180 const jlong capture_time_ns =
181 jni->CallLongMethod(jframe, video_frame_get_timestamp_ns_method_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200182 FrameExtraInfo frame_extra_info;
183 do {
184 if (frame_extra_infos_.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100185 RTC_LOG(LS_WARNING) << "Java decoder produced an unexpected frame.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200186 return;
187 }
188
189 frame_extra_info = frame_extra_infos_.front();
190 frame_extra_infos_.pop_front();
191 // If the decoder might drop frames so iterate through the queue until we
192 // find a matching timestamp.
sakale172d892017-08-31 02:37:28 -0700193 } while (frame_extra_info.capture_time_ns != capture_time_ns);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200194
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100195 VideoFrame frame =
196 JavaToNativeFrame(jni, jframe, frame_extra_info.timestamp_rtp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200197
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100198 rtc::Optional<int32_t> decoding_time_ms =
199 JavaIntegerToOptionalInt(jni, jdecode_time_ms);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200200
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100201 rtc::Optional<uint8_t> decoder_qp =
202 cast_optional<uint8_t, int32_t>(JavaIntegerToOptionalInt(jni, jqp));
203 // If the decoder provides QP values itself, no need to parse the bitstream.
204 // Enable QP parsing if decoder does not provide QP values itself.
205 qp_parsing_enabled_ = !decoder_qp.has_value();
206 callback_->Decoded(frame, decoding_time_ms,
207 decoder_qp ? decoder_qp : frame_extra_info.qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200208}
209
210jobject VideoDecoderWrapper::ConvertEncodedImageToJavaEncodedImage(
211 JNIEnv* jni,
magjeda3d4f682017-08-28 16:24:06 -0700212 const EncodedImage& image) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200213 jobject buffer = jni->NewDirectByteBuffer(image._buffer, image._length);
214 jfieldID frame_type_field;
215 switch (image._frameType) {
magjeda3d4f682017-08-28 16:24:06 -0700216 case kEmptyFrame:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200217 frame_type_field = empty_frame_field_;
218 break;
magjeda3d4f682017-08-28 16:24:06 -0700219 case kVideoFrameKey:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200220 frame_type_field = video_frame_key_field_;
221 break;
magjeda3d4f682017-08-28 16:24:06 -0700222 case kVideoFrameDelta:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200223 frame_type_field = video_frame_delta_field_;
224 break;
225 default:
226 RTC_NOTREACHED();
227 return nullptr;
228 }
229 jobject frame_type =
230 jni->GetStaticObjectField(*frame_type_class_, frame_type_field);
231 jobject qp = nullptr;
232 if (image.qp_ != -1) {
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100233 qp = JavaIntegerFromInt(jni, image.qp_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200234 }
sakale172d892017-08-31 02:37:28 -0700235 return jni->NewObject(
236 *encoded_image_class_, encoded_image_constructor_, buffer,
237 static_cast<jint>(image._encodedWidth),
238 static_cast<jint>(image._encodedHeight),
239 static_cast<jlong>(image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec),
240 frame_type, static_cast<jint>(image.rotation_), image._completeFrame, qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200241}
242
243int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) {
244 int32_t value = jni->CallIntMethod(code, get_number_method_);
245 if (value < 0) { // Any errors are represented by negative values.
246 // Reset the codec.
247 if (Release() == WEBRTC_VIDEO_CODEC_OK) {
248 InitDecodeInternal(jni);
249 }
250
Mirko Bonadei675513b2017-11-09 11:09:25 +0100251 RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200252 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
253 } else {
254 return value;
255 }
256}
257
sakal4dee3442017-08-17 02:18:04 -0700258rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(
magjeda3d4f682017-08-28 16:24:06 -0700259 const EncodedImage& input_image) {
sakal4dee3442017-08-17 02:18:04 -0700260 if (input_image.qp_ != -1) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100261 return input_image.qp_;
sakal4dee3442017-08-17 02:18:04 -0700262 }
263
264 rtc::Optional<uint8_t> qp;
265 switch (codec_settings_.codecType) {
magjeda3d4f682017-08-28 16:24:06 -0700266 case kVideoCodecVP8: {
sakal4dee3442017-08-17 02:18:04 -0700267 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700268 if (vp8::GetQp(input_image._buffer, input_image._length, &qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100269 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700270 }
271 break;
272 }
magjeda3d4f682017-08-28 16:24:06 -0700273 case kVideoCodecVP9: {
sakal4dee3442017-08-17 02:18:04 -0700274 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700275 if (vp9::GetQp(input_image._buffer, input_image._length, &qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100276 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700277 }
278 break;
279 }
magjeda3d4f682017-08-28 16:24:06 -0700280 case kVideoCodecH264: {
sakal4dee3442017-08-17 02:18:04 -0700281 h264_bitstream_parser_.ParseBitstream(input_image._buffer,
282 input_image._length);
283 int qp_int;
284 if (h264_bitstream_parser_.GetLastSliceQp(&qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100285 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700286 }
287 break;
288 }
289 default:
290 break; // Default is to not provide QP.
291 }
292 return qp;
293}
294
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200295std::string VideoDecoderWrapper::GetImplementationName(JNIEnv* jni) const {
296 jstring jname = reinterpret_cast<jstring>(
297 jni->CallObjectMethod(*decoder_, get_implementation_name_method_));
298 return JavaToStdString(jni, jname);
299}
300
magjed09f3f6e2017-08-26 03:25:43 -0700301JNI_FUNCTION_DECLARATION(void,
302 VideoDecoderWrapperCallback_nativeOnDecodedFrame,
303 JNIEnv* jni,
304 jclass,
305 jlong jnative_decoder,
306 jobject jframe,
307 jobject jdecode_time_ms,
308 jobject jqp) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200309 VideoDecoderWrapper* native_decoder =
310 reinterpret_cast<VideoDecoderWrapper*>(jnative_decoder);
311 native_decoder->OnDecodedFrame(jni, jframe, jdecode_time_ms, jqp);
312}
313
magjeda3d4f682017-08-28 16:24:06 -0700314} // namespace jni
315} // namespace webrtc