blob: e1793b8a162a7e872ffd315e93558c894d6fc1e3 [file] [log] [blame]
glaznev@webrtc.org18c92472015-02-18 18:42:55 +00001/*
2 * libjingle
3 * Copyright 2015 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29#include "talk/app/webrtc/java/jni/androidmediaencoder_jni.h"
30#include "talk/app/webrtc/java/jni/classreferenceholder.h"
31#include "talk/app/webrtc/java/jni/androidmediacodeccommon.h"
32#include "webrtc/base/bind.h"
33#include "webrtc/base/checks.h"
34#include "webrtc/base/logging.h"
35#include "webrtc/base/thread.h"
perkj9576e542015-11-12 06:43:16 -080036#include "webrtc/base/thread_checker.h"
Peter Boström2bc68c72015-09-24 16:22:28 +020037#include "webrtc/modules/rtp_rtcp/source/h264_bitstream_parser.h"
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000038#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
jackychen61b4d512015-04-21 15:30:11 -070039#include "webrtc/modules/video_coding/utility/include/quality_scaler.h"
jackychen98d8cf52015-05-21 11:12:02 -070040#include "webrtc/modules/video_coding/utility/include/vp8_header_parser.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010041#include "webrtc/system_wrappers/include/field_trial.h"
42#include "webrtc/system_wrappers/include/logcat_trace_context.h"
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000043#include "third_party/libyuv/include/libyuv/convert.h"
44#include "third_party/libyuv/include/libyuv/convert_from.h"
45#include "third_party/libyuv/include/libyuv/video_common.h"
46
47using rtc::Bind;
48using rtc::Thread;
49using rtc::ThreadManager;
50using rtc::scoped_ptr;
51
52using webrtc::CodecSpecificInfo;
53using webrtc::EncodedImage;
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -070054using webrtc::VideoFrame;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000055using webrtc::RTPFragmentationHeader;
56using webrtc::VideoCodec;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +000057using webrtc::VideoCodecType;
58using webrtc::kVideoCodecH264;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000059using webrtc::kVideoCodecVP8;
60
61namespace webrtc_jni {
62
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +000063// H.264 start code length.
64#define H264_SC_LENGTH 4
65// Maximum allowed NALUs in one output frame.
66#define MAX_NALUS_PERFRAME 32
67// Maximum supported HW video encoder resolution.
68#define MAX_VIDEO_WIDTH 1280
69#define MAX_VIDEO_HEIGHT 1280
70// Maximum supported HW video encoder fps.
71#define MAX_VIDEO_FPS 30
72
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000073// MediaCodecVideoEncoder is a webrtc::VideoEncoder implementation that uses
74// Android's MediaCodec SDK API behind the scenes to implement (hopefully)
75// HW-backed video encode. This C++ class is implemented as a very thin shim,
76// delegating all of the interesting work to org.webrtc.MediaCodecVideoEncoder.
77// MediaCodecVideoEncoder is created, operated, and destroyed on a single
78// thread, currently the libjingle Worker thread.
79class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
80 public rtc::MessageHandler {
81 public:
82 virtual ~MediaCodecVideoEncoder();
perkj9576e542015-11-12 06:43:16 -080083 MediaCodecVideoEncoder(JNIEnv* jni,
84 VideoCodecType codecType);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000085
86 // webrtc::VideoEncoder implementation. Everything trampolines to
87 // |codec_thread_| for execution.
88 int32_t InitEncode(const webrtc::VideoCodec* codec_settings,
89 int32_t /* number_of_cores */,
90 size_t /* max_payload_size */) override;
pbos22993e12015-10-19 02:39:06 -070091 int32_t Encode(const webrtc::VideoFrame& input_image,
92 const webrtc::CodecSpecificInfo* /* codec_specific_info */,
93 const std::vector<webrtc::FrameType>* frame_types) override;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000094 int32_t RegisterEncodeCompleteCallback(
95 webrtc::EncodedImageCallback* callback) override;
96 int32_t Release() override;
97 int32_t SetChannelParameters(uint32_t /* packet_loss */,
98 int64_t /* rtt */) override;
99 int32_t SetRates(uint32_t new_bit_rate, uint32_t frame_rate) override;
100
101 // rtc::MessageHandler implementation.
102 void OnMessage(rtc::Message* msg) override;
103
jackychen61b4d512015-04-21 15:30:11 -0700104 void OnDroppedFrame() override;
105
jackychen6e2ce6e2015-07-13 16:26:33 -0700106 int GetTargetFramerate() override;
107
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000108 private:
perkj9576e542015-11-12 06:43:16 -0800109 // ResetCodecOnCodecThread() calls ReleaseOnCodecThread() and
110 // InitEncodeOnCodecThread() in an attempt to restore the codec to an
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000111 // operable state. Necessary after all manner of OMX-layer errors.
perkj9576e542015-11-12 06:43:16 -0800112 bool ResetCodecOnCodecThread();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000113
114 // Implementation of webrtc::VideoEncoder methods above, all running on the
115 // codec thread exclusively.
116 //
117 // If width==0 then this is assumed to be a re-initialization and the
118 // previously-current values are reused instead of the passed parameters
119 // (makes it easier to reason about thread-safety).
120 int32_t InitEncodeOnCodecThread(int width, int height, int kbps, int fps);
perkj9576e542015-11-12 06:43:16 -0800121 // Reconfigure to match |frame| in width, height. Returns false if
122 // reconfiguring fails.
123 bool MaybeReconfigureEncoderOnCodecThread(const webrtc::VideoFrame& frame);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000124 int32_t EncodeOnCodecThread(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700125 const webrtc::VideoFrame& input_image,
pbos22993e12015-10-19 02:39:06 -0700126 const std::vector<webrtc::FrameType>* frame_types);
perkj9576e542015-11-12 06:43:16 -0800127 bool EncodeByteBufferOnCodecThread(JNIEnv* jni,
128 bool key_frame, const webrtc::VideoFrame& frame, int input_buffer_index);
129
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000130 int32_t RegisterEncodeCompleteCallbackOnCodecThread(
131 webrtc::EncodedImageCallback* callback);
132 int32_t ReleaseOnCodecThread();
133 int32_t SetRatesOnCodecThread(uint32_t new_bit_rate, uint32_t frame_rate);
134
135 // Helper accessors for MediaCodecVideoEncoder$OutputBufferInfo members.
136 int GetOutputBufferInfoIndex(JNIEnv* jni, jobject j_output_buffer_info);
137 jobject GetOutputBufferInfoBuffer(JNIEnv* jni, jobject j_output_buffer_info);
138 bool GetOutputBufferInfoIsKeyFrame(JNIEnv* jni, jobject j_output_buffer_info);
139 jlong GetOutputBufferInfoPresentationTimestampUs(
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000140 JNIEnv* jni, jobject j_output_buffer_info);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000141
142 // Deliver any outputs pending in the MediaCodec to our |callback_| and return
143 // true on success.
144 bool DeliverPendingOutputs(JNIEnv* jni);
145
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000146 // Search for H.264 start codes.
147 int32_t NextNaluPosition(uint8_t *buffer, size_t buffer_size);
148
149 // Type of video codec.
150 VideoCodecType codecType_;
151
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000152 // Valid all the time since RegisterEncodeCompleteCallback() Invoke()s to
153 // |codec_thread_| synchronously.
154 webrtc::EncodedImageCallback* callback_;
155
156 // State that is constant for the lifetime of this object once the ctor
157 // returns.
158 scoped_ptr<Thread> codec_thread_; // Thread on which to operate MediaCodec.
perkj9576e542015-11-12 06:43:16 -0800159 rtc::ThreadChecker codec_thread_checker_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000160 ScopedGlobalRef<jclass> j_media_codec_video_encoder_class_;
161 ScopedGlobalRef<jobject> j_media_codec_video_encoder_;
162 jmethodID j_init_encode_method_;
perkj9576e542015-11-12 06:43:16 -0800163 jmethodID j_get_input_buffers_method_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000164 jmethodID j_dequeue_input_buffer_method_;
perkj9576e542015-11-12 06:43:16 -0800165 jmethodID j_encode_buffer_method_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000166 jmethodID j_release_method_;
167 jmethodID j_set_rates_method_;
168 jmethodID j_dequeue_output_buffer_method_;
169 jmethodID j_release_output_buffer_method_;
170 jfieldID j_color_format_field_;
171 jfieldID j_info_index_field_;
172 jfieldID j_info_buffer_field_;
173 jfieldID j_info_is_key_frame_field_;
174 jfieldID j_info_presentation_timestamp_us_field_;
175
176 // State that is valid only between InitEncode() and the next Release().
177 // Touched only on codec_thread_ so no explicit synchronization necessary.
178 int width_; // Frame width in pixels.
179 int height_; // Frame height in pixels.
180 bool inited_;
181 uint16_t picture_id_;
182 enum libyuv::FourCC encoder_fourcc_; // Encoder color space format.
183 int last_set_bitrate_kbps_; // Last-requested bitrate in kbps.
184 int last_set_fps_; // Last-requested frame rate.
185 int64_t current_timestamp_us_; // Current frame timestamps in us.
186 int frames_received_; // Number of frames received by encoder.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000187 int frames_encoded_; // Number of frames encoded by encoder.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000188 int frames_dropped_; // Number of frames dropped by encoder.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000189 int frames_in_queue_; // Number of frames in encoder queue.
190 int64_t start_time_ms_; // Start time for statistics.
191 int current_frames_; // Number of frames in the current statistics interval.
192 int current_bytes_; // Encoded bytes in the current statistics interval.
193 int current_encoding_time_ms_; // Overall encoding time in the current second
194 int64_t last_input_timestamp_ms_; // Timestamp of last received yuv frame.
195 int64_t last_output_timestamp_ms_; // Timestamp of last encoded frame.
196 std::vector<int32_t> timestamps_; // Video frames timestamp queue.
197 std::vector<int64_t> render_times_ms_; // Video frames render time queue.
198 std::vector<int64_t> frame_rtc_times_ms_; // Time when video frame is sent to
199 // encoder input.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000200 int32_t output_timestamp_; // Last output frame timestamp from timestamps_ Q.
201 int64_t output_render_time_ms_; // Last output frame render time from
202 // render_times_ms_ queue.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000203 // Frame size in bytes fed to MediaCodec.
204 int yuv_size_;
205 // True only when between a callback_->Encoded() call return a positive value
206 // and the next Encode() call being ignored.
207 bool drop_next_input_frame_;
208 // Global references; must be deleted in Release().
209 std::vector<jobject> input_buffers_;
Peter Boström2bc68c72015-09-24 16:22:28 +0200210 webrtc::QualityScaler quality_scaler_;
jackychen61b4d512015-04-21 15:30:11 -0700211 // Dynamic resolution change, off by default.
212 bool scale_;
Peter Boström2bc68c72015-09-24 16:22:28 +0200213
214 // H264 bitstream parser, used to extract QP from encoded bitstreams.
215 webrtc::H264BitstreamParser h264_bitstream_parser_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000216};
217
218MediaCodecVideoEncoder::~MediaCodecVideoEncoder() {
219 // Call Release() to ensure no more callbacks to us after we are deleted.
220 Release();
221}
222
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000223MediaCodecVideoEncoder::MediaCodecVideoEncoder(
224 JNIEnv* jni, VideoCodecType codecType) :
225 codecType_(codecType),
226 callback_(NULL),
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000227 inited_(false),
228 picture_id_(0),
229 codec_thread_(new Thread()),
230 j_media_codec_video_encoder_class_(
231 jni,
232 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")),
233 j_media_codec_video_encoder_(
234 jni,
235 jni->NewObject(*j_media_codec_video_encoder_class_,
236 GetMethodID(jni,
237 *j_media_codec_video_encoder_class_,
238 "<init>",
239 "()V"))) {
240 ScopedLocalRefFrame local_ref_frame(jni);
241 // It would be nice to avoid spinning up a new thread per MediaCodec, and
242 // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug
243 // 2732 means that deadlocks abound. This class synchronously trampolines
244 // to |codec_thread_|, so if anything else can be coming to _us_ from
245 // |codec_thread_|, or from any thread holding the |_sendCritSect| described
246 // in the bug, we have a problem. For now work around that with a dedicated
247 // thread.
248 codec_thread_->SetName("MediaCodecVideoEncoder", NULL);
henrikg91d6ede2015-09-17 00:24:34 -0700249 RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder";
perkj9576e542015-11-12 06:43:16 -0800250 codec_thread_checker_.DetachFromThread();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000251 jclass j_output_buffer_info_class =
252 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000253 j_init_encode_method_ = GetMethodID(
254 jni,
255 *j_media_codec_video_encoder_class_,
256 "initEncode",
perkj9576e542015-11-12 06:43:16 -0800257 "(Lorg/webrtc/MediaCodecVideoEncoder$VideoCodecType;IIII)Z");
258 j_get_input_buffers_method_ = GetMethodID(
259 jni,
260 *j_media_codec_video_encoder_class_,
261 "getInputBuffers",
262 "()[Ljava/nio/ByteBuffer;");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000263 j_dequeue_input_buffer_method_ = GetMethodID(
264 jni, *j_media_codec_video_encoder_class_, "dequeueInputBuffer", "()I");
perkj9576e542015-11-12 06:43:16 -0800265 j_encode_buffer_method_ = GetMethodID(
266 jni, *j_media_codec_video_encoder_class_, "encodeBuffer", "(ZIIJ)Z");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000267 j_release_method_ =
268 GetMethodID(jni, *j_media_codec_video_encoder_class_, "release", "()V");
269 j_set_rates_method_ = GetMethodID(
270 jni, *j_media_codec_video_encoder_class_, "setRates", "(II)Z");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000271 j_dequeue_output_buffer_method_ = GetMethodID(
272 jni,
273 *j_media_codec_video_encoder_class_,
274 "dequeueOutputBuffer",
275 "()Lorg/webrtc/MediaCodecVideoEncoder$OutputBufferInfo;");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000276 j_release_output_buffer_method_ = GetMethodID(
277 jni, *j_media_codec_video_encoder_class_, "releaseOutputBuffer", "(I)Z");
278
279 j_color_format_field_ =
280 GetFieldID(jni, *j_media_codec_video_encoder_class_, "colorFormat", "I");
281 j_info_index_field_ =
282 GetFieldID(jni, j_output_buffer_info_class, "index", "I");
283 j_info_buffer_field_ = GetFieldID(
284 jni, j_output_buffer_info_class, "buffer", "Ljava/nio/ByteBuffer;");
285 j_info_is_key_frame_field_ =
286 GetFieldID(jni, j_output_buffer_info_class, "isKeyFrame", "Z");
287 j_info_presentation_timestamp_us_field_ = GetFieldID(
288 jni, j_output_buffer_info_class, "presentationTimestampUs", "J");
289 CHECK_EXCEPTION(jni) << "MediaCodecVideoEncoder ctor failed";
290 AllowBlockingCalls();
291}
292
293int32_t MediaCodecVideoEncoder::InitEncode(
294 const webrtc::VideoCodec* codec_settings,
295 int32_t /* number_of_cores */,
296 size_t /* max_payload_size */) {
jackychen61b4d512015-04-21 15:30:11 -0700297 const int kMinWidth = 320;
298 const int kMinHeight = 180;
jackychen98d8cf52015-05-21 11:12:02 -0700299 const int kLowQpThresholdDenominator = 3;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000300 if (codec_settings == NULL) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700301 ALOGE << "NULL VideoCodec instance";
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000302 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
303 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000304 // Factory should guard against other codecs being used with us.
henrikg91d6ede2015-09-17 00:24:34 -0700305 RTC_CHECK(codec_settings->codecType == codecType_)
306 << "Unsupported codec " << codec_settings->codecType << " for "
307 << codecType_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000308
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700309 ALOGD << "InitEncode request";
asaperssonef5d5e42015-09-22 01:40:42 -0700310 scale_ = webrtc::field_trial::FindFullName(
311 "WebRTC-MediaCodecVideoEncoder-AutomaticResize") == "Enabled";
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700312 ALOGD << "Encoder automatic resize " << (scale_ ? "enabled" : "disabled");
Peter Boström2bc68c72015-09-24 16:22:28 +0200313 if (scale_) {
314 if (codecType_ == kVideoCodecVP8) {
315 // QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
316 // (internal) range: [0, 127]. And we cannot change QP_max in HW, so it is
317 // always = 127. Note that in SW, QP is that of the user-level range [0,
318 // 63].
319 const int kMaxQp = 127;
Peter Boström17417702015-09-25 17:03:26 +0200320 // TODO(pbos): Investigate whether high-QP thresholds make sense for VP8.
321 // This effectively disables high QP as VP8 QP can't go above this
322 // threshold.
323 const int kDisabledBadQpThreshold = kMaxQp + 1;
324 quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator,
325 kDisabledBadQpThreshold, true);
Peter Boström2bc68c72015-09-24 16:22:28 +0200326 } else if (codecType_ == kVideoCodecH264) {
327 // H264 QP is in the range [0, 51].
328 const int kMaxQp = 51;
Peter Boström17417702015-09-25 17:03:26 +0200329 const int kBadQpThreshold = 40;
330 quality_scaler_.Init(kMaxQp / kLowQpThresholdDenominator, kBadQpThreshold,
331 false);
Peter Boström2bc68c72015-09-24 16:22:28 +0200332 } else {
333 // When adding codec support to additional hardware codecs, also configure
334 // their QP thresholds for scaling.
335 RTC_NOTREACHED() << "Unsupported codec without configured QP thresholds.";
336 }
337 quality_scaler_.SetMinResolution(kMinWidth, kMinHeight);
338 quality_scaler_.ReportFramerate(codec_settings->maxFramerate);
jackychen61b4d512015-04-21 15:30:11 -0700339 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000340 return codec_thread_->Invoke<int32_t>(
341 Bind(&MediaCodecVideoEncoder::InitEncodeOnCodecThread,
342 this,
343 codec_settings->width,
344 codec_settings->height,
345 codec_settings->startBitrate,
346 codec_settings->maxFramerate));
347}
348
349int32_t MediaCodecVideoEncoder::Encode(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700350 const webrtc::VideoFrame& frame,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000351 const webrtc::CodecSpecificInfo* /* codec_specific_info */,
pbos22993e12015-10-19 02:39:06 -0700352 const std::vector<webrtc::FrameType>* frame_types) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000353 return codec_thread_->Invoke<int32_t>(Bind(
354 &MediaCodecVideoEncoder::EncodeOnCodecThread, this, frame, frame_types));
355}
356
357int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallback(
358 webrtc::EncodedImageCallback* callback) {
359 return codec_thread_->Invoke<int32_t>(
360 Bind(&MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread,
361 this,
362 callback));
363}
364
365int32_t MediaCodecVideoEncoder::Release() {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700366 ALOGD << "EncoderRelease request";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000367 return codec_thread_->Invoke<int32_t>(
368 Bind(&MediaCodecVideoEncoder::ReleaseOnCodecThread, this));
369}
370
371int32_t MediaCodecVideoEncoder::SetChannelParameters(uint32_t /* packet_loss */,
372 int64_t /* rtt */) {
373 return WEBRTC_VIDEO_CODEC_OK;
374}
375
376int32_t MediaCodecVideoEncoder::SetRates(uint32_t new_bit_rate,
377 uint32_t frame_rate) {
Peter Boström2bc68c72015-09-24 16:22:28 +0200378 if (scale_)
379 quality_scaler_.ReportFramerate(frame_rate);
380
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000381 return codec_thread_->Invoke<int32_t>(
382 Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread,
383 this,
384 new_bit_rate,
385 frame_rate));
386}
387
388void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
perkj9576e542015-11-12 06:43:16 -0800389 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000390 JNIEnv* jni = AttachCurrentThreadIfNeeded();
391 ScopedLocalRefFrame local_ref_frame(jni);
392
393 // We only ever send one message to |this| directly (not through a Bind()'d
394 // functor), so expect no ID/data.
henrikg91d6ede2015-09-17 00:24:34 -0700395 RTC_CHECK(!msg->message_id) << "Unexpected message!";
396 RTC_CHECK(!msg->pdata) << "Unexpected message!";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000397 if (!inited_) {
398 return;
399 }
400
401 // It would be nice to recover from a failure here if one happened, but it's
402 // unclear how to signal such a failure to the app, so instead we stay silent
403 // about it and let the next app-called API method reveal the borkedness.
404 DeliverPendingOutputs(jni);
405 codec_thread_->PostDelayed(kMediaCodecPollMs, this);
406}
407
perkj9576e542015-11-12 06:43:16 -0800408bool MediaCodecVideoEncoder::ResetCodecOnCodecThread() {
409 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
410 ALOGE << "ResetOnCodecThread";
411 if (ReleaseOnCodecThread() != WEBRTC_VIDEO_CODEC_OK ||
412 InitEncodeOnCodecThread(width_, height_, 0, 0)
413 != WEBRTC_VIDEO_CODEC_OK) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000414 // TODO(fischman): wouldn't it be nice if there was a way to gracefully
415 // degrade to a SW encoder at this point? There isn't one AFAICT :(
416 // https://code.google.com/p/webrtc/issues/detail?id=2920
perkj9576e542015-11-12 06:43:16 -0800417 return false;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000418 }
perkj9576e542015-11-12 06:43:16 -0800419 return true;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000420}
421
422int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
423 int width, int height, int kbps, int fps) {
perkj9576e542015-11-12 06:43:16 -0800424 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000425 JNIEnv* jni = AttachCurrentThreadIfNeeded();
426 ScopedLocalRefFrame local_ref_frame(jni);
427
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700428 ALOGD << "InitEncodeOnCodecThread Type: " << (int)codecType_ << ", " <<
429 width << " x " << height << ". Bitrate: " << kbps <<
430 " kbps. Fps: " << fps;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000431 if (kbps == 0) {
432 kbps = last_set_bitrate_kbps_;
433 }
434 if (fps == 0) {
435 fps = last_set_fps_;
436 }
437
438 width_ = width;
439 height_ = height;
440 last_set_bitrate_kbps_ = kbps;
441 last_set_fps_ = fps;
442 yuv_size_ = width_ * height_ * 3 / 2;
443 frames_received_ = 0;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000444 frames_encoded_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000445 frames_dropped_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000446 frames_in_queue_ = 0;
447 current_timestamp_us_ = 0;
448 start_time_ms_ = GetCurrentTimeMs();
449 current_frames_ = 0;
450 current_bytes_ = 0;
451 current_encoding_time_ms_ = 0;
452 last_input_timestamp_ms_ = -1;
453 last_output_timestamp_ms_ = -1;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000454 output_timestamp_ = 0;
455 output_render_time_ms_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000456 timestamps_.clear();
457 render_times_ms_.clear();
458 frame_rtc_times_ms_.clear();
459 drop_next_input_frame_ = false;
460 picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
perkj9576e542015-11-12 06:43:16 -0800461
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000462 // We enforce no extra stride/padding in the format creation step.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000463 jobject j_video_codec_enum = JavaEnumFromIndex(
464 jni, "MediaCodecVideoEncoder$VideoCodecType", codecType_);
perkj9576e542015-11-12 06:43:16 -0800465 const bool encode_status = jni->CallBooleanMethod(
466 *j_media_codec_video_encoder_, j_init_encode_method_,
467 j_video_codec_enum, width, height, kbps, fps);
468 if (!encode_status) {
469 ALOGE << "Failed to configure encoder.";
470 return WEBRTC_VIDEO_CODEC_ERROR;
471 }
472 CHECK_EXCEPTION(jni);
473
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000474 jobjectArray input_buffers = reinterpret_cast<jobjectArray>(
475 jni->CallObjectMethod(*j_media_codec_video_encoder_,
perkj9576e542015-11-12 06:43:16 -0800476 j_get_input_buffers_method_));
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000477 CHECK_EXCEPTION(jni);
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000478 if (IsNull(jni, input_buffers)) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000479 return WEBRTC_VIDEO_CODEC_ERROR;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000480 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000481
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000482 switch (GetIntField(jni, *j_media_codec_video_encoder_,
483 j_color_format_field_)) {
484 case COLOR_FormatYUV420Planar:
485 encoder_fourcc_ = libyuv::FOURCC_YU12;
486 break;
487 case COLOR_FormatYUV420SemiPlanar:
488 case COLOR_QCOM_FormatYUV420SemiPlanar:
489 case COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m:
490 encoder_fourcc_ = libyuv::FOURCC_NV12;
491 break;
492 default:
493 LOG(LS_ERROR) << "Wrong color format.";
494 return WEBRTC_VIDEO_CODEC_ERROR;
495 }
496 size_t num_input_buffers = jni->GetArrayLength(input_buffers);
henrikg91d6ede2015-09-17 00:24:34 -0700497 RTC_CHECK(input_buffers_.empty())
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000498 << "Unexpected double InitEncode without Release";
499 input_buffers_.resize(num_input_buffers);
500 for (size_t i = 0; i < num_input_buffers; ++i) {
501 input_buffers_[i] =
502 jni->NewGlobalRef(jni->GetObjectArrayElement(input_buffers, i));
Peter Boström0c4e06b2015-10-07 12:23:21 +0200503 int64_t yuv_buffer_capacity =
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000504 jni->GetDirectBufferCapacity(input_buffers_[i]);
505 CHECK_EXCEPTION(jni);
henrikg91d6ede2015-09-17 00:24:34 -0700506 RTC_CHECK(yuv_buffer_capacity >= yuv_size_) << "Insufficient capacity";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000507 }
508 CHECK_EXCEPTION(jni);
509
perkj9576e542015-11-12 06:43:16 -0800510
511 inited_ = true;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000512 codec_thread_->PostDelayed(kMediaCodecPollMs, this);
513 return WEBRTC_VIDEO_CODEC_OK;
514}
515
516int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
Miguel Casas-Sanchez47650702015-05-29 17:21:40 -0700517 const webrtc::VideoFrame& frame,
pbos22993e12015-10-19 02:39:06 -0700518 const std::vector<webrtc::FrameType>* frame_types) {
perkj9576e542015-11-12 06:43:16 -0800519 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000520 JNIEnv* jni = AttachCurrentThreadIfNeeded();
521 ScopedLocalRefFrame local_ref_frame(jni);
522
523 if (!inited_) {
524 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
525 }
perkj9576e542015-11-12 06:43:16 -0800526
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000527 frames_received_++;
528 if (!DeliverPendingOutputs(jni)) {
perkj9576e542015-11-12 06:43:16 -0800529 if (!ResetCodecOnCodecThread())
530 return WEBRTC_VIDEO_CODEC_ERROR;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000531 }
532
533 if (drop_next_input_frame_) {
perkj9576e542015-11-12 06:43:16 -0800534 ALOGW << "Encoder drop frame - failed callback.";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000535 drop_next_input_frame_ = false;
536 return WEBRTC_VIDEO_CODEC_OK;
537 }
538
henrikg91d6ede2015-09-17 00:24:34 -0700539 RTC_CHECK(frame_types->size() == 1) << "Unexpected stream count";
jackychen6e2ce6e2015-07-13 16:26:33 -0700540 // Check framerate before spatial resolution change.
Peter Boström2bc68c72015-09-24 16:22:28 +0200541 if (scale_)
542 quality_scaler_.OnEncodeFrame(frame);
543
544 const VideoFrame& input_frame =
545 scale_ ? quality_scaler_.GetScaledFrame(frame) : frame;
jackychen61b4d512015-04-21 15:30:11 -0700546
perkj9576e542015-11-12 06:43:16 -0800547 if (!MaybeReconfigureEncoderOnCodecThread(input_frame)) {
548 ALOGE << "Failed to reconfigure encoder.";
549 return WEBRTC_VIDEO_CODEC_ERROR;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000550 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000551
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000552 // Check if we accumulated too many frames in encoder input buffers
553 // or the encoder latency exceeds 70 ms and drop frame if so.
554 if (frames_in_queue_ > 0 && last_input_timestamp_ms_ >= 0) {
555 int encoder_latency_ms = last_input_timestamp_ms_ -
556 last_output_timestamp_ms_;
557 if (frames_in_queue_ > 2 || encoder_latency_ms > 70) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700558 ALOGD << "Drop frame - encoder is behind by " << encoder_latency_ms <<
559 " ms. Q size: " << frames_in_queue_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000560 frames_dropped_++;
jackychen61b4d512015-04-21 15:30:11 -0700561 // Report dropped frame to quality_scaler_.
562 OnDroppedFrame();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000563 return WEBRTC_VIDEO_CODEC_OK;
564 }
565 }
566
567 int j_input_buffer_index = jni->CallIntMethod(*j_media_codec_video_encoder_,
perkj9576e542015-11-12 06:43:16 -0800568 j_dequeue_input_buffer_method_);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000569 CHECK_EXCEPTION(jni);
570 if (j_input_buffer_index == -1) {
571 // Video codec falls behind - no input buffer available.
perkj9576e542015-11-12 06:43:16 -0800572 ALOGW << "Encoder drop frame - no input buffers available";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000573 frames_dropped_++;
jackychen61b4d512015-04-21 15:30:11 -0700574 // Report dropped frame to quality_scaler_.
575 OnDroppedFrame();
perkj12f68022015-10-16 13:31:45 +0200576 return WEBRTC_VIDEO_CODEC_OK; // TODO(fischman): see webrtc bug 2887.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000577 }
578 if (j_input_buffer_index == -2) {
perkj9576e542015-11-12 06:43:16 -0800579 ResetCodecOnCodecThread();
perkj12f68022015-10-16 13:31:45 +0200580 return WEBRTC_VIDEO_CODEC_ERROR;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000581 }
582
perkj9576e542015-11-12 06:43:16 -0800583 last_input_timestamp_ms_ =
584 current_timestamp_us_ / rtc::kNumMicrosecsPerMillisec;
perkj12f68022015-10-16 13:31:45 +0200585 frames_in_queue_++;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000586
perkj12f68022015-10-16 13:31:45 +0200587 // Save input image timestamps for later output
588 timestamps_.push_back(input_frame.timestamp());
589 render_times_ms_.push_back(input_frame.render_time_ms());
590 frame_rtc_times_ms_.push_back(GetCurrentTimeMs());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000591
perkj9576e542015-11-12 06:43:16 -0800592 const bool key_frame = frame_types->front() != webrtc::kVideoFrameDelta;
593 const bool encode_status =
594 EncodeByteBufferOnCodecThread(jni, key_frame, input_frame,
595 j_input_buffer_index);
596
597 current_timestamp_us_ += rtc::kNumMicrosecsPerSec / last_set_fps_;
598
599 if (!encode_status || !DeliverPendingOutputs(jni)) {
600 ALOGE << "Failed deliver pending outputs.";
601 ResetCodecOnCodecThread();
602 return WEBRTC_VIDEO_CODEC_ERROR;
603 }
604 return WEBRTC_VIDEO_CODEC_OK;
605}
606
607bool MediaCodecVideoEncoder::MaybeReconfigureEncoderOnCodecThread(
608 const webrtc::VideoFrame& frame) {
609 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
610
611 const bool reconfigure_due_to_size =
612 frame.width() != width_ || frame.height() != height_;
613
614 if (reconfigure_due_to_size) {
615 ALOGD << "Reconfigure encoder due to frame resolution change from "
616 << width_ << " x " << height_ << " to " << frame.width() << " x "
617 << frame.height();
618 width_ = frame.width();
619 height_ = frame.height();
620 }
621
622 if (!reconfigure_due_to_size)
623 return true;
624
625 ReleaseOnCodecThread();
626
627 return InitEncodeOnCodecThread(width_, height_, 0, 0) ==
628 WEBRTC_VIDEO_CODEC_OK;
629}
630
631bool MediaCodecVideoEncoder::EncodeByteBufferOnCodecThread(JNIEnv* jni,
632 bool key_frame, const webrtc::VideoFrame& frame, int input_buffer_index) {
633 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
634
635 ALOGV("Encoder frame in # %d. TS: %lld. Q: %d",
636 frames_received_ - 1, current_timestamp_us_ / 1000, frames_in_queue_);
637
638 jobject j_input_buffer = input_buffers_[input_buffer_index];
639 uint8_t* yuv_buffer =
640 reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_input_buffer));
641 CHECK_EXCEPTION(jni);
642 RTC_CHECK(yuv_buffer) << "Indirect buffer??";
643 RTC_CHECK(!libyuv::ConvertFromI420(
644 frame.buffer(webrtc::kYPlane), frame.stride(webrtc::kYPlane),
645 frame.buffer(webrtc::kUPlane), frame.stride(webrtc::kUPlane),
646 frame.buffer(webrtc::kVPlane), frame.stride(webrtc::kVPlane),
647 yuv_buffer, width_, width_, height_, encoder_fourcc_))
648 << "ConvertFromI420 failed";
649
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000650 bool encode_status = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
perkj9576e542015-11-12 06:43:16 -0800651 j_encode_buffer_method_,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000652 key_frame,
perkj9576e542015-11-12 06:43:16 -0800653 input_buffer_index,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000654 yuv_size_,
655 current_timestamp_us_);
656 CHECK_EXCEPTION(jni);
perkj9576e542015-11-12 06:43:16 -0800657 return encode_status;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000658}
659
660int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread(
661 webrtc::EncodedImageCallback* callback) {
perkj9576e542015-11-12 06:43:16 -0800662 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000663 JNIEnv* jni = AttachCurrentThreadIfNeeded();
664 ScopedLocalRefFrame local_ref_frame(jni);
665 callback_ = callback;
666 return WEBRTC_VIDEO_CODEC_OK;
667}
668
669int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() {
perkj9576e542015-11-12 06:43:16 -0800670 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000671 if (!inited_) {
672 return WEBRTC_VIDEO_CODEC_OK;
673 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000674 JNIEnv* jni = AttachCurrentThreadIfNeeded();
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700675 ALOGD << "EncoderReleaseOnCodecThread: Frames received: " <<
676 frames_received_ << ". Encoded: " << frames_encoded_ <<
677 ". Dropped: " << frames_dropped_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000678 ScopedLocalRefFrame local_ref_frame(jni);
679 for (size_t i = 0; i < input_buffers_.size(); ++i)
680 jni->DeleteGlobalRef(input_buffers_[i]);
681 input_buffers_.clear();
682 jni->CallVoidMethod(*j_media_codec_video_encoder_, j_release_method_);
683 CHECK_EXCEPTION(jni);
684 rtc::MessageQueueManager::Clear(this);
685 inited_ = false;
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700686 ALOGD << "EncoderReleaseOnCodecThread done.";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000687 return WEBRTC_VIDEO_CODEC_OK;
688}
689
690int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate,
691 uint32_t frame_rate) {
perkj9576e542015-11-12 06:43:16 -0800692 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000693 if (last_set_bitrate_kbps_ == new_bit_rate &&
694 last_set_fps_ == frame_rate) {
695 return WEBRTC_VIDEO_CODEC_OK;
696 }
697 JNIEnv* jni = AttachCurrentThreadIfNeeded();
698 ScopedLocalRefFrame local_ref_frame(jni);
699 if (new_bit_rate > 0) {
700 last_set_bitrate_kbps_ = new_bit_rate;
701 }
702 if (frame_rate > 0) {
703 last_set_fps_ = frame_rate;
704 }
705 bool ret = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
706 j_set_rates_method_,
707 last_set_bitrate_kbps_,
708 last_set_fps_);
709 CHECK_EXCEPTION(jni);
710 if (!ret) {
perkj9576e542015-11-12 06:43:16 -0800711 ResetCodecOnCodecThread();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000712 return WEBRTC_VIDEO_CODEC_ERROR;
713 }
714 return WEBRTC_VIDEO_CODEC_OK;
715}
716
717int MediaCodecVideoEncoder::GetOutputBufferInfoIndex(
718 JNIEnv* jni,
719 jobject j_output_buffer_info) {
720 return GetIntField(jni, j_output_buffer_info, j_info_index_field_);
721}
722
723jobject MediaCodecVideoEncoder::GetOutputBufferInfoBuffer(
724 JNIEnv* jni,
725 jobject j_output_buffer_info) {
726 return GetObjectField(jni, j_output_buffer_info, j_info_buffer_field_);
727}
728
729bool MediaCodecVideoEncoder::GetOutputBufferInfoIsKeyFrame(
730 JNIEnv* jni,
731 jobject j_output_buffer_info) {
732 return GetBooleanField(jni, j_output_buffer_info, j_info_is_key_frame_field_);
733}
734
735jlong MediaCodecVideoEncoder::GetOutputBufferInfoPresentationTimestampUs(
736 JNIEnv* jni,
737 jobject j_output_buffer_info) {
738 return GetLongField(
739 jni, j_output_buffer_info, j_info_presentation_timestamp_us_field_);
740}
741
742bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) {
perkj9576e542015-11-12 06:43:16 -0800743 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000744 while (true) {
745 jobject j_output_buffer_info = jni->CallObjectMethod(
746 *j_media_codec_video_encoder_, j_dequeue_output_buffer_method_);
747 CHECK_EXCEPTION(jni);
748 if (IsNull(jni, j_output_buffer_info)) {
749 break;
750 }
751
752 int output_buffer_index =
753 GetOutputBufferInfoIndex(jni, j_output_buffer_info);
754 if (output_buffer_index == -1) {
perkj9576e542015-11-12 06:43:16 -0800755 ResetCodecOnCodecThread();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000756 return false;
757 }
758
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000759 // Get key and config frame flags.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000760 jobject j_output_buffer =
761 GetOutputBufferInfoBuffer(jni, j_output_buffer_info);
762 bool key_frame = GetOutputBufferInfoIsKeyFrame(jni, j_output_buffer_info);
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000763
764 // Get frame timestamps from a queue - for non config frames only.
765 int64_t frame_encoding_time_ms = 0;
766 last_output_timestamp_ms_ =
767 GetOutputBufferInfoPresentationTimestampUs(jni, j_output_buffer_info) /
768 1000;
769 if (frames_in_queue_ > 0) {
770 output_timestamp_ = timestamps_.front();
771 timestamps_.erase(timestamps_.begin());
772 output_render_time_ms_ = render_times_ms_.front();
773 render_times_ms_.erase(render_times_ms_.begin());
774 frame_encoding_time_ms = GetCurrentTimeMs() - frame_rtc_times_ms_.front();
775 frame_rtc_times_ms_.erase(frame_rtc_times_ms_.begin());
776 frames_in_queue_--;
777 }
778
779 // Extract payload.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000780 size_t payload_size = jni->GetDirectBufferCapacity(j_output_buffer);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200781 uint8_t* payload = reinterpret_cast<uint8_t*>(
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000782 jni->GetDirectBufferAddress(j_output_buffer));
783 CHECK_EXCEPTION(jni);
784
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000785 ALOGV("Encoder frame out # %d. Key: %d. Size: %d. TS: %lld."
786 " Latency: %lld. EncTime: %lld",
787 frames_encoded_, key_frame, payload_size,
788 last_output_timestamp_ms_,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000789 last_input_timestamp_ms_ - last_output_timestamp_ms_,
790 frame_encoding_time_ms);
791
792 // Calculate and print encoding statistics - every 3 seconds.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000793 frames_encoded_++;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000794 current_frames_++;
795 current_bytes_ += payload_size;
796 current_encoding_time_ms_ += frame_encoding_time_ms;
797 int statistic_time_ms = GetCurrentTimeMs() - start_time_ms_;
798 if (statistic_time_ms >= kMediaCodecStatisticsIntervalMs &&
799 current_frames_ > 0) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700800 ALOGD << "Encoded frames: " << frames_encoded_ << ". Bitrate: " <<
801 (current_bytes_ * 8 / statistic_time_ms) <<
802 ", target: " << last_set_bitrate_kbps_ << " kbps, fps: " <<
803 ((current_frames_ * 1000 + statistic_time_ms / 2) / statistic_time_ms)
804 << ", encTime: " <<
805 (current_encoding_time_ms_ / current_frames_) << " for last " <<
806 statistic_time_ms << " ms.";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000807 start_time_ms_ = GetCurrentTimeMs();
808 current_frames_ = 0;
809 current_bytes_ = 0;
810 current_encoding_time_ms_ = 0;
811 }
812
813 // Callback - return encoded frame.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000814 int32_t callback_status = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000815 if (callback_) {
816 scoped_ptr<webrtc::EncodedImage> image(
817 new webrtc::EncodedImage(payload, payload_size, payload_size));
818 image->_encodedWidth = width_;
819 image->_encodedHeight = height_;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000820 image->_timeStamp = output_timestamp_;
821 image->capture_time_ms_ = output_render_time_ms_;
Peter Boström49e196a2015-10-23 15:58:18 +0200822 image->_frameType =
823 (key_frame ? webrtc::kVideoFrameKey : webrtc::kVideoFrameDelta);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000824 image->_completeFrame = true;
asapersson075fb4b2015-10-29 08:49:14 -0700825 image->adapt_reason_.quality_resolution_downscales =
826 scale_ ? quality_scaler_.downscale_shift() : -1;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000827
828 webrtc::CodecSpecificInfo info;
829 memset(&info, 0, sizeof(info));
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000830 info.codecType = codecType_;
831 if (codecType_ == kVideoCodecVP8) {
832 info.codecSpecific.VP8.pictureId = picture_id_;
833 info.codecSpecific.VP8.nonReference = false;
834 info.codecSpecific.VP8.simulcastIdx = 0;
835 info.codecSpecific.VP8.temporalIdx = webrtc::kNoTemporalIdx;
836 info.codecSpecific.VP8.layerSync = false;
837 info.codecSpecific.VP8.tl0PicIdx = webrtc::kNoTl0PicIdx;
838 info.codecSpecific.VP8.keyIdx = webrtc::kNoKeyIdx;
839 picture_id_ = (picture_id_ + 1) & 0x7FFF;
840 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000841
842 // Generate a header describing a single fragment.
843 webrtc::RTPFragmentationHeader header;
844 memset(&header, 0, sizeof(header));
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000845 if (codecType_ == kVideoCodecVP8) {
846 header.VerifyAndAllocateFragmentationHeader(1);
847 header.fragmentationOffset[0] = 0;
848 header.fragmentationLength[0] = image->_length;
849 header.fragmentationPlType[0] = 0;
850 header.fragmentationTimeDiff[0] = 0;
asapersson86b01602015-10-20 23:55:26 -0700851 if (scale_) {
852 int qp;
853 if (webrtc::vp8::GetQp(payload, payload_size, &qp))
854 quality_scaler_.ReportQP(qp);
855 }
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000856 } else if (codecType_ == kVideoCodecH264) {
Peter Boström2bc68c72015-09-24 16:22:28 +0200857 if (scale_) {
858 h264_bitstream_parser_.ParseBitstream(payload, payload_size);
859 int qp;
860 if (h264_bitstream_parser_.GetLastSliceQp(&qp))
861 quality_scaler_.ReportQP(qp);
862 }
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000863 // For H.264 search for start codes.
864 int32_t scPositions[MAX_NALUS_PERFRAME + 1] = {};
865 int32_t scPositionsLength = 0;
866 int32_t scPosition = 0;
867 while (scPositionsLength < MAX_NALUS_PERFRAME) {
868 int32_t naluPosition = NextNaluPosition(
869 payload + scPosition, payload_size - scPosition);
870 if (naluPosition < 0) {
871 break;
872 }
873 scPosition += naluPosition;
874 scPositions[scPositionsLength++] = scPosition;
875 scPosition += H264_SC_LENGTH;
876 }
877 if (scPositionsLength == 0) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700878 ALOGE << "Start code is not found!";
879 ALOGE << "Data:" << image->_buffer[0] << " " << image->_buffer[1]
880 << " " << image->_buffer[2] << " " << image->_buffer[3]
881 << " " << image->_buffer[4] << " " << image->_buffer[5];
perkj9576e542015-11-12 06:43:16 -0800882 ResetCodecOnCodecThread();
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000883 return false;
884 }
885 scPositions[scPositionsLength] = payload_size;
886 header.VerifyAndAllocateFragmentationHeader(scPositionsLength);
887 for (size_t i = 0; i < scPositionsLength; i++) {
888 header.fragmentationOffset[i] = scPositions[i] + H264_SC_LENGTH;
889 header.fragmentationLength[i] =
890 scPositions[i + 1] - header.fragmentationOffset[i];
891 header.fragmentationPlType[i] = 0;
892 header.fragmentationTimeDiff[i] = 0;
893 }
894 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000895
896 callback_status = callback_->Encoded(*image, &info, &header);
897 }
898
899 // Return output buffer back to the encoder.
900 bool success = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
901 j_release_output_buffer_method_,
902 output_buffer_index);
903 CHECK_EXCEPTION(jni);
904 if (!success) {
perkj9576e542015-11-12 06:43:16 -0800905 ResetCodecOnCodecThread();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000906 return false;
907 }
908
909 if (callback_status > 0) {
910 drop_next_input_frame_ = true;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000911 // Theoretically could handle callback_status<0 here, but unclear what
912 // that would mean for us.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000913 }
914 }
915
916 return true;
917}
918
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000919int32_t MediaCodecVideoEncoder::NextNaluPosition(
920 uint8_t *buffer, size_t buffer_size) {
921 if (buffer_size < H264_SC_LENGTH) {
922 return -1;
923 }
924 uint8_t *head = buffer;
925 // Set end buffer pointer to 4 bytes before actual buffer end so we can
926 // access head[1], head[2] and head[3] in a loop without buffer overrun.
927 uint8_t *end = buffer + buffer_size - H264_SC_LENGTH;
928
929 while (head < end) {
930 if (head[0]) {
931 head++;
932 continue;
933 }
934 if (head[1]) { // got 00xx
935 head += 2;
936 continue;
937 }
938 if (head[2]) { // got 0000xx
939 head += 3;
940 continue;
941 }
942 if (head[3] != 0x01) { // got 000000xx
glaznev@webrtc.orgdc08a232015-03-06 23:32:20 +0000943 head++; // xx != 1, continue searching.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000944 continue;
945 }
946 return (int32_t)(head - buffer);
947 }
948 return -1;
949}
950
jackychen61b4d512015-04-21 15:30:11 -0700951void MediaCodecVideoEncoder::OnDroppedFrame() {
Peter Boström2bc68c72015-09-24 16:22:28 +0200952 if (scale_)
953 quality_scaler_.ReportDroppedFrame();
jackychen61b4d512015-04-21 15:30:11 -0700954}
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000955
jackychen6e2ce6e2015-07-13 16:26:33 -0700956int MediaCodecVideoEncoder::GetTargetFramerate() {
Peter Boström2bc68c72015-09-24 16:22:28 +0200957 return scale_ ? quality_scaler_.GetTargetFramerate() : -1;
jackychen6e2ce6e2015-07-13 16:26:33 -0700958}
959
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000960MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory() {
961 JNIEnv* jni = AttachCurrentThreadIfNeeded();
962 ScopedLocalRefFrame local_ref_frame(jni);
963 jclass j_encoder_class = FindClass(jni, "org/webrtc/MediaCodecVideoEncoder");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000964 supported_codecs_.clear();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000965
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000966 bool is_vp8_hw_supported = jni->CallStaticBooleanMethod(
967 j_encoder_class,
968 GetStaticMethodID(jni, j_encoder_class, "isVp8HwSupported", "()Z"));
969 CHECK_EXCEPTION(jni);
970 if (is_vp8_hw_supported) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700971 ALOGD << "VP8 HW Encoder supported.";
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000972 supported_codecs_.push_back(VideoCodec(kVideoCodecVP8, "VP8",
973 MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
974 }
975
976 bool is_h264_hw_supported = jni->CallStaticBooleanMethod(
977 j_encoder_class,
978 GetStaticMethodID(jni, j_encoder_class, "isH264HwSupported", "()Z"));
979 CHECK_EXCEPTION(jni);
980 if (is_h264_hw_supported) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700981 ALOGD << "H.264 HW Encoder supported.";
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000982 supported_codecs_.push_back(VideoCodec(kVideoCodecH264, "H264",
983 MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
984 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000985}
986
987MediaCodecVideoEncoderFactory::~MediaCodecVideoEncoderFactory() {}
988
989webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder(
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000990 VideoCodecType type) {
991 if (supported_codecs_.empty()) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000992 return NULL;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000993 }
994 for (std::vector<VideoCodec>::const_iterator it = supported_codecs_.begin();
995 it != supported_codecs_.end(); ++it) {
996 if (it->type == type) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -0700997 ALOGD << "Create HW video encoder for type " << (int)type <<
998 " (" << it->name << ").";
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000999 return new MediaCodecVideoEncoder(AttachCurrentThreadIfNeeded(), type);
1000 }
1001 }
1002 return NULL;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +00001003}
1004
1005const std::vector<MediaCodecVideoEncoderFactory::VideoCodec>&
1006MediaCodecVideoEncoderFactory::codecs() const {
1007 return supported_codecs_;
1008}
1009
1010void MediaCodecVideoEncoderFactory::DestroyVideoEncoder(
1011 webrtc::VideoEncoder* encoder) {
Alex Glaznevfddf6e52015-10-07 16:51:02 -07001012 ALOGD << "Destroy video encoder.";
glaznev@webrtc.org18c92472015-02-18 18:42:55 +00001013 delete encoder;
1014}
1015
1016} // namespace webrtc_jni
1017