Define source-specific desktop stream video modes
This commit is contained in:
1 parent
bb5759728a
commit
ea46c56bc1
2 files changed
+89
-1
No files matched your search
@@ -9,6 +9,7 @@
|
||||
#include <QList>
|
||||
#include <memory>
|
||||
#include "live_video_encoder.h"
|
||||
#include "stream_video_modes.h"
|
||||
#include "audio_capture.h"
|
||||
#include "audio_capture_lease.h"
|
||||
#include "../../../src/stream/stream_publisher.h"
|
||||
@@ -19,12 +20,15 @@ struct StreamVideoSource {
|
||||
QString name;
|
||||
QByteArray id;
|
||||
QVariant window;
|
||||
QSize size;
|
||||
QList<StreamVideoMode> modes;
|
||||
};
|
||||
struct StreamPublishConfig {
|
||||
StreamVideoSource source;
|
||||
LiveVideoConfig video;
|
||||
QString microphone;
|
||||
bool audio = true;
|
||||
bool nativeSize = false, automaticBitrate = false;
|
||||
};
|
||||
class StreamPublishEngine : public QObject {
|
||||
Q_OBJECT
|
||||
@@ -45,6 +49,7 @@ public:
|
||||
bool muted() const;
|
||||
signals:
|
||||
void codecsChanged();
|
||||
void sourcesChanged();
|
||||
void stateChanged(const QString& state);
|
||||
void configurationChanged();
|
||||
void previewVideoChanged(const QVariant& frame);
|
||||
@@ -64,7 +69,7 @@ private:
|
||||
void sourceFailed(uint64_t generation, const QString& error);
|
||||
void publicationFailed(uint64_t id, const QString& error);
|
||||
void tick();
|
||||
void videoLoop();
|
||||
void videoLoop(StreamPublishConfig initial);
|
||||
void audioLoop();
|
||||
void captureAudio(const AudioCapture::Frame& frame);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user