mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2026-02-06 11:27:19 +00:00
Refactor
This commit is contained in:
parent
1c1b98d7dd
commit
9c609b5fc1
@ -157,7 +157,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
encodings_.clear();
|
||||
encodings_.insert(encodingRaw);
|
||||
|
||||
bool canChangeSettings = !shandler || shandler->canChangeKasmSettings();
|
||||
const bool canChangeSettings = !shandler || shandler->canChangeKasmSettings();
|
||||
const bool can_apply = !rfb::Server::ignoreClientSettingsKasm && canChangeSettings;
|
||||
|
||||
for (int i = nEncodings-1; i >= 0; i--) {
|
||||
switch (encodings[i]) {
|
||||
@ -283,116 +284,90 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
clientparlog("fineQualityLevel", fineQualityLevel, true);
|
||||
}
|
||||
|
||||
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings) {
|
||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9) {
|
||||
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
||||
clientparlog("jpegVideoQuality", encodings[i] - pseudoEncodingJpegVideoQualityLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 && encodings[i] <= pseudoEncodingJpegVideoQualityLevel9) {
|
||||
if (can_apply)
|
||||
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
||||
clientparlog("jpegVideoQuality", encodings[i] - pseudoEncodingJpegVideoQualityLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingWebpVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingWebpVideoQualityLevel9) {
|
||||
Server::webpVideoQuality.setParam(encodings[i] - pseudoEncodingWebpVideoQualityLevel0);
|
||||
clientparlog("webpVideoQuality", encodings[i] - pseudoEncodingWebpVideoQualityLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingWebpVideoQualityLevel0 && encodings[i] <= pseudoEncodingWebpVideoQualityLevel9) {
|
||||
if (can_apply)
|
||||
Server::webpVideoQuality.setParam(encodings[i] - pseudoEncodingWebpVideoQualityLevel0);
|
||||
clientparlog("webpVideoQuality", encodings[i] - pseudoEncodingWebpVideoQualityLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingTreatLosslessLevel0 &&
|
||||
encodings[i] <= pseudoEncodingTreatLosslessLevel10) {
|
||||
Server::treatLossless.setParam(encodings[i] - pseudoEncodingTreatLosslessLevel0);
|
||||
clientparlog("treatLossless", encodings[i] - pseudoEncodingTreatLosslessLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingTreatLosslessLevel0 && encodings[i] <= pseudoEncodingTreatLosslessLevel10) {
|
||||
if (can_apply)
|
||||
Server::treatLossless.setParam(encodings[i] - pseudoEncodingTreatLosslessLevel0);
|
||||
clientparlog("treatLossless", encodings[i] - pseudoEncodingTreatLosslessLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMinLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMinLevel9) {
|
||||
Server::dynamicQualityMin.setParam(encodings[i] - pseudoEncodingDynamicQualityMinLevel0);
|
||||
clientparlog("dynamicQualityMin", encodings[i] - pseudoEncodingDynamicQualityMinLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMinLevel0 && encodings[i] <= pseudoEncodingDynamicQualityMinLevel9) {
|
||||
if (can_apply)
|
||||
Server::dynamicQualityMin.setParam(encodings[i] - pseudoEncodingDynamicQualityMinLevel0);
|
||||
clientparlog("dynamicQualityMin", encodings[i] - pseudoEncodingDynamicQualityMinLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMaxLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMaxLevel9) {
|
||||
Server::dynamicQualityMax.setParam(encodings[i] - pseudoEncodingDynamicQualityMaxLevel0);
|
||||
clientparlog("dynamicQualityMax", encodings[i] - pseudoEncodingDynamicQualityMaxLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMaxLevel0 && encodings[i] <= pseudoEncodingDynamicQualityMaxLevel9) {
|
||||
if (can_apply)
|
||||
Server::dynamicQualityMax.setParam(encodings[i] - pseudoEncodingDynamicQualityMaxLevel0);
|
||||
clientparlog("dynamicQualityMax", encodings[i] - pseudoEncodingDynamicQualityMaxLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoAreaLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoAreaLevel100) {
|
||||
Server::videoArea.setParam(encodings[i] - pseudoEncodingVideoAreaLevel1 + 1);
|
||||
clientparlog("videoArea", encodings[i] - pseudoEncodingVideoAreaLevel1 + 1, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingVideoAreaLevel1 && encodings[i] <= pseudoEncodingVideoAreaLevel100) {
|
||||
if (can_apply)
|
||||
Server::videoArea.setParam(encodings[i] - pseudoEncodingVideoAreaLevel1 + 1);
|
||||
clientparlog("videoArea", encodings[i] - pseudoEncodingVideoAreaLevel1 + 1, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoTimeLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoTimeLevel100) {
|
||||
Server::videoTime.setParam(encodings[i] - pseudoEncodingVideoTimeLevel0);
|
||||
clientparlog("videoTime", encodings[i] - pseudoEncodingVideoTimeLevel0, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingVideoTimeLevel0 && encodings[i] <= pseudoEncodingVideoTimeLevel100) {
|
||||
if (can_apply)
|
||||
Server::videoTime.setParam(encodings[i] - pseudoEncodingVideoTimeLevel0);
|
||||
clientparlog("videoTime", encodings[i] - pseudoEncodingVideoTimeLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoOutTimeLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoOutTimeLevel100) {
|
||||
Server::videoOutTime.setParam(encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1);
|
||||
clientparlog("videoOutTime", encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingVideoOutTimeLevel1 && encodings[i] <= pseudoEncodingVideoOutTimeLevel100) {
|
||||
if (can_apply)
|
||||
Server::videoOutTime.setParam(encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1);
|
||||
clientparlog("videoOutTime", encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingFrameRateLevel10 &&
|
||||
encodings[i] <= pseudoEncodingFrameRateLevel60) {
|
||||
Server::frameRate.setParam(encodings[i] - pseudoEncodingFrameRateLevel10 + 10);
|
||||
clientparlog("frameRate", encodings[i] - pseudoEncodingFrameRateLevel10 + 10, true);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingFrameRateLevel10 && encodings[i] <= pseudoEncodingFrameRateLevel60) {
|
||||
if (can_apply)
|
||||
Server::frameRate.setParam(encodings[i] - pseudoEncodingFrameRateLevel10 + 10);
|
||||
clientparlog("frameRate", encodings[i] - pseudoEncodingFrameRateLevel10 + 10, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoScalingLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoScalingLevel9) {
|
||||
Server::videoScaling.setParam(encodings[i] - pseudoEncodingVideoScalingLevel0);
|
||||
clientparlog("videoScaling", encodings[i] - pseudoEncodingVideoScalingLevel0, true);
|
||||
}
|
||||
} else {
|
||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9) {
|
||||
clientparlog("jpegVideoQuality", encodings[i] - pseudoEncodingJpegVideoQualityLevel0, false);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingVideoScalingLevel0 && encodings[i] <= pseudoEncodingVideoScalingLevel9) {
|
||||
if (can_apply)
|
||||
Server::videoScaling.setParam(encodings[i] - pseudoEncodingVideoScalingLevel0);
|
||||
clientparlog("videoScaling", encodings[i] - pseudoEncodingVideoScalingLevel0, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingWebpVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingWebpVideoQualityLevel9) {
|
||||
clientparlog("webpVideoQuality", encodings[i] - pseudoEncodingWebpVideoQualityLevel0, false);
|
||||
}
|
||||
// encs.push(encodings.pseudoEncodingStreamingMode + this.streamMode);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingTreatLosslessLevel0 &&
|
||||
encodings[i] <= pseudoEncodingTreatLosslessLevel10) {
|
||||
clientparlog("treatLossless", encodings[i] - pseudoEncodingTreatLosslessLevel0, false);
|
||||
}
|
||||
// if (encodings[i] >= pseudoEncodingHardwareProfile0 && encodings[i] <= pseudoEncodingHardwareProfile4) {
|
||||
// if (appliable)
|
||||
// Server::hardwareProfile.setParam(encodings[i] - pseudoEncodingHardwareProfile0);
|
||||
// clientparlog("hardwareProfile", encodings[i] - pseudoEncodingHardwareProfile0, appliable);
|
||||
// }
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMinLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMinLevel9) {
|
||||
clientparlog("dynamicQualityMin", encodings[i] - pseudoEncodingDynamicQualityMinLevel0, false);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingGOP1 && encodings[i] <= pseudoEncodingGOP60) {
|
||||
if (can_apply)
|
||||
Server::groupOfPicture.setParam(encodings[i] - pseudoEncodingGOP1);
|
||||
clientparlog("groupOfPicture", encodings[i] - pseudoEncodingGOP1, can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMaxLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMaxLevel9) {
|
||||
clientparlog("dynamicQualityMax", encodings[i] - pseudoEncodingDynamicQualityMaxLevel0, false);
|
||||
}
|
||||
if (encodings[i] >= pseudoEncodingStreamingVideoQualityLevel0 && encodings[i] <= pseudoEncodingStreamingVideoQualityLevel63) {
|
||||
if (can_apply)
|
||||
Server::videoQualityCRFCQP.setParam(pseudoEncodingStreamingVideoQualityLevel63 - encodings[i]);
|
||||
clientparlog("videoQualityCRFCQP", pseudoEncodingStreamingVideoQualityLevel63 - encodings[i], can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoAreaLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoAreaLevel100) {
|
||||
clientparlog("videoArea", encodings[i] - pseudoEncodingVideoAreaLevel1 + 1, false);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoTimeLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoTimeLevel100) {
|
||||
clientparlog("videoTime", encodings[i] - pseudoEncodingVideoTimeLevel0, false);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoOutTimeLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoOutTimeLevel100) {
|
||||
clientparlog("videoOutTime", encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1, false);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingFrameRateLevel10 &&
|
||||
encodings[i] <= pseudoEncodingFrameRateLevel60) {
|
||||
clientparlog("frameRate", encodings[i] - pseudoEncodingFrameRateLevel10 + 10, false);
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoScalingLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoScalingLevel9) {
|
||||
clientparlog("videoScaling", encodings[i] - pseudoEncodingVideoScalingLevel0, false);
|
||||
}
|
||||
if (encodings[i] >=pseudoEncodingStreamingModeAV1QSV && encodings[i] <= pseudoEncodingStreamingModeJpegWebp) {
|
||||
if (can_apply)
|
||||
encoder = KasmVideoEncoders::from_encoding(encodings[i]);
|
||||
clientparlog("Encoder", encodings[i], can_apply);
|
||||
}
|
||||
|
||||
if (encodings[i] > 0)
|
||||
|
||||
@ -29,18 +29,19 @@
|
||||
#include <rfb/Cursor.h>
|
||||
#include <rfb/PixelFormat.h>
|
||||
#include <rfb/ScreenSet.h>
|
||||
#include <rfb/encoders/KasmVideoConstants.h>
|
||||
|
||||
namespace rdr { class InStream; }
|
||||
|
||||
namespace rfb {
|
||||
|
||||
const int subsampleUndefined = -1;
|
||||
const int subsampleNone = 0;
|
||||
const int subsampleGray = 1;
|
||||
const int subsample2X = 2;
|
||||
const int subsample4X = 3;
|
||||
const int subsample8X = 4;
|
||||
const int subsample16X = 5;
|
||||
constexpr int subsampleUndefined = -1;
|
||||
constexpr int subsampleNone = 0;
|
||||
constexpr int subsampleGray = 1;
|
||||
constexpr int subsample2X = 2;
|
||||
constexpr int subsample4X = 3;
|
||||
constexpr int subsample8X = 4;
|
||||
constexpr int subsample16X = 5;
|
||||
|
||||
class SMsgHandler;
|
||||
|
||||
@ -91,7 +92,7 @@ namespace rfb {
|
||||
|
||||
void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||
|
||||
unsigned int ledState() { return ledState_; }
|
||||
unsigned int ledState() const { return ledState_; }
|
||||
void setLEDState(unsigned int state);
|
||||
|
||||
rdr::U32 clipboardFlags() const { return clipFlags; }
|
||||
@ -143,6 +144,8 @@ namespace rfb {
|
||||
};
|
||||
|
||||
bool kasmPassed[KASM_NUM_SETTINGS];
|
||||
KasmVideoEncoders::Encoder encoder{KasmVideoEncoders::Encoder::unavailable};
|
||||
KasmVideoEncoders::Encoders available_encoders;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@ -167,9 +167,9 @@ static void updateMaxVideoRes(uint16_t *x, uint16_t *y) {
|
||||
}
|
||||
}
|
||||
|
||||
EncodeManager::EncodeManager(SConnection *conn_, EncCache *encCache_, const FFmpeg& ffmpeg) :
|
||||
EncodeManager::EncodeManager(SConnection *conn_, EncCache *encCache_, const FFmpeg& ffmpeg_) :
|
||||
conn(conn_), dynamicQualityMin(-1), dynamicQualityOff(-1), areaCur(0), videoDetected(false), videoTimer(this),
|
||||
watermarkStats(0), maxEncodingTime(0), framesSinceEncPrint(0), ffmpeg_available(ffmpeg.is_available()), encCache(encCache_)
|
||||
watermarkStats(0), maxEncodingTime(0), framesSinceEncPrint(0), ffmpeg(ffmpeg_), ffmpeg_available(ffmpeg.is_available()), encCache(encCache_)
|
||||
{
|
||||
encoders.resize(encoderClassMax, nullptr);
|
||||
activeEncoders.resize(encoderTypeMax, encoderRaw);
|
||||
@ -437,24 +437,41 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
writeCopyRects(copied, copyDelta);
|
||||
writeCopyPassRects(copypassed);
|
||||
|
||||
// If a video codec is enabled, send that stream constantly
|
||||
/*if (ffmpeg_available && video_mode_available) {
|
||||
startRect(pb->getRect(), encoderFullColour, true, STARTRECT_OVERRIDE_KASMVIDEO);
|
||||
if (video_mode_available && conn->cp.encoder != KasmVideoEncoders::Encoder::unavailable) {
|
||||
|
||||
encoders[encoderKasmVideo]->writeRect(pb, Palette());
|
||||
|
||||
endRect(STARTRECT_OVERRIDE_KASMVIDEO);
|
||||
}*/
|
||||
|
||||
if (video_mode_available) {
|
||||
auto *screen_encoder_manager = dynamic_cast<ScreenEncoderManager *>(encoders[encoderKasmVideo]);
|
||||
auto begin = msSince(&start);
|
||||
auto *screen_encoder_manager = dynamic_cast<ScreenEncoderManager<> *>(encoders[encoderKasmVideo]);
|
||||
if (screen_encoder_manager) {
|
||||
if (screen_encoder_manager->get_encoder() != conn->cp.encoder) {
|
||||
delete encoders[encoderKasmVideo];
|
||||
|
||||
//puts("CHANGING ENCODER!!!");
|
||||
//puts("CHANGING ENCODER!!!");
|
||||
//puts("CHANGING ENCODER!!!");
|
||||
//puts("CHANGING ENCODER!!!");
|
||||
//puts("CHANGING ENCODER!!!");
|
||||
|
||||
encoders[encoderKasmVideo] = new ScreenEncoderManager(ffmpeg,
|
||||
conn->cp.encoder,
|
||||
video_encoders::available_encoders,
|
||||
conn,
|
||||
{0,
|
||||
0,
|
||||
static_cast<uint8_t>(Server::frameRate),
|
||||
static_cast<uint8_t>(Server::groupOfPicture),
|
||||
static_cast<uint8_t>(Server::videoQualityCRFCQP)});
|
||||
|
||||
}
|
||||
screen_encoder_manager->sync_layout(layout);
|
||||
|
||||
for (auto screen_encoder: *screen_encoder_manager) {
|
||||
uint32_t screen_id = screen_encoder->getId();
|
||||
auto *encoder = startRect(pb->getRect(), encoderFullColour, true, STARTRECT_OVERRIDE_KASMVIDEO);
|
||||
screen_encoder->writeRect(pb, Palette());
|
||||
const Palette palette;
|
||||
|
||||
for (auto &screen: *screen_encoder_manager) {
|
||||
if (!screen.encoder)
|
||||
continue;
|
||||
|
||||
startRect(screen.layout.dimensions, encoderFullColour, true, STARTRECT_OVERRIDE_KASMVIDEO);
|
||||
screen.encoder->writeRect(pb, palette);
|
||||
endRect(STARTRECT_OVERRIDE_KASMVIDEO);
|
||||
}
|
||||
}
|
||||
@ -462,6 +479,10 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
std::vector<Rect> rects;
|
||||
changed.get_rects(&rects);
|
||||
updateVideoStats(rects, pb);
|
||||
|
||||
auto end = msSince(&start) - begin;
|
||||
|
||||
printf("ENCODING TOOK: %d ms\n", end);
|
||||
} else {
|
||||
/*
|
||||
* We start by searching for solid rects, which are then removed
|
||||
@ -747,7 +768,7 @@ Encoder *EncodeManager::startRect(const Rect &rect, int type, const bool trackQu
|
||||
return encoder;
|
||||
}
|
||||
|
||||
void EncodeManager::endRect(const enum startRectOverride overrider)
|
||||
void EncodeManager::endRect(const startRectOverride overrider)
|
||||
{
|
||||
conn->writer()->endRect();
|
||||
const auto length = conn->getOutStream(conn->cp.supportsUdp)->length() - beforeLength;
|
||||
|
||||
@ -216,6 +216,7 @@ namespace rfb {
|
||||
unsigned maxEncodingTime, framesSinceEncPrint;
|
||||
unsigned scalingTime;
|
||||
|
||||
const FFmpeg &ffmpeg;
|
||||
bool ffmpeg_available;
|
||||
bool video_mode_available{false};
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ namespace rfb {
|
||||
// isSupported() should return a boolean indicating if this encoder
|
||||
// is okay to use with the current connection. This usually involves
|
||||
// checking the list of encodings in the connection parameters.
|
||||
virtual bool isSupported()=0;
|
||||
virtual bool isSupported() const = 0;
|
||||
|
||||
virtual void setCompressLevel(int level) {};
|
||||
virtual void setQualityLevel(int level) {};
|
||||
|
||||
@ -49,11 +49,7 @@ HextileEncoder::HextileEncoder(SConnection* conn) :
|
||||
{
|
||||
}
|
||||
|
||||
HextileEncoder::~HextileEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool HextileEncoder::isSupported()
|
||||
bool HextileEncoder::isSupported() const
|
||||
{
|
||||
return conn->cp.supportsEncoding(encodingHextile);
|
||||
}
|
||||
|
||||
@ -23,15 +23,13 @@
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class HextileEncoder : public Encoder {
|
||||
public:
|
||||
HextileEncoder(SConnection* conn);
|
||||
virtual ~HextileEncoder();
|
||||
virtual bool isSupported();
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
};
|
||||
class HextileEncoder : public Encoder {
|
||||
public:
|
||||
HextileEncoder(SConnection* conn);
|
||||
~HextileEncoder() override = default;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -41,11 +41,7 @@ RREEncoder::RREEncoder(SConnection* conn) :
|
||||
{
|
||||
}
|
||||
|
||||
RREEncoder::~RREEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool RREEncoder::isSupported()
|
||||
bool RREEncoder::isSupported() const
|
||||
{
|
||||
return conn->cp.supportsEncoding(encodingRRE);
|
||||
}
|
||||
|
||||
@ -28,12 +28,12 @@ namespace rfb {
|
||||
class RREEncoder : public Encoder {
|
||||
public:
|
||||
RREEncoder(SConnection* conn);
|
||||
virtual ~RREEncoder();
|
||||
virtual bool isSupported();
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
~RREEncoder() override = default;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
private:
|
||||
rdr::MemOutStream mos;
|
||||
ManagedPixelBuffer bufferCopy;
|
||||
|
||||
@ -29,11 +29,7 @@ RawEncoder::RawEncoder(SConnection* conn) :
|
||||
{
|
||||
}
|
||||
|
||||
RawEncoder::~RawEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool RawEncoder::isSupported()
|
||||
bool RawEncoder::isSupported() const
|
||||
{
|
||||
// Implicitly required;
|
||||
return true;
|
||||
|
||||
@ -26,12 +26,12 @@ namespace rfb {
|
||||
class RawEncoder : public Encoder {
|
||||
public:
|
||||
RawEncoder(SConnection* conn);
|
||||
virtual ~RawEncoder();
|
||||
virtual bool isSupported();
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
~RawEncoder() override = default;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -101,6 +101,7 @@ namespace rfb {
|
||||
|
||||
virtual void subscribeUnixRelay(const char *name) = 0;
|
||||
virtual void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len) = 0;
|
||||
virtual void videoEncodersRequest(const std::vector<int32_t> &encoders) = 0;
|
||||
|
||||
ConnParams cp;
|
||||
};
|
||||
|
||||
@ -53,7 +53,7 @@ void SMsgReader::readClientInit()
|
||||
|
||||
void SMsgReader::readMsg()
|
||||
{
|
||||
int msgType = is->readU8();
|
||||
const int msgType = is->readU8();
|
||||
switch (msgType) {
|
||||
case msgTypeSetPixelFormat:
|
||||
readSetPixelFormat();
|
||||
@ -106,6 +106,9 @@ void SMsgReader::readMsg()
|
||||
case msgTypeUnixRelay:
|
||||
readUnixRelay();
|
||||
break;
|
||||
case msgTypeVideoEncoders:
|
||||
readVideoEncodersRequest();
|
||||
break;
|
||||
case msgTypeKeepAlive:
|
||||
readKeepAlive();
|
||||
break;
|
||||
@ -413,3 +416,13 @@ void SMsgReader::readUnixRelay()
|
||||
|
||||
handler->unixRelay(name, buf, len);
|
||||
}
|
||||
|
||||
void SMsgReader::readVideoEncodersRequest() const {
|
||||
const auto len = is->readU8();
|
||||
std::vector<int32_t> buf(len);
|
||||
|
||||
for (int i = 0; i < len; ++i)
|
||||
buf[i] = is->readU32();
|
||||
|
||||
handler->videoEncodersRequest(buf);
|
||||
}
|
||||
|
||||
@ -68,6 +68,7 @@ namespace rfb {
|
||||
|
||||
void readSubscribeUnixRelay();
|
||||
void readUnixRelay();
|
||||
void readVideoEncodersRequest() const;
|
||||
|
||||
SMsgHandler* handler;
|
||||
rdr::InStream* is;
|
||||
|
||||
@ -778,6 +778,26 @@ void SMsgWriter::writeUnixRelay(const char *name, const rdr::U8 *buf, const unsi
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeVideoEncoders(const std::vector<int32_t> &encoders) {
|
||||
startMsg(msgTypeVideoEncoders);
|
||||
|
||||
std::vector<int32_t> conjunction;
|
||||
|
||||
for (const auto encoder: cp->available_encoders) {
|
||||
if (std::ranges::find(encoders, KasmVideoEncoders::to_streaming_mode(encoder)) != encoders.end()) {
|
||||
conjunction.push_back(KasmVideoEncoders::to_encoding(encoder));
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t size = conjunction.size();
|
||||
os->writeU8(size);
|
||||
|
||||
for (auto encoder: conjunction)
|
||||
os->writeS32(encoder);
|
||||
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeUserJoinedSession(const std::string& username)
|
||||
{
|
||||
startMsg(msgTypeUserAddedToSession);
|
||||
|
||||
@ -132,6 +132,7 @@ namespace rfb {
|
||||
|
||||
void writeSubscribeUnixRelay(const bool success, const char *msg);
|
||||
void writeUnixRelay(const char *name, const rdr::U8 *buf, const unsigned len);
|
||||
void writeVideoEncoders(const std::vector<int32_t> &encoders);
|
||||
|
||||
void writeUserJoinedSession(const std::string& username);
|
||||
void writeUserLeftSession(const std::string& username);
|
||||
|
||||
@ -62,11 +62,7 @@ TightEncoder::TightEncoder(SConnection* conn) :
|
||||
setCompressLevel(-1);
|
||||
}
|
||||
|
||||
TightEncoder::~TightEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool TightEncoder::isSupported()
|
||||
bool TightEncoder::isSupported() const
|
||||
{
|
||||
return conn->cp.supportsEncoding(encodingTight);
|
||||
}
|
||||
|
||||
@ -29,16 +29,16 @@ namespace rfb {
|
||||
class TightEncoder : public Encoder {
|
||||
public:
|
||||
TightEncoder(SConnection* conn);
|
||||
virtual ~TightEncoder();
|
||||
~TightEncoder() override = default;
|
||||
|
||||
virtual bool isSupported();
|
||||
bool isSupported() const override;
|
||||
|
||||
virtual void setCompressLevel(int level);
|
||||
void setCompressLevel(int level) override;
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
void writeWatermarkRect(const rdr::U8 *data, const unsigned len,
|
||||
const rdr::U8 r,
|
||||
const rdr::U8 g,
|
||||
|
||||
@ -76,11 +76,7 @@ TightJPEGEncoder::TightJPEGEncoder(SConnection* conn) :
|
||||
{
|
||||
}
|
||||
|
||||
TightJPEGEncoder::~TightJPEGEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool TightJPEGEncoder::isSupported()
|
||||
bool TightJPEGEncoder::isSupported() const
|
||||
{
|
||||
if (!conn->cp.supportsEncoding(encodingTight))
|
||||
return false;
|
||||
|
||||
@ -30,22 +30,22 @@ namespace rfb {
|
||||
class TightJPEGEncoder : public Encoder {
|
||||
public:
|
||||
TightJPEGEncoder(SConnection* conn);
|
||||
virtual ~TightJPEGEncoder();
|
||||
~TightJPEGEncoder() override = default;
|
||||
|
||||
virtual bool isSupported();
|
||||
bool isSupported() const override;
|
||||
|
||||
virtual void setQualityLevel(int level);
|
||||
virtual void setFineQualityLevel(int quality, int subsampling);
|
||||
void setQualityLevel(int level) override;
|
||||
void setFineQualityLevel(int quality, int subsampling) override;
|
||||
|
||||
virtual bool treatLossless();
|
||||
bool treatLossless() override;
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
virtual void compressOnly(const PixelBuffer* pb, const uint8_t quality,
|
||||
std::vector<uint8_t> &out, const bool lowVideoQuality) const;
|
||||
virtual void writeOnly(const std::vector<uint8_t> &out) const;
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
|
||||
protected:
|
||||
void writeCompact(rdr::U32 value, rdr::OutStream* os) const;
|
||||
|
||||
@ -156,11 +156,7 @@ TightQOIEncoder::TightQOIEncoder(SConnection* conn) :
|
||||
{
|
||||
}
|
||||
|
||||
TightQOIEncoder::~TightQOIEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool TightQOIEncoder::isSupported()
|
||||
bool TightQOIEncoder::isSupported() const
|
||||
{
|
||||
if (!conn->cp.supportsEncoding(encodingTight))
|
||||
return false;
|
||||
|
||||
@ -27,17 +27,17 @@ namespace rfb {
|
||||
class TightQOIEncoder : public Encoder {
|
||||
public:
|
||||
TightQOIEncoder(SConnection* conn);
|
||||
virtual ~TightQOIEncoder();
|
||||
~TightQOIEncoder() override = default;
|
||||
|
||||
virtual bool isSupported();
|
||||
bool isSupported() const override;
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
virtual void compressOnly(const PixelBuffer* pb, const uint8_t quality,
|
||||
std::vector<uint8_t> &out, const bool lowVideoQuality) const;
|
||||
virtual void writeOnly(const std::vector<uint8_t> &out) const;
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
|
||||
protected:
|
||||
void writeCompact(rdr::U32 value, rdr::OutStream* os) const;
|
||||
|
||||
@ -90,7 +90,7 @@ TightWEBPEncoder::~TightWEBPEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool TightWEBPEncoder::isSupported()
|
||||
bool TightWEBPEncoder::isSupported() const
|
||||
{
|
||||
if (!conn->cp.supportsEncoding(encodingTight))
|
||||
return false;
|
||||
|
||||
@ -29,7 +29,7 @@ namespace rfb {
|
||||
TightWEBPEncoder(SConnection* conn);
|
||||
virtual ~TightWEBPEncoder();
|
||||
|
||||
virtual bool isSupported();
|
||||
bool isSupported() const override;
|
||||
|
||||
virtual void setQualityLevel(int level);
|
||||
virtual void setFineQualityLevel(int quality, int subsampling);
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include <cstdint>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include "encoders/EncoderProbe.h"
|
||||
#include "kasmpasswd.h"
|
||||
|
||||
using namespace rfb;
|
||||
@ -89,9 +90,11 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
|
||||
user[0] = '\0';
|
||||
const char *at = strrchr(peerEndpoint.buf, '@');
|
||||
if (at && at - peerEndpoint.buf > 1 && at - peerEndpoint.buf < USERNAME_LEN) {
|
||||
memcpy(user, peerEndpoint.buf, at - peerEndpoint.buf);
|
||||
user[at - peerEndpoint.buf] = '\0';
|
||||
|
||||
const auto offset = at - peerEndpoint.buf;
|
||||
if (at && offset > 1 && static_cast<size_t>(offset) < USERNAME_LEN) {
|
||||
memcpy(user, peerEndpoint.buf, offset);
|
||||
user[offset] = '\0';
|
||||
}
|
||||
|
||||
bool read, write, owner;
|
||||
@ -112,6 +115,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
gettimeofday(&lastClipboardOp, nullptr);
|
||||
gettimeofday(&lastKeyEvent, nullptr);
|
||||
|
||||
cp.available_encoders = video_encoders::available_encoders;
|
||||
|
||||
server->clients.push_front(this);
|
||||
|
||||
if (server->apimessager)
|
||||
@ -1908,6 +1913,10 @@ void VNCSConnectionST::unixRelay(const char *name, const rdr::U8 *buf, const uns
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::videoEncodersRequest(const std::vector<int32_t> &encoders) {
|
||||
writer()->writeVideoEncoders(encoders);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::sendUnixRelayData(const char name[], const unsigned char *buf,
|
||||
const unsigned len)
|
||||
{
|
||||
|
||||
@ -258,12 +258,13 @@ namespace rfb {
|
||||
virtual void udpUpgrade(const char *resp);
|
||||
virtual void subscribeUnixRelay(const char *name);
|
||||
virtual void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len);
|
||||
void videoEncodersRequest(std::vector<int32_t> const &encoders) override;
|
||||
virtual void supportsLocalCursor();
|
||||
virtual void supportsFence();
|
||||
virtual void supportsContinuousUpdates();
|
||||
virtual void supportsLEDState();
|
||||
|
||||
virtual bool canChangeKasmSettings() const {
|
||||
bool canChangeKasmSettings() const override {
|
||||
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
||||
(AccessPtrEvents | AccessKeyEvents);
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
|
||||
std::string available_accelerators{};
|
||||
for (const auto encoder: video_encoders::available_encoders) {
|
||||
if (encoder != KasmVideoEncoders::Encoder::h264_software) {
|
||||
if (KasmVideoEncoders::is_accelerated(encoder)) {
|
||||
if (!available_accelerators.empty())
|
||||
available_accelerators.append(", ");
|
||||
|
||||
@ -168,8 +168,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
}
|
||||
}
|
||||
|
||||
const auto str = available_accelerators;
|
||||
slog.info("Hardware video encoding acceleration capability: %s", str.empty() ? "none" : str.c_str());
|
||||
slog.info("Hardware video encoding acceleration capability: %s",
|
||||
available_accelerators.empty() ? "none" : available_accelerators.c_str());
|
||||
|
||||
DLPRegion.enabled = DLPRegion.percents = false;
|
||||
|
||||
@ -259,6 +259,20 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
if (selected_codec[0] && !SupportedVideoEncoders::is_supported(selected_codec))
|
||||
throw std::invalid_argument(fmt::format("Unknown video codec: {}", selected_codec));
|
||||
|
||||
const auto selected_encoders = SupportedVideoEncoders::parse(selected_codec);
|
||||
|
||||
for (auto encoder: video_encoders::available_encoders) {
|
||||
if (std::ranges::find(selected_encoders, encoder) != selected_encoders.end())
|
||||
encoders.push_back(encoder);
|
||||
}
|
||||
|
||||
std::string encoder_names;
|
||||
|
||||
for (auto encoder: encoders)
|
||||
encoder_names.append(KasmVideoEncoders::to_string(encoder)).append(" ");
|
||||
|
||||
slog.info("Using CLI-specified video codecs (supported subset): %s", encoder_names.c_str());
|
||||
|
||||
if (Server::selfBench)
|
||||
SelfBench();
|
||||
|
||||
|
||||
@ -26,15 +26,16 @@
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <rfb/EncCache.h>
|
||||
#include <rfb/SDesktop.h>
|
||||
#include <rfb/VNCServer.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <network/Socket.h>
|
||||
#include <rfb/Blacklist.h>
|
||||
#include <rfb/Cursor.h>
|
||||
#include <rfb/Timer.h>
|
||||
#include <network/Socket.h>
|
||||
#include <rfb/EncCache.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/SDesktop.h>
|
||||
#include <rfb/ScreenSet.h>
|
||||
#include <rfb/Timer.h>
|
||||
#include <rfb/VNCServer.h>
|
||||
#include <rfb/encoders/KasmVideoConstants.h>
|
||||
#include <string>
|
||||
|
||||
namespace rfb {
|
||||
@ -237,6 +238,7 @@ namespace rfb {
|
||||
std::list<network::Socket*> closingSockets;
|
||||
|
||||
static EncCache encCache;
|
||||
std::vector<KasmVideoEncoders::Encoder> encoders{};
|
||||
|
||||
ComparingUpdateTracker* comparer;
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ ZRLEEncoder::~ZRLEEncoder()
|
||||
zos.setUnderlying(NULL);
|
||||
}
|
||||
|
||||
bool ZRLEEncoder::isSupported()
|
||||
bool ZRLEEncoder::isSupported() const
|
||||
{
|
||||
return conn->cp.supportsEncoding(encodingZRLE);
|
||||
}
|
||||
|
||||
@ -28,14 +28,14 @@ namespace rfb {
|
||||
class ZRLEEncoder : public Encoder {
|
||||
public:
|
||||
ZRLEEncoder(SConnection* conn);
|
||||
virtual ~ZRLEEncoder();
|
||||
~ZRLEEncoder() override;
|
||||
|
||||
virtual bool isSupported();
|
||||
bool isSupported() const override;
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
void writeRect(const PixelBuffer* pb, const Palette& palette) override;
|
||||
void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
const rdr::U8* colour) override;
|
||||
|
||||
protected:
|
||||
void writePaletteTile(const Rect& tile, const PixelBuffer* pb,
|
||||
|
||||
@ -118,6 +118,8 @@ namespace benchmarking {
|
||||
|
||||
void unixRelay(const char *name, const rdr::U8 *buf, const unsigned len) override {}
|
||||
|
||||
void videoEncodersRequest(const std::vector<int32_t> &encoders) override {}
|
||||
|
||||
void handleFrameStats(rdr::U32 all, rdr::U32 render) override {}
|
||||
|
||||
[[nodiscard]] auto getJpegStats() const {
|
||||
|
||||
@ -21,21 +21,21 @@
|
||||
namespace rfb {
|
||||
|
||||
// Formats
|
||||
const unsigned int clipboardUTF8 = 1 << 0;
|
||||
const unsigned int clipboardRTF = 1 << 1;
|
||||
const unsigned int clipboardHTML = 1 << 2;
|
||||
const unsigned int clipboardDIB = 1 << 3;
|
||||
const unsigned int clipboardFiles = 1 << 4;
|
||||
constexpr unsigned int clipboardUTF8 = 1 << 0;
|
||||
constexpr unsigned int clipboardRTF = 1 << 1;
|
||||
constexpr unsigned int clipboardHTML = 1 << 2;
|
||||
constexpr unsigned int clipboardDIB = 1 << 3;
|
||||
constexpr unsigned int clipboardFiles = 1 << 4;
|
||||
|
||||
const unsigned int clipboardFormatMask = 0x0000ffff;
|
||||
constexpr unsigned int clipboardFormatMask = 0x0000ffff;
|
||||
|
||||
// Actions
|
||||
const unsigned int clipboardCaps = 1 << 24;
|
||||
const unsigned int clipboardRequest = 1 << 25;
|
||||
const unsigned int clipboardPeek = 1 << 26;
|
||||
const unsigned int clipboardNotify = 1 << 27;
|
||||
const unsigned int clipboardProvide = 1 << 28;
|
||||
constexpr unsigned int clipboardCaps = 1 << 24;
|
||||
constexpr unsigned int clipboardRequest = 1 << 25;
|
||||
constexpr unsigned int clipboardPeek = 1 << 26;
|
||||
constexpr unsigned int clipboardNotify = 1 << 27;
|
||||
constexpr unsigned int clipboardProvide = 1 << 28;
|
||||
|
||||
const unsigned int clipboardActionMask = 0xff000000;
|
||||
constexpr unsigned int clipboardActionMask = 0xff000000;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -19,7 +19,7 @@ namespace rfb {
|
||||
FFMPEGVAAPIEncoder::FFMPEGVAAPIEncoder(Screen layout_, const FFmpeg &ffmpeg_, SConnection *conn,
|
||||
KasmVideoEncoders::Encoder encoder_, VideoEncoderParams params) :
|
||||
Encoder(layout_.id, conn, encodingKasmVideo, static_cast<EncoderFlags>(EncoderUseNativePF | EncoderLossy), -1), layout(layout_),
|
||||
ffmpeg(ffmpeg_), encoder(encoder_), current_params(params) {
|
||||
ffmpeg(ffmpeg_), encoder(encoder_), current_params(params), msg_codec_id(KasmVideoEncoders::to_msg_id(encoder)) {
|
||||
AVBufferRef *hw_device_ctx{};
|
||||
int err{};
|
||||
|
||||
@ -29,9 +29,10 @@ namespace rfb {
|
||||
}
|
||||
|
||||
hw_device_ctx_guard.reset(hw_device_ctx);
|
||||
codec = ffmpeg.avcodec_find_encoder_by_name("h264_vaapi");
|
||||
const auto *enc_name = KasmVideoEncoders::to_string(encoder);
|
||||
codec = ffmpeg.avcodec_find_encoder_by_name(enc_name);
|
||||
if (!codec)
|
||||
throw std::runtime_error("Could not find h264_vaapi encoder");
|
||||
throw std::runtime_error(fmt::format("Could not find {} encoder", enc_name));
|
||||
|
||||
auto *frame = ffmpeg.av_frame_alloc();
|
||||
if (!frame)
|
||||
@ -160,14 +161,14 @@ namespace rfb {
|
||||
}
|
||||
|
||||
auto *sws_ctx = ffmpeg.sws_getContext(
|
||||
width, height, AV_PIX_FMT_RGB32, params.width, params.height, AV_PIX_FMT_NV12, SWS_BILINEAR, nullptr, nullptr, nullptr);
|
||||
width, height, AV_PIX_FMT_RGB32, params.width, params.height, AV_PIX_FMT_NV12, SWS_BILINEAR, nullptr, nullptr, nullptr);
|
||||
|
||||
sws_guard.reset(sws_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FFMPEGVAAPIEncoder::isSupported() {
|
||||
bool FFMPEGVAAPIEncoder::isSupported() const {
|
||||
return conn->cp.supportsEncoding(encodingKasmVideo);
|
||||
}
|
||||
|
||||
@ -191,10 +192,10 @@ namespace rfb {
|
||||
dst_height = height & ~1;
|
||||
|
||||
VideoEncoderParams params{dst_width,
|
||||
dst_height,
|
||||
static_cast<uint8_t>(Server::frameRate),
|
||||
static_cast<uint8_t>(Server::groupOfPicture),
|
||||
static_cast<uint8_t>(Server::videoQualityCRFCQP)};
|
||||
dst_height,
|
||||
static_cast<uint8_t>(Server::frameRate),
|
||||
static_cast<uint8_t>(Server::groupOfPicture),
|
||||
static_cast<uint8_t>(Server::videoQualityCRFCQP)};
|
||||
|
||||
if (current_params != params) {
|
||||
bpp = pb->getPF().bpp >> 3;
|
||||
@ -221,7 +222,7 @@ namespace rfb {
|
||||
|
||||
if (err = ffmpeg.av_hwframe_transfer_data(hw_frame_guard.get(), frame, 0); err < 0) {
|
||||
vlog.error(
|
||||
"Error while transferring frame data to surface (%s). Error code: %d", ffmpeg.get_error_description(err).c_str(), err);
|
||||
"Error while transferring frame data to surface (%s). Error code: %d", ffmpeg.get_error_description(err).c_str(), err);
|
||||
}
|
||||
|
||||
if (err = ffmpeg.avcodec_send_frame(ctx_guard.get(), hw_frame_guard.get()); err < 0) {
|
||||
@ -247,7 +248,8 @@ namespace rfb {
|
||||
vlog.debug("Key frame %ld", frame->pts);
|
||||
|
||||
auto *os = conn->getOutStream(conn->cp.supportsUdp);
|
||||
os->writeU8(kasmVideoH264 << 4);
|
||||
os->writeU8(layout.id);
|
||||
os->writeU8(msg_codec_id);
|
||||
os->writeU8(pkt->flags & AV_PKT_FLAG_KEY);
|
||||
write_compact(os, pkt->size);
|
||||
os->writeBytes(&pkt->data[0], pkt->size);
|
||||
|
||||
@ -23,6 +23,7 @@ class FFMPEGVAAPIEncoder final : public Encoder, public VideoEncoder {
|
||||
|
||||
KasmVideoEncoders::Encoder encoder;
|
||||
VideoEncoderParams current_params{};
|
||||
uint8_t msg_codec_id;
|
||||
|
||||
int64_t pts{};
|
||||
int bpp{};
|
||||
@ -36,7 +37,7 @@ class FFMPEGVAAPIEncoder final : public Encoder, public VideoEncoder {
|
||||
VideoEncoderParams params);
|
||||
|
||||
public:
|
||||
bool isSupported() override;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer *pb, const Palette &palette) override;
|
||||
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;
|
||||
void writeSkipRect() override;
|
||||
|
||||
@ -17,33 +17,273 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <rfb/encodings.h>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
template<typename E>
|
||||
class EnumRange {
|
||||
public:
|
||||
using val_t = std::underlying_type_t<E>;
|
||||
|
||||
class EnumIterator {
|
||||
val_t value;
|
||||
|
||||
public:
|
||||
explicit EnumIterator(E v) :
|
||||
value(static_cast<std::underlying_type_t<E>>(v)) {}
|
||||
E operator*() const {
|
||||
return static_cast<E>(value);
|
||||
}
|
||||
EnumIterator &operator++() {
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const EnumIterator &other) const {
|
||||
return value != other.value;
|
||||
}
|
||||
};
|
||||
|
||||
EnumRange(E begin, E end) :
|
||||
begin_iter(EnumIterator(begin)),
|
||||
end_iter(++EnumIterator(end)) {}
|
||||
EnumIterator begin() const {
|
||||
return begin_iter;
|
||||
}
|
||||
EnumIterator end() const {
|
||||
return end_iter;
|
||||
}
|
||||
EnumIterator begin() {
|
||||
return begin_iter;
|
||||
}
|
||||
EnumIterator end() {
|
||||
return end_iter;
|
||||
}
|
||||
|
||||
private:
|
||||
EnumIterator begin_iter;
|
||||
EnumIterator end_iter;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
auto enum_range(T begin, T end) {
|
||||
return EnumRange<T>(begin, end);
|
||||
}
|
||||
|
||||
namespace rfb {
|
||||
// Compression control
|
||||
static constexpr unsigned int kasmVideoH264 = 0x01; // H.264 encoding
|
||||
static constexpr unsigned int kasmVideoH264 = 0x01 << 4; // H.264 encoding
|
||||
static constexpr unsigned int kasmVideoH265 = 0x02 << 4; // H.265 encoding
|
||||
static constexpr unsigned int kasmVideoAV1 = 0x03 << 4; // AV1 encoding
|
||||
static constexpr unsigned int kasmVideoSkip = 0x00; // Skip frame
|
||||
|
||||
static constexpr int GroupOfPictureSize = 10; // interval between I-frames
|
||||
static constexpr auto drm_device_paths = std::to_array<std::string_view>({
|
||||
"/dev/dri/renderD128",
|
||||
"/dev/dri/card0",
|
||||
"/dev/dri/renderD129",
|
||||
"/dev/dri/card1",
|
||||
});
|
||||
|
||||
static constexpr std::array<std::string_view, 4> drm_device_paths = {
|
||||
"/dev/dri/renderD128",
|
||||
"/dev/dri/card0",
|
||||
"/dev/dri/renderD129",
|
||||
"/dev/dri/card1",
|
||||
struct KasmVideoEncoders {
|
||||
// Codecs are ordered by preferred usage quality
|
||||
enum class Encoder : uint8_t
|
||||
{
|
||||
av1_vaapi,
|
||||
av1_ffmpeg_vaapi,
|
||||
av1_nvenc,
|
||||
av1_software,
|
||||
|
||||
h265_vaapi, // h265
|
||||
h265_ffmpeg_vaapi,
|
||||
h265_nvenc,
|
||||
h265_software,
|
||||
|
||||
h264_vaapi,
|
||||
h264_ffmpeg_vaapi,
|
||||
h264_nvenc,
|
||||
h264_software,
|
||||
|
||||
unavailable
|
||||
};
|
||||
|
||||
using Encoders = std::vector<Encoder>;
|
||||
|
||||
static inline auto EncoderNames = std::to_array<std::string_view>({"av1_vaapi",
|
||||
"av1_vaapi",
|
||||
"av1_nvenc",
|
||||
"libsvtav1",
|
||||
|
||||
"hevc_vaapi",
|
||||
"hevc_vaapi",
|
||||
"hevc_nvenc",
|
||||
"libx265",
|
||||
|
||||
"h264_vaapi",
|
||||
"h264_vaapi",
|
||||
"h264_nvenc",
|
||||
"libx264",
|
||||
"unavailable"});
|
||||
|
||||
static inline auto Encodings = std::to_array<int>({pseudoEncodingStreamingModeAV1VAAPI,
|
||||
pseudoEncodingStreamingModeAV1VAAPI,
|
||||
pseudoEncodingStreamingModeAV1NVENC,
|
||||
pseudoEncodingStreamingModeAV1SW,
|
||||
|
||||
pseudoEncodingStreamingModeHEVCVAAPI,
|
||||
pseudoEncodingStreamingModeHEVCVAAPI,
|
||||
pseudoEncodingStreamingModeHEVCNVENC,
|
||||
pseudoEncodingStreamingModeHEVCSW,
|
||||
|
||||
pseudoEncodingStreamingModeAVCVAAPI,
|
||||
pseudoEncodingStreamingModeAVCVAAPI,
|
||||
pseudoEncodingStreamingModeAVCNVENC,
|
||||
pseudoEncodingStreamingModeAVCSW,
|
||||
|
||||
pseudoEncodingStreamingModeJpegWebp});
|
||||
|
||||
static bool is_accelerated(Encoder encoder) {
|
||||
return encoder != Encoder::h264_software && encoder != Encoder::h265_software && encoder != Encoder::av1_software;
|
||||
}
|
||||
|
||||
static std::string_view to_string(Encoder encoder) {
|
||||
return EncoderNames[static_cast<uint8_t>(encoder)];
|
||||
}
|
||||
|
||||
static int to_encoding(Encoder encoder) {
|
||||
return Encodings[static_cast<uint8_t>(encoder)];
|
||||
}
|
||||
|
||||
static Encoder from_encoding(int encoding) {
|
||||
for (auto encoder: enum_range(Encoder::av1_vaapi, Encoder::unavailable)) {
|
||||
if (to_encoding(encoder) == encoding) {
|
||||
switch (encoder) {
|
||||
case Encoder::av1_vaapi:
|
||||
return Encoder::av1_ffmpeg_vaapi;
|
||||
case Encoder::h265_vaapi:
|
||||
return Encoder::h265_ffmpeg_vaapi;
|
||||
case Encoder::h264_vaapi:
|
||||
return Encoder::h264_ffmpeg_vaapi;
|
||||
default:
|
||||
return encoder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Encoder::unavailable;
|
||||
}
|
||||
|
||||
static unsigned int to_msg_id(Encoder encoder) {
|
||||
switch (encoder) {
|
||||
case Encoder::av1_vaapi:
|
||||
case Encoder::av1_ffmpeg_vaapi:
|
||||
case Encoder::av1_nvenc:
|
||||
case Encoder::av1_software:
|
||||
return kasmVideoAV1;
|
||||
case Encoder::h265_vaapi: // h265
|
||||
case Encoder::h265_ffmpeg_vaapi:
|
||||
case Encoder::h265_nvenc:
|
||||
case Encoder::h265_software:
|
||||
return kasmVideoH265;
|
||||
case Encoder::h264_vaapi:
|
||||
case Encoder::h264_ffmpeg_vaapi:
|
||||
case Encoder::h264_nvenc:
|
||||
case Encoder::h264_software:
|
||||
return kasmVideoH264;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t to_streaming_mode(Encoder encoder) {
|
||||
switch (encoder) {
|
||||
case Encoder::av1_vaapi:
|
||||
case Encoder::av1_ffmpeg_vaapi:
|
||||
case Encoder::av1_nvenc:
|
||||
case Encoder::av1_software:
|
||||
return pseudoEncodingStreamingModeAV1;
|
||||
case Encoder::h265_vaapi: // h265
|
||||
case Encoder::h265_ffmpeg_vaapi:
|
||||
case Encoder::h265_nvenc:
|
||||
case Encoder::h265_software:
|
||||
return pseudoEncodingStreamingModeHEVC;
|
||||
case Encoder::h264_vaapi:
|
||||
case Encoder::h264_ffmpeg_vaapi:
|
||||
case Encoder::h264_nvenc:
|
||||
case Encoder::h264_software:
|
||||
return pseudoEncodingStreamingModeAVC;
|
||||
default:
|
||||
return pseudoEncodingStreamingModeJpegWebp;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SupportedVideoEncoders {
|
||||
enum class Codecs : uint8_t
|
||||
{
|
||||
H264,
|
||||
H265,
|
||||
AV1
|
||||
h264,
|
||||
h264_vaapi,
|
||||
h264_nvenc,
|
||||
avc,
|
||||
avc_vaapi,
|
||||
avc_nvenc,
|
||||
|
||||
h265,
|
||||
h265_vaapi,
|
||||
h265_nvenc,
|
||||
hevc,
|
||||
hevc_vaapi,
|
||||
hevc_nvenc,
|
||||
|
||||
av1,
|
||||
av1_vaapi,
|
||||
av1_nvenc,
|
||||
auto_detect,
|
||||
unavailable
|
||||
};
|
||||
|
||||
static inline std::array<std::string_view, 3> CodecNames = {"h264", "h265", "av1"};
|
||||
static constexpr auto MappedCodecs = std::to_array<KasmVideoEncoders::Encoder>({KasmVideoEncoders::Encoder::h264_software,
|
||||
KasmVideoEncoders::Encoder::h264_ffmpeg_vaapi,
|
||||
KasmVideoEncoders::Encoder::h264_nvenc,
|
||||
KasmVideoEncoders::Encoder::h264_software,
|
||||
KasmVideoEncoders::Encoder::h264_ffmpeg_vaapi,
|
||||
KasmVideoEncoders::Encoder::h264_nvenc,
|
||||
|
||||
KasmVideoEncoders::Encoder::h265_software,
|
||||
KasmVideoEncoders::Encoder::h265_ffmpeg_vaapi,
|
||||
KasmVideoEncoders::Encoder::h265_nvenc,
|
||||
KasmVideoEncoders::Encoder::h265_software,
|
||||
KasmVideoEncoders::Encoder::h265_ffmpeg_vaapi,
|
||||
KasmVideoEncoders::Encoder::h265_nvenc,
|
||||
|
||||
KasmVideoEncoders::Encoder::av1_software,
|
||||
KasmVideoEncoders::Encoder::av1_ffmpeg_vaapi,
|
||||
KasmVideoEncoders::Encoder::av1_nvenc,
|
||||
KasmVideoEncoders::Encoder::h264_software,
|
||||
KasmVideoEncoders::Encoder::unavailable});
|
||||
|
||||
static inline auto CodecNames = std::to_array<std::string_view>({"h264",
|
||||
"h264_vaapi",
|
||||
"h264_nvenc",
|
||||
"avc",
|
||||
"avc_vaapi",
|
||||
"avc_nvenc",
|
||||
|
||||
"h265",
|
||||
"h265_vaapi",
|
||||
"h265_nvenc",
|
||||
"hevc",
|
||||
"hevc_vaapi",
|
||||
"hevc_nvenc",
|
||||
|
||||
"av1",
|
||||
"av1_vaapi",
|
||||
"av1_nvenc",
|
||||
"auto"});
|
||||
|
||||
static std::string_view to_string(Codecs codec) {
|
||||
return CodecNames[static_cast<uint8_t>(codec)];
|
||||
@ -53,38 +293,96 @@ namespace rfb {
|
||||
if (codec.empty())
|
||||
return false;
|
||||
|
||||
for (auto supported_codec: CodecNames)
|
||||
for (const auto supported_codec: CodecNames)
|
||||
if (supported_codec == codec)
|
||||
return true;
|
||||
|
||||
if (codec == "hevc")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct KasmVideoEncoders {
|
||||
enum class Encoder : uint8_t
|
||||
{
|
||||
av1_vaapi,
|
||||
av1_software,
|
||||
hevc_vaapi, // h265
|
||||
h265_software,
|
||||
h264_vaapi,
|
||||
h264_ffmpeg_vaapi,
|
||||
h264_nvenc,
|
||||
h264_software,
|
||||
unavailable
|
||||
};
|
||||
static auto get_codec(std::string_view codec) {
|
||||
for (auto codec_impl: enum_range(Codecs::h264, Codecs::auto_detect)) {
|
||||
if (to_string(codec_impl) == codec)
|
||||
return codec_impl;
|
||||
}
|
||||
|
||||
static inline std::array<std::string_view, 9> EncoderNames = {
|
||||
"av1_vaapi", "av1_software", "hevc_vaapi", "libx265", "h264_vaapi", "h264_vaapi", "h264_nvenc", "libx264", "unavailable"};
|
||||
return Codecs::unavailable;
|
||||
}
|
||||
|
||||
static std::string_view to_string(Encoder encoder) {
|
||||
return EncoderNames[static_cast<uint8_t>(encoder)];
|
||||
static constexpr auto map_encoder(Codecs impl) {
|
||||
return MappedCodecs[static_cast<uint8_t>(impl)];
|
||||
}
|
||||
|
||||
static std::vector<std::string_view> parse(std::string_view codecs) {
|
||||
std::vector<std::string_view> result;
|
||||
|
||||
if (codecs.empty())
|
||||
return {};
|
||||
|
||||
size_t pos{};
|
||||
size_t start{};
|
||||
|
||||
while (pos < codecs.size()) {
|
||||
pos = codecs.find_first_of(',', pos);
|
||||
if (pos == std::string_view::npos)
|
||||
pos = codecs.size();
|
||||
|
||||
result.push_back(codecs.substr(start, pos - start));
|
||||
|
||||
start = ++pos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static KasmVideoEncoders::Encoders map_encoders(const std::vector<std::string_view> &codecs) {
|
||||
KasmVideoEncoders::Encoders result;
|
||||
|
||||
if (codecs.empty())
|
||||
return {};
|
||||
|
||||
for (auto codec_name: codecs) {
|
||||
const auto codec = get_codec(codec_name);
|
||||
|
||||
switch (codec) {
|
||||
case Codecs::auto_detect:
|
||||
if (!result.empty())
|
||||
result.clear();
|
||||
|
||||
result.push_back(map_encoder(Codecs::av1_nvenc));
|
||||
result.push_back(map_encoder(Codecs::av1_vaapi));
|
||||
result.push_back(map_encoder(Codecs::av1));
|
||||
result.push_back(map_encoder(Codecs::h265_nvenc));
|
||||
result.push_back(map_encoder(Codecs::h265_vaapi));
|
||||
result.push_back(map_encoder(Codecs::h265));
|
||||
result.push_back(map_encoder(Codecs::h264_nvenc));
|
||||
result.push_back(map_encoder(Codecs::h264_vaapi));
|
||||
result.push_back(map_encoder(Codecs::h264));
|
||||
|
||||
return result;
|
||||
default:
|
||||
{
|
||||
const auto encoder = map_encoder(codec);
|
||||
if (std::find(result.begin(), result.end(), encoder) == result.end())
|
||||
result.push_back(encoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static KasmVideoEncoders::Encoders filter_available_encoders(
|
||||
const KasmVideoEncoders::Encoders &encoders, const KasmVideoEncoders::Encoders &available) {
|
||||
KasmVideoEncoders::Encoders result;
|
||||
|
||||
for (auto encoder: available) {
|
||||
if (std::ranges::find(encoders.begin(), encoders.end(), encoder) != encoders.end())
|
||||
result.push_back(encoder);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace rfb
|
||||
|
||||
@ -6,15 +6,21 @@
|
||||
#include "rfb/encodings.h"
|
||||
|
||||
namespace rfb {
|
||||
ScreenEncoderManager::ScreenEncoderManager(const FFmpeg &ffmpeg_, KasmVideoEncoders::Encoder encoder,
|
||||
template<int T>
|
||||
ScreenEncoderManager<T>::ScreenEncoderManager(const FFmpeg &ffmpeg_, KasmVideoEncoders::Encoder encoder,
|
||||
const std::vector<KasmVideoEncoders::Encoder> &encoders, SConnection *conn,
|
||||
VideoEncoderParams params) :
|
||||
Encoder(conn, encodingKasmVideo, static_cast<EncoderFlags>(EncoderUseNativePF | EncoderLossy), -1), ffmpeg(ffmpeg_),
|
||||
current_params(params), base_video_encoder(encoder), available_encoders(encoders) {}
|
||||
|
||||
ScreenEncoderManager::~ScreenEncoderManager() = default;
|
||||
template<int T>
|
||||
ScreenEncoderManager<T>::~ScreenEncoderManager() {
|
||||
for (uint8_t i = 0; i < get_screen_count(); ++i)
|
||||
remove_screen(i);
|
||||
};
|
||||
|
||||
Encoder *ScreenEncoderManager::add_screen(const Screen &layout) const {
|
||||
template<int T>
|
||||
Encoder *ScreenEncoderManager<T>::add_encoder(const Screen &layout) const {
|
||||
Encoder *encoder{};
|
||||
try {
|
||||
encoder = create_encoder(layout, &ffmpeg, conn, base_video_encoder, current_params);
|
||||
@ -33,63 +39,65 @@ namespace rfb {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
size_t ScreenEncoderManager::get_screen_count() const {
|
||||
return encoders.size();
|
||||
template<int T>
|
||||
void ScreenEncoderManager<T>::add_screen(uint8_t index, const Screen &layout) {
|
||||
printf("SCREEN ADDED: %d (%d, %d, %d, %d)\n", index, layout.dimensions.tl.x, layout.dimensions.tl.y, layout.dimensions.br.x, layout.dimensions.br.y);
|
||||
auto *encoder = add_encoder(layout);
|
||||
assert(encoder);
|
||||
screens[index] = {layout, encoder};
|
||||
head = std::min(head, index);
|
||||
tail = std::max(tail, index);
|
||||
}
|
||||
|
||||
void ScreenEncoderManager::remove_screen(Encoder *encoder) {
|
||||
std::erase(encoders, encoder);
|
||||
template<int T>
|
||||
size_t ScreenEncoderManager<T>::get_screen_count() const {
|
||||
return std::max(0, tail - head + 1);
|
||||
}
|
||||
|
||||
Encoder *ScreenEncoderManager::get_screen(size_t idx) const {
|
||||
assert(idx < encoders.size());
|
||||
return encoders[idx];
|
||||
}
|
||||
VideoEncoder *ScreenEncoderManager::get_video_encoder(size_t index) const {
|
||||
assert(index < encoders.size());
|
||||
return reinterpret_cast<VideoEncoder *>(encoders[index]);
|
||||
}
|
||||
|
||||
void ScreenEncoderManager::sync_layout(const ScreenSet &layout) {
|
||||
const auto new_count = layout.num_screens();
|
||||
const auto current_count = static_cast<int>(encoders.size());
|
||||
const auto min_count = std::min(new_count, current_count);
|
||||
|
||||
for (size_t i = 0; i < min_count; ++i) {
|
||||
auto *encoder = encoders[i];
|
||||
if (!encoder || layout.screens[i].id != encoder->getId()) {
|
||||
delete encoders[i];
|
||||
encoders[i] = add_screen(layout.screens[i]);
|
||||
}
|
||||
template<int T>
|
||||
void ScreenEncoderManager<T>::remove_screen(uint8_t index) {
|
||||
if (screens[index].encoder) {
|
||||
delete screens[index].encoder;
|
||||
screens[index].encoder = nullptr;
|
||||
}
|
||||
screens[index].layout = {};
|
||||
}
|
||||
|
||||
if (new_count < current_count) {
|
||||
encoders.erase(encoders.begin() + new_count, encoders.end());
|
||||
} else if (new_count > current_count) {
|
||||
encoders.reserve(new_count);
|
||||
template<int T>
|
||||
void ScreenEncoderManager<T>::sync_layout(const ScreenSet &layout) {
|
||||
for (uint8_t i = 0; i < layout.num_screens(); ++i) {
|
||||
const auto &screen = layout.screens[i];
|
||||
auto id = screen.id;
|
||||
if (id > ScreenSet::MAX_SCREENS) {
|
||||
assert("Wrong id");
|
||||
id = 0;
|
||||
}
|
||||
|
||||
for (size_t i = current_count; i < new_count; ++i) {
|
||||
encoders.emplace_back(add_screen(layout.screens[i]));
|
||||
if (!screens[id].layout.dimensions.equals(screen.dimensions)) {
|
||||
remove_screen(id);
|
||||
add_screen(id, screen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenEncoderManager::isSupported() {
|
||||
auto *base_encoder = encoders.front();
|
||||
assert(!base_encoder);
|
||||
return base_encoder->isSupported();
|
||||
template<int T>
|
||||
bool ScreenEncoderManager<T>::isSupported() const {
|
||||
if (const auto *encoder = screens[head].encoder; encoder)
|
||||
return encoder->isSupported();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScreenEncoderManager::writeRect(const PixelBuffer *pb, const Palette &palette) {
|
||||
auto *base_encoder = encoders.front();
|
||||
assert(!base_encoder);
|
||||
base_encoder->writeRect(pb, palette);
|
||||
template<int T>
|
||||
void ScreenEncoderManager<T>::writeRect(const PixelBuffer *pb, const Palette &palette) {
|
||||
if (auto *encoder = screens[head].encoder; encoder)
|
||||
return encoder->writeRect(pb, palette);
|
||||
}
|
||||
|
||||
void ScreenEncoderManager::writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) {
|
||||
auto *base_encoder = encoders.front();
|
||||
assert(!base_encoder);
|
||||
base_encoder->writeSolidRect(width, height, pf, colour);
|
||||
template<int T>
|
||||
void ScreenEncoderManager<T>::writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) {
|
||||
if (auto *encoder = screens[head].encoder; encoder)
|
||||
encoder->writeSolidRect(width, height, pf, colour);
|
||||
}
|
||||
|
||||
} // namespace rfb
|
||||
|
||||
@ -6,36 +6,45 @@
|
||||
#include "rfb/ffmpeg.h"
|
||||
|
||||
namespace rfb {
|
||||
template<int T = ScreenSet::MAX_SCREENS>
|
||||
class ScreenEncoderManager final : public Encoder {
|
||||
struct screen_t {
|
||||
Screen layout{};
|
||||
Encoder *encoder{};
|
||||
};
|
||||
|
||||
uint8_t head{};
|
||||
uint8_t tail{};
|
||||
|
||||
std::array<screen_t, T> screens{};
|
||||
const FFmpeg &ffmpeg;
|
||||
VideoEncoderParams current_params;
|
||||
|
||||
KasmVideoEncoders::Encoder base_video_encoder;
|
||||
std::vector<KasmVideoEncoders::Encoder> available_encoders;
|
||||
std::vector<Encoder *> encoders;
|
||||
|
||||
Encoder *add_screen(const Screen &layout) const;
|
||||
Encoder *add_encoder(const Screen &layout) const;
|
||||
void add_screen(uint8_t index, const Screen &layout);
|
||||
[[nodiscard]] size_t get_screen_count() const;
|
||||
void remove_screen(Encoder *encoder);
|
||||
Encoder *get_screen(size_t screen_id) const;
|
||||
VideoEncoder *get_video_encoder(size_t index) const;
|
||||
void remove_screen(uint8_t index);
|
||||
|
||||
public:
|
||||
// Iterators
|
||||
using iterator = std::vector<Encoder *>::iterator;
|
||||
using const_iterator = std::vector<Encoder *>::const_iterator;
|
||||
// Iterator
|
||||
using iterator = typename std::array<screen_t, T>::iterator;
|
||||
using const_iterator = typename std::array<screen_t, T>::const_iterator;
|
||||
|
||||
iterator begin() {
|
||||
return encoders.begin();
|
||||
return screens.begin();
|
||||
}
|
||||
iterator end() {
|
||||
return encoders.end();
|
||||
return screens.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] const_iterator cbegin() const {
|
||||
return encoders.begin();
|
||||
return screens.begin();
|
||||
}
|
||||
[[nodiscard]] const_iterator cend() const {
|
||||
return encoders.end();
|
||||
return screens.end();
|
||||
}
|
||||
|
||||
explicit ScreenEncoderManager(const FFmpeg &ffmpeg_, KasmVideoEncoders::Encoder encoder,
|
||||
@ -50,10 +59,14 @@ namespace rfb {
|
||||
|
||||
void sync_layout(const ScreenSet &layout);
|
||||
|
||||
KasmVideoEncoders::Encoder get_encoder() const { return base_video_encoder; }
|
||||
|
||||
// Encoder
|
||||
bool isSupported() override;
|
||||
bool isSupported() const override;
|
||||
|
||||
void writeRect(const PixelBuffer *pb, const Palette &palette) override;
|
||||
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;
|
||||
};
|
||||
|
||||
template class ScreenEncoderManager<>;
|
||||
} // namespace rfb
|
||||
|
||||
@ -4,22 +4,24 @@ extern "C" {
|
||||
#include <libavutil/opt.h>
|
||||
}
|
||||
#include "KasmVideoConstants.h"
|
||||
#include "rfb/LogWriter.h"
|
||||
#include "rfb/SConnection.h"
|
||||
#include "rfb/ServerCore.h"
|
||||
#include "rfb/encodings.h"
|
||||
#include "rfb/ffmpeg.h"
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/SConnection.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/ffmpeg.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
static rfb::LogWriter vlog("H264SoftwareEncoder");
|
||||
static rfb::LogWriter vlog("SoftwareEncoder");
|
||||
|
||||
namespace rfb {
|
||||
SoftwareEncoder::SoftwareEncoder(Screen layout_, const FFmpeg &ffmpeg_, SConnection *conn, KasmVideoEncoders::Encoder encoder_,
|
||||
VideoEncoderParams params) :
|
||||
Encoder(conn, encodingKasmVideo, static_cast<EncoderFlags>(EncoderUseNativePF | EncoderLossy), -1), layout(layout_),
|
||||
ffmpeg(ffmpeg_), encoder(encoder_), current_params(params) {
|
||||
codec = ffmpeg.avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||
ffmpeg(ffmpeg_), encoder(encoder_), current_params(params), msg_codec_id(KasmVideoEncoders::to_msg_id(encoder)) {
|
||||
const auto *enc_name = KasmVideoEncoders::to_string(encoder);
|
||||
codec = ffmpeg.avcodec_find_encoder_by_name(enc_name);
|
||||
if (!codec)
|
||||
throw std::runtime_error("Could not find H264 encoder");
|
||||
throw std::runtime_error(fmt::format("Could not find {} encoder", enc_name));
|
||||
|
||||
auto *frame = ffmpeg.av_frame_alloc();
|
||||
if (!frame) {
|
||||
@ -34,7 +36,7 @@ namespace rfb {
|
||||
pkt_guard.reset(pkt);
|
||||
}
|
||||
|
||||
bool SoftwareEncoder::isSupported() {
|
||||
bool SoftwareEncoder::isSupported() const {
|
||||
return conn->cp.supportsEncoding(encodingKasmVideo);
|
||||
}
|
||||
|
||||
@ -111,7 +113,8 @@ namespace rfb {
|
||||
vlog.debug("Key frame %ld", frame->pts);
|
||||
|
||||
auto *os = conn->getOutStream(conn->cp.supportsUdp);
|
||||
os->writeU8(kasmVideoH264 << 4);
|
||||
os->writeU8(layout.id);
|
||||
os->writeU8(msg_codec_id);
|
||||
os->writeU8(pkt->flags & AV_PKT_FLAG_KEY);
|
||||
write_compact(os, pkt->size);
|
||||
os->writeBytes(&pkt->data[0], pkt->size);
|
||||
|
||||
@ -19,6 +19,7 @@ namespace rfb {
|
||||
|
||||
KasmVideoEncoders::Encoder encoder;
|
||||
VideoEncoderParams current_params{};
|
||||
uint8_t msg_codec_id;
|
||||
|
||||
int64_t pts{};
|
||||
int bpp{};
|
||||
@ -30,7 +31,7 @@ namespace rfb {
|
||||
SoftwareEncoder(Screen layout, const FFmpeg &ffmpeg, SConnection *conn, KasmVideoEncoders::Encoder encoder,
|
||||
VideoEncoderParams params);
|
||||
public:
|
||||
bool isSupported() override;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer *pb, const Palette &palette) override;
|
||||
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;
|
||||
void writeSkipRect() override;
|
||||
|
||||
@ -259,7 +259,7 @@ namespace rfb {
|
||||
return true;*/
|
||||
}
|
||||
|
||||
bool VAAPIEncoder::isSupported() {
|
||||
bool VAAPIEncoder::isSupported() const {
|
||||
return conn->cp.supportsEncoding(encodingKasmVideo);
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ namespace rfb {
|
||||
|
||||
public:
|
||||
VAAPIEncoder(uint32_t id, SConnection *conn, uint8_t frame_rate, uint16_t bit_rate);
|
||||
bool isSupported() override;
|
||||
bool isSupported() const override;
|
||||
void writeRect(const PixelBuffer *pb, const Palette &palette) override;
|
||||
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;
|
||||
void writeSkipRect() override;
|
||||
|
||||
@ -100,15 +100,23 @@ namespace rfb {
|
||||
VideoEncoderParams params) {
|
||||
switch (video_encoder) {
|
||||
case KasmVideoEncoders::Encoder::h264_vaapi:
|
||||
case KasmVideoEncoders::Encoder::h265_vaapi:
|
||||
case KasmVideoEncoders::Encoder::av1_vaapi:
|
||||
// return
|
||||
// H264VAAPIEncoderBuilder::create().with_connection(conn).with_frame_rate(frame_rate).with_bit_rate(bit_rate).build();
|
||||
case KasmVideoEncoders::Encoder::h264_ffmpeg_vaapi:
|
||||
case KasmVideoEncoders::Encoder::h265_ffmpeg_vaapi:
|
||||
case KasmVideoEncoders::Encoder::av1_ffmpeg_vaapi:
|
||||
return FFMPEGVAAPIEncoderBuilder::create(ffmpeg)
|
||||
.with_layout(layout)
|
||||
.with_connection(conn)
|
||||
.with_encoder(video_encoder)
|
||||
.with_params(params)
|
||||
.build();
|
||||
case KasmVideoEncoders::Encoder::h264_nvenc:
|
||||
case KasmVideoEncoders::Encoder::h265_nvenc:
|
||||
case KasmVideoEncoders::Encoder::av1_nvenc:
|
||||
throw std::runtime_error("NVENC is not supported yet");
|
||||
default:
|
||||
return SoftwareEncoderBuilder::create(ffmpeg)
|
||||
.with_layout(layout)
|
||||
|
||||
@ -21,80 +21,109 @@
|
||||
|
||||
namespace rfb {
|
||||
|
||||
const int encodingRaw = 0;
|
||||
const int encodingCopyRect = 1;
|
||||
const int encodingRRE = 2;
|
||||
const int encodingCoRRE = 4;
|
||||
const int encodingHextile = 5;
|
||||
const int encodingTight = 7;
|
||||
const int encodingUdp = 8;
|
||||
const int encodingZRLE = 16;
|
||||
const int encodingKasmVideo = 17;
|
||||
constexpr int encodingRaw = 0;
|
||||
constexpr int encodingCopyRect = 1;
|
||||
constexpr int encodingRRE = 2;
|
||||
constexpr int encodingCoRRE = 4;
|
||||
constexpr int encodingHextile = 5;
|
||||
constexpr int encodingTight = 7;
|
||||
constexpr int encodingUdp = 8;
|
||||
constexpr int encodingZRLE = 16;
|
||||
constexpr int encodingKasmVideo = 17;
|
||||
|
||||
const int encodingMax = 255;
|
||||
constexpr int encodingMax = 255;
|
||||
|
||||
const int pseudoEncodingXCursor = -240;
|
||||
const int pseudoEncodingCursor = -239;
|
||||
const int pseudoEncodingDesktopSize = -223;
|
||||
const int pseudoEncodingLEDState = -261;
|
||||
const int pseudoEncodingExtendedDesktopSize = -308;
|
||||
const int pseudoEncodingDesktopName = -307;
|
||||
const int pseudoEncodingFence = -312;
|
||||
const int pseudoEncodingContinuousUpdates = -313;
|
||||
const int pseudoEncodingCursorWithAlpha = -314;
|
||||
const int pseudoEncodingQEMUKeyEvent = -258;
|
||||
constexpr int pseudoEncodingXCursor = -240;
|
||||
constexpr int pseudoEncodingCursor = -239;
|
||||
constexpr int pseudoEncodingDesktopSize = -223;
|
||||
constexpr int pseudoEncodingLEDState = -261;
|
||||
constexpr int pseudoEncodingExtendedDesktopSize = -308;
|
||||
constexpr int pseudoEncodingDesktopName = -307;
|
||||
constexpr int pseudoEncodingFence = -312;
|
||||
constexpr int pseudoEncodingContinuousUpdates = -313;
|
||||
constexpr int pseudoEncodingCursorWithAlpha = -314;
|
||||
constexpr int pseudoEncodingQEMUKeyEvent = -258;
|
||||
|
||||
// TightVNC-specific
|
||||
const int pseudoEncodingLastRect = -224;
|
||||
const int pseudoEncodingQualityLevel0 = -32;
|
||||
const int pseudoEncodingQualityLevel9 = -23;
|
||||
const int pseudoEncodingCompressLevel0 = -256;
|
||||
const int pseudoEncodingCompressLevel9 = -247;
|
||||
constexpr int pseudoEncodingLastRect = -224;
|
||||
constexpr int pseudoEncodingQualityLevel0 = -32;
|
||||
constexpr int pseudoEncodingQualityLevel9 = -23;
|
||||
constexpr int pseudoEncodingCompressLevel0 = -256;
|
||||
constexpr int pseudoEncodingCompressLevel9 = -247;
|
||||
|
||||
// TurboVNC-specific
|
||||
const int pseudoEncodingFineQualityLevel0 = -512;
|
||||
const int pseudoEncodingFineQualityLevel100 = -412;
|
||||
const int pseudoEncodingSubsamp1X = -768;
|
||||
const int pseudoEncodingSubsamp4X = -767;
|
||||
const int pseudoEncodingSubsamp2X = -766;
|
||||
const int pseudoEncodingSubsampGray = -765;
|
||||
const int pseudoEncodingSubsamp8X = -764;
|
||||
const int pseudoEncodingSubsamp16X = -763;
|
||||
constexpr int pseudoEncodingFineQualityLevel0 = -512;
|
||||
constexpr int pseudoEncodingFineQualityLevel100 = -412;
|
||||
constexpr int pseudoEncodingSubsamp1X = -768;
|
||||
constexpr int pseudoEncodingSubsamp4X = -767;
|
||||
constexpr int pseudoEncodingSubsamp2X = -766;
|
||||
constexpr int pseudoEncodingSubsampGray = -765;
|
||||
constexpr int pseudoEncodingSubsamp8X = -764;
|
||||
constexpr int pseudoEncodingSubsamp16X = -763;
|
||||
|
||||
// Kasm-specific
|
||||
const int pseudoEncodingWEBP = -1024;
|
||||
const int pseudoEncodingJpegVideoQualityLevel0 = -1023;
|
||||
const int pseudoEncodingJpegVideoQualityLevel9 = -1014;
|
||||
const int pseudoEncodingWebpVideoQualityLevel0 = -1013;
|
||||
const int pseudoEncodingWebpVideoQualityLevel9 = -1004;
|
||||
const int pseudoEncodingTreatLosslessLevel0 = -1003;
|
||||
const int pseudoEncodingTreatLosslessLevel10 = -993;
|
||||
const int pseudoEncodingPreferBandwidth = -992;
|
||||
const int pseudoEncodingDynamicQualityMinLevel0 = -991;
|
||||
const int pseudoEncodingDynamicQualityMinLevel9 = -982;
|
||||
const int pseudoEncodingDynamicQualityMaxLevel0 = -981;
|
||||
const int pseudoEncodingDynamicQualityMaxLevel9 = -972;
|
||||
const int pseudoEncodingVideoAreaLevel1 = -971;
|
||||
const int pseudoEncodingVideoAreaLevel100 = -871;
|
||||
const int pseudoEncodingVideoTimeLevel0 = -870;
|
||||
const int pseudoEncodingVideoTimeLevel100 = -770;
|
||||
constexpr int pseudoEncodingWEBP = -1024;
|
||||
constexpr int pseudoEncodingJpegVideoQualityLevel0 = -1023;
|
||||
constexpr int pseudoEncodingJpegVideoQualityLevel9 = -1014;
|
||||
constexpr int pseudoEncodingWebpVideoQualityLevel0 = -1013;
|
||||
constexpr int pseudoEncodingWebpVideoQualityLevel9 = -1004;
|
||||
constexpr int pseudoEncodingTreatLosslessLevel0 = -1003;
|
||||
constexpr int pseudoEncodingTreatLosslessLevel10 = -993;
|
||||
constexpr int pseudoEncodingPreferBandwidth = -992;
|
||||
constexpr int pseudoEncodingDynamicQualityMinLevel0 = -991;
|
||||
constexpr int pseudoEncodingDynamicQualityMinLevel9 = -982;
|
||||
constexpr int pseudoEncodingDynamicQualityMaxLevel0 = -981;
|
||||
constexpr int pseudoEncodingDynamicQualityMaxLevel9 = -972;
|
||||
constexpr int pseudoEncodingVideoAreaLevel1 = -971;
|
||||
constexpr int pseudoEncodingVideoAreaLevel100 = -871;
|
||||
constexpr int pseudoEncodingVideoTimeLevel0 = -870;
|
||||
constexpr int pseudoEncodingVideoTimeLevel100 = -770;
|
||||
|
||||
const int pseudoEncodingFrameRateLevel10 = -2048;
|
||||
const int pseudoEncodingFrameRateLevel60 = -1998;
|
||||
const int pseudoEncodingMaxVideoResolution = -1997;
|
||||
const int pseudoEncodingVideoScalingLevel0 = -1996;
|
||||
const int pseudoEncodingVideoScalingLevel9 = -1987;
|
||||
const int pseudoEncodingVideoOutTimeLevel1 = -1986;
|
||||
const int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
||||
const int pseudoEncodingQOI = -1886;
|
||||
const int pseudoEncodingKasmDisconnectNotify = -1885;
|
||||
constexpr int pseudoEncodingFrameRateLevel10 = -2048;
|
||||
constexpr int pseudoEncodingFrameRateLevel60 = -1998;
|
||||
constexpr int pseudoEncodingMaxVideoResolution = -1997;
|
||||
constexpr int pseudoEncodingVideoScalingLevel0 = -1996;
|
||||
constexpr int pseudoEncodingVideoScalingLevel9 = -1987;
|
||||
constexpr int pseudoEncodingVideoOutTimeLevel1 = -1986;
|
||||
constexpr int pseudoEncodingVideoOutTimeLevel100 = -1887;
|
||||
constexpr int pseudoEncodingQOI = -1886;
|
||||
constexpr int pseudoEncodingKasmDisconnectNotify = -1885;
|
||||
|
||||
constexpr int pseudoEncodingHardwareProfile0 = -1170;
|
||||
constexpr int pseudoEncodingHardwareProfile4 = -1166;
|
||||
|
||||
constexpr int pseudoEncodingGOP1 = -1165;
|
||||
constexpr int pseudoEncodingGOP60 = -1105;
|
||||
constexpr int pseudoEncodingStreamingVideoQualityLevel0 = -1104;
|
||||
constexpr int pseudoEncodingStreamingVideoQualityLevel63 = -1041;
|
||||
|
||||
// AV1
|
||||
constexpr int pseudoEncodingStreamingModeAV1QSV = -1040;
|
||||
constexpr int pseudoEncodingStreamingModeAV1NVENC = -1039;
|
||||
constexpr int pseudoEncodingStreamingModeAV1VAAPI = -1038;
|
||||
constexpr int pseudoEncodingStreamingModeAV1SW = -1037;
|
||||
constexpr int pseudoEncodingStreamingModeAV1 = -1036;
|
||||
// h.265
|
||||
constexpr int pseudoEncodingStreamingModeHEVCQSV = -1035;
|
||||
constexpr int pseudoEncodingStreamingModeHEVCNVENC = -1034;
|
||||
constexpr int pseudoEncodingStreamingModeHEVCVAAPI = -1033;
|
||||
constexpr int pseudoEncodingStreamingModeHEVCSW = -1032;
|
||||
constexpr int pseudoEncodingStreamingModeHEVC = -1031;
|
||||
// h.264
|
||||
constexpr int pseudoEncodingStreamingModeAVCQSV = -1030;
|
||||
constexpr int pseudoEncodingStreamingModeAVCNVENC = -1029;
|
||||
constexpr int pseudoEncodingStreamingModeAVCVAAPI = -1028;
|
||||
constexpr int pseudoEncodingStreamingModeAVCSW = -1027;
|
||||
constexpr int pseudoEncodingStreamingModeAVC = -1026;
|
||||
|
||||
constexpr int pseudoEncodingStreamingModeJpegWebp = -1025;
|
||||
|
||||
// VMware-specific
|
||||
const int pseudoEncodingVMwareCursor = 0x574d5664;
|
||||
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||
constexpr int pseudoEncodingVMwareCursor = 0x574d5664;
|
||||
constexpr int pseudoEncodingVMwareCursorPosition = 0x574d5666;
|
||||
|
||||
// UltraVNC-specific
|
||||
const int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
|
||||
constexpr int pseudoEncodingExtendedClipboard = 0xC0A1E5CE;
|
||||
|
||||
int encodingNum(const char* name);
|
||||
const char* encodingName(int num);
|
||||
|
||||
@ -21,57 +21,58 @@
|
||||
namespace rfb {
|
||||
// server to client
|
||||
|
||||
const int msgTypeFramebufferUpdate = 0;
|
||||
const int msgTypeSetColourMapEntries = 1;
|
||||
const int msgTypeBell = 2;
|
||||
const int msgTypeServerCutText = 3;
|
||||
constexpr int msgTypeFramebufferUpdate = 0;
|
||||
constexpr int msgTypeSetColourMapEntries = 1;
|
||||
constexpr int msgTypeBell = 2;
|
||||
constexpr int msgTypeServerCutText = 3;
|
||||
|
||||
const int msgTypeEndOfContinuousUpdates = 150;
|
||||
constexpr int msgTypeEndOfContinuousUpdates = 150;
|
||||
|
||||
// kasm
|
||||
const int msgTypeStats = 178;
|
||||
const int msgTypeRequestFrameStats = 179;
|
||||
const int msgTypeBinaryClipboard = 180;
|
||||
const int msgTypeUpgradeToUdp = 181;
|
||||
const int msgTypeSubscribeUnixRelay = 182;
|
||||
const int msgTypeUnixRelay = 183;
|
||||
const int msgTypeKeepAlive = 184;
|
||||
const int msgTypeServerDisconnect = 185;
|
||||
|
||||
const int msgTypeServerFence = 248;
|
||||
const int msgTypeUserAddedToSession = 253;
|
||||
const int msgTypeUserRemovedFromSession = 254;
|
||||
constexpr int msgTypeStats = 178;
|
||||
constexpr int msgTypeRequestFrameStats = 179;
|
||||
constexpr int msgTypeBinaryClipboard = 180;
|
||||
constexpr int msgTypeUpgradeToUdp = 181;
|
||||
constexpr int msgTypeSubscribeUnixRelay = 182;
|
||||
constexpr int msgTypeUnixRelay = 183;
|
||||
constexpr int msgTypeVideoEncoders = 184;
|
||||
constexpr int msgTypeKeepAlive = 185;
|
||||
constexpr int msgTypeServerDisconnect = 186;
|
||||
|
||||
constexpr int msgTypeServerFence = 248;
|
||||
constexpr int msgTypeUserAddedToSession = 253;
|
||||
constexpr int msgTypeUserRemovedFromSession = 254;
|
||||
|
||||
// client to server
|
||||
|
||||
const int msgTypeSetPixelFormat = 0;
|
||||
const int msgTypeFixColourMapEntries = 1;
|
||||
const int msgTypeSetEncodings = 2;
|
||||
const int msgTypeFramebufferUpdateRequest = 3;
|
||||
const int msgTypeKeyEvent = 4;
|
||||
const int msgTypePointerEvent = 5;
|
||||
const int msgTypeClientCutText = 6;
|
||||
constexpr int msgTypeSetPixelFormat = 0;
|
||||
constexpr int msgTypeFixColourMapEntries = 1;
|
||||
constexpr int msgTypeSetEncodings = 2;
|
||||
constexpr int msgTypeFramebufferUpdateRequest = 3;
|
||||
constexpr int msgTypeKeyEvent = 4;
|
||||
constexpr int msgTypePointerEvent = 5;
|
||||
constexpr int msgTypeClientCutText = 6;
|
||||
|
||||
const int msgTypeEnableContinuousUpdates = 150;
|
||||
constexpr int msgTypeEnableContinuousUpdates = 150;
|
||||
|
||||
// kasm
|
||||
const int msgTypeRequestStats = 178;
|
||||
const int msgTypeFrameStats = 179;
|
||||
constexpr int msgTypeRequestStats = 178;
|
||||
constexpr int msgTypeFrameStats = 179;
|
||||
// same as the other direction
|
||||
//const int msgTypeBinaryClipboard = 180;
|
||||
//const int msgTypeUpgradeToUdp = 181;
|
||||
//const int msgTypeSubscribeUnixRelay = 182;
|
||||
//const int msgTypeUnixRelay = 183;
|
||||
//const int msgTypeKeepAlive = 184;
|
||||
//const int msgTypeServerDisconnect = 185;
|
||||
//constexpr int msgTypeBinaryClipboard = 180;
|
||||
//constexpr int msgTypeUpgradeToUdp = 181;
|
||||
//constexpr int msgTypeSubscribeUnixRelay = 182;
|
||||
//constexpr int msgTypeUnixRelay = 183;
|
||||
//constexpr int msgTypeVideoEncoders = 184;
|
||||
//constexpr int msgTypeKeepAlive = 185;
|
||||
//constexpr int msgTypeServerDisconnect = 186;
|
||||
|
||||
const int msgTypeClientFence = 248;
|
||||
constexpr int msgTypeClientFence = 248;
|
||||
|
||||
const int msgTypeSetDesktopSize = 251;
|
||||
constexpr int msgTypeSetDesktopSize = 251;
|
||||
|
||||
const int msgTypeSetMaxVideoResolution = 252;
|
||||
constexpr int msgTypeSetMaxVideoResolution = 252;
|
||||
|
||||
const int msgTypeQEMUClientMessage = 255;
|
||||
constexpr int msgTypeQEMUClientMessage = 255;
|
||||
}
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user