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 d999b01b0ac7684a957486e69949c934b0122559..eb50e2e160d45da7b48b4fd5d7c479bb9997d54e 100644 |
--- a/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
+++ b/webrtc/sdk/android/src/jni/androidmediadecoder_jni.cc |
@@ -729,6 +729,7 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
int stride = GetIntField(jni, *j_media_codec_video_decoder_, j_stride_field_); |
int slice_height = GetIntField(jni, *j_media_codec_video_decoder_, |
j_slice_height_field_); |
+ RTC_CHECK_GE(slice_height, height); |
rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer; |
int64_t presentation_timestamps_ms = 0; |
@@ -804,22 +805,46 @@ bool MediaCodecVideoDecoder::DeliverPendingOutputs( |
payload += output_buffer_offset; |
// Create yuv420 frame. |
- rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer; |
- |
- i420_buffer = decoded_frame_pool_.CreateBuffer(width, height); |
+ 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_CHECK_EQ(0, slice_height % 2); |
const int uv_stride = stride / 2; |
- const int u_slice_height = slice_height / 2; |
const uint8_t* y_ptr = payload; |
const uint8_t* u_ptr = y_ptr + stride * slice_height; |
- const uint8_t* v_ptr = u_ptr + uv_stride * u_slice_height; |
- libyuv::I420Copy(y_ptr, stride, u_ptr, uv_stride, v_ptr, uv_stride, |
- i420_buffer->MutableDataY(), i420_buffer->StrideY(), |
- i420_buffer->MutableDataU(), i420_buffer->StrideU(), |
- i420_buffer->MutableDataV(), i420_buffer->StrideV(), |
- width, height); |
+ |
+ // Note that the case with odd |slice_height| is handled in a special way. |
+ // The chroma height contained in the payload is rounded down instead of |
+ // up, making it one row less than what we expect in WebRTC. Therefore, we |
+ // have to duplicate the last chroma rows for this case. Also, the offset |
+ // between the Y plane and the U plane is unintuitive for this case. See |
+ // http://bugs.webrtc.org/6651 for more info. |
+ const int chroma_width = (width + 1) / 2; |
+ const int chroma_height = |
+ (slice_height % 2 == 0) ? (height + 1) / 2 : height / 2; |
+ const int u_offset = uv_stride * slice_height / 2; |
+ const uint8_t* v_ptr = u_ptr + u_offset; |
+ libyuv::CopyPlane(y_ptr, stride, |
+ i420_buffer->MutableDataY(), i420_buffer->StrideY(), |
+ width, height); |
+ libyuv::CopyPlane(u_ptr, uv_stride, |
+ i420_buffer->MutableDataU(), i420_buffer->StrideU(), |
+ chroma_width, chroma_height); |
+ libyuv::CopyPlane(v_ptr, uv_stride, |
+ i420_buffer->MutableDataV(), i420_buffer->StrideV(), |
+ chroma_width, chroma_height); |
+ if (slice_height % 2 == 1) { |
+ RTC_CHECK_EQ(height, slice_height); |
+ // Duplicate the last chroma rows. |
+ uint8_t* u_last_row_ptr = i420_buffer->MutableDataU() + |
+ chroma_height * i420_buffer->StrideU(); |
+ memcpy(u_last_row_ptr, u_last_row_ptr - i420_buffer->StrideU(), |
+ i420_buffer->StrideU()); |
+ uint8_t* v_last_row_ptr = i420_buffer->MutableDataV() + |
+ chroma_height * i420_buffer->StrideV(); |
+ memcpy(v_last_row_ptr, v_last_row_ptr - i420_buffer->StrideV(), |
+ i420_buffer->StrideV()); |
+ } |
} else { |
// All other supported formats are nv12. |
const uint8_t* y_ptr = payload; |