Browse Source

Define source-specific desktop stream video modes

stream-multiview
evgeny 1 week ago
parent
commit
ea46c56bc1
  1. 7
      tools/chatgui/src/stream_publish_engine.h
  2. 83
      tools/chatgui/src/stream_video_modes.h

7
tools/chatgui/src/stream_publish_engine.h

@ -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);
};

83
tools/chatgui/src/stream_video_modes.h

@ -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…
Cancel
Save