public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Jan Grulich <jgrulich@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/firefox] f45: WebRTC backport: video_capture - implement buffer stride support for PipeWire
Date: Tue, 18 Aug 2026 13:30:43 GMT [thread overview]
Message-ID: <178705984383.1.18373842319191627553.rpms-firefox-7dcfa2b5fe27@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/firefox
Branch : f45
Commit : 7dcfa2b5fe27a5262f4b64f70d069020aff20df4
Author : Jan Grulich <jgrulich@redhat.com>
Date : 2026-08-18T15:28:08+02:00
Stats : +706/-1 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/firefox/c/7dcfa2b5fe27a5262f4b64f70d069020aff20df4?branch=f45
Log:
WebRTC backport: video_capture - implement buffer stride support for PipeWire
---
diff --git a/firefox.spec b/firefox.spec
index a03cb32..d94031d 100644
--- a/firefox.spec
+++ b/firefox.spec
@@ -186,7 +186,7 @@ ExcludeArch: i686
Summary: Mozilla Firefox Web browser
Name: firefox
Version: 154.0
-Release: 2%{?pre_tag}%{?dist}
+Release: 3%{?pre_tag}%{?dist}
URL: https://www.mozilla.org/firefox/
# Automatically converted from old format: MPLv1.1 or GPLv2+ or LGPLv2+ - review is highly recommended.
License: LicenseRef-Callaway-MPLv1.1 OR GPL-2.0-or-later OR LicenseRef-Callaway-LGPLv2+
@@ -256,6 +256,10 @@ Patch242: 0026-Add-KDE-integration-to-Firefox.patch
Patch400: mozilla-1196777.patch
Patch401: mozilla-1667096.patch
+# https://phabricator.services.mozilla.com/D312871
+# Drop with Firefox 156
+Patch410: libwebrtc-video-capture-implement-buffer-stride-support-for-pipewire.patch
+
# PGO/LTO patches
Patch600: pgo.patch
Patch602: mozilla-1516803.patch
@@ -539,6 +543,8 @@ cat %{SOURCE49} | sed -e "s|LIBCLANG_RT_PLACEHOLDER|`pwd`/wasi-sdk-30/build/sysr
%patch -P400 -p1 -b .1196777
%patch -P401 -p1 -b .1667096
+%patch -P410 -p1 -b .libwebrtc-video-capture-implement-buffer-stride-support-for-pipewire
+
# PGO patches
%if %{build_with_pgo}
%if !%{with build_with_clang}
@@ -1205,6 +1211,9 @@ fi
#---------------------------------------------------------------------
%changelog
+* Tue Aug 18 2026 Jan Grulich <jgrulich@redhat.com> - 154.0-3
+- WebRTC backport: video_capture - implement buffer stride support for PipeWire
+
* Mon Aug 17 2026 Martin Stransky <stransky@redhat.com> - 154.0-2
- Disabled session restore on Fedora 45+ dues to crashes.
diff --git a/libwebrtc-video-capture-implement-buffer-stride-support-for-pipewire.patch b/libwebrtc-video-capture-implement-buffer-stride-support-for-pipewire.patch
new file mode 100644
index 0000000..bcf8a1b
--- /dev/null
+++ b/libwebrtc-video-capture-implement-buffer-stride-support-for-pipewire.patch
@@ -0,0 +1,696 @@
+From 958cc9da5f101c1b4118cf6013e5a9ca15925019 Mon Sep 17 00:00:00 2001
+From: Robert Mader <robert.mader@collabora.com>
+Date: Mon, 17 Aug 2026 13:43:10 +0000
+Subject: [PATCH] Bug 2056029 - WebRTC backport: video_capture: Implement
+ buffer stride support for Pipewire r=pehrsons,grulja
+
+Simple backport of https://webrtc-review.googlesource.com/c/src/+/491680
+
+Upstream commit: 5da5a6e4e00c69ef60c7643e99ef34f65f7af51a
+
+From the commit message:
+Add optional buffer stride support to VideoCaptureImpl and use it in
+VideoCaptureModulePipeWire, allowing us to drop the requirement for
+unpadded buffers. This notably improves compatibility with libcamera,
+which supports a wider range of devices than the V4L2 backend - some
+having hardware requirements for stride-alignment.
+
+Extending `ConvertToI420()` in libyuv was rejected on the grounds that
+there are various approaches to stride handling - using a single stride
+value for multiple planes, having explicit values for each one etc. - as
+well as the fact that we might well want to extend the helper further in
+the future - e.g. in order to better handle color spaces such as BT709
+(instead of always assuming BT601).
+
+Thus this patch now adds a local copy of `ConvertToI420()`, supporting
+implicit and a single explicit stride value for now.
+
+Differential Revision: https://phabricator.services.mozilla.com/D312871
+---
+ third_party/libwebrtc/common_video/BUILD.gn | 4 +
+ .../common_video/common_video_gn/moz.build | 1 +
+ .../libyuv/include/webrtc_libyuv.h | 16 +
+ .../common_video/libyuv/libyuv_unittest.cc | 30 +-
+ .../common_video/libyuv/webrtc_libyuv.cc | 320 ++++++++++++++++++
+ .../linux/video_capture_pipewire.cc | 54 +--
+ .../modules/video_capture/video_capture.h | 3 +
+ .../video_capture/video_capture_impl.cc | 18 +-
+ .../video_capture/video_capture_impl.h | 6 +
+ .../5da5a6e4e0.no-op-cherry-pick-msg | 1 +
+ 10 files changed, 393 insertions(+), 60 deletions(-)
+ create mode 100644 third_party/libwebrtc/moz-patch-stack/5da5a6e4e0.no-op-cherry-pick-msg
+
+diff --git a/third_party/libwebrtc/common_video/BUILD.gn b/third_party/libwebrtc/common_video/BUILD.gn
+index 868d09a2eb1ea..eb3cc07f948ad 100644
+--- a/third_party/libwebrtc/common_video/BUILD.gn
++++ b/third_party/libwebrtc/common_video/BUILD.gn
+@@ -97,6 +97,10 @@ rtc_library("common_video") {
+ "../rtc_base/containers:flat_map",
+ ]
+ }
++
++ if (!is_ios) {
++ defines = [ "HAVE_LIBYUV_JPEG" ]
++ }
+ }
+
+ rtc_source_set("frame_counts") {
+diff --git a/third_party/libwebrtc/common_video/common_video_gn/moz.build b/third_party/libwebrtc/common_video/common_video_gn/moz.build
+index 731a68d3323c3..16645ce2d192c 100644
+--- a/third_party/libwebrtc/common_video/common_video_gn/moz.build
++++ b/third_party/libwebrtc/common_video/common_video_gn/moz.build
+@@ -20,6 +20,7 @@ DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+ DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+ DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+ DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0"
++DEFINES["HAVE_LIBYUV_JPEG"] = True
+
+ FINAL_LIBRARY = "xul"
+
+diff --git a/third_party/libwebrtc/common_video/libyuv/include/webrtc_libyuv.h b/third_party/libwebrtc/common_video/libyuv/include/webrtc_libyuv.h
+index b18957b6be686..a1899dba88b1c 100644
+--- a/third_party/libwebrtc/common_video/libyuv/include/webrtc_libyuv.h
++++ b/third_party/libwebrtc/common_video/libyuv/include/webrtc_libyuv.h
+@@ -84,6 +84,22 @@ int ConvertFromI420(const VideoFrame& src_frame,
+ int dst_sample_size,
+ uint8_t* dst_frame);
+
++int ConvertToI420(const uint8_t* sample,
++ size_t sample_size,
++ uint8_t* dst_y,
++ int dst_stride_y,
++ uint8_t* dst_u,
++ int dst_stride_u,
++ uint8_t* dst_v,
++ int dst_stride_v,
++ int src_width,
++ int src_height,
++ int src_stride,
++ int dst_width,
++ int dst_height,
++ uint32_t rotation,
++ uint32_t fourcc);
++
+ scoped_refptr<I420BufferInterface> ScaleVideoFrameBuffer(
+ const I420BufferInterface& source,
+ int dst_width,
+diff --git a/third_party/libwebrtc/common_video/libyuv/libyuv_unittest.cc b/third_party/libwebrtc/common_video/libyuv/libyuv_unittest.cc
+index 6b47ec65ea01e..448ace8dfcea1 100644
+--- a/third_party/libwebrtc/common_video/libyuv/libyuv_unittest.cc
++++ b/third_party/libwebrtc/common_video/libyuv/libyuv_unittest.cc
+@@ -172,12 +172,12 @@ TEST_F(TestLibYuv, ConvertTest) {
+ EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB24, 0,
+ res_rgb_buffer2.get()));
+
+- ret = libyuv::ConvertToI420(
++ ret = ConvertToI420(
+ res_rgb_buffer2.get(), 0, res_i420_buffer->MutableDataY(),
+ res_i420_buffer->StrideY(), res_i420_buffer->MutableDataU(),
+ res_i420_buffer->StrideU(), res_i420_buffer->MutableDataV(),
+- res_i420_buffer->StrideV(), 0, 0, width_, height_,
+- res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
++ res_i420_buffer->StrideV(), width_, height_, 0, res_i420_buffer->width(),
++ res_i420_buffer->height(), static_cast<uint32_t>(libyuv::kRotate0),
+ ConvertVideoType(VideoType::kRGB24));
+
+ EXPECT_EQ(0, ret);
+@@ -196,12 +196,12 @@ TEST_F(TestLibYuv, ConvertTest) {
+ EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kUYVY, 0,
+ out_uyvy_buffer.get()));
+
+- ret = libyuv::ConvertToI420(
++ ret = ConvertToI420(
+ out_uyvy_buffer.get(), 0, res_i420_buffer->MutableDataY(),
+ res_i420_buffer->StrideY(), res_i420_buffer->MutableDataU(),
+ res_i420_buffer->StrideU(), res_i420_buffer->MutableDataV(),
+- res_i420_buffer->StrideV(), 0, 0, width_, height_,
+- res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
++ res_i420_buffer->StrideV(), width_, height_, 0, res_i420_buffer->width(),
++ res_i420_buffer->height(), static_cast<uint32_t>(libyuv::kRotate0),
+ ConvertVideoType(VideoType::kUYVY));
+
+ EXPECT_EQ(0, ret);
+@@ -218,12 +218,12 @@ TEST_F(TestLibYuv, ConvertTest) {
+ EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kYUY2, 0,
+ out_yuy2_buffer.get()));
+
+- ret = libyuv::ConvertToI420(
++ ret = ConvertToI420(
+ out_yuy2_buffer.get(), 0, res_i420_buffer->MutableDataY(),
+ res_i420_buffer->StrideY(), res_i420_buffer->MutableDataU(),
+ res_i420_buffer->StrideU(), res_i420_buffer->MutableDataV(),
+- res_i420_buffer->StrideV(), 0, 0, width_, height_,
+- res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
++ res_i420_buffer->StrideV(), width_, height_, 0, res_i420_buffer->width(),
++ res_i420_buffer->height(), static_cast<uint32_t>(libyuv::kRotate0),
+ ConvertVideoType(VideoType::kYUY2));
+
+ EXPECT_EQ(0, ret);
+@@ -242,12 +242,12 @@ TEST_F(TestLibYuv, ConvertTest) {
+ EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kRGB565, 0,
+ out_rgb565_buffer.get()));
+
+- ret = libyuv::ConvertToI420(
++ ret = ConvertToI420(
+ out_rgb565_buffer.get(), 0, res_i420_buffer->MutableDataY(),
+ res_i420_buffer->StrideY(), res_i420_buffer->MutableDataU(),
+ res_i420_buffer->StrideU(), res_i420_buffer->MutableDataV(),
+- res_i420_buffer->StrideV(), 0, 0, width_, height_,
+- res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
++ res_i420_buffer->StrideV(), width_, height_, 0, res_i420_buffer->width(),
++ res_i420_buffer->height(), static_cast<uint32_t>(libyuv::kRotate0),
+ ConvertVideoType(VideoType::kRGB565));
+
+ EXPECT_EQ(0, ret);
+@@ -269,12 +269,12 @@ TEST_F(TestLibYuv, ConvertTest) {
+ EXPECT_EQ(0, ConvertFromI420(*orig_frame_, VideoType::kARGB, 0,
+ out_argb8888_buffer.get()));
+
+- ret = libyuv::ConvertToI420(
++ ret = ConvertToI420(
+ out_argb8888_buffer.get(), 0, res_i420_buffer->MutableDataY(),
+ res_i420_buffer->StrideY(), res_i420_buffer->MutableDataU(),
+ res_i420_buffer->StrideU(), res_i420_buffer->MutableDataV(),
+- res_i420_buffer->StrideV(), 0, 0, width_, height_,
+- res_i420_buffer->width(), res_i420_buffer->height(), libyuv::kRotate0,
++ res_i420_buffer->StrideV(), width_, height_, 0, res_i420_buffer->width(),
++ res_i420_buffer->height(), static_cast<uint32_t>(libyuv::kRotate0),
+ ConvertVideoType(VideoType::kARGB));
+
+ EXPECT_EQ(0, ret);
+diff --git a/third_party/libwebrtc/common_video/libyuv/webrtc_libyuv.cc b/third_party/libwebrtc/common_video/libyuv/webrtc_libyuv.cc
+index 9dd12b42f223f..8527f34163b4e 100644
+--- a/third_party/libwebrtc/common_video/libyuv/webrtc_libyuv.cc
++++ b/third_party/libwebrtc/common_video/libyuv/webrtc_libyuv.cc
+@@ -10,8 +10,10 @@
+
+ #include "common_video/libyuv/include/webrtc_libyuv.h"
+
++#include <climits>
+ #include <cstddef>
+ #include <cstdint>
++#include <cstdlib>
+
+ #include "api/scoped_refptr.h"
+ #include "api/video/i420_buffer.h"
+@@ -23,6 +25,7 @@
+ #include "third_party/libyuv/include/libyuv/convert.h"
+ #include "third_party/libyuv/include/libyuv/convert_from.h"
+ #include "third_party/libyuv/include/libyuv/planar_functions.h"
++#include "third_party/libyuv/include/libyuv/rotate.h"
+ #include "third_party/libyuv/include/libyuv/scale.h"
+ #include "third_party/libyuv/include/libyuv/video_common.h"
+
+@@ -146,6 +149,323 @@ int ConvertFromI420(const VideoFrame& src_frame,
+ ConvertVideoType(dst_video_type));
+ }
+
++int ConvertToI420(const uint8_t* sample,
++ size_t sample_size,
++ uint8_t* dst_y,
++ int dst_stride_y,
++ uint8_t* dst_u,
++ int dst_stride_u,
++ uint8_t* dst_v,
++ int dst_stride_v,
++ int src_width,
++ int src_height,
++ int src_stride,
++ int dst_width,
++ int dst_height,
++ uint32_t rotation,
++ uint32_t fourcc) {
++ if (src_height == INT_MIN || dst_height == INT_MIN) {
++ return -1;
++ }
++
++ const int abs_src_height = (src_height < 0) ? -src_height : src_height;
++ const int abs_dst_height = (dst_height < 0) ? -dst_height : dst_height;
++
++ if (!dst_y || !dst_u || !dst_v || !sample || src_width <= 0 ||
++ src_width > INT_MAX / 4 || dst_width <= 0 || src_height == 0 ||
++ dst_height == 0 || dst_width > src_width ||
++ abs_dst_height > abs_src_height) {
++ return -1;
++ }
++
++ uint32_t format = libyuv::CanonicalFourCC(fourcc);
++
++ // Calculate the unpadded stride if no explicit value is set.
++ if (src_stride == 0) {
++ switch (format) {
++ case libyuv::FOURCC_YUY2:
++ case libyuv::FOURCC_UYVY:
++ case libyuv::FOURCC_RGBP:
++ case libyuv::FOURCC_RGBO:
++ case libyuv::FOURCC_R444:
++ src_stride = src_width * 2;
++ break;
++ case libyuv::FOURCC_24BG:
++ case libyuv::FOURCC_RAW:
++ src_stride = src_width * 3;
++ break;
++ case libyuv::FOURCC_ARGB:
++ case libyuv::FOURCC_BGRA:
++ case libyuv::FOURCC_ABGR:
++ case libyuv::FOURCC_RGBA:
++ src_stride = src_width * 4;
++ break;
++ case libyuv::FOURCC_I400:
++ case libyuv::FOURCC_NV12:
++ case libyuv::FOURCC_NV21:
++ case libyuv::FOURCC_I420:
++ case libyuv::FOURCC_YV12:
++ case libyuv::FOURCC_I422:
++ case libyuv::FOURCC_YV16:
++ // Follow the V4L2 definition for strides of subsampled formats:
++ // > To avoid ambiguities drivers must return a bytesperline value
++ // > rounded up to a multiple of the scale factor.
++ // https://www.kernel.org/doc/html/v7.1/userspace-api/media/v4l/pixfmt-v4l2.html
++ src_stride = (src_width + 1) & ~1;
++ break;
++ case libyuv::FOURCC_I444:
++ case libyuv::FOURCC_YV24:
++ src_stride = src_width;
++ break;
++ case libyuv::FOURCC_MJPG:
++ break;
++ default:
++ return -1;
++ }
++ } else {
++ switch (format) {
++ case libyuv::FOURCC_YUY2:
++ case libyuv::FOURCC_UYVY:
++ case libyuv::FOURCC_I400:
++ case libyuv::FOURCC_NV12:
++ case libyuv::FOURCC_NV21:
++ case libyuv::FOURCC_I420:
++ case libyuv::FOURCC_YV12:
++ case libyuv::FOURCC_I422:
++ case libyuv::FOURCC_YV16:
++ // Follow the V4L2 definition for strides of subsampled formats:
++ // > To avoid ambiguities drivers must return a bytesperline value
++ // > rounded up to a multiple of the scale factor.
++ // https://www.kernel.org/doc/html/v7.1/userspace-api/media/v4l/pixfmt-v4l2.html
++ if ((src_stride % 2) != 0)
++ return -1;
++ break;
++ default:
++ break;
++ }
++ }
++
++ int r = 0;
++ const bool need_buf =
++ (rotation && format != libyuv::FOURCC_I420 &&
++ format != libyuv::FOURCC_NV12 && format != libyuv::FOURCC_NV21 &&
++ format != libyuv::FOURCC_YV12) ||
++ dst_y == sample;
++ const int inv_dst_height =
++ (src_height < 0) ? -abs_dst_height : abs_dst_height;
++ uint8_t* rotate_buffer = NULL;
++ uint8_t* tmp_y;
++ uint8_t* tmp_u;
++ uint8_t* tmp_v;
++ int tmp_y_stride;
++ int tmp_u_stride;
++ int tmp_v_stride;
++
++ // One pass rotation is available for some formats. For the rest, convert
++ // to I420 (with optional vertical flipping) into a temporary I420 buffer,
++ // and then rotate the I420 to the final destination buffer.
++ // For in-place conversion, if destination dst_y is same as source sample,
++ // also enable temporary buffer.
++ if (need_buf) {
++ size_t y_size = (size_t)dst_width * abs_dst_height;
++ size_t uv_size = (size_t)((dst_width + 1) / 2) * ((abs_dst_height + 1) / 2);
++ if (uv_size > SIZE_MAX / 2 || y_size > SIZE_MAX - uv_size * 2) {
++ return -1; // Invalid size.
++ }
++ const size_t rotate_buffer_size = y_size + uv_size * 2;
++ rotate_buffer = new uint8_t[rotate_buffer_size];
++ if (!rotate_buffer) {
++ return 1; // Out of memory runtime error.
++ }
++ tmp_y = dst_y;
++ tmp_u = dst_u;
++ tmp_v = dst_v;
++ tmp_y_stride = dst_stride_y;
++ tmp_u_stride = dst_stride_u;
++ tmp_v_stride = dst_stride_v;
++ dst_y = rotate_buffer;
++ dst_u = dst_y + y_size;
++ dst_v = dst_u + uv_size;
++ dst_stride_y = dst_width;
++ dst_stride_u = dst_stride_v = ((dst_width + 1) / 2);
++ }
++
++ switch (format) {
++ // Single plane formats
++ case libyuv::FOURCC_YUY2: {
++ r = libyuv::YUY2ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ }
++ case libyuv::FOURCC_UYVY: {
++ r = libyuv::UYVYToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ }
++ case libyuv::FOURCC_RGBP:
++ r = libyuv::RGB565ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_RGBO:
++ r = libyuv::ARGB1555ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_R444:
++ r = libyuv::ARGB4444ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_24BG:
++ r = libyuv::RGB24ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_RAW:
++ r = libyuv::RAWToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_ARGB:
++ r = libyuv::ARGBToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_BGRA:
++ r = libyuv::BGRAToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_ABGR:
++ r = libyuv::ABGRToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_RGBA:
++ r = libyuv::RGBAToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ case libyuv::FOURCC_I400:
++ r = libyuv::I400ToI420(sample, src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ // Biplanar formats
++ case libyuv::FOURCC_NV12: {
++ const uint8_t* src_y = sample;
++ const uint8_t* src_uv = src_y + ((ptrdiff_t)src_stride * abs_src_height);
++ r = libyuv::NV12ToI420Rotate(src_y, src_stride, src_uv, src_stride, dst_y,
++ dst_stride_y, dst_u, dst_stride_u, dst_v,
++ dst_stride_v, dst_width, inv_dst_height,
++ (libyuv::RotationMode)rotation);
++ break;
++ }
++ case libyuv::FOURCC_NV21: {
++ const uint8_t* src_y = sample;
++ const uint8_t* src_uv = src_y + ((ptrdiff_t)src_stride * abs_src_height);
++ // Call NV12 but with dst_u and dst_v parameters swapped.
++ r = libyuv::NV12ToI420Rotate(src_y, src_stride, src_uv, src_stride, dst_y,
++ dst_stride_y, dst_v, dst_stride_v, dst_u,
++ dst_stride_u, dst_width, inv_dst_height,
++ (libyuv::RotationMode)rotation);
++ break;
++ }
++ // Triplanar formats
++ case libyuv::FOURCC_I420:
++ case libyuv::FOURCC_YV12: {
++ const uint8_t* src_y = sample;
++ const uint8_t* src_u;
++ const uint8_t* src_v;
++ // Follow the V4L2 definition:
++ // > When the image format is planar the bytesperline value applies to the
++ // > first plane and is divided by the same factor as the width field for
++ // > the other planes.
++ // https://www.kernel.org/doc/html/v7.1/userspace-api/media/v4l/pixfmt-v4l2.html
++ int halfstride = src_stride / 2;
++ int halfheight = (abs_src_height + 1) / 2;
++ if (format == libyuv::FOURCC_YV12) {
++ src_v = src_y + (ptrdiff_t)src_stride * abs_src_height;
++ src_u = src_v + halfstride * (ptrdiff_t)halfheight;
++ } else {
++ src_u = src_y + (ptrdiff_t)src_stride * abs_src_height;
++ src_v = src_u + halfstride * (ptrdiff_t)halfheight;
++ }
++ r = libyuv::I420Rotate(src_y, src_stride, src_u, halfstride, src_v,
++ halfstride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height, (libyuv::RotationMode)rotation);
++ break;
++ }
++ case libyuv::FOURCC_I422:
++ case libyuv::FOURCC_YV16: {
++ const uint8_t* src_y = sample;
++ const uint8_t* src_u;
++ const uint8_t* src_v;
++ // Follow the V4L2 definition:
++ // > When the image format is planar the bytesperline value applies to the
++ // > first plane and is divided by the same factor as the width field for
++ // > the other planes.
++ // https://www.kernel.org/doc/html/v7.1/userspace-api/media/v4l/pixfmt-v4l2.html
++ int halfstride = src_stride / 2;
++ if (format == libyuv::FOURCC_YV16) {
++ src_v = src_y + (ptrdiff_t)src_stride * abs_src_height;
++ src_u = src_v + halfstride * (ptrdiff_t)abs_src_height;
++ } else {
++ src_u = src_y + (ptrdiff_t)src_stride * abs_src_height;
++ src_v = src_u + halfstride * (ptrdiff_t)abs_src_height;
++ }
++ r = libyuv::I422ToI420(src_y, src_stride, src_u, halfstride, src_v,
++ halfstride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ }
++ case libyuv::FOURCC_I444:
++ case libyuv::FOURCC_YV24: {
++ const uint8_t* src_y = sample;
++ const uint8_t* src_u;
++ const uint8_t* src_v;
++ if (format == libyuv::FOURCC_YV24) {
++ src_v = src_y + src_stride * (ptrdiff_t)abs_src_height;
++ src_u = src_v + src_stride * (ptrdiff_t)abs_src_height;
++ } else {
++ src_u = src_y + src_stride * (ptrdiff_t)abs_src_height;
++ src_v = src_u + src_stride * (ptrdiff_t)abs_src_height;
++ }
++ r = libyuv::I444ToI420(src_y, src_stride, src_u, src_stride, src_v,
++ src_stride, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, dst_width,
++ inv_dst_height);
++ break;
++ }
++#ifdef HAVE_LIBYUV_JPEG
++ case libyuv::FOURCC_MJPG:
++ r = libyuv::MJPGToI420(sample, sample_size, dst_y, dst_stride_y, dst_u,
++ dst_stride_u, dst_v, dst_stride_v, src_width,
++ abs_src_height, dst_width, inv_dst_height);
++ break;
++#endif
++ default:
++ r = -1; // unknown fourcc - return failure code.
++ }
++
++ if (need_buf) {
++ if (!r) {
++ r = libyuv::I420Rotate(dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v,
++ dst_stride_v, tmp_y, tmp_y_stride, tmp_u,
++ tmp_u_stride, tmp_v, tmp_v_stride, dst_width,
++ abs_dst_height, (libyuv::RotationMode)rotation);
++ }
++ free(rotate_buffer);
++ }
++
++ return r;
++}
++
+ scoped_refptr<I420ABufferInterface> ScaleI420ABuffer(
+ const I420ABufferInterface& buffer,
+ int target_width,
+diff --git a/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc b/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
+index 8118f5e2b2a67..5ca96108e9dd7 100644
+--- a/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
++++ b/third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
+@@ -363,36 +363,6 @@ void VideoCaptureModulePipeWire::OnFormatChanged(const struct spa_pod* format) {
+ spa_pod_builder_push_object(&builder, &frame, SPA_TYPE_OBJECT_ParamBuffers,
+ SPA_PARAM_Buffers);
+
+- if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
+- // Enforce stride without padding.
+- size_t stride;
+- switch (configured_capability_.videoType) {
+- case VideoType::kI420:
+- case VideoType::kNV12:
+- stride = configured_capability_.width;
+- break;
+- case VideoType::kYUY2:
+- case VideoType::kUYVY:
+- case VideoType::kRGB565:
+- stride = configured_capability_.width * 2;
+- break;
+- case VideoType::kRGB24:
+- case VideoType::kBGR24:
+- stride = configured_capability_.width * 3;
+- break;
+- case VideoType::kARGB:
+- case VideoType::kABGR:
+- case VideoType::kBGRA:
+- stride = configured_capability_.width * 4;
+- break;
+- default:
+- RTC_LOG(LS_ERROR) << "Unsupported video format.";
+- return;
+- }
+- spa_pod_builder_add(&builder, SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride),
+- 0);
+- }
+-
+ const int buffer_types =
+ (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
+ spa_pod_builder_add(
+@@ -471,17 +441,6 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
+ h = static_cast<struct spa_meta_header*>(
+ spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h)));
+
+- struct spa_meta_videotransform* videotransform;
+- videotransform =
+- static_cast<struct spa_meta_videotransform*>(spa_buffer_find_meta_data(
+- spaBuffer, SPA_META_VideoTransform, sizeof(*videotransform)));
+- if (videotransform) {
+- VideoRotation rotation =
+- VideorotationFromPipeWireTransform(videotransform->transform);
+- SetCaptureRotation(rotation);
+- SetApplyRotation(rotation != kVideoRotation_0);
+- }
+-
+ if (h->flags & SPA_META_HEADER_FLAG_CORRUPTED) {
+ RTC_LOG(LS_INFO) << "Dropping corruped frame.";
+ pw_stream_queue_buffer(stream_, buffer);
+@@ -496,6 +455,19 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
+ continue;
+ }
+
++ SetStride(spaBuffer->datas[0].chunk->stride);
++
++ struct spa_meta_videotransform* videotransform;
++ videotransform =
++ static_cast<struct spa_meta_videotransform*>(spa_buffer_find_meta_data(
++ spaBuffer, SPA_META_VideoTransform, sizeof(*videotransform)));
++ if (videotransform) {
++ VideoRotation rotation =
++ VideorotationFromPipeWireTransform(videotransform->transform);
++ SetCaptureRotation(rotation);
++ SetApplyRotation(rotation != kVideoRotation_0);
++ }
++
+ if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf ||
+ spaBuffer->datas[0].type == SPA_DATA_MemFd) {
+ ScopedBuf frame;
+diff --git a/third_party/libwebrtc/modules/video_capture/video_capture.h b/third_party/libwebrtc/modules/video_capture/video_capture.h
+index d88b63466eb28..563f00c3da462 100644
+--- a/third_party/libwebrtc/modules/video_capture/video_capture.h
++++ b/third_party/libwebrtc/modules/video_capture/video_capture.h
+@@ -155,6 +155,9 @@ class VideoCaptureModule : public RefCountInterface {
+ // Return whether the rotation is applied or left pending.
+ virtual bool GetApplyRotation() = 0;
+
++ virtual void SetStride(int32_t stride) {};
++ virtual int32_t GetStride() { return 0; };
++
+ // Mozilla: TrackingId setter for use in profiler markers.
+ virtual void SetTrackingId(uint32_t aTrackingIdProcId) {}
+
+diff --git a/third_party/libwebrtc/modules/video_capture/video_capture_impl.cc b/third_party/libwebrtc/modules/video_capture/video_capture_impl.cc
+index f4f9827ef55ad..ed83ab6e57c48 100644
+--- a/third_party/libwebrtc/modules/video_capture/video_capture_impl.cc
++++ b/third_party/libwebrtc/modules/video_capture/video_capture_impl.cc
+@@ -31,7 +31,6 @@
+ #include "rtc_base/time_utils.h"
+ #include "rtc_base/trace_event.h"
+ #include "system_wrappers/include/clock.h"
+-#include "third_party/libyuv/include/libyuv/convert.h"
+ #include "third_party/libyuv/include/libyuv/rotate.h"
+
+ namespace webrtc {
+@@ -94,6 +93,7 @@ VideoCaptureImpl::VideoCaptureImpl(Clock* clock)
+ _lastProcessFrameTimeNanos(clock->TimeInMicroseconds() * 1000),
+ _rotateFrame(kVideoRotation_0),
+ apply_rotation_(false),
++ stride_(0),
+ clock_(clock) {
+ _requestedCapability.width = kDefaultWidth;
+ _requestedCapability.height = kDefaultHeight;
+@@ -230,11 +230,11 @@ int32_t VideoCaptureImpl::IncomingFrame(uint8_t* videoFrame,
+ std::swap(dst_width, dst_height);
+ }
+
+- const int conversionResult = libyuv::ConvertToI420(
++ const int conversionResult = ConvertToI420(
+ videoFrame, videoFrameLength, buffer->MutableDataY(), buffer->StrideY(),
+ buffer->MutableDataU(), buffer->StrideU(), buffer->MutableDataV(),
+- buffer->StrideV(), 0, 0, // No Cropping
+- width, height, dst_width, dst_height, rotation_mode,
++ buffer->StrideV(), width, height, stride_, dst_width, dst_height,
++ static_cast<uint32_t>(rotation_mode),
+ ConvertVideoType(frameInfo.videoType));
+ if (conversionResult != 0) {
+ RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
+@@ -300,6 +300,16 @@ bool VideoCaptureImpl::GetApplyRotation() {
+ return apply_rotation_;
+ }
+
++void VideoCaptureImpl::SetStride(int32_t stride) {
++ MutexLock lock(&api_lock_);
++ stride_ = stride;
++}
++
++int32_t VideoCaptureImpl::GetStride() {
++ MutexLock lock(&api_lock_);
++ return stride_;
++}
++
+ void VideoCaptureImpl::UpdateFrameCount() {
+ RTC_CHECK_RUNS_SERIALIZED(&capture_checker_);
+
+diff --git a/third_party/libwebrtc/modules/video_capture/video_capture_impl.h b/third_party/libwebrtc/modules/video_capture/video_capture_impl.h
+index ec404f098d4a3..3ad11498b9767 100644
+--- a/third_party/libwebrtc/modules/video_capture/video_capture_impl.h
++++ b/third_party/libwebrtc/modules/video_capture/video_capture_impl.h
+@@ -70,6 +70,8 @@ class RTC_EXPORT VideoCaptureImpl : public VideoCaptureModule {
+ int32_t SetCaptureRotation(VideoRotation rotation) override;
+ bool SetApplyRotation(bool enable) override;
+ bool GetApplyRotation() override;
++ void SetStride(int32_t stride) override;
++ int32_t GetStride() override;
+
+ const char* CurrentDeviceName() const override;
+
+@@ -131,6 +133,10 @@ class RTC_EXPORT VideoCaptureImpl : public VideoCaptureModule {
+ // Indicate whether rotation should be applied before delivered externally.
+ bool apply_rotation_ RTC_GUARDED_BY(api_lock_);
+
++ // Explicit input buffer stride. Left to 0 implies implicit stride based on
++ // format and width.
++ int32_t stride_ RTC_GUARDED_BY(api_lock_);
++
+ Clock* const clock_;
+ };
+ } // namespace videocapturemodule
reply other threads:[~2026-08-18 13:30 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178705984383.1.18373842319191627553.rpms-firefox-7dcfa2b5fe27@fedoraproject.org \
--to=jgrulich@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox