diff --git a/AUTHORS b/AUTHORS index 285cde44f6..3a73bd6166 100644 --- a/AUTHORS +++ b/AUTHORS @@ -28,6 +28,7 @@ Jens Nielsen Jiawei Ou Jie Mao Luke Weber +Mallikarjuna Rao V Manish Jethani Martin Storsjo Matthias Liebig diff --git a/common_video/libyuv/include/webrtc_libyuv.h b/common_video/libyuv/include/webrtc_libyuv.h index 8230c1fde2..5b2a3af835 100644 --- a/common_video/libyuv/include/webrtc_libyuv.h +++ b/common_video/libyuv/include/webrtc_libyuv.h @@ -24,7 +24,6 @@ namespace webrtc { -class I420Buffer; // This is the max PSNR value our algorithms can return. const double kPerfectPSNR = 48.0f; @@ -64,34 +63,6 @@ int ExtractBuffer(const rtc::scoped_refptr& input_frame, size_t size, uint8_t* buffer); int ExtractBuffer(const VideoFrame& input_frame, size_t size, uint8_t* buffer); -// Convert To I420 -// Input: -// - src_video_type : Type of input video. -// - src_frame : Pointer to a source frame. -// - crop_x/crop_y : Starting positions for cropping (0 for no crop). -// - src_width : src width in pixels. -// - src_height : src height in pixels. -// - sample_size : Required only for the parsing of MJPG (set to 0 else). -// - rotate : Rotation mode of output image. -// Output: -// - dst_buffer : Reference to a destination frame buffer. -// Return value: 0 if OK, < 0 otherwise. - -// TODO(nisse): Delete this wrapper, and let users call libyuv directly. Most -// calls pass |src_video_type| == kI420, and should use libyuv::I420Copy. Also -// remember to delete the I420Buffer forward declaration above. The only -// exception at the time of this writing is VideoCaptureImpl::IncomingFrame, -// which still needs libyuv::ConvertToI420. -int ConvertToI420(VideoType src_video_type, - const uint8_t* src_frame, - int crop_x, - int crop_y, - int src_width, - int src_height, - size_t sample_size, - VideoRotation rotation, - I420Buffer* dst_buffer); - // Convert From I420 // Input: // - src_frame : Reference to a source frame. @@ -147,6 +118,9 @@ class NV12ToI420Scaler { std::vector tmp_uv_planes_; }; +// Convert VideoType to libyuv FourCC type +int ConvertVideoType(VideoType video_type); + } // namespace webrtc #endif // COMMON_VIDEO_LIBYUV_INCLUDE_WEBRTC_LIBYUV_H_ diff --git a/common_video/libyuv/libyuv_unittest.cc b/common_video/libyuv/libyuv_unittest.cc index 4e3a1ac885..e5a09bf9ca 100644 --- a/common_video/libyuv/libyuv_unittest.cc +++ b/common_video/libyuv/libyuv_unittest.cc @@ -16,6 +16,7 @@ #include "api/video/i420_buffer.h" #include "api/video/video_frame.h" #include "common_video/libyuv/include/webrtc_libyuv.h" +#include "libyuv.h" // NOLINT #include "test/frame_utils.h" #include "test/gmock.h" #include "test/gtest.h" @@ -96,9 +97,16 @@ TEST_F(TestLibYuv, ConvertTest) { std::unique_ptr out_i420_buffer(new uint8_t[frame_length_]); EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kI420, 0, out_i420_buffer.get())); - EXPECT_EQ(0, - ConvertToI420(VideoType::kI420, out_i420_buffer.get(), 0, 0, width_, - height_, 0, kVideoRotation_0, res_i420_buffer.get())); + int y_size = width_ * height_; + int u_size = res_i420_buffer->ChromaWidth() * res_i420_buffer->ChromaHeight(); + int ret = libyuv::I420Copy( + out_i420_buffer.get(), width_, out_i420_buffer.get() + y_size, + width_ >> 1, out_i420_buffer.get() + y_size + u_size, width_ >> 1, + res_i420_buffer.get()->MutableDataY(), res_i420_buffer.get()->StrideY(), + res_i420_buffer.get()->MutableDataU(), res_i420_buffer.get()->StrideU(), + res_i420_buffer.get()->MutableDataV(), res_i420_buffer.get()->StrideV(), + width_, height_); + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; @@ -119,10 +127,15 @@ TEST_F(TestLibYuv, ConvertTest) { EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB24, 0, res_rgb_buffer2.get())); - EXPECT_EQ( - 0, ConvertToI420(VideoType::kRGB24, res_rgb_buffer2.get(), 0, 0, width_, - height_, 0, kVideoRotation_0, res_i420_buffer.get())); + ret = libyuv::ConvertToI420( + res_rgb_buffer2.get(), 0, res_i420_buffer.get()->MutableDataY(), + res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(), + res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(), + res_i420_buffer.get()->StrideV(), 0, 0, width_, height_, + res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0, + ConvertVideoType(VideoType::kRGB24)); + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; } @@ -137,9 +150,16 @@ TEST_F(TestLibYuv, ConvertTest) { std::unique_ptr out_uyvy_buffer(new uint8_t[width_ * height_ * 2]); EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kUYVY, 0, out_uyvy_buffer.get())); - EXPECT_EQ(0, - ConvertToI420(VideoType::kUYVY, out_uyvy_buffer.get(), 0, 0, width_, - height_, 0, kVideoRotation_0, res_i420_buffer.get())); + + ret = libyuv::ConvertToI420( + out_uyvy_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(), + res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(), + res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(), + res_i420_buffer.get()->StrideV(), 0, 0, width_, height_, + res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0, + ConvertVideoType(VideoType::kUYVY)); + + EXPECT_EQ(0, ret); psnr = I420PSNR(*orig_frame_->video_frame_buffer()->GetI420(), *res_i420_buffer); EXPECT_EQ(48.0, psnr); @@ -153,9 +173,15 @@ TEST_F(TestLibYuv, ConvertTest) { EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kYUY2, 0, out_yuy2_buffer.get())); - EXPECT_EQ(0, - ConvertToI420(VideoType::kYUY2, out_yuy2_buffer.get(), 0, 0, width_, - height_, 0, kVideoRotation_0, res_i420_buffer.get())); + ret = libyuv::ConvertToI420( + out_yuy2_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(), + res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(), + res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(), + res_i420_buffer.get()->StrideV(), 0, 0, width_, height_, + res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0, + ConvertVideoType(VideoType::kYUY2)); + + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; @@ -171,9 +197,15 @@ TEST_F(TestLibYuv, ConvertTest) { EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB565, 0, out_rgb565_buffer.get())); - EXPECT_EQ(0, ConvertToI420(VideoType::kRGB565, out_rgb565_buffer.get(), 0, 0, - width_, height_, 0, kVideoRotation_0, - res_i420_buffer.get())); + ret = libyuv::ConvertToI420( + out_rgb565_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(), + res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(), + res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(), + res_i420_buffer.get()->StrideV(), 0, 0, width_, height_, + res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0, + ConvertVideoType(VideoType::kRGB565)); + + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; } @@ -192,9 +224,15 @@ TEST_F(TestLibYuv, ConvertTest) { EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kARGB, 0, out_argb8888_buffer.get())); - EXPECT_EQ(0, ConvertToI420(VideoType::kARGB, out_argb8888_buffer.get(), 0, 0, - width_, height_, 0, kVideoRotation_0, - res_i420_buffer.get())); + ret = libyuv::ConvertToI420( + out_argb8888_buffer.get(), 0, res_i420_buffer.get()->MutableDataY(), + res_i420_buffer.get()->StrideY(), res_i420_buffer.get()->MutableDataU(), + res_i420_buffer.get()->StrideU(), res_i420_buffer.get()->MutableDataV(), + res_i420_buffer.get()->StrideV(), 0, 0, width_, height_, + res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0, + ConvertVideoType(VideoType::kARGB)); + + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; @@ -227,9 +265,17 @@ TEST_F(TestLibYuv, ConvertAlignedFrame) { std::unique_ptr out_i420_buffer(new uint8_t[frame_length_]); EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kI420, 0, out_i420_buffer.get())); - EXPECT_EQ(0, - ConvertToI420(VideoType::kI420, out_i420_buffer.get(), 0, 0, width_, - height_, 0, kVideoRotation_0, res_i420_buffer.get())); + int y_size = width_ * height_; + int u_size = res_i420_buffer->ChromaWidth() * res_i420_buffer->ChromaHeight(); + int ret = libyuv::I420Copy( + out_i420_buffer.get(), width_, out_i420_buffer.get() + y_size, + width_ >> 1, out_i420_buffer.get() + y_size + u_size, width_ >> 1, + res_i420_buffer.get()->MutableDataY(), res_i420_buffer.get()->StrideY(), + res_i420_buffer.get()->MutableDataU(), res_i420_buffer.get()->StrideU(), + res_i420_buffer.get()->MutableDataV(), res_i420_buffer.get()->StrideV(), + width_, height_); + + EXPECT_EQ(0, ret); if (PrintVideoFrame(*res_i420_buffer, output_file) < 0) { return; @@ -239,33 +285,6 @@ TEST_F(TestLibYuv, ConvertAlignedFrame) { EXPECT_EQ(48.0, psnr); } -TEST_F(TestLibYuv, RotateTest) { - // Use ConvertToI420 for multiple rotations - see that nothing breaks, all - // memory is properly allocated and end result is equal to the starting point. - int rotated_width = height_; - int rotated_height = width_; - int stride_y; - int stride_uv; - - // Assume compact layout, no padding. - const uint8_t* orig_buffer = - orig_frame_->video_frame_buffer()->GetI420()->DataY(); - - Calc16ByteAlignedStride(rotated_width, &stride_y, &stride_uv); - rtc::scoped_refptr rotated_res_i420_buffer = I420Buffer::Create( - rotated_width, rotated_height, stride_y, stride_uv, stride_uv); - EXPECT_EQ( - 0, ConvertToI420(VideoType::kI420, orig_buffer, 0, 0, width_, height_, 0, - kVideoRotation_90, rotated_res_i420_buffer.get())); - EXPECT_EQ( - 0, ConvertToI420(VideoType::kI420, orig_buffer, 0, 0, width_, height_, 0, - kVideoRotation_270, rotated_res_i420_buffer.get())); - rotated_res_i420_buffer = I420Buffer::Create(width_, height_); - EXPECT_EQ( - 0, ConvertToI420(VideoType::kI420, orig_buffer, 0, 0, width_, height_, 0, - kVideoRotation_180, rotated_res_i420_buffer.get())); -} - static uint8_t Average(int a, int b, int c, int d) { return (a + b + c + d + 2) / 4; } diff --git a/common_video/libyuv/webrtc_libyuv.cc b/common_video/libyuv/webrtc_libyuv.cc index 4cb8686780..4384b85710 100644 --- a/common_video/libyuv/webrtc_libyuv.cc +++ b/common_video/libyuv/webrtc_libyuv.cc @@ -13,7 +13,6 @@ #include #include "rtc_base/checks.h" -// TODO(nisse): Only needed for the deprecated ConvertToI420. #include "api/video/i420_buffer.h" // NOTE(ajm): Path provided by gn. @@ -155,21 +154,6 @@ int ConvertRGB24ToARGB(const uint8_t* src_frame, uint8_t* dst_frame, width, height); } -libyuv::RotationMode ConvertRotationMode(VideoRotation rotation) { - switch (rotation) { - case kVideoRotation_0: - return libyuv::kRotate0; - case kVideoRotation_90: - return libyuv::kRotate90; - case kVideoRotation_180: - return libyuv::kRotate180; - case kVideoRotation_270: - return libyuv::kRotate270; - } - RTC_NOTREACHED(); - return libyuv::kRotate0; -} - int ConvertVideoType(VideoType video_type) { switch (video_type) { case VideoType::kUnknown: @@ -208,35 +192,6 @@ int ConvertVideoType(VideoType video_type) { return libyuv::FOURCC_ANY; } -// TODO(nisse): Delete this wrapper, let callers use libyuv directly. -int ConvertToI420(VideoType src_video_type, - const uint8_t* src_frame, - int crop_x, - int crop_y, - int src_width, - int src_height, - size_t sample_size, - VideoRotation rotation, - I420Buffer* dst_buffer) { - int dst_width = dst_buffer->width(); - int dst_height = dst_buffer->height(); - // LibYuv expects pre-rotation values for dst. - // Stride values should correspond to the destination values. - if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) { - std::swap(dst_width, dst_height); - } - return libyuv::ConvertToI420( - src_frame, sample_size, - dst_buffer->MutableDataY(), dst_buffer->StrideY(), - dst_buffer->MutableDataU(), dst_buffer->StrideU(), - dst_buffer->MutableDataV(), dst_buffer->StrideV(), - crop_x, crop_y, - src_width, src_height, - dst_width, dst_height, - ConvertRotationMode(rotation), - ConvertVideoType(src_video_type)); -} - int ConvertFromI420(const VideoFrame& src_frame, VideoType dst_video_type, int dst_sample_size, diff --git a/modules/video_capture/video_capture_impl.cc b/modules/video_capture/video_capture_impl.cc index 6a24f801e2..1eb77ee7c1 100644 --- a/modules/video_capture/video_capture_impl.cc +++ b/modules/video_capture/video_capture_impl.cc @@ -14,6 +14,7 @@ #include "api/video/i420_buffer.h" #include "common_video/libyuv/include/webrtc_libyuv.h" +#include "libyuv.h" // NOLINT #include "modules/include/module_common_types.h" #include "modules/video_capture/video_capture_config.h" #include "rtc_base/logging.h" @@ -164,10 +165,32 @@ int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame, // TODO(nisse): Use a pool? rtc::scoped_refptr buffer = I420Buffer::Create( target_width, abs(target_height), stride_y, stride_uv, stride_uv); - const int conversionResult = ConvertToI420( - frameInfo.videoType, videoFrame, 0, 0, // No cropping - width, height, videoFrameLength, - apply_rotation ? _rotateFrame : kVideoRotation_0, buffer.get()); + + libyuv::RotationMode rotation_mode = libyuv::kRotate0; + if (apply_rotation) { + switch (_rotateFrame) { + case kVideoRotation_0: + rotation_mode = libyuv::kRotate0; + break; + case kVideoRotation_90: + rotation_mode = libyuv::kRotate90; + break; + case kVideoRotation_180: + rotation_mode = libyuv::kRotate180; + break; + case kVideoRotation_270: + rotation_mode = libyuv::kRotate270; + break; + } + } + + const int conversionResult = libyuv::ConvertToI420( + videoFrame, videoFrameLength, buffer.get()->MutableDataY(), + buffer.get()->StrideY(), buffer.get()->MutableDataU(), + buffer.get()->StrideU(), buffer.get()->MutableDataV(), + buffer.get()->StrideV(), 0, 0, // No Cropping + width, height, target_width, target_height, rotation_mode, + ConvertVideoType(frameInfo.videoType)); if (conversionResult < 0) { RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type " << static_cast(frameInfo.videoType) << "to I420."; diff --git a/modules/video_coding/codecs/i420/i420.cc b/modules/video_coding/codecs/i420/i420.cc index 2749d0e297..8c8c3f0a35 100644 --- a/modules/video_coding/codecs/i420/i420.cc +++ b/modules/video_coding/codecs/i420/i420.cc @@ -15,6 +15,7 @@ #include "api/video/i420_buffer.h" #include "common_video/libyuv/include/webrtc_libyuv.h" +#include "libyuv.h" // NOLINT namespace { const size_t kI420HeaderSize = 4; @@ -204,8 +205,16 @@ int I420Decoder::Decode(const EncodedImage& inputImage, I420Buffer::Create(_width, _height); // Converting from raw buffer I420Buffer. - int ret = ConvertToI420(VideoType::kI420, buffer, 0, 0, _width, _height, 0, - kVideoRotation_0, frame_buffer.get()); + int y_stride = 16 * ((_width + 15) / 16); + int uv_stride = 16 * ((_width + 31) / 32); + int y_size = y_stride * height; + int u_size = uv_stride * frame_buffer->ChromaHeight(); + int ret = libyuv::I420Copy( + buffer, y_stride, buffer + y_size, uv_stride, buffer + y_size + u_size, + uv_stride, frame_buffer.get()->MutableDataY(), + frame_buffer.get()->StrideY(), frame_buffer.get()->MutableDataU(), + frame_buffer.get()->StrideU(), frame_buffer.get()->MutableDataV(), + frame_buffer.get()->StrideV(), _width, _height); if (ret < 0) { return WEBRTC_VIDEO_CODEC_MEMORY; }