272 lines
9.6 KiB
Diff
272 lines
9.6 KiB
Diff
--- a/app/streaming/input/mouse.cpp
|
|
+++ b/app/streaming/input/mouse.cpp
|
|
@@ -1,4 +1,5 @@
|
|
#include "input.h"
|
|
+#include "vdi_mouse_batch.h"
|
|
|
|
#include <Limelight.h>
|
|
#include "SDL_compat.h"
|
|
@@ -84,6 +85,11 @@
|
|
button = BUTTON_RIGHT;
|
|
}
|
|
|
|
+ // Button coordinates are captured with this event, not a later mouse state.
|
|
+ if (m_AbsoluteMouseMode) {
|
|
+ sendAbsoluteMousePosition(event->x, event->y);
|
|
+ }
|
|
+
|
|
LiSendMouseButtonEvent(event->state == SDL_PRESSED ?
|
|
BUTTON_ACTION_PRESS :
|
|
BUTTON_ACTION_RELEASE,
|
|
@@ -101,81 +107,78 @@
|
|
return;
|
|
}
|
|
|
|
- // Batch all pending mouse motion events to save CPU time
|
|
- Sint32 x = event->x, y = event->y, xrel = event->xrel, yrel = event->yrel;
|
|
- SDL_Event nextEvent;
|
|
- while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
|
- event = &nextEvent.motion;
|
|
-
|
|
- // Ignore synthetic mouse events
|
|
- if (event->which != SDL_TOUCH_MOUSEID) {
|
|
- x = event->x;
|
|
- y = event->y;
|
|
- xrel += event->xrel;
|
|
- yrel += event->yrel;
|
|
- }
|
|
- }
|
|
+ // Coalesce only adjacent motions. Never consume motion past a click/key
|
|
+ // or render event, and bound work so a fast mouse cannot starve rendering.
|
|
+ SDL_MouseMotionEvent batched = *event;
|
|
+ vdiBatchMouseMotion(batched);
|
|
+ Sint32 x = batched.x, y = batched.y;
|
|
+ Sint32 xrel = batched.xrel, yrel = batched.yrel;
|
|
|
|
// We should not reference the original event anymore
|
|
event = nullptr;
|
|
|
|
if (m_AbsoluteMouseMode) {
|
|
- int windowWidth, windowHeight;
|
|
- SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight);
|
|
-
|
|
- SDL_Rect src, dst;
|
|
- bool mouseInVideoRegion;
|
|
-
|
|
- src.x = src.y = 0;
|
|
- src.w = m_StreamWidth;
|
|
- src.h = m_StreamHeight;
|
|
-
|
|
- dst.x = dst.y = 0;
|
|
- dst.w = windowWidth;
|
|
- dst.h = windowHeight;
|
|
-
|
|
- // Use the stream and window sizes to determine the video region
|
|
- StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
|
-
|
|
- mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight);
|
|
-
|
|
- // Clamp motion to the video region
|
|
- x = qMin(qMax(x - dst.x, 0), dst.w);
|
|
- y = qMin(qMax(y - dst.y, 0), dst.h);
|
|
-
|
|
- // Send the mouse position update if one of the following is true:
|
|
- // a) it is in the video region now
|
|
- // b) it just left the video region (to ensure the mouse is clamped to the video boundary)
|
|
- // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging)
|
|
- Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr);
|
|
- if (buttonState == 0) {
|
|
- if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
|
- if (m_NeedsManualCaptureOnLeave) {
|
|
- // Stop capturing the mouse now
|
|
- SDL_CaptureMouse(SDL_FALSE);
|
|
- }
|
|
- m_PendingMouseButtonsAllUpOnVideoRegionLeave = false;
|
|
- }
|
|
- }
|
|
- if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
|
- LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h);
|
|
- }
|
|
-
|
|
- // Adjust the cursor visibility if applicable
|
|
- if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) {
|
|
- SDL_ShowCursor((mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? SDL_DISABLE : SDL_ENABLE);
|
|
- if (!mouseInVideoRegion && buttonState != 0) {
|
|
- // If we still have a button pressed on leave, wait for that to come up
|
|
- // before we stop sending mouse position events.
|
|
- m_PendingMouseButtonsAllUpOnVideoRegionLeave = true;
|
|
- }
|
|
- }
|
|
-
|
|
- m_MouseWasInVideoRegion = mouseInVideoRegion;
|
|
+ sendAbsoluteMousePosition(x, y);
|
|
}
|
|
else {
|
|
LiSendMouseMoveEvent(xrel, yrel);
|
|
}
|
|
+}
|
|
+
|
|
+void SdlInputHandler::sendAbsoluteMousePosition(Sint32 x, Sint32 y)
|
|
+{
|
|
+ int windowWidth, windowHeight;
|
|
+ SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight);
|
|
+
|
|
+ SDL_Rect src, dst;
|
|
+ bool mouseInVideoRegion;
|
|
+
|
|
+ src.x = src.y = 0;
|
|
+ src.w = m_StreamWidth;
|
|
+ src.h = m_StreamHeight;
|
|
+
|
|
+ dst.x = dst.y = 0;
|
|
+ dst.w = windowWidth;
|
|
+ dst.h = windowHeight;
|
|
+
|
|
+ // Use the stream and window sizes to determine the video region
|
|
+ StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
|
|
+
|
|
+ mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight);
|
|
+
|
|
+ // Clamp motion to the video region
|
|
+ x = qMin(qMax(x - dst.x, 0), dst.w);
|
|
+ y = qMin(qMax(y - dst.y, 0), dst.h);
|
|
+
|
|
+ // Send the mouse position update if one of the following is true:
|
|
+ // a) it is in the video region now
|
|
+ // b) it just left the video region (to ensure the mouse is clamped to the video boundary)
|
|
+ // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging)
|
|
+ Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr);
|
|
+ if (buttonState == 0) {
|
|
+ if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
|
+ if (m_NeedsManualCaptureOnLeave) {
|
|
+ // Stop capturing the mouse now
|
|
+ SDL_CaptureMouse(SDL_FALSE);
|
|
+ }
|
|
+ m_PendingMouseButtonsAllUpOnVideoRegionLeave = false;
|
|
+ }
|
|
+ }
|
|
+ if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) {
|
|
+ LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h);
|
|
+ }
|
|
+
|
|
+ // Adjust the cursor visibility if applicable
|
|
+ if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) {
|
|
+ SDL_ShowCursor((mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? SDL_DISABLE : SDL_ENABLE);
|
|
+ if (!mouseInVideoRegion && buttonState != 0) {
|
|
+ // If we still have a button pressed on leave, wait for that to come up
|
|
+ // before we stop sending mouse position events.
|
|
+ m_PendingMouseButtonsAllUpOnVideoRegionLeave = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ m_MouseWasInVideoRegion = mouseInVideoRegion;
|
|
}
|
|
|
|
void SdlInputHandler::handleMouseWheelEvent(SDL_MouseWheelEvent* event)
|
|
--- a/app/streaming/input/input.h
|
|
+++ b/app/streaming/input/input.h
|
|
@@ -95,6 +95,8 @@
|
|
void handleMouseButtonEvent(SDL_MouseButtonEvent* event);
|
|
|
|
void handleMouseMotionEvent(SDL_MouseMotionEvent* event);
|
|
+
|
|
+ void sendAbsoluteMousePosition(Sint32 x, Sint32 y);
|
|
|
|
void handleMouseWheelEvent(SDL_MouseWheelEvent* event);
|
|
|
|
--- /dev/null
|
|
+++ b/app/streaming/input/vdi_mouse_batch.h
|
|
@@ -0,0 +1,22 @@
|
|
+#pragma once
|
|
+#include <SDL.h>
|
|
+
|
|
+// Only the SDL main thread consumes events; other threads may append events.
|
|
+inline void vdiBatchMouseMotion(SDL_MouseMotionEvent& motion)
|
|
+{
|
|
+ for (int i = 0; i < 64; ++i) {
|
|
+ SDL_Event next;
|
|
+ if (SDL_PeepEvents(&next, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 1 ||
|
|
+ next.type != SDL_MOUSEMOTION || next.motion.which != motion.which ||
|
|
+ next.motion.windowID != motion.windowID || next.motion.state != motion.state) {
|
|
+ break;
|
|
+ }
|
|
+ if (SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 1) {
|
|
+ break;
|
|
+ }
|
|
+ motion.x = next.motion.x;
|
|
+ motion.y = next.motion.y;
|
|
+ motion.xrel += next.motion.xrel;
|
|
+ motion.yrel += next.motion.yrel;
|
|
+ }
|
|
+}
|
|
--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
|
|
+++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp
|
|
@@ -1,4 +1,5 @@
|
|
#include "pacer.h"
|
|
+#include "vdi_latest_frame.h"
|
|
#include "streaming/streamutils.h"
|
|
|
|
#ifdef Q_OS_WIN32
|
|
@@ -34,6 +35,7 @@
|
|
m_VsyncThread(nullptr),
|
|
m_DeferredFreeFrame(nullptr),
|
|
m_Stopping(false),
|
|
+ m_LatestFrameOnly(qEnvironmentVariableIntValue("VDI_LATEST_FRAME") == 1),
|
|
m_VsyncSource(nullptr),
|
|
m_VsyncRenderer(renderer),
|
|
m_MaxVideoFps(0),
|
|
@@ -178,7 +180,14 @@
|
|
|
|
void Pacer::enqueueFrameForRenderingAndUnlock(AVFrame *frame)
|
|
{
|
|
- dropFrameForEnqueue(m_RenderQueue);
|
|
+ if (m_LatestFrameOnly && m_VsyncSource == nullptr) {
|
|
+ // Drop only decoded, never presented frames. Keep the decoder reference
|
|
+ // chain and the deferred free of the last presented frame intact.
|
|
+ m_VideoStats->pacerDroppedFrames += vdiDiscardQueuedFrames(m_RenderQueue);
|
|
+ }
|
|
+ else {
|
|
+ dropFrameForEnqueue(m_RenderQueue);
|
|
+ }
|
|
m_RenderQueue.enqueue(frame);
|
|
|
|
m_FrameQueueLock.unlock();
|
|
@@ -261,6 +270,8 @@
|
|
|
|
bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing)
|
|
{
|
|
+ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "VDI latest-frame queue: %s",
|
|
+ (m_LatestFrameOnly && !enablePacing) ? "enabled" : "disabled");
|
|
m_MaxVideoFps = maxVideoFps;
|
|
m_DisplayFps = StreamUtils::getDisplayRefreshRate(window);
|
|
m_RendererAttributes = m_VsyncRenderer->getRendererAttributes();
|
|
--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.h
|
|
+++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.h
|
|
@@ -68,6 +68,7 @@
|
|
SDL_Thread* m_VsyncThread;
|
|
AVFrame* m_DeferredFreeFrame;
|
|
bool m_Stopping;
|
|
+ bool m_LatestFrameOnly;
|
|
|
|
IVsyncSource* m_VsyncSource;
|
|
IFFmpegRenderer* m_VsyncRenderer;
|
|
--- /dev/null
|
|
+++ b/app/streaming/video/ffmpeg-renderers/pacer/vdi_latest_frame.h
|
|
@@ -0,0 +1,16 @@
|
|
+#pragma once
|
|
+#include <QQueue>
|
|
+extern "C" {
|
|
+#include <libavutil/frame.h>
|
|
+}
|
|
+
|
|
+inline unsigned vdiDiscardQueuedFrames(QQueue<AVFrame*>& queue)
|
|
+{
|
|
+ unsigned dropped = 0;
|
|
+ while (!queue.isEmpty()) {
|
|
+ AVFrame* stale = queue.dequeue();
|
|
+ av_frame_free(&stale);
|
|
+ ++dropped;
|
|
+ }
|
|
+ return dropped;
|
|
+}
|