blob: 69b5c2d6566114d1d690b4b87aebb3d26a79e074 [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)
25 : android_video_buffer_factory_(jni),
26 decoder_(jni, decoder),
27 encoded_image_class_(jni, FindClass(jni, "org/webrtc/EncodedImage")),
28 frame_type_class_(jni,
29 FindClass(jni, "org/webrtc/EncodedImage$FrameType")),
30 settings_class_(jni, FindClass(jni, "org/webrtc/VideoDecoder$Settings")),
31 video_frame_class_(jni, FindClass(jni, "org/webrtc/VideoFrame")),
32 video_codec_status_class_(jni,
33 FindClass(jni, "org/webrtc/VideoCodecStatus")),
34 integer_class_(jni, jni->FindClass("java/lang/Integer")) {
35 encoded_image_constructor_ =
36 jni->GetMethodID(*encoded_image_class_, "<init>",
37 "(Ljava/nio/ByteBuffer;IIJLorg/webrtc/"
38 "EncodedImage$FrameType;IZLjava/lang/Integer;)V");
39 settings_constructor_ =
40 jni->GetMethodID(*settings_class_, "<init>", "(III)V");
41
42 empty_frame_field_ = jni->GetStaticFieldID(
43 *frame_type_class_, "EmptyFrame", "Lorg/webrtc/EncodedImage$FrameType;");
44 video_frame_key_field_ =
45 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameKey",
46 "Lorg/webrtc/EncodedImage$FrameType;");
47 video_frame_delta_field_ =
48 jni->GetStaticFieldID(*frame_type_class_, "VideoFrameDelta",
49 "Lorg/webrtc/EncodedImage$FrameType;");
50
51 video_frame_get_timestamp_ns_method_ =
52 jni->GetMethodID(*video_frame_class_, "getTimestampNs", "()J");
53
54 jclass decoder_class = jni->GetObjectClass(decoder);
55 init_decode_method_ =
56 jni->GetMethodID(decoder_class, "initDecode",
57 "(Lorg/webrtc/VideoDecoder$Settings;Lorg/webrtc/"
58 "VideoDecoder$Callback;)Lorg/webrtc/VideoCodecStatus;");
59 release_method_ = jni->GetMethodID(decoder_class, "release",
60 "()Lorg/webrtc/VideoCodecStatus;");
61 decode_method_ = jni->GetMethodID(decoder_class, "decode",
62 "(Lorg/webrtc/EncodedImage;Lorg/webrtc/"
63 "VideoDecoder$DecodeInfo;)Lorg/webrtc/"
64 "VideoCodecStatus;");
65 get_prefers_late_decoding_method_ =
66 jni->GetMethodID(decoder_class, "getPrefersLateDecoding", "()Z");
67 get_implementation_name_method_ = jni->GetMethodID(
68 decoder_class, "getImplementationName", "()Ljava/lang/String;");
69
70 get_number_method_ =
71 jni->GetMethodID(*video_codec_status_class_, "getNumber", "()I");
72
73 integer_constructor_ = jni->GetMethodID(*integer_class_, "<init>", "(I)V");
74 int_value_method_ = jni->GetMethodID(*integer_class_, "intValue", "()I");
75
76 initialized_ = false;
sakal4dee3442017-08-17 02:18:04 -070077 // QP parsing starts enabled and we disable it if the decoder provides frames.
78 qp_parsing_enabled_ = true;
Sami Kalliomäkicee3e532017-10-13 15:20:32 +020079
80 implementation_name_ = GetImplementationName(jni);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020081}
82
magjeda3d4f682017-08-28 16:24:06 -070083int32_t VideoDecoderWrapper::InitDecode(const VideoCodec* codec_settings,
84 int32_t number_of_cores) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020085 JNIEnv* jni = AttachCurrentThreadIfNeeded();
86 ScopedLocalRefFrame local_ref_frame(jni);
87
88 codec_settings_ = *codec_settings;
89 number_of_cores_ = number_of_cores;
90 return InitDecodeInternal(jni);
91}
92
93int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
94 jobject settings =
95 jni->NewObject(*settings_class_, settings_constructor_, number_of_cores_,
96 codec_settings_.width, codec_settings_.height);
97
98 jclass callback_class =
99 FindClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
100 jmethodID callback_constructor =
101 jni->GetMethodID(callback_class, "<init>", "(J)V");
102 jobject callback = jni->NewObject(callback_class, callback_constructor,
103 jlongFromPointer(this));
104
105 jobject ret =
106 jni->CallObjectMethod(*decoder_, init_decode_method_, settings, callback);
107 if (jni->CallIntMethod(ret, get_number_method_) == WEBRTC_VIDEO_CODEC_OK) {
108 initialized_ = true;
109 }
sakal4dee3442017-08-17 02:18:04 -0700110
111 // The decoder was reinitialized so re-enable the QP parsing in case it stops
112 // providing QP values.
113 qp_parsing_enabled_ = true;
114
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200115 return HandleReturnCode(jni, ret);
116}
117
118int32_t VideoDecoderWrapper::Decode(
magjeda3d4f682017-08-28 16:24:06 -0700119 const EncodedImage& input_image,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200120 bool missing_frames,
magjeda3d4f682017-08-28 16:24:06 -0700121 const RTPFragmentationHeader* fragmentation,
122 const CodecSpecificInfo* codec_specific_info,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200123 int64_t render_time_ms) {
124 if (!initialized_) {
125 // Most likely initializing the codec failed.
126 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
127 }
128
129 JNIEnv* jni = AttachCurrentThreadIfNeeded();
130 ScopedLocalRefFrame local_ref_frame(jni);
131
132 FrameExtraInfo frame_extra_info;
sakale172d892017-08-31 02:37:28 -0700133 frame_extra_info.capture_time_ns =
134 input_image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200135 frame_extra_info.timestamp_rtp = input_image._timeStamp;
sakal4dee3442017-08-17 02:18:04 -0700136 frame_extra_info.qp =
137 qp_parsing_enabled_ ? ParseQP(input_image) : rtc::Optional<uint8_t>();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200138 frame_extra_infos_.push_back(frame_extra_info);
139
140 jobject jinput_image =
141 ConvertEncodedImageToJavaEncodedImage(jni, input_image);
142 jobject ret =
143 jni->CallObjectMethod(*decoder_, decode_method_, jinput_image, nullptr);
144 return HandleReturnCode(jni, ret);
145}
146
147int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
magjeda3d4f682017-08-28 16:24:06 -0700148 DecodedImageCallback* callback) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200149 callback_ = callback;
150 return WEBRTC_VIDEO_CODEC_OK;
151}
152
153int32_t VideoDecoderWrapper::Release() {
154 JNIEnv* jni = AttachCurrentThreadIfNeeded();
155 ScopedLocalRefFrame local_ref_frame(jni);
156 jobject ret = jni->CallObjectMethod(*decoder_, release_method_);
157 frame_extra_infos_.clear();
158 initialized_ = false;
159 return HandleReturnCode(jni, ret);
160}
161
162bool VideoDecoderWrapper::PrefersLateDecoding() const {
163 JNIEnv* jni = AttachCurrentThreadIfNeeded();
164 return jni->CallBooleanMethod(*decoder_, get_prefers_late_decoding_method_);
165}
166
167const char* VideoDecoderWrapper::ImplementationName() const {
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200168 return implementation_name_.c_str();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200169}
170
171void VideoDecoderWrapper::OnDecodedFrame(JNIEnv* jni,
172 jobject jframe,
173 jobject jdecode_time_ms,
174 jobject jqp) {
175 const jlong capture_time_ns =
176 jni->CallLongMethod(jframe, video_frame_get_timestamp_ns_method_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200177 FrameExtraInfo frame_extra_info;
178 do {
179 if (frame_extra_infos_.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100180 RTC_LOG(LS_WARNING) << "Java decoder produced an unexpected frame.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200181 return;
182 }
183
184 frame_extra_info = frame_extra_infos_.front();
185 frame_extra_infos_.pop_front();
186 // If the decoder might drop frames so iterate through the queue until we
187 // find a matching timestamp.
sakale172d892017-08-31 02:37:28 -0700188 } while (frame_extra_info.capture_time_ns != capture_time_ns);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200189
magjeda3d4f682017-08-28 16:24:06 -0700190 VideoFrame frame = android_video_buffer_factory_.CreateFrame(
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200191 jni, jframe, frame_extra_info.timestamp_rtp);
192
193 rtc::Optional<int32_t> decoding_time_ms;
194 if (jdecode_time_ms != nullptr) {
195 decoding_time_ms = rtc::Optional<int32_t>(
196 jni->CallIntMethod(jdecode_time_ms, int_value_method_));
197 }
198
199 rtc::Optional<uint8_t> qp;
200 if (jqp != nullptr) {
201 qp = rtc::Optional<uint8_t>(jni->CallIntMethod(jqp, int_value_method_));
sakal4dee3442017-08-17 02:18:04 -0700202 // The decoder provides QP values itself, no need to parse the bitstream.
203 qp_parsing_enabled_ = false;
204 } else {
205 qp = frame_extra_info.qp;
206 // The decoder doesn't provide QP values, ensure bitstream parsing is
207 // enabled.
208 qp_parsing_enabled_ = true;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200209 }
210
sakal4dee3442017-08-17 02:18:04 -0700211 callback_->Decoded(frame, decoding_time_ms, qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200212}
213
214jobject VideoDecoderWrapper::ConvertEncodedImageToJavaEncodedImage(
215 JNIEnv* jni,
magjeda3d4f682017-08-28 16:24:06 -0700216 const EncodedImage& image) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200217 jobject buffer = jni->NewDirectByteBuffer(image._buffer, image._length);
218 jfieldID frame_type_field;
219 switch (image._frameType) {
magjeda3d4f682017-08-28 16:24:06 -0700220 case kEmptyFrame:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200221 frame_type_field = empty_frame_field_;
222 break;
magjeda3d4f682017-08-28 16:24:06 -0700223 case kVideoFrameKey:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200224 frame_type_field = video_frame_key_field_;
225 break;
magjeda3d4f682017-08-28 16:24:06 -0700226 case kVideoFrameDelta:
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200227 frame_type_field = video_frame_delta_field_;
228 break;
229 default:
230 RTC_NOTREACHED();
231 return nullptr;
232 }
233 jobject frame_type =
234 jni->GetStaticObjectField(*frame_type_class_, frame_type_field);
235 jobject qp = nullptr;
236 if (image.qp_ != -1) {
237 qp = jni->NewObject(*integer_class_, integer_constructor_, image.qp_);
238 }
sakale172d892017-08-31 02:37:28 -0700239 return jni->NewObject(
240 *encoded_image_class_, encoded_image_constructor_, buffer,
241 static_cast<jint>(image._encodedWidth),
242 static_cast<jint>(image._encodedHeight),
243 static_cast<jlong>(image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec),
244 frame_type, static_cast<jint>(image.rotation_), image._completeFrame, qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200245}
246
247int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) {
248 int32_t value = jni->CallIntMethod(code, get_number_method_);
249 if (value < 0) { // Any errors are represented by negative values.
250 // Reset the codec.
251 if (Release() == WEBRTC_VIDEO_CODEC_OK) {
252 InitDecodeInternal(jni);
253 }
254
Mirko Bonadei675513b2017-11-09 11:09:25 +0100255 RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200256 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
257 } else {
258 return value;
259 }
260}
261
sakal4dee3442017-08-17 02:18:04 -0700262rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(
magjeda3d4f682017-08-28 16:24:06 -0700263 const EncodedImage& input_image) {
sakal4dee3442017-08-17 02:18:04 -0700264 if (input_image.qp_ != -1) {
265 return rtc::Optional<uint8_t>(input_image.qp_);
266 }
267
268 rtc::Optional<uint8_t> qp;
269 switch (codec_settings_.codecType) {
magjeda3d4f682017-08-28 16:24:06 -0700270 case kVideoCodecVP8: {
sakal4dee3442017-08-17 02:18:04 -0700271 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700272 if (vp8::GetQp(input_image._buffer, input_image._length, &qp_int)) {
sakal4dee3442017-08-17 02:18:04 -0700273 qp = rtc::Optional<uint8_t>(qp_int);
274 }
275 break;
276 }
magjeda3d4f682017-08-28 16:24:06 -0700277 case kVideoCodecVP9: {
sakal4dee3442017-08-17 02:18:04 -0700278 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700279 if (vp9::GetQp(input_image._buffer, input_image._length, &qp_int)) {
sakal4dee3442017-08-17 02:18:04 -0700280 qp = rtc::Optional<uint8_t>(qp_int);
281 }
282 break;
283 }
magjeda3d4f682017-08-28 16:24:06 -0700284 case kVideoCodecH264: {
sakal4dee3442017-08-17 02:18:04 -0700285 h264_bitstream_parser_.ParseBitstream(input_image._buffer,
286 input_image._length);
287 int qp_int;
288 if (h264_bitstream_parser_.GetLastSliceQp(&qp_int)) {
289 qp = rtc::Optional<uint8_t>(qp_int);
290 }
291 break;
292 }
293 default:
294 break; // Default is to not provide QP.
295 }
296 return qp;
297}
298
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200299std::string VideoDecoderWrapper::GetImplementationName(JNIEnv* jni) const {
300 jstring jname = reinterpret_cast<jstring>(
301 jni->CallObjectMethod(*decoder_, get_implementation_name_method_));
302 return JavaToStdString(jni, jname);
303}
304
magjed09f3f6e2017-08-26 03:25:43 -0700305JNI_FUNCTION_DECLARATION(void,
306 VideoDecoderWrapperCallback_nativeOnDecodedFrame,
307 JNIEnv* jni,
308 jclass,
309 jlong jnative_decoder,
310 jobject jframe,
311 jobject jdecode_time_ms,
312 jobject jqp) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200313 VideoDecoderWrapper* native_decoder =
314 reinterpret_cast<VideoDecoderWrapper*>(jnative_decoder);
315 native_decoder->OnDecodedFrame(jni, jframe, jdecode_time_ms, jqp);
316}
317
magjeda3d4f682017-08-28 16:24:06 -0700318} // namespace jni
319} // namespace webrtc