Fix cropping in H264 decoder wrapper.

FFmpeg applies cropping (if needed) by moving plane pointers and
by adjusting frame resolution. Wrap AVframe into WrapI420Buffer.

Bug: webrtc:10892
Change-Id: I9814518759c9fc37f2bb6e16248fc32017ca4f4e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/155662
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29404}
diff --git a/modules/video_coding/codecs/h264/h264_decoder_impl.cc b/modules/video_coding/codecs/h264/h264_decoder_impl.cc
index bfbdf75..7327c41 100644
--- a/modules/video_coding/codecs/h264/h264_decoder_impl.cc
+++ b/modules/video_coding/codecs/h264/h264_decoder_impl.cc
@@ -284,16 +284,6 @@
   // the input one.
   RTC_DCHECK_EQ(av_frame_->reordered_opaque, frame_timestamp_us);
 
-  // Obtain the |video_frame| containing the decoded image.
-  VideoFrame* input_frame =
-      static_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0]));
-  RTC_DCHECK(input_frame);
-  const webrtc::I420BufferInterface* i420_buffer =
-      input_frame->video_frame_buffer()->GetI420();
-  RTC_CHECK_EQ(av_frame_->data[kYPlaneIndex], i420_buffer->DataY());
-  RTC_CHECK_EQ(av_frame_->data[kUPlaneIndex], i420_buffer->DataU());
-  RTC_CHECK_EQ(av_frame_->data[kVPlaneIndex], i420_buffer->DataV());
-
   absl::optional<uint8_t> qp;
   // TODO(sakal): Maybe it is possible to get QP directly from FFmpeg.
   h264_bitstream_parser_.ParseBitstream(input_image.data(), input_image.size());
@@ -302,7 +292,39 @@
     qp.emplace(qp_int);
   }
 
-  rtc::scoped_refptr<VideoFrameBuffer> decoded_buffer;
+  // Obtain the |video_frame| containing the decoded image.
+  VideoFrame* input_frame =
+      static_cast<VideoFrame*>(av_buffer_get_opaque(av_frame_->buf[0]));
+  RTC_DCHECK(input_frame);
+  const webrtc::I420BufferInterface* i420_buffer =
+      input_frame->video_frame_buffer()->GetI420();
+
+  // When needed, FFmpeg applies cropping by moving plane pointers and adjusting
+  // frame width/height. Ensure that cropped buffers lie within the allocated
+  // memory.
+  RTC_DCHECK_LE(av_frame_->width, i420_buffer->width());
+  RTC_DCHECK_LE(av_frame_->height, i420_buffer->height());
+  RTC_DCHECK_GE(av_frame_->data[kYPlaneIndex], i420_buffer->DataY());
+  RTC_DCHECK_LE(
+      av_frame_->data[kYPlaneIndex] +
+          av_frame_->linesize[kYPlaneIndex] * av_frame_->height,
+      i420_buffer->DataY() + i420_buffer->StrideY() * i420_buffer->height());
+  RTC_DCHECK_GE(av_frame_->data[kUPlaneIndex], i420_buffer->DataU());
+  RTC_DCHECK_LE(av_frame_->data[kUPlaneIndex] +
+                    av_frame_->linesize[kUPlaneIndex] * av_frame_->height / 2,
+                i420_buffer->DataU() +
+                    i420_buffer->StrideU() * i420_buffer->height() / 2);
+  RTC_DCHECK_GE(av_frame_->data[kVPlaneIndex], i420_buffer->DataV());
+  RTC_DCHECK_LE(av_frame_->data[kVPlaneIndex] +
+                    av_frame_->linesize[kVPlaneIndex] * av_frame_->height / 2,
+                i420_buffer->DataV() +
+                    i420_buffer->StrideV() * i420_buffer->height() / 2);
+
+  auto cropped_buffer = WrapI420Buffer(
+      av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex],
+      av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex],
+      av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex],
+      av_frame_->linesize[kVPlaneIndex], rtc::KeepRefUntilDone(i420_buffer));
 
   // Pass on color space from input frame if explicitly specified.
   const ColorSpace& color_space =
@@ -316,26 +338,12 @@
   // TODO(chromium:956468): Remove this code and fix the underlying problem.
   bool hdr_color_space =
       color_space.transfer() == ColorSpace::TransferID::kSMPTEST2084;
+
+  rtc::scoped_refptr<VideoFrameBuffer> decoded_buffer;
   if (kEnable8bitHdrFix_ && hdr_color_space) {
-    auto i010_buffer = I010Buffer::Copy(*i420_buffer);
-    // Crop image, see comment below.
-    decoded_buffer = WrapI010Buffer(
-        av_frame_->width, av_frame_->height, i010_buffer->DataY(),
-        i010_buffer->StrideY(), i010_buffer->DataU(), i010_buffer->StrideU(),
-        i010_buffer->DataV(), i010_buffer->StrideV(),
-        rtc::KeepRefUntilDone(i010_buffer));
-  } else if (av_frame_->width != i420_buffer->width() ||
-             av_frame_->height != i420_buffer->height()) {
-    // The decoded image may be larger than what is supposed to be visible, see
-    // |AVGetBuffer2|'s use of |avcodec_align_dimensions|. This crops the image
-    // without copying the underlying buffer.
-    decoded_buffer = WrapI420Buffer(
-        av_frame_->width, av_frame_->height, i420_buffer->DataY(),
-        i420_buffer->StrideY(), i420_buffer->DataU(), i420_buffer->StrideU(),
-        i420_buffer->DataV(), i420_buffer->StrideV(),
-        rtc::KeepRefUntilDone(i420_buffer));
+    decoded_buffer = I010Buffer::Copy(*cropped_buffer);
   } else {
-    decoded_buffer = input_frame->video_frame_buffer();
+    decoded_buffer = cropped_buffer;
   }
 
   VideoFrame decoded_frame = VideoFrame::Builder()