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