84 lines
4.6 KiB
C++
84 lines
4.6 KiB
C++
/* Режимы трансляции: экран масштабируется пропорционально, камера сохраняет аппаратные размеры.
|
|
* Пустой размер экрана означает ожидание первого кадра системного выбора Wayland.
|
|
* FPS — предел частоты трансляции; Qt захватывает с максимальной частотой выбранного формата. */
|
|
#pragma once
|
|
#include <QList>
|
|
#include <QSize>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
struct StreamVideoMode {
|
|
QSize size;
|
|
int fps = 30;
|
|
bool operator==(const StreamVideoMode&) const = default;
|
|
};
|
|
|
|
/* Ограничиваем длинную сторону и, при необходимости, короткую; не увеличиваем исходник. */
|
|
inline QSize stream_video_screen_size(QSize source, int shortSide = 4096) {
|
|
if (source.width() < 16 || source.height() < 16 || shortSide < 16) return {};
|
|
double scale = std::min({1.0, 4096.0 / std::max(source.width(), source.height()),
|
|
double(shortSide) / std::min(source.width(), source.height())});
|
|
QSize size(int(std::floor(source.width() * scale)) & ~1, int(std::floor(source.height() * scale)) & ~1);
|
|
return size.width() >= 16 && size.height() >= 16 ? size : QSize();
|
|
}
|
|
|
|
/* Стандартные ступени короткой стороны дают одинаковые пропорции в обеих ориентациях. */
|
|
inline QList<StreamVideoMode> stream_video_screen_modes(QSize source) {
|
|
QList<StreamVideoMode> modes;
|
|
if (source.isEmpty()) {
|
|
for (int fps : {15, 30, 60}) modes.append({QSize(), fps});
|
|
return modes;
|
|
}
|
|
QSize maximum = stream_video_screen_size(source);
|
|
if (maximum.isEmpty()) return modes;
|
|
QList<QSize> sizes{maximum};
|
|
for (int side : {2160, 1440, 1080, 720, 480, 360}) {
|
|
QSize size = stream_video_screen_size(source, side);
|
|
if (!size.isEmpty() && !sizes.contains(size)) sizes.append(size);
|
|
}
|
|
for (QSize size : sizes) for (int fps : {15, 30, 60}) modes.append({size, fps});
|
|
return modes;
|
|
}
|
|
|
|
/* 29.97/59.94 соответствуют пределам 30/60; фактический диапазон камеры не округляется. */
|
|
inline bool stream_video_camera_supports(QSize size, double minimum, double maximum, const StreamVideoMode& mode) {
|
|
return size == mode.size && size.width() >= 16 && size.height() >= 16 && size.width() <= 4096 && size.height() <= 4096
|
|
&& !(size.width() % 2) && !(size.height() % 2) && std::isfinite(minimum) && std::isfinite(maximum)
|
|
&& minimum >= 0 && maximum > 0 && minimum <= maximum && mode.fps >= 1 && mode.fps <= 60
|
|
&& mode.fps + 0.1 >= minimum && mode.fps <= maximum + 0.1;
|
|
}
|
|
|
|
/* Объединяем одинаковые режимы разных pixel format, сохраняя границы необычных диапазонов FPS. */
|
|
inline void stream_video_add_camera_modes(QList<StreamVideoMode>& modes, QSize size, double minimum, double maximum) {
|
|
QList<int> rates{15, 24, 25, 30, 50, 60};
|
|
if (std::isfinite(minimum) && minimum >= 1 && minimum <= 60.1) rates.append(int(std::ceil(minimum - 0.1)));
|
|
if (std::isfinite(maximum) && maximum >= 0.9 && maximum <= 60.1) rates.append(int(std::floor(maximum + 0.1)));
|
|
for (int fps : rates) {
|
|
StreamVideoMode mode{size, fps};
|
|
if (stream_video_camera_supports(size, minimum, maximum, mode) && !modes.contains(mode)) modes.append(mode);
|
|
}
|
|
std::sort(modes.begin(), modes.end(), [](const auto& left, const auto& right) {
|
|
if (left.size != right.size) {
|
|
int area = left.size.width() * left.size.height(), other = right.size.width() * right.size.height();
|
|
return area != other ? area > other : left.size.width() > right.size.width();
|
|
}
|
|
return left.fps < right.fps;
|
|
});
|
|
}
|
|
|
|
/* При явном выборе экрана берём максимум; обновление списка сохраняет допустимый ручной выбор. */
|
|
inline int stream_video_select_mode(const QList<StreamVideoMode>& modes, StreamVideoMode previous, bool maximum) {
|
|
if (modes.isEmpty()) return -1;
|
|
if (!maximum) {
|
|
int index = modes.indexOf(previous);
|
|
if (index >= 0) return index;
|
|
}
|
|
QSize size = maximum ? modes.front().size : QSize(1280, 720);
|
|
for (int fps : {previous.fps, 30}) {
|
|
int index = modes.indexOf({size, fps});
|
|
if (index >= 0) return index;
|
|
}
|
|
for (int i = 0; i < modes.size(); i++) if (modes[i].fps == 30) return i;
|
|
return 0;
|
|
}
|