PipeWire camera: use exact stream parameters specified by capability
We currently specify stream parameters to be a range for both framerate and resolution, where preferred value is specified. The preferred value doesn't seem to be taken into account and we end up accepting resolution from 1x1 to MAX_INTxMAX_INT. In case the other side tries to first match with lower resolution than requested, we will happily match it and start streaming low quality video. We should instead request the exact stream parameters as specified by requested capability. This capability always come from what has been originally reported as supported so it shouldn't happen we don't find a matching stream. This also applies to requested video format. We previously requested mjpg for streams with resolution higher than 640x480, but it doesn't necessarily mean the camera supports mjpg for the requested resolution. Again, refer to requested capability in this case as it should indicate what is supported and we know we can request exactly the same video format. It can happen that framerate is set to 0 as unspecified. In that case keep using a range as before, but with more sane values. Bug: webrtc:42225999 Change-Id: I46d8e83c636e25e12c45a462596fee1d5e59888e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/362820 Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Commit-Queue: Jan Grulich <grulja@gmail.com> Reviewed-by: Andreas Pehrson <apehrson@mozilla.com> Cr-Commit-Position: refs/heads/main@{#43067}
This commit is contained in:
parent
518de1673e
commit
9703f8474f
@ -52,10 +52,20 @@ VideoType VideoCaptureModulePipeWire::PipeWireRawFormatToVideoType(
|
||||
if (spa_and_pixel_format.spa_format == spa_format)
|
||||
return spa_and_pixel_format.video_type;
|
||||
}
|
||||
RTC_LOG(LS_INFO) << "Unsupported pixel format: " << spa_format;
|
||||
RTC_LOG(LS_WARNING) << "Unsupported pixel format: " << spa_format;
|
||||
return VideoType::kUnknown;
|
||||
}
|
||||
|
||||
uint32_t VideoCaptureModulePipeWire::VideoTypeToPipeWireRawFormat(
|
||||
VideoType type) {
|
||||
for (const auto& spa_and_pixel_format : kSupportedFormats) {
|
||||
if (spa_and_pixel_format.video_type == type)
|
||||
return spa_and_pixel_format.spa_format;
|
||||
}
|
||||
RTC_LOG(LS_WARNING) << "Unsupported video type: " << static_cast<int>(type);
|
||||
return SPA_VIDEO_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
VideoCaptureModulePipeWire::VideoCaptureModulePipeWire(
|
||||
VideoCaptureOptions* options)
|
||||
: VideoCaptureImpl(),
|
||||
@ -88,45 +98,53 @@ int32_t VideoCaptureModulePipeWire::Init(const char* deviceUniqueId) {
|
||||
}
|
||||
|
||||
static spa_pod* BuildFormat(spa_pod_builder* builder,
|
||||
uint32_t format,
|
||||
VideoType video_type,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
float frame_rate) {
|
||||
spa_pod_frame frames[2];
|
||||
spa_pod_frame frame;
|
||||
|
||||
spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format,
|
||||
const uint32_t media_subtype = video_type == VideoType::kMJPEG
|
||||
? SPA_MEDIA_SUBTYPE_mjpg
|
||||
: SPA_MEDIA_SUBTYPE_raw;
|
||||
|
||||
spa_pod_builder_push_object(builder, &frame, SPA_TYPE_OBJECT_Format,
|
||||
SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_mediaType,
|
||||
SPA_POD_Id(SPA_MEDIA_TYPE_video), SPA_FORMAT_mediaSubtype,
|
||||
SPA_POD_Id(format), 0);
|
||||
SPA_POD_Id(media_subtype), 0);
|
||||
|
||||
if (format == SPA_MEDIA_SUBTYPE_raw) {
|
||||
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_format, 0);
|
||||
spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0);
|
||||
spa_pod_builder_id(builder, kSupportedFormats[0].spa_format);
|
||||
for (const auto& spa_and_pixel_format : kSupportedFormats)
|
||||
spa_pod_builder_id(builder, spa_and_pixel_format.spa_format);
|
||||
spa_pod_builder_pop(builder, &frames[1]);
|
||||
if (media_subtype == SPA_MEDIA_SUBTYPE_raw) {
|
||||
const uint32_t format =
|
||||
VideoCaptureModulePipeWire::VideoTypeToPipeWireRawFormat(video_type);
|
||||
RTC_CHECK(format != SPA_VIDEO_FORMAT_UNKNOWN);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format),
|
||||
0);
|
||||
}
|
||||
|
||||
spa_rectangle preferred_size = spa_rectangle{width, height};
|
||||
spa_rectangle min_size = spa_rectangle{1, 1};
|
||||
spa_rectangle max_size = spa_rectangle{4096, 4096};
|
||||
spa_pod_builder_add(
|
||||
builder, SPA_FORMAT_VIDEO_size,
|
||||
SPA_POD_CHOICE_RANGE_Rectangle(&preferred_size, &min_size, &max_size), 0);
|
||||
spa_rectangle resolution = spa_rectangle{width, height};
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size,
|
||||
SPA_POD_Rectangle(&resolution), 0);
|
||||
|
||||
spa_fraction preferred_frame_rate =
|
||||
spa_fraction{static_cast<uint32_t>(frame_rate), 1};
|
||||
spa_fraction min_frame_rate = spa_fraction{0, 1};
|
||||
spa_fraction max_frame_rate = spa_fraction{INT32_MAX, 1};
|
||||
spa_pod_builder_add(
|
||||
builder, SPA_FORMAT_VIDEO_framerate,
|
||||
SPA_POD_CHOICE_RANGE_Fraction(&preferred_frame_rate, &min_frame_rate,
|
||||
&max_frame_rate),
|
||||
0);
|
||||
// Framerate can be also set to 0 to be unspecified
|
||||
if (frame_rate) {
|
||||
spa_fraction framerate = spa_fraction{static_cast<uint32_t>(frame_rate), 1};
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate,
|
||||
SPA_POD_Fraction(&framerate), 0);
|
||||
} else {
|
||||
// Default to some reasonable values
|
||||
spa_fraction preferred_frame_rate =
|
||||
spa_fraction{static_cast<uint32_t>(30), 1};
|
||||
spa_fraction min_frame_rate = spa_fraction{1, 1};
|
||||
spa_fraction max_frame_rate = spa_fraction{30, 1};
|
||||
spa_pod_builder_add(
|
||||
builder, SPA_FORMAT_VIDEO_framerate,
|
||||
SPA_POD_CHOICE_RANGE_Fraction(&preferred_frame_rate, &min_frame_rate,
|
||||
&max_frame_rate),
|
||||
0);
|
||||
}
|
||||
|
||||
return static_cast<spa_pod*>(spa_pod_builder_pop(builder, &frames[0]));
|
||||
return static_cast<spa_pod*>(spa_pod_builder_pop(builder, &frame));
|
||||
}
|
||||
|
||||
RTC_NO_SANITIZE("cfi-icall")
|
||||
@ -178,13 +196,10 @@ int32_t VideoCaptureModulePipeWire::StartCapture(
|
||||
uint32_t width = capability.width;
|
||||
uint32_t height = capability.height;
|
||||
uint32_t frame_rate = capability.maxFPS;
|
||||
bool prefer_jpeg = (width > 640) || (height > 480);
|
||||
VideoType video_type = capability.videoType;
|
||||
|
||||
params.push_back(
|
||||
BuildFormat(&builder, SPA_MEDIA_SUBTYPE_raw, width, height, frame_rate));
|
||||
params.insert(
|
||||
prefer_jpeg ? params.begin() : params.end(),
|
||||
BuildFormat(&builder, SPA_MEDIA_SUBTYPE_mjpg, width, height, frame_rate));
|
||||
BuildFormat(&builder, video_type, width, height, frame_rate));
|
||||
|
||||
int res = pw_stream_connect(
|
||||
stream_, PW_DIRECTION_INPUT, node_id_,
|
||||
|
||||
@ -28,6 +28,7 @@ class VideoCaptureModulePipeWire : public VideoCaptureImpl {
|
||||
int32_t CaptureSettings(VideoCaptureCapability& settings) override;
|
||||
|
||||
static VideoType PipeWireRawFormatToVideoType(uint32_t format);
|
||||
static uint32_t VideoTypeToPipeWireRawFormat(VideoType type);
|
||||
|
||||
private:
|
||||
static void OnStreamParamChanged(void* data,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user