Chromium Code Reviews| Index: webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
| diff --git a/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc b/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
| index d30e94c58fbbb9ed3424a2959ce52cd0253fd003..a9f1756beaa1b9634803d20a0ab65cab1b271777 100644 |
| --- a/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
| +++ b/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
| @@ -36,7 +36,19 @@ |
| #include "webrtc/sdk/android/src/jni/surfacetexturehelper_jni.h" |
| #include "webrtc/system_wrappers/include/logcat_trace_context.h" |
| -using rtc::Bind; |
| +// Logging macros. |
| +#define TAG_DECODER "MediaCodecVideoDecoder" |
| +#ifdef TRACK_BUFFER_TIMING |
| +#define ALOGV(...) |
| +__android_log_print(ANDROID_LOG_VERBOSE, TAG_DECODER, __VA_ARGS__) |
| +#else |
| +#define ALOGV(...) |
| +#endif |
| +#define ALOGD LOG_TAG(rtc::LS_INFO, TAG_DECODER) |
| +#define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_DECODER) |
| +#define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_DECODER) |
| + |
| + using rtc::Bind; |
|
sakal
2017/03/21 10:05:58
nit: indentation
tommi
2017/03/21 16:21:43
Done.
|
| using rtc::Thread; |
| using rtc::ThreadManager; |
| @@ -53,22 +65,7 @@ using webrtc::kVideoCodecVP9; |
| namespace webrtc_jni { |
| -// Logging macros. |
| -#define TAG_DECODER "MediaCodecVideoDecoder" |
| -#ifdef TRACK_BUFFER_TIMING |
| -#define ALOGV(...) |
| - __android_log_print(ANDROID_LOG_VERBOSE, TAG_DECODER, __VA_ARGS__) |
| -#else |
| -#define ALOGV(...) |
| -#endif |
| -#define ALOGD LOG_TAG(rtc::LS_INFO, TAG_DECODER) |
| -#define ALOGW LOG_TAG(rtc::LS_WARNING, TAG_DECODER) |
| -#define ALOGE LOG_TAG(rtc::LS_ERROR, TAG_DECODER) |
| - |
| -enum { kMaxWarningLogFrames = 2 }; |
| - |
| -class MediaCodecVideoDecoder : public webrtc::VideoDecoder, |
| - public rtc::MessageHandler { |
| +class MediaCodecVideoDecoder : public webrtc::VideoDecoder { |
| public: |
| explicit MediaCodecVideoDecoder( |
| JNIEnv* jni, VideoCodecType codecType, jobject render_egl_context); |
| @@ -83,6 +80,8 @@ class MediaCodecVideoDecoder : public webrtc::VideoDecoder, |
| const CodecSpecificInfo* codecSpecificInfo = NULL, |
| int64_t renderTimeMs = -1) override; |
| + void PollDecodedFrames() override; |
| + |
| int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback) |
| override; |
| @@ -90,22 +89,41 @@ class MediaCodecVideoDecoder : public webrtc::VideoDecoder, |
| bool PrefersLateDecoding() const override { return true; } |
| - // rtc::MessageHandler implementation. |
| - void OnMessage(rtc::Message* msg) override; |
| - |
| const char* ImplementationName() const override; |
| private: |
| - // CHECK-fail if not running on |codec_thread_|. |
| - void CheckOnCodecThread(); |
| + struct DecodedFrame { |
| + DecodedFrame(VideoFrame frame, |
| + int decode_time_ms, |
| + int64_t timestamp, |
| + int64_t ntp_timestamp, |
| + rtc::Optional<uint8_t> qp) |
| + : frame(std::move(frame)), |
| + decode_time_ms(decode_time_ms), |
| + qp(std::move(qp)) { |
| + frame.set_timestamp(timestamp); |
| + frame.set_ntp_time_ms(ntp_timestamp); |
| + } |
| + |
| + VideoFrame frame; |
| + int decode_time_ms; |
| + rtc::Optional<uint8_t> qp; |
| + }; |
| + |
| + // Returns true if running on |codec_thread_|. Used for DCHECKing. |
| + bool IsOnCodecThread(); |
| int32_t InitDecodeOnCodecThread(); |
| int32_t ResetDecodeOnCodecThread(); |
| int32_t ReleaseOnCodecThread(); |
| - int32_t DecodeOnCodecThread(const EncodedImage& inputImage); |
| + int32_t DecodeOnCodecThread(const EncodedImage& inputImage, |
| + std::vector<DecodedFrame>* frames); |
| + void PollDecodedFramesOnCodecThread(std::vector<DecodedFrame>* frames); |
| // Deliver any outputs pending in the MediaCodec to our |callback_| and return |
| // true on success. |
| - bool DeliverPendingOutputs(JNIEnv* jni, int dequeue_timeout_us); |
| + bool DeliverPendingOutputs(JNIEnv* jni, |
| + int dequeue_timeout_us, |
| + std::vector<DecodedFrame>* frames); |
| int32_t ProcessHWErrorOnCodecThread(); |
| void EnableFrameLogOnWarning(); |
| void ResetVariables(); |
| @@ -179,6 +197,9 @@ class MediaCodecVideoDecoder : public webrtc::VideoDecoder, |
| // Global references; must be deleted in Release(). |
| std::vector<jobject> input_buffers_; |
| + |
| + // Added to on the codec thread, frames are delivered on the decoder thread. |
| + std::vector<DecodedFrame> decoded_frames_; |
| }; |
| MediaCodecVideoDecoder::MediaCodecVideoDecoder( |
| @@ -201,7 +222,7 @@ MediaCodecVideoDecoder::MediaCodecVideoDecoder( |
| "()V"))) { |
| ScopedLocalRefFrame local_ref_frame(jni); |
| codec_thread_->SetName("MediaCodecVideoDecoder", NULL); |
| - RTC_CHECK(codec_thread_->Start()) << "Failed to start MediaCodecVideoDecoder"; |
| + RTC_CHECK(codec_thread_->Start()); |
| j_init_decode_method_ = GetMethodID( |
| jni, *j_media_codec_video_decoder_class_, "initDecode", |
| @@ -296,7 +317,7 @@ int32_t MediaCodecVideoDecoder::InitDecode(const VideoCodec* inst, |
| return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| } |
| // Factory should guard against other codecs being used with us. |
| - RTC_CHECK(inst->codecType == codecType_) |
| + RTC_DCHECK(inst->codecType == codecType_) |
| << "Unsupported codec " << inst->codecType << " for " << codecType_; |
| if (sw_fallback_required_) { |
| @@ -317,7 +338,7 @@ int32_t MediaCodecVideoDecoder::InitDecode(const VideoCodec* inst, |
| } |
| void MediaCodecVideoDecoder::ResetVariables() { |
| - CheckOnCodecThread(); |
| + RTC_DCHECK(IsOnCodecThread()); |
| key_frame_required_ = true; |
| frames_received_ = 0; |
| @@ -332,7 +353,7 @@ void MediaCodecVideoDecoder::ResetVariables() { |
| } |
| int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() { |
| - CheckOnCodecThread(); |
| + RTC_DCHECK(IsOnCodecThread()); |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ScopedLocalRefFrame local_ref_frame(jni); |
| ALOGD << "InitDecodeOnCodecThread Type: " << (int)codecType_ << ". " |
| @@ -406,13 +427,11 @@ int32_t MediaCodecVideoDecoder::InitDecodeOnCodecThread() { |
| } |
| } |
| - codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); |
| - |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| int32_t MediaCodecVideoDecoder::ResetDecodeOnCodecThread() { |
| - CheckOnCodecThread(); |
| + RTC_DCHECK(IsOnCodecThread()); |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ScopedLocalRefFrame local_ref_frame(jni); |
| ALOGD << "ResetDecodeOnCodecThread Type: " << (int)codecType_ << ". " |
| @@ -421,7 +440,6 @@ int32_t MediaCodecVideoDecoder::ResetDecodeOnCodecThread() { |
| ". Frames decoded: " << frames_decoded_; |
| inited_ = false; |
| - rtc::MessageQueueManager::Clear(this); |
| ResetVariables(); |
| jni->CallVoidMethod( |
| @@ -437,8 +455,6 @@ int32_t MediaCodecVideoDecoder::ResetDecodeOnCodecThread() { |
| } |
| inited_ = true; |
| - codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); |
| - |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -449,10 +465,10 @@ int32_t MediaCodecVideoDecoder::Release() { |
| } |
| int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() { |
| + RTC_DCHECK(IsOnCodecThread()); |
| if (!inited_) { |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| - CheckOnCodecThread(); |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ALOGD << "DecoderReleaseOnCodecThread: Frames received: " << |
| frames_received_ << ". Frames decoded: " << frames_decoded_; |
| @@ -464,7 +480,6 @@ int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() { |
| jni->CallVoidMethod(*j_media_codec_video_decoder_, j_release_method_); |
| surface_texture_helper_ = nullptr; |
| inited_ = false; |
| - rtc::MessageQueueManager::Clear(this); |
| if (CheckException(jni)) { |
| ALOGE << "Decoder release exception"; |
| return WEBRTC_VIDEO_CODEC_ERROR; |
| @@ -473,19 +488,19 @@ int32_t MediaCodecVideoDecoder::ReleaseOnCodecThread() { |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| -void MediaCodecVideoDecoder::CheckOnCodecThread() { |
| - RTC_CHECK(codec_thread_.get() == ThreadManager::Instance()->CurrentThread()) |
| - << "Running on wrong thread!"; |
| +bool MediaCodecVideoDecoder::IsOnCodecThread() { |
| + return codec_thread_.get() == ThreadManager::Instance()->CurrentThread(); |
| } |
| void MediaCodecVideoDecoder::EnableFrameLogOnWarning() { |
| // Log next 2 output frames. |
| + static const int kMaxWarningLogFrames = 2; |
| frames_decoded_logged_ = std::max( |
| frames_decoded_logged_, frames_decoded_ + kMaxWarningLogFrames); |
| } |
| int32_t MediaCodecVideoDecoder::ProcessHWErrorOnCodecThread() { |
| - CheckOnCodecThread(); |
| + RTC_DCHECK(IsOnCodecThread()); |
| int ret_val = ReleaseOnCodecThread(); |
| if (ret_val < 0) { |
| ALOGE << "ProcessHWError: Release failure"; |
| @@ -516,22 +531,17 @@ int32_t MediaCodecVideoDecoder::Decode( |
| const RTPFragmentationHeader* fragmentation, |
| const CodecSpecificInfo* codecSpecificInfo, |
| int64_t renderTimeMs) { |
| + RTC_DCHECK(callback_); |
| + RTC_DCHECK(inited_); |
| + |
| if (sw_fallback_required_) { |
| ALOGE << "Decode() - fallback to SW codec"; |
| return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; |
| } |
| - if (callback_ == NULL) { |
|
sakal
2017/03/21 10:05:57
Is this behavior change intended / relevant for th
tommi
2017/03/21 16:21:43
It's intended. The callback should never be null.
|
| - ALOGE << "Decode() - callback_ is NULL"; |
| - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| - } |
| if (inputImage._buffer == NULL && inputImage._length > 0) { |
| ALOGE << "Decode() - inputImage is incorrect"; |
| return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| } |
| - if (!inited_) { |
| - ALOGE << "Decode() - decoder is not initialized"; |
| - return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| - } |
| // Check if encoded frame dimension has changed. |
| if ((inputImage._encodedWidth * inputImage._encodedHeight > 0) && |
| @@ -576,14 +586,34 @@ int32_t MediaCodecVideoDecoder::Decode( |
| return WEBRTC_VIDEO_CODEC_ERROR; |
| } |
| - return codec_thread_->Invoke<int32_t>( |
| + ALOGD << "Decode() - about to call DecodeOnCodecThread"; |
|
sakal
2017/03/21 10:05:57
nit: Please remove excessive logging.
tommi
2017/03/21 16:21:43
Done.
|
| + std::vector<DecodedFrame> frames; |
| + int32_t ret = codec_thread_->Invoke<int32_t>( |
| + RTC_FROM_HERE, Bind(&MediaCodecVideoDecoder::DecodeOnCodecThread, this, |
| + inputImage, &frames)); |
| + ALOGD << "Decode() - after call to DecodeOnCodecThread"; |
|
sakal
2017/03/21 10:05:58
nit: Please remove excessive logging.
tommi
2017/03/21 16:21:42
Done.
|
| + for (auto& f : frames) |
| + callback_->Decoded(f.frame, rtc::Optional<int32_t>(f.decode_time_ms), f.qp); |
| + return ret; |
| +} |
| + |
| +void MediaCodecVideoDecoder::PollDecodedFrames() { |
| + RTC_DCHECK(callback_); |
| + |
| + std::vector<DecodedFrame> frames; |
| + codec_thread_->Invoke<void>( |
| RTC_FROM_HERE, |
| - Bind(&MediaCodecVideoDecoder::DecodeOnCodecThread, this, inputImage)); |
| + Bind(&MediaCodecVideoDecoder::PollDecodedFramesOnCodecThread, this, |
| + &frames)); |
| + |
| + for (auto& f : frames) |
| + callback_->Decoded(f.frame, rtc::Optional<int32_t>(f.decode_time_ms), f.qp); |
| } |
| int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| - const EncodedImage& inputImage) { |
| - CheckOnCodecThread(); |
| + const EncodedImage& inputImage, |
| + std::vector<DecodedFrame>* frames) { |
| + RTC_DCHECK(IsOnCodecThread()); |
| JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| ScopedLocalRefFrame local_ref_frame(jni); |
| @@ -599,7 +629,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| const int64 drain_start = rtc::TimeMillis(); |
| while ((frames_received_ > frames_decoded_ + max_pending_frames_) && |
| (rtc::TimeMillis() - drain_start) < kMediaCodecTimeoutMs) { |
| - if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) { |
| + if (!DeliverPendingOutputs(jni, kMediaCodecPollMs, frames)) { |
| ALOGE << "DeliverPendingOutputs error. Frames received: " << |
| frames_received_ << ". Frames decoded: " << frames_decoded_; |
| return ProcessHWErrorOnCodecThread(); |
| @@ -619,7 +649,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| ". Retry DeliverPendingOutputs."; |
| EnableFrameLogOnWarning(); |
| // Try to drain the decoder. |
| - if (!DeliverPendingOutputs(jni, kMediaCodecPollMs)) { |
| + if (!DeliverPendingOutputs(jni, kMediaCodecPollMs, frames)) { |
| ALOGE << "DeliverPendingOutputs error. Frames received: " << |
| frames_received_ << ". Frames decoded: " << frames_decoded_; |
| return ProcessHWErrorOnCodecThread(); |
| @@ -637,7 +667,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| jobject j_input_buffer = input_buffers_[j_input_buffer_index]; |
| uint8_t* buffer = |
| reinterpret_cast<uint8_t*>(jni->GetDirectBufferAddress(j_input_buffer)); |
| - RTC_CHECK(buffer) << "Indirect buffer??"; |
| + RTC_DCHECK(buffer) << "Indirect buffer??"; |
| int64_t buffer_capacity = jni->GetDirectBufferCapacity(j_input_buffer); |
| if (CheckException(jni) || buffer_capacity < inputImage._length) { |
| ALOGE << "Input frame size "<< inputImage._length << |
| @@ -690,7 +720,7 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| } |
| // Try to drain the decoder |
| - if (!DeliverPendingOutputs(jni, 0)) { |
| + if (!DeliverPendingOutputs(jni, 0, frames)) { |
| ALOGE << "DeliverPendingOutputs error"; |
| return ProcessHWErrorOnCodecThread(); |
| } |
| @@ -698,9 +728,24 @@ int32_t MediaCodecVideoDecoder::DecodeOnCodecThread( |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| +void MediaCodecVideoDecoder::PollDecodedFramesOnCodecThread( |
| + std::vector<DecodedFrame>* frames) { |
| + RTC_DCHECK(IsOnCodecThread()); |
| + |
| + JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| + ScopedLocalRefFrame local_ref_frame(jni); |
| + |
| + if (!DeliverPendingOutputs(jni, 0, frames)) { |
| + ALOGE << "PollDecodedFramesOnCodecThread: DeliverPendingOutputs error"; |
| + ProcessHWErrorOnCodecThread(); |
| + } |
| +} |
| + |
| bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
| - JNIEnv* jni, int dequeue_timeout_ms) { |
| - CheckOnCodecThread(); |
| + JNIEnv* jni, |
| + int dequeue_timeout_ms, |
| + std::vector<DecodedFrame>* frames) { |
| + RTC_DCHECK(IsOnCodecThread()); |
| if (frames_received_ <= frames_decoded_) { |
| // No need to query for output buffers - decoder is drained. |
| return true; |
| @@ -782,7 +827,7 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
| decode_time_ms = GetLongField(jni, j_decoder_output_buffer, |
| j_byte_buffer_decode_time_ms_field_); |
| - RTC_CHECK_GE(slice_height, height); |
| + RTC_DCHECK_GE(slice_height, height); |
|
sakal
2017/03/21 10:05:57
If this check fails it result in reading outside o
tommi
2017/03/21 16:21:42
good point, changed back to a CHECK.
|
| if (output_buffer_size < width * height * 3 / 2) { |
| ALOGE << "Insufficient output buffer size: " << output_buffer_size; |
| @@ -809,7 +854,7 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
| rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer = |
| decoded_frame_pool_.CreateBuffer(width, height); |
| if (color_format == COLOR_FormatYUV420Planar) { |
| - RTC_CHECK_EQ(0, stride % 2); |
| + RTC_DCHECK_EQ(0, stride % 2); |
| const int uv_stride = stride / 2; |
| const uint8_t* y_ptr = payload; |
| const uint8_t* u_ptr = y_ptr + stride * slice_height; |
| @@ -835,7 +880,7 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
| i420_buffer->MutableDataV(), i420_buffer->StrideV(), |
| chroma_width, chroma_height); |
| if (slice_height % 2 == 1) { |
| - RTC_CHECK_EQ(height, slice_height); |
| + RTC_DCHECK_EQ(height, slice_height); |
| // Duplicate the last chroma rows. |
| uint8_t* u_last_row_ptr = i420_buffer->MutableDataU() + |
| chroma_height * i420_buffer->StrideU(); |
| @@ -904,14 +949,24 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
| // If the frame was dropped, frame_buffer is left as nullptr. |
| if (frame_buffer) { |
| + ALOGD << "DeliverPendingOutputs: Have frame on the codec thread."; |
|
sakal
2017/03/21 10:05:57
This log seems a little excessive.
tommi
2017/03/21 16:21:42
Done.
|
| VideoFrame decoded_frame(frame_buffer, 0, 0, webrtc::kVideoRotation_0); |
| decoded_frame.set_timestamp(output_timestamps_ms); |
| decoded_frame.set_ntp_time_ms(output_ntp_timestamps_ms); |
| rtc::Optional<uint8_t> qp = pending_frame_qps_.front(); |
| pending_frame_qps_.pop_front(); |
| - callback_->Decoded(decoded_frame, rtc::Optional<int32_t>(decode_time_ms), |
| - qp); |
| + decoded_frames_.push_back(DecodedFrame(std::move(decoded_frame), |
| + decode_time_ms, output_timestamps_ms, |
| + output_ntp_timestamps_ms, qp)); |
| + ALOGD << "DeliverPendingOutputs: Decoded frame delivered."; |
|
sakal
2017/03/21 10:05:58
Same here.
tommi
2017/03/21 16:21:43
Done.
|
| + } |
| + |
| + if (frames) { |
|
sakal
2017/03/21 10:05:57
I don't see frames ever actually being null. DCHEC
tommi
2017/03/21 16:21:43
Done.
|
| + frames->reserve(frames->size() + decoded_frames_.size()); |
| + std::move(decoded_frames_.begin(), decoded_frames_.end(), |
| + std::back_inserter(*frames)); |
| + decoded_frames_.clear(); |
| } |
| return true; |
| } |
| @@ -922,26 +977,6 @@ int32_t MediaCodecVideoDecoder::RegisterDecodeCompleteCallback( |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| -void MediaCodecVideoDecoder::OnMessage(rtc::Message* msg) { |
| - JNIEnv* jni = AttachCurrentThreadIfNeeded(); |
| - ScopedLocalRefFrame local_ref_frame(jni); |
| - if (!inited_) { |
| - return; |
| - } |
| - // We only ever send one message to |this| directly (not through a Bind()'d |
| - // functor), so expect no ID/data. |
| - RTC_CHECK(!msg->message_id) << "Unexpected message!"; |
| - RTC_CHECK(!msg->pdata) << "Unexpected message!"; |
| - CheckOnCodecThread(); |
| - |
| - if (!DeliverPendingOutputs(jni, 0)) { |
| - ALOGE << "OnMessage: DeliverPendingOutputs error"; |
| - ProcessHWErrorOnCodecThread(); |
| - return; |
| - } |
| - codec_thread_->PostDelayed(RTC_FROM_HERE, kMediaCodecPollMs, this); |
| -} |
| - |
| MediaCodecVideoDecoderFactory::MediaCodecVideoDecoderFactory() |
| : egl_context_(nullptr) { |
| ALOGD << "MediaCodecVideoDecoderFactory ctor"; |