blob: 3dd398e9df54470db766a5b176ef35cf8748fbed [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 Jedvert4eb01882017-11-20 22:33:40 +010018#include "sdk/android/generated_video_jni/jni/VideoDecoderWrapper_jni.h"
19#include "sdk/android/generated_video_jni/jni/VideoDecoder_jni.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "sdk/android/src/jni/classreferenceholder.h"
Magnus Jedvert4eb01882017-11-20 22:33:40 +010021#include "sdk/android/src/jni/encodedimage.h"
22#include "sdk/android/src/jni/videocodecstatus.h"
23#include "sdk/android/src/jni/videoframe.h"
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020024
magjeda3d4f682017-08-28 16:24:06 -070025namespace webrtc {
26namespace jni {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020027
Magnus Jedvertcdda0d92017-11-18 14:02:59 +010028namespace {
29template <typename Dst, typename Src>
30inline rtc::Optional<Dst> cast_optional(const rtc::Optional<Src>& value) {
31 return value ? rtc::Optional<Dst>(rtc::dchecked_cast<Dst, Src>(*value))
32 : rtc::nullopt;
33}
34} // namespace
35
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020036VideoDecoderWrapper::VideoDecoderWrapper(JNIEnv* jni, jobject decoder)
Magnus Jedvert4eb01882017-11-20 22:33:40 +010037 : decoder_(jni, decoder) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020038 initialized_ = false;
sakal4dee3442017-08-17 02:18:04 -070039 // QP parsing starts enabled and we disable it if the decoder provides frames.
40 qp_parsing_enabled_ = true;
Sami Kalliomäkicee3e532017-10-13 15:20:32 +020041
Magnus Jedvert4eb01882017-11-20 22:33:40 +010042 implementation_name_ = JavaToStdString(
43 jni, Java_VideoDecoder_getImplementationName(jni, decoder));
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020044}
45
magjeda3d4f682017-08-28 16:24:06 -070046int32_t VideoDecoderWrapper::InitDecode(const VideoCodec* codec_settings,
47 int32_t number_of_cores) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020048 JNIEnv* jni = AttachCurrentThreadIfNeeded();
49 ScopedLocalRefFrame local_ref_frame(jni);
50
51 codec_settings_ = *codec_settings;
52 number_of_cores_ = number_of_cores;
53 return InitDecodeInternal(jni);
54}
55
56int32_t VideoDecoderWrapper::InitDecodeInternal(JNIEnv* jni) {
Magnus Jedvert4eb01882017-11-20 22:33:40 +010057 jobject settings = Java_VideoDecoderWrapper_createSettings(
58 jni, number_of_cores_, codec_settings_.width, codec_settings_.height);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020059
Magnus Jedvert4eb01882017-11-20 22:33:40 +010060 jobject callback = Java_VideoDecoderWrapper_createDecoderCallback(
61 jni, jlongFromPointer(this));
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020062
63 jobject ret =
Magnus Jedvert4eb01882017-11-20 22:33:40 +010064 Java_VideoDecoder_initDecode(jni, *decoder_, settings, callback);
65 if (JavaToNativeVideoCodecStatus(jni, ret) == WEBRTC_VIDEO_CODEC_OK) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020066 initialized_ = true;
67 }
sakal4dee3442017-08-17 02:18:04 -070068
69 // The decoder was reinitialized so re-enable the QP parsing in case it stops
70 // providing QP values.
71 qp_parsing_enabled_ = true;
72
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020073 return HandleReturnCode(jni, ret);
74}
75
76int32_t VideoDecoderWrapper::Decode(
magjeda3d4f682017-08-28 16:24:06 -070077 const EncodedImage& input_image,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020078 bool missing_frames,
magjeda3d4f682017-08-28 16:24:06 -070079 const RTPFragmentationHeader* fragmentation,
80 const CodecSpecificInfo* codec_specific_info,
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020081 int64_t render_time_ms) {
82 if (!initialized_) {
83 // Most likely initializing the codec failed.
84 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
85 }
86
87 JNIEnv* jni = AttachCurrentThreadIfNeeded();
88 ScopedLocalRefFrame local_ref_frame(jni);
89
90 FrameExtraInfo frame_extra_info;
sakale172d892017-08-31 02:37:28 -070091 frame_extra_info.capture_time_ns =
92 input_image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020093 frame_extra_info.timestamp_rtp = input_image._timeStamp;
sakal4dee3442017-08-17 02:18:04 -070094 frame_extra_info.qp =
Oskar Sundbom9ced7e52017-11-16 10:52:24 +010095 qp_parsing_enabled_ ? ParseQP(input_image) : rtc::nullopt;
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +020096 frame_extra_infos_.push_back(frame_extra_info);
97
Magnus Jedvert4eb01882017-11-20 22:33:40 +010098 jobject jinput_image = NativeToJavaEncodedImage(jni, input_image);
99 jobject ret = Java_VideoDecoder_decode(jni, *decoder_, jinput_image, nullptr);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200100 return HandleReturnCode(jni, ret);
101}
102
103int32_t VideoDecoderWrapper::RegisterDecodeCompleteCallback(
magjeda3d4f682017-08-28 16:24:06 -0700104 DecodedImageCallback* callback) {
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200105 callback_ = callback;
106 return WEBRTC_VIDEO_CODEC_OK;
107}
108
109int32_t VideoDecoderWrapper::Release() {
110 JNIEnv* jni = AttachCurrentThreadIfNeeded();
111 ScopedLocalRefFrame local_ref_frame(jni);
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100112 jobject ret = Java_VideoDecoder_release(jni, *decoder_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200113 frame_extra_infos_.clear();
114 initialized_ = false;
115 return HandleReturnCode(jni, ret);
116}
117
118bool VideoDecoderWrapper::PrefersLateDecoding() const {
119 JNIEnv* jni = AttachCurrentThreadIfNeeded();
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100120 return Java_VideoDecoder_getPrefersLateDecoding(jni, *decoder_);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200121}
122
123const char* VideoDecoderWrapper::ImplementationName() const {
Sami Kalliomäkicee3e532017-10-13 15:20:32 +0200124 return implementation_name_.c_str();
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200125}
126
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100127void VideoDecoderWrapper::OnDecodedFrame(JNIEnv* env,
128 jobject j_caller,
129 jobject j_frame,
130 jobject j_decode_time_ms,
131 jobject j_qp) {
132 const uint64_t capture_time_ns = GetJavaVideoFrameTimestampNs(env, j_frame);
133
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200134 FrameExtraInfo frame_extra_info;
135 do {
136 if (frame_extra_infos_.empty()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100137 RTC_LOG(LS_WARNING) << "Java decoder produced an unexpected frame.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200138 return;
139 }
140
141 frame_extra_info = frame_extra_infos_.front();
142 frame_extra_infos_.pop_front();
143 // If the decoder might drop frames so iterate through the queue until we
144 // find a matching timestamp.
sakale172d892017-08-31 02:37:28 -0700145 } while (frame_extra_info.capture_time_ns != capture_time_ns);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200146
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100147 VideoFrame frame =
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100148 JavaToNativeFrame(env, j_frame, frame_extra_info.timestamp_rtp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200149
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100150 rtc::Optional<int32_t> decoding_time_ms =
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100151 JavaIntegerToOptionalInt(env, j_decode_time_ms);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200152
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100153 rtc::Optional<uint8_t> decoder_qp =
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100154 cast_optional<uint8_t, int32_t>(JavaIntegerToOptionalInt(env, j_qp));
Magnus Jedvertcdda0d92017-11-18 14:02:59 +0100155 // If the decoder provides QP values itself, no need to parse the bitstream.
156 // Enable QP parsing if decoder does not provide QP values itself.
157 qp_parsing_enabled_ = !decoder_qp.has_value();
158 callback_->Decoded(frame, decoding_time_ms,
159 decoder_qp ? decoder_qp : frame_extra_info.qp);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200160}
161
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200162int32_t VideoDecoderWrapper::HandleReturnCode(JNIEnv* jni, jobject code) {
Magnus Jedvert4eb01882017-11-20 22:33:40 +0100163 int32_t value = JavaToNativeVideoCodecStatus(jni, code);
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200164 if (value < 0) { // Any errors are represented by negative values.
165 // Reset the codec.
166 if (Release() == WEBRTC_VIDEO_CODEC_OK) {
167 InitDecodeInternal(jni);
168 }
169
Mirko Bonadei675513b2017-11-09 11:09:25 +0100170 RTC_LOG(LS_WARNING) << "Falling back to software decoder.";
Sami Kalliomäki93ad1f72017-06-27 10:03:49 +0200171 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
172 } else {
173 return value;
174 }
175}
176
sakal4dee3442017-08-17 02:18:04 -0700177rtc::Optional<uint8_t> VideoDecoderWrapper::ParseQP(
magjeda3d4f682017-08-28 16:24:06 -0700178 const EncodedImage& input_image) {
sakal4dee3442017-08-17 02:18:04 -0700179 if (input_image.qp_ != -1) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100180 return input_image.qp_;
sakal4dee3442017-08-17 02:18:04 -0700181 }
182
183 rtc::Optional<uint8_t> qp;
184 switch (codec_settings_.codecType) {
magjeda3d4f682017-08-28 16:24:06 -0700185 case kVideoCodecVP8: {
sakal4dee3442017-08-17 02:18:04 -0700186 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700187 if (vp8::GetQp(input_image._buffer, input_image._length, &qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100188 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700189 }
190 break;
191 }
magjeda3d4f682017-08-28 16:24:06 -0700192 case kVideoCodecVP9: {
sakal4dee3442017-08-17 02:18:04 -0700193 int qp_int;
magjeda3d4f682017-08-28 16:24:06 -0700194 if (vp9::GetQp(input_image._buffer, input_image._length, &qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100195 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700196 }
197 break;
198 }
magjeda3d4f682017-08-28 16:24:06 -0700199 case kVideoCodecH264: {
sakal4dee3442017-08-17 02:18:04 -0700200 h264_bitstream_parser_.ParseBitstream(input_image._buffer,
201 input_image._length);
202 int qp_int;
203 if (h264_bitstream_parser_.GetLastSliceQp(&qp_int)) {
Oskar Sundbom9ced7e52017-11-16 10:52:24 +0100204 qp = qp_int;
sakal4dee3442017-08-17 02:18:04 -0700205 }
206 break;
207 }
208 default:
209 break; // Default is to not provide QP.
210 }
211 return qp;
212}
213
magjeda3d4f682017-08-28 16:24:06 -0700214} // namespace jni
215} // namespace webrtc