blob: 00e6a381e0d903ed35cffa2aff152ea89c3d08f5 [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"
36#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
jackychen61b4d512015-04-21 15:30:11 -070037#include "webrtc/modules/video_coding/utility/include/quality_scaler.h"
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000038#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
39#include "third_party/libyuv/include/libyuv/convert.h"
40#include "third_party/libyuv/include/libyuv/convert_from.h"
41#include "third_party/libyuv/include/libyuv/video_common.h"
42
43using rtc::Bind;
44using rtc::Thread;
45using rtc::ThreadManager;
46using rtc::scoped_ptr;
47
48using webrtc::CodecSpecificInfo;
49using webrtc::EncodedImage;
50using webrtc::I420VideoFrame;
51using webrtc::RTPFragmentationHeader;
52using webrtc::VideoCodec;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +000053using webrtc::VideoCodecType;
54using webrtc::kVideoCodecH264;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000055using webrtc::kVideoCodecVP8;
56
57namespace webrtc_jni {
58
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +000059// H.264 start code length.
60#define H264_SC_LENGTH 4
61// Maximum allowed NALUs in one output frame.
62#define MAX_NALUS_PERFRAME 32
63// Maximum supported HW video encoder resolution.
64#define MAX_VIDEO_WIDTH 1280
65#define MAX_VIDEO_HEIGHT 1280
66// Maximum supported HW video encoder fps.
67#define MAX_VIDEO_FPS 30
68
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000069// MediaCodecVideoEncoder is a webrtc::VideoEncoder implementation that uses
70// Android's MediaCodec SDK API behind the scenes to implement (hopefully)
71// HW-backed video encode. This C++ class is implemented as a very thin shim,
72// delegating all of the interesting work to org.webrtc.MediaCodecVideoEncoder.
73// MediaCodecVideoEncoder is created, operated, and destroyed on a single
74// thread, currently the libjingle Worker thread.
75class MediaCodecVideoEncoder : public webrtc::VideoEncoder,
76 public rtc::MessageHandler {
77 public:
78 virtual ~MediaCodecVideoEncoder();
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +000079 explicit MediaCodecVideoEncoder(JNIEnv* jni, VideoCodecType codecType);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +000080
81 // webrtc::VideoEncoder implementation. Everything trampolines to
82 // |codec_thread_| for execution.
83 int32_t InitEncode(const webrtc::VideoCodec* codec_settings,
84 int32_t /* number_of_cores */,
85 size_t /* max_payload_size */) override;
86 int32_t Encode(
87 const webrtc::I420VideoFrame& input_image,
88 const webrtc::CodecSpecificInfo* /* codec_specific_info */,
89 const std::vector<webrtc::VideoFrameType>* frame_types) override;
90 int32_t RegisterEncodeCompleteCallback(
91 webrtc::EncodedImageCallback* callback) override;
92 int32_t Release() override;
93 int32_t SetChannelParameters(uint32_t /* packet_loss */,
94 int64_t /* rtt */) override;
95 int32_t SetRates(uint32_t new_bit_rate, uint32_t frame_rate) override;
96
97 // rtc::MessageHandler implementation.
98 void OnMessage(rtc::Message* msg) override;
99
jackychen61b4d512015-04-21 15:30:11 -0700100 void OnDroppedFrame() override;
101
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000102 private:
103 // CHECK-fail if not running on |codec_thread_|.
104 void CheckOnCodecThread();
105
106 // Release() and InitEncode() in an attempt to restore the codec to an
107 // operable state. Necessary after all manner of OMX-layer errors.
108 void ResetCodec();
109
110 // Implementation of webrtc::VideoEncoder methods above, all running on the
111 // codec thread exclusively.
112 //
113 // If width==0 then this is assumed to be a re-initialization and the
114 // previously-current values are reused instead of the passed parameters
115 // (makes it easier to reason about thread-safety).
116 int32_t InitEncodeOnCodecThread(int width, int height, int kbps, int fps);
117 int32_t EncodeOnCodecThread(
118 const webrtc::I420VideoFrame& input_image,
119 const std::vector<webrtc::VideoFrameType>* frame_types);
120 int32_t RegisterEncodeCompleteCallbackOnCodecThread(
121 webrtc::EncodedImageCallback* callback);
122 int32_t ReleaseOnCodecThread();
123 int32_t SetRatesOnCodecThread(uint32_t new_bit_rate, uint32_t frame_rate);
124
125 // Helper accessors for MediaCodecVideoEncoder$OutputBufferInfo members.
126 int GetOutputBufferInfoIndex(JNIEnv* jni, jobject j_output_buffer_info);
127 jobject GetOutputBufferInfoBuffer(JNIEnv* jni, jobject j_output_buffer_info);
128 bool GetOutputBufferInfoIsKeyFrame(JNIEnv* jni, jobject j_output_buffer_info);
129 jlong GetOutputBufferInfoPresentationTimestampUs(
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000130 JNIEnv* jni, jobject j_output_buffer_info);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000131
132 // Deliver any outputs pending in the MediaCodec to our |callback_| and return
133 // true on success.
134 bool DeliverPendingOutputs(JNIEnv* jni);
135
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000136 // Search for H.264 start codes.
137 int32_t NextNaluPosition(uint8_t *buffer, size_t buffer_size);
138
139 // Type of video codec.
140 VideoCodecType codecType_;
141
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000142 // Valid all the time since RegisterEncodeCompleteCallback() Invoke()s to
143 // |codec_thread_| synchronously.
144 webrtc::EncodedImageCallback* callback_;
145
146 // State that is constant for the lifetime of this object once the ctor
147 // returns.
148 scoped_ptr<Thread> codec_thread_; // Thread on which to operate MediaCodec.
149 ScopedGlobalRef<jclass> j_media_codec_video_encoder_class_;
150 ScopedGlobalRef<jobject> j_media_codec_video_encoder_;
151 jmethodID j_init_encode_method_;
152 jmethodID j_dequeue_input_buffer_method_;
153 jmethodID j_encode_method_;
154 jmethodID j_release_method_;
155 jmethodID j_set_rates_method_;
156 jmethodID j_dequeue_output_buffer_method_;
157 jmethodID j_release_output_buffer_method_;
158 jfieldID j_color_format_field_;
159 jfieldID j_info_index_field_;
160 jfieldID j_info_buffer_field_;
161 jfieldID j_info_is_key_frame_field_;
162 jfieldID j_info_presentation_timestamp_us_field_;
163
164 // State that is valid only between InitEncode() and the next Release().
165 // Touched only on codec_thread_ so no explicit synchronization necessary.
166 int width_; // Frame width in pixels.
167 int height_; // Frame height in pixels.
168 bool inited_;
169 uint16_t picture_id_;
170 enum libyuv::FourCC encoder_fourcc_; // Encoder color space format.
171 int last_set_bitrate_kbps_; // Last-requested bitrate in kbps.
172 int last_set_fps_; // Last-requested frame rate.
173 int64_t current_timestamp_us_; // Current frame timestamps in us.
174 int frames_received_; // Number of frames received by encoder.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000175 int frames_encoded_; // Number of frames encoded by encoder.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000176 int frames_dropped_; // Number of frames dropped by encoder.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000177 int frames_in_queue_; // Number of frames in encoder queue.
178 int64_t start_time_ms_; // Start time for statistics.
179 int current_frames_; // Number of frames in the current statistics interval.
180 int current_bytes_; // Encoded bytes in the current statistics interval.
181 int current_encoding_time_ms_; // Overall encoding time in the current second
182 int64_t last_input_timestamp_ms_; // Timestamp of last received yuv frame.
183 int64_t last_output_timestamp_ms_; // Timestamp of last encoded frame.
184 std::vector<int32_t> timestamps_; // Video frames timestamp queue.
185 std::vector<int64_t> render_times_ms_; // Video frames render time queue.
186 std::vector<int64_t> frame_rtc_times_ms_; // Time when video frame is sent to
187 // encoder input.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000188 int32_t output_timestamp_; // Last output frame timestamp from timestamps_ Q.
189 int64_t output_render_time_ms_; // Last output frame render time from
190 // render_times_ms_ queue.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000191 // Frame size in bytes fed to MediaCodec.
192 int yuv_size_;
193 // True only when between a callback_->Encoded() call return a positive value
194 // and the next Encode() call being ignored.
195 bool drop_next_input_frame_;
196 // Global references; must be deleted in Release().
197 std::vector<jobject> input_buffers_;
jackychen61b4d512015-04-21 15:30:11 -0700198 scoped_ptr<webrtc::QualityScaler> quality_scaler_;
199 // Target frame size in bytes.
200 int target_framesize_;
201 // Dynamic resolution change, off by default.
202 bool scale_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000203};
204
205MediaCodecVideoEncoder::~MediaCodecVideoEncoder() {
206 // Call Release() to ensure no more callbacks to us after we are deleted.
207 Release();
208}
209
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000210MediaCodecVideoEncoder::MediaCodecVideoEncoder(
211 JNIEnv* jni, VideoCodecType codecType) :
212 codecType_(codecType),
213 callback_(NULL),
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000214 inited_(false),
215 picture_id_(0),
216 codec_thread_(new Thread()),
jackychen61b4d512015-04-21 15:30:11 -0700217 quality_scaler_(new webrtc::QualityScaler()),
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000218 j_media_codec_video_encoder_class_(
219 jni,
220 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")),
221 j_media_codec_video_encoder_(
222 jni,
223 jni->NewObject(*j_media_codec_video_encoder_class_,
224 GetMethodID(jni,
225 *j_media_codec_video_encoder_class_,
226 "<init>",
227 "()V"))) {
228 ScopedLocalRefFrame local_ref_frame(jni);
229 // It would be nice to avoid spinning up a new thread per MediaCodec, and
230 // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug
231 // 2732 means that deadlocks abound. This class synchronously trampolines
232 // to |codec_thread_|, so if anything else can be coming to _us_ from
233 // |codec_thread_|, or from any thread holding the |_sendCritSect| described
234 // in the bug, we have a problem. For now work around that with a dedicated
235 // thread.
236 codec_thread_->SetName("MediaCodecVideoEncoder", NULL);
237 CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder";
238
239 jclass j_output_buffer_info_class =
240 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder$OutputBufferInfo");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000241 j_init_encode_method_ = GetMethodID(
242 jni,
243 *j_media_codec_video_encoder_class_,
244 "initEncode",
245 "(Lorg/webrtc/MediaCodecVideoEncoder$VideoCodecType;IIII)"
246 "[Ljava/nio/ByteBuffer;");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000247 j_dequeue_input_buffer_method_ = GetMethodID(
248 jni, *j_media_codec_video_encoder_class_, "dequeueInputBuffer", "()I");
249 j_encode_method_ = GetMethodID(
250 jni, *j_media_codec_video_encoder_class_, "encode", "(ZIIJ)Z");
251 j_release_method_ =
252 GetMethodID(jni, *j_media_codec_video_encoder_class_, "release", "()V");
253 j_set_rates_method_ = GetMethodID(
254 jni, *j_media_codec_video_encoder_class_, "setRates", "(II)Z");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000255 j_dequeue_output_buffer_method_ = GetMethodID(
256 jni,
257 *j_media_codec_video_encoder_class_,
258 "dequeueOutputBuffer",
259 "()Lorg/webrtc/MediaCodecVideoEncoder$OutputBufferInfo;");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000260 j_release_output_buffer_method_ = GetMethodID(
261 jni, *j_media_codec_video_encoder_class_, "releaseOutputBuffer", "(I)Z");
262
263 j_color_format_field_ =
264 GetFieldID(jni, *j_media_codec_video_encoder_class_, "colorFormat", "I");
265 j_info_index_field_ =
266 GetFieldID(jni, j_output_buffer_info_class, "index", "I");
267 j_info_buffer_field_ = GetFieldID(
268 jni, j_output_buffer_info_class, "buffer", "Ljava/nio/ByteBuffer;");
269 j_info_is_key_frame_field_ =
270 GetFieldID(jni, j_output_buffer_info_class, "isKeyFrame", "Z");
271 j_info_presentation_timestamp_us_field_ = GetFieldID(
272 jni, j_output_buffer_info_class, "presentationTimestampUs", "J");
273 CHECK_EXCEPTION(jni) << "MediaCodecVideoEncoder ctor failed";
274 AllowBlockingCalls();
275}
276
277int32_t MediaCodecVideoEncoder::InitEncode(
278 const webrtc::VideoCodec* codec_settings,
279 int32_t /* number_of_cores */,
280 size_t /* max_payload_size */) {
jackychen61b4d512015-04-21 15:30:11 -0700281 const int kMinWidth = 320;
282 const int kMinHeight = 180;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000283 if (codec_settings == NULL) {
284 ALOGE("NULL VideoCodec instance");
285 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
286 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000287 // Factory should guard against other codecs being used with us.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000288 CHECK(codec_settings->codecType == codecType_) << "Unsupported codec " <<
289 codec_settings->codecType << " for " << codecType_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000290
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000291 ALOGD("InitEncode request");
jackychen61b4d512015-04-21 15:30:11 -0700292 scale_ = false;
293 quality_scaler_->Init(0);
294 quality_scaler_->SetMinResolution(kMinWidth, kMinHeight);
295 quality_scaler_->ReportFramerate(codec_settings->maxFramerate);
296 if (codec_settings->maxFramerate > 0) {
297 target_framesize_ = codec_settings->startBitrate * 1000 /
298 codec_settings->maxFramerate / 8;
299 } else {
300 target_framesize_ = 0;
301 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000302 return codec_thread_->Invoke<int32_t>(
303 Bind(&MediaCodecVideoEncoder::InitEncodeOnCodecThread,
304 this,
305 codec_settings->width,
306 codec_settings->height,
307 codec_settings->startBitrate,
308 codec_settings->maxFramerate));
309}
310
311int32_t MediaCodecVideoEncoder::Encode(
312 const webrtc::I420VideoFrame& frame,
313 const webrtc::CodecSpecificInfo* /* codec_specific_info */,
314 const std::vector<webrtc::VideoFrameType>* frame_types) {
315 return codec_thread_->Invoke<int32_t>(Bind(
316 &MediaCodecVideoEncoder::EncodeOnCodecThread, this, frame, frame_types));
317}
318
319int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallback(
320 webrtc::EncodedImageCallback* callback) {
321 return codec_thread_->Invoke<int32_t>(
322 Bind(&MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread,
323 this,
324 callback));
325}
326
327int32_t MediaCodecVideoEncoder::Release() {
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000328 ALOGD("EncoderRelease request");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000329 return codec_thread_->Invoke<int32_t>(
330 Bind(&MediaCodecVideoEncoder::ReleaseOnCodecThread, this));
331}
332
333int32_t MediaCodecVideoEncoder::SetChannelParameters(uint32_t /* packet_loss */,
334 int64_t /* rtt */) {
335 return WEBRTC_VIDEO_CODEC_OK;
336}
337
338int32_t MediaCodecVideoEncoder::SetRates(uint32_t new_bit_rate,
339 uint32_t frame_rate) {
jackychen61b4d512015-04-21 15:30:11 -0700340 quality_scaler_->ReportFramerate(frame_rate);
341 if (frame_rate > 0) {
342 target_framesize_ = new_bit_rate * 1000 / frame_rate / 8;
343 } else {
344 target_framesize_ = 0;
345 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000346 return codec_thread_->Invoke<int32_t>(
347 Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread,
348 this,
349 new_bit_rate,
350 frame_rate));
351}
352
353void MediaCodecVideoEncoder::OnMessage(rtc::Message* msg) {
354 JNIEnv* jni = AttachCurrentThreadIfNeeded();
355 ScopedLocalRefFrame local_ref_frame(jni);
356
357 // We only ever send one message to |this| directly (not through a Bind()'d
358 // functor), so expect no ID/data.
359 CHECK(!msg->message_id) << "Unexpected message!";
360 CHECK(!msg->pdata) << "Unexpected message!";
361 CheckOnCodecThread();
362 if (!inited_) {
363 return;
364 }
365
366 // It would be nice to recover from a failure here if one happened, but it's
367 // unclear how to signal such a failure to the app, so instead we stay silent
368 // about it and let the next app-called API method reveal the borkedness.
369 DeliverPendingOutputs(jni);
370 codec_thread_->PostDelayed(kMediaCodecPollMs, this);
371}
372
373void MediaCodecVideoEncoder::CheckOnCodecThread() {
374 CHECK(codec_thread_ == ThreadManager::Instance()->CurrentThread())
375 << "Running on wrong thread!";
376}
377
378void MediaCodecVideoEncoder::ResetCodec() {
379 ALOGE("ResetCodec");
380 if (Release() != WEBRTC_VIDEO_CODEC_OK ||
381 codec_thread_->Invoke<int32_t>(Bind(
382 &MediaCodecVideoEncoder::InitEncodeOnCodecThread, this,
383 width_, height_, 0, 0)) != WEBRTC_VIDEO_CODEC_OK) {
384 // TODO(fischman): wouldn't it be nice if there was a way to gracefully
385 // degrade to a SW encoder at this point? There isn't one AFAICT :(
386 // https://code.google.com/p/webrtc/issues/detail?id=2920
387 }
388}
389
390int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread(
391 int width, int height, int kbps, int fps) {
392 CheckOnCodecThread();
393 JNIEnv* jni = AttachCurrentThreadIfNeeded();
394 ScopedLocalRefFrame local_ref_frame(jni);
395
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000396 ALOGD("InitEncodeOnCodecThread Type: %d. %d x %d. Bitrate: %d kbps. Fps: %d",
397 (int)codecType_, width, height, kbps, fps);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000398 if (kbps == 0) {
399 kbps = last_set_bitrate_kbps_;
400 }
401 if (fps == 0) {
402 fps = last_set_fps_;
403 }
404
405 width_ = width;
406 height_ = height;
407 last_set_bitrate_kbps_ = kbps;
408 last_set_fps_ = fps;
409 yuv_size_ = width_ * height_ * 3 / 2;
410 frames_received_ = 0;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000411 frames_encoded_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000412 frames_dropped_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000413 frames_in_queue_ = 0;
414 current_timestamp_us_ = 0;
415 start_time_ms_ = GetCurrentTimeMs();
416 current_frames_ = 0;
417 current_bytes_ = 0;
418 current_encoding_time_ms_ = 0;
419 last_input_timestamp_ms_ = -1;
420 last_output_timestamp_ms_ = -1;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000421 output_timestamp_ = 0;
422 output_render_time_ms_ = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000423 timestamps_.clear();
424 render_times_ms_.clear();
425 frame_rtc_times_ms_.clear();
426 drop_next_input_frame_ = false;
427 picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
428 // We enforce no extra stride/padding in the format creation step.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000429 jobject j_video_codec_enum = JavaEnumFromIndex(
430 jni, "MediaCodecVideoEncoder$VideoCodecType", codecType_);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000431 jobjectArray input_buffers = reinterpret_cast<jobjectArray>(
432 jni->CallObjectMethod(*j_media_codec_video_encoder_,
433 j_init_encode_method_,
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000434 j_video_codec_enum,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000435 width_,
436 height_,
437 kbps,
438 fps));
439 CHECK_EXCEPTION(jni);
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000440 if (IsNull(jni, input_buffers)) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000441 return WEBRTC_VIDEO_CODEC_ERROR;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000442 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000443
444 inited_ = true;
445 switch (GetIntField(jni, *j_media_codec_video_encoder_,
446 j_color_format_field_)) {
447 case COLOR_FormatYUV420Planar:
448 encoder_fourcc_ = libyuv::FOURCC_YU12;
449 break;
450 case COLOR_FormatYUV420SemiPlanar:
451 case COLOR_QCOM_FormatYUV420SemiPlanar:
452 case COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m:
453 encoder_fourcc_ = libyuv::FOURCC_NV12;
454 break;
455 default:
456 LOG(LS_ERROR) << "Wrong color format.";
457 return WEBRTC_VIDEO_CODEC_ERROR;
458 }
459 size_t num_input_buffers = jni->GetArrayLength(input_buffers);
460 CHECK(input_buffers_.empty())
461 << "Unexpected double InitEncode without Release";
462 input_buffers_.resize(num_input_buffers);
463 for (size_t i = 0; i < num_input_buffers; ++i) {
464 input_buffers_[i] =
465 jni->NewGlobalRef(jni->GetObjectArrayElement(input_buffers, i));
466 int64 yuv_buffer_capacity =
467 jni->GetDirectBufferCapacity(input_buffers_[i]);
468 CHECK_EXCEPTION(jni);
469 CHECK(yuv_buffer_capacity >= yuv_size_) << "Insufficient capacity";
470 }
471 CHECK_EXCEPTION(jni);
472
473 codec_thread_->PostDelayed(kMediaCodecPollMs, this);
474 return WEBRTC_VIDEO_CODEC_OK;
475}
476
477int32_t MediaCodecVideoEncoder::EncodeOnCodecThread(
478 const webrtc::I420VideoFrame& frame,
479 const std::vector<webrtc::VideoFrameType>* frame_types) {
480 CheckOnCodecThread();
481 JNIEnv* jni = AttachCurrentThreadIfNeeded();
482 ScopedLocalRefFrame local_ref_frame(jni);
483
484 if (!inited_) {
485 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
486 }
487 frames_received_++;
488 if (!DeliverPendingOutputs(jni)) {
489 ResetCodec();
490 // Continue as if everything's fine.
491 }
492
493 if (drop_next_input_frame_) {
494 ALOGV("Encoder drop frame - failed callback.");
495 drop_next_input_frame_ = false;
496 return WEBRTC_VIDEO_CODEC_OK;
497 }
498
499 CHECK(frame_types->size() == 1) << "Unexpected stream count";
jackychen61b4d512015-04-21 15:30:11 -0700500 const I420VideoFrame& input_frame =
501 (scale_ && codecType_ == kVideoCodecVP8) ?
502 quality_scaler_->GetScaledFrame(frame) : frame;
503
504 if (input_frame.width() != width_ || input_frame.height() != height_) {
505 ALOGD("Frame resolution change from %d x %d to %d x %d",
506 width_, height_, input_frame.width(), input_frame.height());
507 width_ = input_frame.width();
508 height_ = input_frame.height();
509 ResetCodec();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000510 return WEBRTC_VIDEO_CODEC_OK;
511 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000512
513 bool key_frame = frame_types->front() != webrtc::kDeltaFrame;
514
515 // Check if we accumulated too many frames in encoder input buffers
516 // or the encoder latency exceeds 70 ms and drop frame if so.
517 if (frames_in_queue_ > 0 && last_input_timestamp_ms_ >= 0) {
518 int encoder_latency_ms = last_input_timestamp_ms_ -
519 last_output_timestamp_ms_;
520 if (frames_in_queue_ > 2 || encoder_latency_ms > 70) {
521 ALOGD("Drop frame - encoder is behind by %d ms. Q size: %d",
522 encoder_latency_ms, frames_in_queue_);
523 frames_dropped_++;
jackychen61b4d512015-04-21 15:30:11 -0700524 // Report dropped frame to quality_scaler_.
525 OnDroppedFrame();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000526 return WEBRTC_VIDEO_CODEC_OK;
527 }
528 }
529
530 int j_input_buffer_index = jni->CallIntMethod(*j_media_codec_video_encoder_,
531 j_dequeue_input_buffer_method_);
532 CHECK_EXCEPTION(jni);
533 if (j_input_buffer_index == -1) {
534 // Video codec falls behind - no input buffer available.
535 ALOGV("Encoder drop frame - no input buffers available");
536 frames_dropped_++;
jackychen61b4d512015-04-21 15:30:11 -0700537 // Report dropped frame to quality_scaler_.
538 OnDroppedFrame();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000539 return WEBRTC_VIDEO_CODEC_OK; // TODO(fischman): see webrtc bug 2887.
540 }
541 if (j_input_buffer_index == -2) {
542 ResetCodec();
543 return WEBRTC_VIDEO_CODEC_ERROR;
544 }
545
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000546 ALOGV("Encoder frame in # %d. TS: %lld. Q: %d",
547 frames_received_ - 1, current_timestamp_us_ / 1000, frames_in_queue_);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000548
549 jobject j_input_buffer = input_buffers_[j_input_buffer_index];
550 uint8* yuv_buffer =
551 reinterpret_cast<uint8*>(jni->GetDirectBufferAddress(j_input_buffer));
552 CHECK_EXCEPTION(jni);
553 CHECK(yuv_buffer) << "Indirect buffer??";
554 CHECK(!libyuv::ConvertFromI420(
jackychen61b4d512015-04-21 15:30:11 -0700555 input_frame.buffer(webrtc::kYPlane),
556 input_frame.stride(webrtc::kYPlane),
557 input_frame.buffer(webrtc::kUPlane),
558 input_frame.stride(webrtc::kUPlane),
559 input_frame.buffer(webrtc::kVPlane),
560 input_frame.stride(webrtc::kVPlane),
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000561 yuv_buffer, width_,
562 width_, height_,
563 encoder_fourcc_))
564 << "ConvertFromI420 failed";
565 last_input_timestamp_ms_ = current_timestamp_us_ / 1000;
566 frames_in_queue_++;
567
568 // Save input image timestamps for later output
jackychen61b4d512015-04-21 15:30:11 -0700569 timestamps_.push_back(input_frame.timestamp());
570 render_times_ms_.push_back(input_frame.render_time_ms());
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000571 frame_rtc_times_ms_.push_back(GetCurrentTimeMs());
572
573 bool encode_status = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
574 j_encode_method_,
575 key_frame,
576 j_input_buffer_index,
577 yuv_size_,
578 current_timestamp_us_);
579 CHECK_EXCEPTION(jni);
580 current_timestamp_us_ += 1000000 / last_set_fps_;
581
582 if (!encode_status || !DeliverPendingOutputs(jni)) {
583 ResetCodec();
584 return WEBRTC_VIDEO_CODEC_ERROR;
585 }
586
587 return WEBRTC_VIDEO_CODEC_OK;
588}
589
590int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread(
591 webrtc::EncodedImageCallback* callback) {
592 CheckOnCodecThread();
593 JNIEnv* jni = AttachCurrentThreadIfNeeded();
594 ScopedLocalRefFrame local_ref_frame(jni);
595 callback_ = callback;
596 return WEBRTC_VIDEO_CODEC_OK;
597}
598
599int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() {
600 if (!inited_) {
601 return WEBRTC_VIDEO_CODEC_OK;
602 }
603 CheckOnCodecThread();
604 JNIEnv* jni = AttachCurrentThreadIfNeeded();
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000605 ALOGD("EncoderReleaseOnCodecThread: Frames received: %d. Encoded: %d. "
606 "Dropped: %d.", frames_received_, frames_encoded_, frames_dropped_);
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000607 ScopedLocalRefFrame local_ref_frame(jni);
608 for (size_t i = 0; i < input_buffers_.size(); ++i)
609 jni->DeleteGlobalRef(input_buffers_[i]);
610 input_buffers_.clear();
611 jni->CallVoidMethod(*j_media_codec_video_encoder_, j_release_method_);
612 CHECK_EXCEPTION(jni);
613 rtc::MessageQueueManager::Clear(this);
614 inited_ = false;
615 return WEBRTC_VIDEO_CODEC_OK;
616}
617
618int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate,
619 uint32_t frame_rate) {
620 CheckOnCodecThread();
621 if (last_set_bitrate_kbps_ == new_bit_rate &&
622 last_set_fps_ == frame_rate) {
623 return WEBRTC_VIDEO_CODEC_OK;
624 }
625 JNIEnv* jni = AttachCurrentThreadIfNeeded();
626 ScopedLocalRefFrame local_ref_frame(jni);
627 if (new_bit_rate > 0) {
628 last_set_bitrate_kbps_ = new_bit_rate;
629 }
630 if (frame_rate > 0) {
631 last_set_fps_ = frame_rate;
632 }
633 bool ret = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
634 j_set_rates_method_,
635 last_set_bitrate_kbps_,
636 last_set_fps_);
637 CHECK_EXCEPTION(jni);
638 if (!ret) {
639 ResetCodec();
640 return WEBRTC_VIDEO_CODEC_ERROR;
641 }
642 return WEBRTC_VIDEO_CODEC_OK;
643}
644
645int MediaCodecVideoEncoder::GetOutputBufferInfoIndex(
646 JNIEnv* jni,
647 jobject j_output_buffer_info) {
648 return GetIntField(jni, j_output_buffer_info, j_info_index_field_);
649}
650
651jobject MediaCodecVideoEncoder::GetOutputBufferInfoBuffer(
652 JNIEnv* jni,
653 jobject j_output_buffer_info) {
654 return GetObjectField(jni, j_output_buffer_info, j_info_buffer_field_);
655}
656
657bool MediaCodecVideoEncoder::GetOutputBufferInfoIsKeyFrame(
658 JNIEnv* jni,
659 jobject j_output_buffer_info) {
660 return GetBooleanField(jni, j_output_buffer_info, j_info_is_key_frame_field_);
661}
662
663jlong MediaCodecVideoEncoder::GetOutputBufferInfoPresentationTimestampUs(
664 JNIEnv* jni,
665 jobject j_output_buffer_info) {
666 return GetLongField(
667 jni, j_output_buffer_info, j_info_presentation_timestamp_us_field_);
668}
669
670bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) {
671 while (true) {
672 jobject j_output_buffer_info = jni->CallObjectMethod(
673 *j_media_codec_video_encoder_, j_dequeue_output_buffer_method_);
674 CHECK_EXCEPTION(jni);
675 if (IsNull(jni, j_output_buffer_info)) {
676 break;
677 }
678
679 int output_buffer_index =
680 GetOutputBufferInfoIndex(jni, j_output_buffer_info);
681 if (output_buffer_index == -1) {
682 ResetCodec();
683 return false;
684 }
685
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000686 // Get key and config frame flags.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000687 jobject j_output_buffer =
688 GetOutputBufferInfoBuffer(jni, j_output_buffer_info);
689 bool key_frame = GetOutputBufferInfoIsKeyFrame(jni, j_output_buffer_info);
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000690
691 // Get frame timestamps from a queue - for non config frames only.
692 int64_t frame_encoding_time_ms = 0;
693 last_output_timestamp_ms_ =
694 GetOutputBufferInfoPresentationTimestampUs(jni, j_output_buffer_info) /
695 1000;
696 if (frames_in_queue_ > 0) {
697 output_timestamp_ = timestamps_.front();
698 timestamps_.erase(timestamps_.begin());
699 output_render_time_ms_ = render_times_ms_.front();
700 render_times_ms_.erase(render_times_ms_.begin());
701 frame_encoding_time_ms = GetCurrentTimeMs() - frame_rtc_times_ms_.front();
702 frame_rtc_times_ms_.erase(frame_rtc_times_ms_.begin());
703 frames_in_queue_--;
704 }
705
706 // Extract payload.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000707 size_t payload_size = jni->GetDirectBufferCapacity(j_output_buffer);
708 uint8* payload = reinterpret_cast<uint8_t*>(
709 jni->GetDirectBufferAddress(j_output_buffer));
710 CHECK_EXCEPTION(jni);
711
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000712 ALOGV("Encoder frame out # %d. Key: %d. Size: %d. TS: %lld."
713 " Latency: %lld. EncTime: %lld",
714 frames_encoded_, key_frame, payload_size,
715 last_output_timestamp_ms_,
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000716 last_input_timestamp_ms_ - last_output_timestamp_ms_,
717 frame_encoding_time_ms);
718
jackychen61b4d512015-04-21 15:30:11 -0700719 if (payload_size) {
720 double framesize_deviation = 0.0;
721 if (target_framesize_ > 0) {
722 framesize_deviation =
723 (double)abs((int)payload_size - target_framesize_) /
724 target_framesize_;
725 }
726 quality_scaler_->ReportNormalizedFrameSizeFluctuation(
727 framesize_deviation);
728 }
729
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000730 // Calculate and print encoding statistics - every 3 seconds.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000731 frames_encoded_++;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000732 current_frames_++;
733 current_bytes_ += payload_size;
734 current_encoding_time_ms_ += frame_encoding_time_ms;
735 int statistic_time_ms = GetCurrentTimeMs() - start_time_ms_;
736 if (statistic_time_ms >= kMediaCodecStatisticsIntervalMs &&
737 current_frames_ > 0) {
738 ALOGD("Encoder bitrate: %d, target: %d kbps, fps: %d,"
739 " encTime: %d for last %d ms",
740 current_bytes_ * 8 / statistic_time_ms,
741 last_set_bitrate_kbps_,
742 (current_frames_ * 1000 + statistic_time_ms / 2) / statistic_time_ms,
743 current_encoding_time_ms_ / current_frames_, statistic_time_ms);
744 start_time_ms_ = GetCurrentTimeMs();
745 current_frames_ = 0;
746 current_bytes_ = 0;
747 current_encoding_time_ms_ = 0;
748 }
749
750 // Callback - return encoded frame.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000751 int32_t callback_status = 0;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000752 if (callback_) {
753 scoped_ptr<webrtc::EncodedImage> image(
754 new webrtc::EncodedImage(payload, payload_size, payload_size));
755 image->_encodedWidth = width_;
756 image->_encodedHeight = height_;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000757 image->_timeStamp = output_timestamp_;
758 image->capture_time_ms_ = output_render_time_ms_;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000759 image->_frameType = (key_frame ? webrtc::kKeyFrame : webrtc::kDeltaFrame);
760 image->_completeFrame = true;
761
762 webrtc::CodecSpecificInfo info;
763 memset(&info, 0, sizeof(info));
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000764 info.codecType = codecType_;
765 if (codecType_ == kVideoCodecVP8) {
766 info.codecSpecific.VP8.pictureId = picture_id_;
767 info.codecSpecific.VP8.nonReference = false;
768 info.codecSpecific.VP8.simulcastIdx = 0;
769 info.codecSpecific.VP8.temporalIdx = webrtc::kNoTemporalIdx;
770 info.codecSpecific.VP8.layerSync = false;
771 info.codecSpecific.VP8.tl0PicIdx = webrtc::kNoTl0PicIdx;
772 info.codecSpecific.VP8.keyIdx = webrtc::kNoKeyIdx;
773 picture_id_ = (picture_id_ + 1) & 0x7FFF;
774 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000775
776 // Generate a header describing a single fragment.
777 webrtc::RTPFragmentationHeader header;
778 memset(&header, 0, sizeof(header));
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000779 if (codecType_ == kVideoCodecVP8) {
780 header.VerifyAndAllocateFragmentationHeader(1);
781 header.fragmentationOffset[0] = 0;
782 header.fragmentationLength[0] = image->_length;
783 header.fragmentationPlType[0] = 0;
784 header.fragmentationTimeDiff[0] = 0;
785 } else if (codecType_ == kVideoCodecH264) {
786 // For H.264 search for start codes.
787 int32_t scPositions[MAX_NALUS_PERFRAME + 1] = {};
788 int32_t scPositionsLength = 0;
789 int32_t scPosition = 0;
790 while (scPositionsLength < MAX_NALUS_PERFRAME) {
791 int32_t naluPosition = NextNaluPosition(
792 payload + scPosition, payload_size - scPosition);
793 if (naluPosition < 0) {
794 break;
795 }
796 scPosition += naluPosition;
797 scPositions[scPositionsLength++] = scPosition;
798 scPosition += H264_SC_LENGTH;
799 }
800 if (scPositionsLength == 0) {
801 ALOGE("Start code is not found!");
802 ALOGE("Data 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
803 image->_buffer[0], image->_buffer[1], image->_buffer[2],
804 image->_buffer[3], image->_buffer[4], image->_buffer[5]);
805 ResetCodec();
806 return false;
807 }
808 scPositions[scPositionsLength] = payload_size;
809 header.VerifyAndAllocateFragmentationHeader(scPositionsLength);
810 for (size_t i = 0; i < scPositionsLength; i++) {
811 header.fragmentationOffset[i] = scPositions[i] + H264_SC_LENGTH;
812 header.fragmentationLength[i] =
813 scPositions[i + 1] - header.fragmentationOffset[i];
814 header.fragmentationPlType[i] = 0;
815 header.fragmentationTimeDiff[i] = 0;
816 }
817 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000818
819 callback_status = callback_->Encoded(*image, &info, &header);
820 }
821
822 // Return output buffer back to the encoder.
823 bool success = jni->CallBooleanMethod(*j_media_codec_video_encoder_,
824 j_release_output_buffer_method_,
825 output_buffer_index);
826 CHECK_EXCEPTION(jni);
827 if (!success) {
828 ResetCodec();
829 return false;
830 }
831
832 if (callback_status > 0) {
833 drop_next_input_frame_ = true;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000834 // Theoretically could handle callback_status<0 here, but unclear what
835 // that would mean for us.
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000836 }
837 }
838
839 return true;
840}
841
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000842int32_t MediaCodecVideoEncoder::NextNaluPosition(
843 uint8_t *buffer, size_t buffer_size) {
844 if (buffer_size < H264_SC_LENGTH) {
845 return -1;
846 }
847 uint8_t *head = buffer;
848 // Set end buffer pointer to 4 bytes before actual buffer end so we can
849 // access head[1], head[2] and head[3] in a loop without buffer overrun.
850 uint8_t *end = buffer + buffer_size - H264_SC_LENGTH;
851
852 while (head < end) {
853 if (head[0]) {
854 head++;
855 continue;
856 }
857 if (head[1]) { // got 00xx
858 head += 2;
859 continue;
860 }
861 if (head[2]) { // got 0000xx
862 head += 3;
863 continue;
864 }
865 if (head[3] != 0x01) { // got 000000xx
glaznev@webrtc.orgdc08a232015-03-06 23:32:20 +0000866 head++; // xx != 1, continue searching.
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000867 continue;
868 }
869 return (int32_t)(head - buffer);
870 }
871 return -1;
872}
873
jackychen61b4d512015-04-21 15:30:11 -0700874void MediaCodecVideoEncoder::OnDroppedFrame() {
875 quality_scaler_->ReportDroppedFrame();
876}
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000877
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000878MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory() {
879 JNIEnv* jni = AttachCurrentThreadIfNeeded();
880 ScopedLocalRefFrame local_ref_frame(jni);
881 jclass j_encoder_class = FindClass(jni, "org/webrtc/MediaCodecVideoEncoder");
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000882 supported_codecs_.clear();
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000883
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000884 bool is_vp8_hw_supported = jni->CallStaticBooleanMethod(
885 j_encoder_class,
886 GetStaticMethodID(jni, j_encoder_class, "isVp8HwSupported", "()Z"));
887 CHECK_EXCEPTION(jni);
888 if (is_vp8_hw_supported) {
889 ALOGD("VP8 HW Encoder supported.");
890 supported_codecs_.push_back(VideoCodec(kVideoCodecVP8, "VP8",
891 MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
892 }
893
894 bool is_h264_hw_supported = jni->CallStaticBooleanMethod(
895 j_encoder_class,
896 GetStaticMethodID(jni, j_encoder_class, "isH264HwSupported", "()Z"));
897 CHECK_EXCEPTION(jni);
898 if (is_h264_hw_supported) {
899 ALOGD("H.264 HW Encoder supported.");
900 supported_codecs_.push_back(VideoCodec(kVideoCodecH264, "H264",
901 MAX_VIDEO_WIDTH, MAX_VIDEO_HEIGHT, MAX_VIDEO_FPS));
902 }
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000903}
904
905MediaCodecVideoEncoderFactory::~MediaCodecVideoEncoderFactory() {}
906
907webrtc::VideoEncoder* MediaCodecVideoEncoderFactory::CreateVideoEncoder(
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000908 VideoCodecType type) {
909 if (supported_codecs_.empty()) {
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000910 return NULL;
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000911 }
912 for (std::vector<VideoCodec>::const_iterator it = supported_codecs_.begin();
913 it != supported_codecs_.end(); ++it) {
914 if (it->type == type) {
915 ALOGD("Create HW video encoder for type %d (%s).",
916 (int)type, it->name.c_str());
917 return new MediaCodecVideoEncoder(AttachCurrentThreadIfNeeded(), type);
918 }
919 }
920 return NULL;
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000921}
922
923const std::vector<MediaCodecVideoEncoderFactory::VideoCodec>&
924MediaCodecVideoEncoderFactory::codecs() const {
925 return supported_codecs_;
926}
927
928void MediaCodecVideoEncoderFactory::DestroyVideoEncoder(
929 webrtc::VideoEncoder* encoder) {
glaznev@webrtc.orgb28474c2015-02-23 17:44:27 +0000930 ALOGD("Destroy video encoder.");
glaznev@webrtc.org18c92472015-02-18 18:42:55 +0000931 delete encoder;
932}
933
934} // namespace webrtc_jni
935