diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index 18ad342..2a4299a 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -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& 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 *>(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 rects; diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h index 19ec5f0..b9f9761 100644 --- a/common/rfb/EncodeManager.h +++ b/common/rfb/EncodeManager.h @@ -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 ©passed, 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); diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index e0fb63a..7f1d628 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -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); diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index c317525..f6ad214 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -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 diff --git a/common/rfb/benchmark/benchmark.cxx b/common/rfb/benchmark/benchmark.cxx index 9295687..8174769 100644 --- a/common/rfb/benchmark/benchmark.cxx +++ b/common/rfb/benchmark/benchmark.cxx @@ -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); diff --git a/common/rfb/encoders/FFMPEGHWEncoder.cxx b/common/rfb/encoders/FFMPEGHWEncoder.cxx index 9ca58b9..dcf5c66 100644 --- a/common/rfb/encoders/FFMPEGHWEncoder.cxx +++ b/common/rfb/encoders/FFMPEGHWEncoder.cxx @@ -241,7 +241,7 @@ namespace rfb { } template - bool FFMPEGHWEncoder::render(const PixelBuffer *pb) { + bool FFMPEGHWEncoder::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", diff --git a/common/rfb/encoders/FFMPEGHWEncoder.h b/common/rfb/encoders/FFMPEGHWEncoder.h index ceff205..59f01a7 100644 --- a/common/rfb/encoders/FFMPEGHWEncoder.h +++ b/common/rfb/encoders/FFMPEGHWEncoder.h @@ -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; }; diff --git a/common/rfb/encoders/ScreenEncoderManager.cxx b/common/rfb/encoders/ScreenEncoderManager.cxx index 7a97bff..6927986 100644 --- a/common/rfb/encoders/ScreenEncoderManager.cxx +++ b/common/rfb/encoders/ScreenEncoderManager.cxx @@ -182,7 +182,7 @@ namespace rfb { } template - bool ScreenEncoderManager::writeFrame(const PixelBuffer *pb, const Palette &palette) { + bool ScreenEncoderManager::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; diff --git a/common/rfb/encoders/ScreenEncoderManager.h b/common/rfb/encoders/ScreenEncoderManager.h index 4465efe..bb7c810 100644 --- a/common/rfb/encoders/ScreenEncoderManager.h +++ b/common/rfb/encoders/ScreenEncoderManager.h @@ -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; diff --git a/common/rfb/encoders/SoftwareEncoder.cxx b/common/rfb/encoders/SoftwareEncoder.cxx index e7926b3..4054f5a 100644 --- a/common/rfb/encoders/SoftwareEncoder.cxx +++ b/common/rfb/encoders/SoftwareEncoder.cxx @@ -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 diff --git a/common/rfb/encoders/SoftwareEncoder.h b/common/rfb/encoders/SoftwareEncoder.h index 6a6e2c4..5c64752 100644 --- a/common/rfb/encoders/SoftwareEncoder.h +++ b/common/rfb/encoders/SoftwareEncoder.h @@ -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 diff --git a/common/rfb/encoders/VideoEncoder.h b/common/rfb/encoders/VideoEncoder.h index 7d3a19b..9024511 100644 --- a/common/rfb/encoders/VideoEncoder.h +++ b/common/rfb/encoders/VideoEncoder.h @@ -41,7 +41,7 @@ namespace rfb { public: VideoEncoder(Id id, SConnection *conn) : Encoder(id, conn, encodingKasmVideo, static_cast(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; }; diff --git a/kasmweb b/kasmweb index 0057369..9b4cf1d 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit 005736944431d03699e09a17efe0d2eda7fa20da +Subproject commit 9b4cf1d90b05779821dede55062841dda946bb87