OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
79 #define ALOGV(...) | 79 #define ALOGV(...) |
80 #endif | 80 #endif |
81 #define ALOGD LOG_TAG(rtc::LS_INFO, TAG_ENCODER) | 81 #define ALOGD LOG_TAG(rtc::LS_INFO, TAG_ENCODER) |
82 #define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_ENCODER) | 82 #define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_ENCODER) |
83 #define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_ENCODER) | 83 #define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_ENCODER) |
84 | 84 |
85 namespace { | 85 namespace { |
86 // Maximum time limit between incoming frames before requesting a key frame. | 86 // Maximum time limit between incoming frames before requesting a key frame. |
87 const size_t kFrameDiffThresholdMs = 1100; | 87 const size_t kFrameDiffThresholdMs = 1100; |
88 const int kMinKeyFrameInterval = 2; | 88 const int kMinKeyFrameInterval = 2; |
89 // Maximum number of encoder resets before falling back to a software | |
90 // implementation. | |
91 const int kMaxEncoderResetsBeforeFallback = 3; | |
magjed_webrtc
2016/08/23 12:09:50
Now it will fall back to software the fourth time,
sakal
2016/08/23 12:26:51
Resetting the encoder is fairly quick so 3 resets
| |
89 } // namespace | 92 } // namespace |
90 | 93 |
91 // MediaCodecVideoEncoder is a webrtc::VideoEncoder implementation that uses | 94 // MediaCodecVideoEncoder is a webrtc::VideoEncoder implementation that uses |
92 // Android's MediaCodec SDK API behind the scenes to implement (hopefully) | 95 // Android's MediaCodec SDK API behind the scenes to implement (hopefully) |
93 // HW-backed video encode. This C++ class is implemented as a very thin shim, | 96 // HW-backed video encode. This C++ class is implemented as a very thin shim, |
94 // delegating all of the interesting work to org.webrtc.MediaCodecVideoEncoder. | 97 // delegating all of the interesting work to org.webrtc.MediaCodecVideoEncoder. |
95 // MediaCodecVideoEncoder is created, operated, and destroyed on a single | 98 // MediaCodecVideoEncoder is created, operated, and destroyed on a single |
96 // thread, currently the libjingle Worker thread. | 99 // thread, currently the libjingle Worker thread. |
97 class MediaCodecVideoEncoder : public webrtc::VideoEncoder, | 100 class MediaCodecVideoEncoder : public webrtc::VideoEncoder, |
98 public rtc::MessageHandler { | 101 public rtc::MessageHandler { |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
275 // EGL context - owned by factory, should not be allocated/destroyed | 278 // EGL context - owned by factory, should not be allocated/destroyed |
276 // by MediaCodecVideoEncoder. | 279 // by MediaCodecVideoEncoder. |
277 jobject egl_context_; | 280 jobject egl_context_; |
278 | 281 |
279 // Temporary fix for VP8. | 282 // Temporary fix for VP8. |
280 // Sends a key frame if frames are largely spaced apart (possibly | 283 // Sends a key frame if frames are largely spaced apart (possibly |
281 // corresponding to a large image change). | 284 // corresponding to a large image change). |
282 int64_t last_frame_received_ms_; | 285 int64_t last_frame_received_ms_; |
283 int frames_received_since_last_key_; | 286 int frames_received_since_last_key_; |
284 webrtc::VideoCodecMode codec_mode_; | 287 webrtc::VideoCodecMode codec_mode_; |
288 | |
289 bool software_fallback_needed_; | |
290 int encoder_reset_counter_; | |
285 }; | 291 }; |
286 | 292 |
287 MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { | 293 MediaCodecVideoEncoder::~MediaCodecVideoEncoder() { |
288 // Call Release() to ensure no more callbacks to us after we are deleted. | 294 // Call Release() to ensure no more callbacks to us after we are deleted. |
289 Release(); | 295 Release(); |
290 } | 296 } |
291 | 297 |
292 MediaCodecVideoEncoder::MediaCodecVideoEncoder( | 298 MediaCodecVideoEncoder::MediaCodecVideoEncoder( |
293 JNIEnv* jni, VideoCodecType codecType, jobject egl_context) : | 299 JNIEnv* jni, VideoCodecType codecType, jobject egl_context) : |
294 codecType_(codecType), | 300 codecType_(codecType), |
295 callback_(NULL), | 301 callback_(NULL), |
296 codec_thread_(new Thread()), | 302 codec_thread_(new Thread()), |
297 j_media_codec_video_encoder_class_( | 303 j_media_codec_video_encoder_class_( |
298 jni, | 304 jni, |
299 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")), | 305 FindClass(jni, "org/webrtc/MediaCodecVideoEncoder")), |
300 j_media_codec_video_encoder_( | 306 j_media_codec_video_encoder_( |
301 jni, | 307 jni, |
302 jni->NewObject(*j_media_codec_video_encoder_class_, | 308 jni->NewObject(*j_media_codec_video_encoder_class_, |
303 GetMethodID(jni, | 309 GetMethodID(jni, |
304 *j_media_codec_video_encoder_class_, | 310 *j_media_codec_video_encoder_class_, |
305 "<init>", | 311 "<init>", |
306 "()V"))), | 312 "()V"))), |
307 inited_(false), | 313 inited_(false), |
308 use_surface_(false), | 314 use_surface_(false), |
309 picture_id_(0), | 315 picture_id_(0), |
310 egl_context_(egl_context) { | 316 egl_context_(egl_context), |
317 software_fallback_needed_(false), | |
318 encoder_reset_counter_(0) { | |
311 ScopedLocalRefFrame local_ref_frame(jni); | 319 ScopedLocalRefFrame local_ref_frame(jni); |
312 // It would be nice to avoid spinning up a new thread per MediaCodec, and | 320 // It would be nice to avoid spinning up a new thread per MediaCodec, and |
313 // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug | 321 // instead re-use e.g. the PeerConnectionFactory's |worker_thread_|, but bug |
314 // 2732 means that deadlocks abound. This class synchronously trampolines | 322 // 2732 means that deadlocks abound. This class synchronously trampolines |
315 // to |codec_thread_|, so if anything else can be coming to _us_ from | 323 // to |codec_thread_|, so if anything else can be coming to _us_ from |
316 // |codec_thread_|, or from any thread holding the |_sendCritSect| described | 324 // |codec_thread_|, or from any thread holding the |_sendCritSect| described |
317 // in the bug, we have a problem. For now work around that with a dedicated | 325 // in the bug, we have a problem. For now work around that with a dedicated |
318 // thread. | 326 // thread. |
319 codec_thread_->SetName("MediaCodecVideoEncoder", NULL); | 327 codec_thread_->SetName("MediaCodecVideoEncoder", NULL); |
320 RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder"; | 328 RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoEncoder"; |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
487 } | 495 } |
488 | 496 |
489 // Call log statistics here so it's called even if no frames are being | 497 // Call log statistics here so it's called even if no frames are being |
490 // delivered. | 498 // delivered. |
491 LogStatistics(false); | 499 LogStatistics(false); |
492 } | 500 } |
493 | 501 |
494 bool MediaCodecVideoEncoder::ResetCodecOnCodecThread() { | 502 bool MediaCodecVideoEncoder::ResetCodecOnCodecThread() { |
495 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 503 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
496 ALOGE << "ResetOnCodecThread"; | 504 ALOGE << "ResetOnCodecThread"; |
497 if (ReleaseOnCodecThread() != WEBRTC_VIDEO_CODEC_OK || | 505 encoder_reset_counter_++; |
498 InitEncodeOnCodecThread(width_, height_, 0, 0, false) != | 506 if (ReleaseOnCodecThread() != WEBRTC_VIDEO_CODEC_OK) { |
499 WEBRTC_VIDEO_CODEC_OK) { | 507 LOG(LS_ERROR) << "Releasing codec failed. Fallback to SW encoder."; |
500 // TODO(fischman): wouldn't it be nice if there was a way to gracefully | 508 software_fallback_needed_ = true; |
magjed_webrtc
2016/08/23 12:09:50
Is it possible to remove |software_fallback_needed
sakal
2016/08/23 12:26:51
There would be some trouble. The codec might not b
| |
501 // degrade to a SW encoder at this point? There isn't one AFAICT :( | 509 return false; |
502 // https://code.google.com/p/webrtc/issues/detail?id=2920 | 510 } |
511 if (encoder_reset_counter_ > kMaxEncoderResetsBeforeFallback) { | |
512 LOG(LS_ERROR) << "Codec has failed too many times. Fallback to SW encoder."; | |
513 software_fallback_needed_ = true; | |
514 return false; | |
515 } | |
516 if (InitEncodeOnCodecThread(width_, height_, 0, 0, false) != | |
517 WEBRTC_VIDEO_CODEC_OK) { | |
518 LOG(LS_ERROR) << "Init encode failed. Fallback to SW encoder."; | |
519 software_fallback_needed_ = true; | |
503 return false; | 520 return false; |
504 } | 521 } |
505 return true; | 522 return true; |
506 } | 523 } |
507 | 524 |
508 int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread( | 525 int32_t MediaCodecVideoEncoder::InitEncodeOnCodecThread( |
509 int width, int height, int kbps, int fps, bool use_surface) { | 526 int width, int height, int kbps, int fps, bool use_surface) { |
510 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 527 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
528 if (software_fallback_needed_) { | |
529 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
530 } | |
511 RTC_CHECK(!use_surface || egl_context_ != nullptr) << "EGL context not set."; | 531 RTC_CHECK(!use_surface || egl_context_ != nullptr) << "EGL context not set."; |
512 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | 532 JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
513 ScopedLocalRefFrame local_ref_frame(jni); | 533 ScopedLocalRefFrame local_ref_frame(jni); |
514 | 534 |
515 ALOGD << "InitEncodeOnCodecThread Type: " << (int)codecType_ << ", " << | 535 ALOGD << "InitEncodeOnCodecThread Type: " << (int)codecType_ << ", " << |
516 width << " x " << height << ". Bitrate: " << kbps << | 536 width << " x " << height << ". Bitrate: " << kbps << |
517 " kbps. Fps: " << fps; | 537 " kbps. Fps: " << fps; |
518 if (kbps == 0) { | 538 if (kbps == 0) { |
519 kbps = last_set_bitrate_kbps_; | 539 kbps = last_set_bitrate_kbps_; |
520 } | 540 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
603 | 623 |
604 inited_ = true; | 624 inited_ = true; |
605 return WEBRTC_VIDEO_CODEC_OK; | 625 return WEBRTC_VIDEO_CODEC_OK; |
606 } | 626 } |
607 | 627 |
608 int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( | 628 int32_t MediaCodecVideoEncoder::EncodeOnCodecThread( |
609 const webrtc::VideoFrame& frame, | 629 const webrtc::VideoFrame& frame, |
610 const std::vector<webrtc::FrameType>* frame_types, | 630 const std::vector<webrtc::FrameType>* frame_types, |
611 const int64_t frame_input_time_ms) { | 631 const int64_t frame_input_time_ms) { |
612 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 632 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
633 if (software_fallback_needed_) { | |
634 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
635 } | |
613 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | 636 JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
614 ScopedLocalRefFrame local_ref_frame(jni); | 637 ScopedLocalRefFrame local_ref_frame(jni); |
615 | 638 |
616 if (!inited_) { | 639 if (!inited_) { |
617 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 640 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
618 } | 641 } |
619 | 642 |
620 bool send_key_frame = false; | 643 bool send_key_frame = false; |
621 if (codec_mode_ == webrtc::kRealtimeVideo) { | 644 if (codec_mode_ == webrtc::kRealtimeVideo) { |
622 ++frames_received_since_last_key_; | 645 ++frames_received_since_last_key_; |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
845 handle->oes_texture_id, | 868 handle->oes_texture_id, |
846 sampling_matrix, | 869 sampling_matrix, |
847 current_timestamp_us_); | 870 current_timestamp_us_); |
848 CHECK_EXCEPTION(jni); | 871 CHECK_EXCEPTION(jni); |
849 return encode_status; | 872 return encode_status; |
850 } | 873 } |
851 | 874 |
852 int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread( | 875 int32_t MediaCodecVideoEncoder::RegisterEncodeCompleteCallbackOnCodecThread( |
853 webrtc::EncodedImageCallback* callback) { | 876 webrtc::EncodedImageCallback* callback) { |
854 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 877 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
878 if (software_fallback_needed_) { | |
879 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
880 } | |
855 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | 881 JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
856 ScopedLocalRefFrame local_ref_frame(jni); | 882 ScopedLocalRefFrame local_ref_frame(jni); |
857 callback_ = callback; | 883 callback_ = callback; |
858 return WEBRTC_VIDEO_CODEC_OK; | 884 return WEBRTC_VIDEO_CODEC_OK; |
859 } | 885 } |
860 | 886 |
861 int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { | 887 int32_t MediaCodecVideoEncoder::ReleaseOnCodecThread() { |
862 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 888 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
863 if (!inited_) { | 889 if (!inited_) { |
864 return WEBRTC_VIDEO_CODEC_OK; | 890 return WEBRTC_VIDEO_CODEC_OK; |
(...skipping 11 matching lines...) Expand all Loading... | |
876 rtc::MessageQueueManager::Clear(this); | 902 rtc::MessageQueueManager::Clear(this); |
877 inited_ = false; | 903 inited_ = false; |
878 use_surface_ = false; | 904 use_surface_ = false; |
879 ALOGD << "EncoderReleaseOnCodecThread done."; | 905 ALOGD << "EncoderReleaseOnCodecThread done."; |
880 return WEBRTC_VIDEO_CODEC_OK; | 906 return WEBRTC_VIDEO_CODEC_OK; |
881 } | 907 } |
882 | 908 |
883 int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate, | 909 int32_t MediaCodecVideoEncoder::SetRatesOnCodecThread(uint32_t new_bit_rate, |
884 uint32_t frame_rate) { | 910 uint32_t frame_rate) { |
885 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); | 911 RTC_DCHECK(codec_thread_checker_.CalledOnValidThread()); |
912 if (software_fallback_needed_) { | |
913 return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; | |
914 } | |
886 frame_rate = (frame_rate < MAX_ALLOWED_VIDEO_FPS) ? | 915 frame_rate = (frame_rate < MAX_ALLOWED_VIDEO_FPS) ? |
887 frame_rate : MAX_ALLOWED_VIDEO_FPS; | 916 frame_rate : MAX_ALLOWED_VIDEO_FPS; |
888 if (last_set_bitrate_kbps_ == new_bit_rate && | 917 if (last_set_bitrate_kbps_ == new_bit_rate && |
889 last_set_fps_ == frame_rate) { | 918 last_set_fps_ == frame_rate) { |
890 return WEBRTC_VIDEO_CODEC_OK; | 919 return WEBRTC_VIDEO_CODEC_OK; |
891 } | 920 } |
892 if (scale_) { | 921 if (scale_) { |
893 quality_scaler_.ReportFramerate(frame_rate); | 922 quality_scaler_.ReportFramerate(frame_rate); |
894 } | 923 } |
895 JNIEnv* jni = AttachCurrentThreadIfNeeded(); | 924 JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
(...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1297 return supported_codecs_; | 1326 return supported_codecs_; |
1298 } | 1327 } |
1299 | 1328 |
1300 void MediaCodecVideoEncoderFactory::DestroyVideoEncoder( | 1329 void MediaCodecVideoEncoderFactory::DestroyVideoEncoder( |
1301 webrtc::VideoEncoder* encoder) { | 1330 webrtc::VideoEncoder* encoder) { |
1302 ALOGD << "Destroy video encoder."; | 1331 ALOGD << "Destroy video encoder."; |
1303 delete encoder; | 1332 delete encoder; |
1304 } | 1333 } |
1305 | 1334 |
1306 } // namespace webrtc_jni | 1335 } // namespace webrtc_jni |
OLD | NEW |