2 changed files with 89 additions and 1 deletions
@ -0,0 +1,83 @@ |
|||||||
|
/* Режимы трансляции: экран масштабируется пропорционально, камера сохраняет аппаратные размеры.
|
||||||
|
* Пустой размер экрана означает ожидание первого кадра системного выбора 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; |
||||||
|
} |
||||||
Loading…
Reference in new issue