screencast_stream_utils.cc (5160B)
1 /* 2 * Copyright 2022 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h" 12 13 #include <libdrm/drm_fourcc.h> 14 #include <pipewire/pipewire.h> 15 #include <spa/param/format.h> 16 #include <spa/param/param.h> 17 #include <spa/pod/builder.h> 18 #include <spa/pod/iter.h> 19 #include <spa/pod/pod.h> 20 #include <spa/pod/vararg.h> 21 #include <spa/utils/type.h> 22 23 #include <cstdint> 24 #include <optional> 25 #include <tuple> 26 #include <vector> 27 28 #include "absl/strings/string_view.h" 29 #include "rtc_base/string_encode.h" 30 #include "rtc_base/string_to_number.h" 31 32 #if !PW_CHECK_VERSION(0, 3, 29) 33 #define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) 34 #endif 35 #if !PW_CHECK_VERSION(0, 3, 33) 36 #define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) 37 #endif 38 39 namespace webrtc { 40 41 PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) { 42 std::vector<absl::string_view> parsed_version = split(version, '.'); 43 44 if (parsed_version.size() != 3) { 45 return {}; 46 } 47 48 std::optional<int> major = StringToNumber<int>(parsed_version.at(0)); 49 std::optional<int> minor = StringToNumber<int>(parsed_version.at(1)); 50 std::optional<int> micro = StringToNumber<int>(parsed_version.at(2)); 51 52 // Return invalid version if we failed to parse it 53 if (!major || !minor || !micro) { 54 return {}; 55 } 56 57 return { 58 .major = major.value(), .minor = minor.value(), .micro = micro.value()}; 59 } 60 61 bool PipeWireVersion::operator>=(const PipeWireVersion& other) { 62 if (!major && !minor && !micro) { 63 return false; 64 } 65 66 return std::tie(major, minor, micro) >= 67 std::tie(other.major, other.minor, other.micro); 68 } 69 70 bool PipeWireVersion::operator<=(const PipeWireVersion& other) { 71 if (!major && !minor && !micro) { 72 return false; 73 } 74 75 return std::tie(major, minor, micro) <= 76 std::tie(other.major, other.minor, other.micro); 77 } 78 79 spa_pod* BuildFormat(spa_pod_builder* builder, 80 uint32_t format, 81 const std::vector<uint64_t>& modifiers, 82 const struct spa_rectangle* resolution, 83 const struct spa_fraction* frame_rate) { 84 spa_pod_frame frames[2]; 85 spa_rectangle pw_min_screen_bounds = spa_rectangle{.width = 1, .height = 1}; 86 spa_rectangle pw_max_screen_bounds = 87 spa_rectangle{.width = UINT32_MAX, .height = UINT32_MAX}; 88 spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, 89 SPA_PARAM_EnumFormat); 90 spa_pod_builder_add(builder, SPA_FORMAT_mediaType, 91 SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); 92 spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, 93 SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); 94 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); 95 96 if (!modifiers.empty()) { 97 if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { 98 spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, 99 SPA_POD_PROP_FLAG_MANDATORY); 100 spa_pod_builder_long(builder, modifiers[0]); 101 } else { 102 spa_pod_builder_prop( 103 builder, SPA_FORMAT_VIDEO_modifier, 104 SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); 105 spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); 106 107 // modifiers from the array 108 bool first = true; 109 for (int64_t val : modifiers) { 110 spa_pod_builder_long(builder, val); 111 // Add the first modifier twice as the very first value is the default 112 // option 113 if (first) { 114 spa_pod_builder_long(builder, val); 115 first = false; 116 } 117 } 118 spa_pod_builder_pop(builder, &frames[1]); 119 } 120 } 121 122 if (resolution) { 123 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, 124 SPA_POD_Rectangle(resolution), 0); 125 } else { 126 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, 127 SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, 128 &pw_min_screen_bounds, 129 &pw_max_screen_bounds), 130 0); 131 } 132 if (frame_rate) { 133 static const spa_fraction pw_min_frame_rate = 134 spa_fraction{.num = 0, .denom = 1}; 135 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, 136 SPA_POD_CHOICE_RANGE_Fraction( 137 frame_rate, &pw_min_frame_rate, frame_rate), 138 0); 139 spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_maxFramerate, 140 SPA_POD_CHOICE_RANGE_Fraction( 141 frame_rate, &pw_min_frame_rate, frame_rate), 142 0); 143 } 144 return static_cast<spa_pod*>(spa_pod_builder_pop(builder, &frames[0])); 145 } 146 147 } // namespace webrtc