Add support of filtering step values in pipewire

Note: The patch in this post is out-dated because pipewire has already added the support in this commit. The commit message describes the exact case we need that support: encode a screencast with a hardware encoder.

The issue

When constructing a gstreamer pipeline for RTSP, we use pipewiresrc element to get the video stream from Weston and vpuenc_h264 element to encode it to H.264 format with VPU before streaming out to rtph264pay element. But between pipewiresrc and vpuenc_h264, we have to explicitly specify the resolution of the video:

pipewiresrc ! video/x-raw,format=BGRx,width=800,height=480 ! vpuenc_h264 ! rtph264pay name=pay0 pt=96

After enabling the debug log of GStreamer, I’ve figured out that during the negotiation of capabilities, the size in capabilities of pipewiresrc is determined values 800x480 (type SPA_CHOICE_None in pipewire), but the size in capabilities of vpuenc_h264 is a step values [64, 1920, 8] and [64, 1088, 8] (type SPA_CHOICE_Step in pipewire).

However, pipewire doesn’t support comparing (filtering) SPA_CHOICE_None with SPA_CHOICE_Step or vice versa, so it will fail to negotiate the capabilities if pipewiresrc and vpuenc_h264 is connected directly without explicitly specify the resolution inbetween.

The fix

The fix is obvious: add the support of filtering step values in pipewire.

SPA_CHOICE_Step is pretty much similar to SPA_CHOICE_Range so could deal them in the same way. So the patch is as simple as below (also add support of comparing with SPA_CHOICE_Enum to keep consistency):

diff --git a/spa/include/spa/pod/filter.h b/spa/include/spa/pod/filter.h
index dd2e9931..f2c996be 100644
--- a/spa/include/spa/pod/filter.h
+++ b/spa/include/spa/pod/filter.h
@@ -153,7 +153,9 @@ spa_pod_filter_prop(struct spa_pod_builder *b,
        }
 
        if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Range) ||
-           (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range)) {
+           (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Range) ||
+           (p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) ||
+           (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) {
                int n_copied = 0;
                /* copy all values inside the range */
                for (j = 0, a1 = alt1, a2 = alt2; j < nalt1; j++, a1 = SPA_MEMBER(a1,size,void)) {
@@ -169,13 +171,10 @@ spa_pod_filter_prop(struct spa_pod_builder *b,
                nc->body.type = SPA_CHOICE_Enum;
        }
 
-       if ((p1c == SPA_CHOICE_None && p2c == SPA_CHOICE_Step) ||
-           (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Step)) {
-               return -ENOTSUP;
-       }
-
        if ((p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_None) ||
-           (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum)) {
+           (p1c == SPA_CHOICE_Range && p2c == SPA_CHOICE_Enum) ||
+           (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None) ||
+           (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum)) {
                int n_copied = 0;
                /* copy all values inside the range */
                for (k = 0, a1 = alt1, a2 = alt2; k < nalt2; k++, a2 = SPA_MEMBER(a2,size,void)) {
@@ -220,15 +219,11 @@ spa_pod_filter_prop(struct spa_pod_builder *b,
        if (p1c == SPA_CHOICE_Enum && p2c == SPA_CHOICE_Flags)
                return -ENOTSUP;
 
-       if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_None)
-               return -ENOTSUP;
        if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Range)
                return -ENOTSUP;
 
        if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Step)
                return -ENOTSUP;
-       if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Enum)
-               return -ENOTSUP;
        if (p1c == SPA_CHOICE_Step && p2c == SPA_CHOICE_Flags)
                return -ENOTSUP;
 

After applying this patch, the pipeline for RTSP can be simplified to:

pipewiresrc ! vpuenc_h264 ! rtph264pay name=pay0 pt=96

which is screen resolution agnostic and can be used generally on different systems.

Have fun!

comments powered by Disqus