VNC-330 Fix decoder recovery and improve video frame handling

This commit is contained in:
El 2026-03-24 11:09:40 +00:00
parent 1bb839897a
commit d4af227b41
No known key found for this signature in database
GPG key ID: EB3F4C9EA29CDE59
13 changed files with 36 additions and 24 deletions

View file

@ -353,11 +353,11 @@ void EncodeManager::pruneLosslessRefresh(const Region& limits)
}
void EncodeManager::writeUpdate(const UpdateInfo& ui, const ScreenSet &layout, const PixelBuffer* pb,
const RenderedCursor* renderedCursor,
const RenderedCursor* renderedCursor, bool fullRefreshRequested,
size_t maxUpdateSize)
{
curMaxUpdateSize = maxUpdateSize;
doUpdate(true, ui.changed, ui.copied, ui.copy_delta, ui.copypassed, layout, pb, renderedCursor);
doUpdate(true, ui.changed, ui.copied, ui.copy_delta, ui.copypassed, layout, pb, renderedCursor, fullRefreshRequested);
}
void EncodeManager::writeLosslessRefresh(const Region& req, const ScreenSet &layout, const PixelBuffer* pb,
@ -376,7 +376,8 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
const std::vector<CopyPassRect>& copypassed,
const ScreenSet &layout,
const PixelBuffer* pb,
const RenderedCursor* renderedCursor) {
const RenderedCursor* renderedCursor,
bool fullRefreshRequested) {
int nRects;
Region changed, cursorRegion;
struct timeval start;
@ -442,7 +443,7 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
bool video_mode = video_mode_available && conn->cp.encoder_config.encoder != KasmVideoEncoders::Encoder::unavailable;
if (video_mode) {
video_mode = updateVideo(changed, layout, pb);
video_mode = updateVideo(changed, layout, pb, fullRefreshRequested);
if (!video_mode)
conn->cp.encoder_config.encoder = KasmVideoEncoders::Encoder::unavailable;
}
@ -483,13 +484,11 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
conn->writer()->writeFramebufferUpdateEnd();
}
bool EncodeManager::updateVideo(const Region &changed, const ScreenSet &layout, const PixelBuffer *pb) {
bool EncodeManager::updateVideo(const Region &changed, const ScreenSet &layout, const PixelBuffer *pb, bool fullRefreshRequested) {
auto *screen_encoder_manager = dynamic_cast<ScreenEncoderManager<> *>(encoders[encoderKasmVideo]);
if (!screen_encoder_manager)
return false;
//const auto start = std::chrono::high_resolution_clock::now();
if (screen_encoder_manager->get_encoder_config().encoder != conn->cp.encoder_config.encoder) {
screen_encoder_manager->clear();
screen_encoder_manager->set_params(conn->cp.encoder_config,
@ -505,7 +504,7 @@ bool EncodeManager::updateVideo(const Region &changed, const ScreenSet &layout,
return false;
static const Palette palette;
if (!screen_encoder_manager->writeFrame(pb, palette))
if (!screen_encoder_manager->writeFrame(pb, palette, fullRefreshRequested))
return false;
std::vector<Rect> rects;

View file

@ -71,7 +71,7 @@ namespace rfb {
void pruneLosslessRefresh(const Region& limits);
void writeUpdate(const UpdateInfo& ui, const ScreenSet &layout, const PixelBuffer* pb,
const RenderedCursor* renderedCursor,
const RenderedCursor* renderedCursor, bool fullRefreshRequested,
size_t maxUpdateSize = 2000);
void writeLosslessRefresh(const Region& req, const ScreenSet &layout, const PixelBuffer* pb,
@ -105,9 +105,10 @@ namespace rfb {
const std::vector<CopyPassRect> &copypassed,
const ScreenSet &layout,
const PixelBuffer* pb,
const RenderedCursor* renderedCursor);
const RenderedCursor* renderedCursor,
bool fullRefreshRequested = false);
bool updateVideo(const Region& changed, const ScreenSet &layout, const PixelBuffer* pb);
bool updateVideo(const Region& changed, const ScreenSet &layout, const PixelBuffer* pb, bool fullRefreshRequested);
void prepareEncoders(bool allowLossy);

View file

@ -1038,6 +1038,8 @@ void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
// Non-incremental update - treat as if area requested has changed
updates.add_changed(reqRgn);
pendingClientRefresh = true;
// And send the screen layout to the client (which, unlike the
// framebuffer dimensions, the client doesn't get during init)
writer()->writeExtendedDesktopSize();
@ -1571,7 +1573,10 @@ void VNCSConnectionST::writeDataUpdate()
server->msToNextUpdate() / 1000;
if (!ui.is_empty()) {
encodeManager.writeUpdate(ui, server->screenLayout, server->getPixelBuffer(), cursor, maxUpdateSize);
encodeManager.writeUpdate(ui, server->screenLayout, server->getPixelBuffer(), cursor, pendingClientRefresh, maxUpdateSize);
if (pendingClientRefresh)
pendingClientRefresh = false;
copypassed.clear();
gettimeofday(&lastRealUpdate, nullptr);
losslessTimer.start(losslessThreshold);

View file

@ -366,6 +366,7 @@ namespace rfb {
char unixRelaySubscriptions[MAX_UNIX_RELAYS][MAX_UNIX_RELAY_NAME_LEN] = {};
bool complainedAboutNoViewRights;
std::string clientUsername;
bool pendingClientRefresh{false};
};
}
#endif

View file

@ -89,7 +89,7 @@ namespace benchmarking {
manager.clearEncodingTime();
if (!ui.is_empty()) {
manager.writeUpdate(ui, layout, pb, nullptr);
manager.writeUpdate(ui, layout, pb, nullptr, false);
} else {
rfb::Region region{pb->getRect()};
manager.writeLosslessRefresh(region, layout, pb, nullptr, 2000);

View file

@ -241,7 +241,7 @@ namespace rfb {
}
template<AVHWDeviceType HWDeviceType, AVPixelFormat AVPixFmt>
bool FFMPEGHWEncoder<HWDeviceType, AVPixFmt>::render(const PixelBuffer *pb) {
bool FFMPEGHWEncoder<HWDeviceType, AVPixFmt>::render(const PixelBuffer *pb, bool forceKeyFrame) {
// compress
int stride;
const auto rect = layout.dimensions;
@ -282,6 +282,9 @@ namespace rfb {
frame->pict_type = AV_PICTURE_TYPE_NONE;
}
if (forceKeyFrame)
frame->pict_type = AV_PICTURE_TYPE_I;
const int src_stride_bytes = stride * bpp;
DEBUG_LOG(vlog, "render(): width=%d, height=%d, dst_width=%d, dst_height=%d, stride=%d pixels, stride_bytes=%d, bpp=%d",

View file

@ -57,7 +57,7 @@ public:
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;
bool render(const PixelBuffer *pb) override;
bool render(const PixelBuffer *pb, bool forceKeyFrame = false) override;
void writeSkipRect() override;
};

View file

@ -182,7 +182,7 @@ namespace rfb {
}
template<uint8_t T>
bool ScreenEncoderManager<T>::writeFrame(const PixelBuffer *pb, const Palette &palette) {
bool ScreenEncoderManager<T>::writeFrame(const PixelBuffer *pb, const Palette &palette, bool forceKeyFrame) {
if (screens_to_refresh.empty())
return true;
@ -219,13 +219,13 @@ namespace rfb {
if (screens_to_refresh.size() > 1) {
tbb::task_group_context ctx;
tbb::parallel_for_each(screens_to_refresh.begin(), screens_to_refresh.end(), [this, pb, &ctx](uint8_t index) {
tbb::parallel_for_each(screens_to_refresh.begin(), screens_to_refresh.end(), [this, pb, &ctx, forceKeyFrame](uint8_t index) {
if (ctx.is_group_execution_cancelled())
return;
auto &screen = screens[index];
if (auto *encoder = screen.encoder; encoder) {
screen.dirty = encoder->render(pb);
screen.dirty = encoder->render(pb, forceKeyFrame);
if (!screen.dirty)
ctx.cancel_group_execution();
}
@ -242,7 +242,7 @@ namespace rfb {
}
} else {
if (auto encoder = screens[head].encoder; encoder) {
if (encoder->render(pb))
if (encoder->render(pb, forceKeyFrame))
send_frame(screens[head]);
else
return false;

View file

@ -104,7 +104,7 @@ namespace rfb {
// Encoder
[[nodiscard]] bool isSupported() const override;
bool writeFrame(const PixelBuffer *pb, const Palette &palette);
bool writeFrame(const PixelBuffer *pb, const Palette &palette, bool forceKeyFrame = false);
void writeRect(const PixelBuffer *pb, const Palette &palette) override {}
void writeSolidRect(int width, int height, const PixelFormat &pf, const rdr::U8 *colour) override;

View file

@ -55,7 +55,7 @@ namespace rfb {
return conn->cp.supportsEncoding(encodingKasmVideo);
}
bool SoftwareEncoder::render(const PixelBuffer *pb) {
bool SoftwareEncoder::render(const PixelBuffer *pb, bool forceKeyFrame) {
// compress
int stride;
@ -93,6 +93,9 @@ namespace rfb {
frame->pict_type = AV_PICTURE_TYPE_NONE;
}
if (forceKeyFrame)
frame->pict_type = AV_PICTURE_TYPE_I;
const uint8_t *src_data[1] = {buffer};
const int src_line_size[1] = {stride * bpp}; // RGB has bpp bytes per pixel

View file

@ -50,7 +50,7 @@ namespace rfb {
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;
bool render(const PixelBuffer *pb) override;
bool render(const PixelBuffer *pb, bool forceKeyFrame = false) override;
void writeSkipRect() override;
};
} // namespace rfb

View file

@ -41,7 +41,7 @@ namespace rfb {
public:
VideoEncoder(Id id, SConnection *conn) :
Encoder(id, conn, encodingKasmVideo, static_cast<EncoderFlags>(EncoderUseNativePF | EncoderLossy), -1) {}
virtual bool render(const PixelBuffer *pb) = 0;
virtual bool render(const PixelBuffer *pb, bool forceKeyFrame = false) = 0;
virtual void writeSkipRect() = 0;
~VideoEncoder() override = default;
};

@ -1 +1 @@
Subproject commit 005736944431d03699e09a17efe0d2eda7fa20da
Subproject commit 9b4cf1d90b05779821dede55062841dda946bb87