QtScrcpy

repository·dev·Indexed 12 days ago

https://github.com/barry-ran/qtscrcpy

A lightweight, high-performance tool for displaying and controlling Android devices via USB or WiFi. It features a Qt-based GUI for screen mirroring, key mapping for games, group control for multiple devices, and screen recording without requiring root access. It includes specialized high-performance video decoding for Apple Silicon (M1/M2/M3) using VideoToolbox and Metal for zero-copy GPU rendering.

Tokens
10.8K
Snippets
17
Records
53
Agent score
98%

What's inside QtScrcpy

  1. High-performance video decoding on Apple Silicon (M1) using VideoToolbox and Metal

    dev

    For users running QtScrcpy on macOS with Apple Silicon (M1/M2/M3), the recommended high-performance architecture (Scheme C) utilizes VideoToolbox for hardware-accelerated H.264 decoding and Metal for zero-copy GPU rendering. This approach significantly reduces CPU usage (from ~68% down to ~5-8%) by avoiding FFmpeg software decoding and bypassing the OpenGL-to-Metal translation layer.

    Key Performance Benefits:

    • Hardware Decoding: Uses the M1 Media Engine via VTDecompressionSession, resulting in ~0% CPU decoding load.
    • Zero-Copy Data Flow: Leverages Unified Memory Architecture (UMA) by passing CVPixelBufferRef (IOSurface) directly from the decoder to Metal textures, eliminating CPU-to-GPU memory copies.
    • Native Rendering: Uses Metal shaders to convert NV12 YUV data to BT.709 RGB, avoiding the slow paths associated with GL_LUMINANCE in OpenGL.

    Architecture Overview

    The data flow follows this pipeline:

    1. Demuxer Thread: Receives AVPacket $\rightarrow$ VTDecoder::decode $\rightarrow$ creates CMSampleBuffer $\rightarrow$ hardware decodes via VTDecompressionSessionDecodeFrame $\rightarrow$ returns CVPixelBufferRef.
    2. Main (GUI) Thread: Receives CVPixelBufferRef $\rightarrow$ CVMetalTextureCacheCreateTextureFromImage (creates zero-copy MTLTexture) $\rightarrow$ MTLCommandBuffer executes shaders $\rightarrow$ renders to a QWidget container.
  2. How the client handles video streaming and decoding

    dev

    The client's stream thread receives the H.264 video stream from the socket.

    • Decoding: If a decoder is present (i.e., --no-display is not set), it uses libav (FFmpeg) to decode the stream and notifies the main thread when a new frame is ready.
    • Double Buffering: To minimize latency, the client maintains two frames in memory: a decoding frame (written by the decoder thread) and a rendering frame (rendered as a texture in the main thread). The threads swap these frames using proper synchronization.
    • Recording: If --record is enabled, the raw H.264 packets are muxed into an output video file by a recorder component.
                                       +----------+      +----------+ 
                                  ---> | decoder  | ---> |  screen  | 
                 +---------+     /     +----------+      +----------+ 
     socket ---> | stream  | ---- 
                 +---------+     \ 
                                  ---> | recorder | 
  3. How the QtScrcpy architecture works

    dev

    QtScrcpy operates using a client-server model consisting of two main components:

    1. The Server (scrcpy-server): A Java application executed on the Android device as the shell user. It captures the screen, encodes it into an H.264 video stream, and receives input events (keyboard/mouse) from the client to inject them into the device.
    2. The Client (scrcpy binary): Executed on the host computer. It is responsible for pushing the server to the device, starting its execution, decoding the video stream (using libav/FFmpeg), displaying it (using SDL), and capturing user input events to send back to the server.

    Communication Flow:

    • Video: The server sends a raw H.264 stream; the client decodes and displays it with minimal buffering to reduce latency.
    • Input: The client captures keyboard/mouse events and transmits them to the server, which injects them into the Android system.
    • Network Roles: At the application level, the server serves video and the client controls the device. However, at the network level, the client opens a server socket and listens, while the server connects to the client to avoid race conditions.
  4. Understand the key mapping JSON structure

    dev

    The key map JSON file consists of several top-level configuration sections:

    • switchKey: The key used to toggle between the default mapping and your custom mapping.
    • mouseMoveMap: Configuration for mouse movement mapping (useful for FPS games to control camera/vision).
    • keyMapNodes: A JSON array containing all general key mappings (mapping keyboard keys to finger clicks/drags).

    Mouse Movement Mapping (mouseMoveMap)

    This maps mouse movement to a finger drag operation. When enabled, the mouse is hidden and its movement is restricted to a specific range.

    KeyDescription
    startPosThe starting point for the finger drag operation.
    speedRatioMouse sensitivity. Minimum value is 0.00225. Higher values decrease sensitivity.
    speedRatioXX-axis sensitivity. Minimum value is 0.001.
    speedRatioYY-axis sensitivity. Minimum value is 0.001.
    smallEyesA button object that, when pressed, triggers mouse movement starting from smallEyes.pos.
  5. How the client manages input and UI

    dev

    The client uses SDL for cross-platform UI, input, and threading.

    • Main Thread: Manages the SDL event loop, initialization, input events, and rendering. It handles the screen updates and delegates events to the input manager.
    • Controller Thread: Runs in a separate thread to avoid I/O blocking on the main thread. It receives SDL events from the main thread, converts them to Android-compatible control messages via the input manager, and serializes/sends them to the server.
    • Receiver Thread: Managed by the controller, it receives device messages (like clipboard content) from the server.
  6. Implement Apple Silicon (M1) VideoToolbox + Metal Acceleration

    dev

    For high-performance screen casting on Apple Silicon (M1/M2/M3), use the VideoToolbox + Metal pipeline. This approach utilizes hardware-accelerated decoding via VTDecompressionSession and zero-copy rendering via Metal and IOSurface.

    Key architectural components:

    • VTDecoder: Manages VTDecompressionSession and handles AVCDecoderConfigurationRecord parsing for SPS/PPS extraction.
    • MetalVideoWindow: A QWindow subclass that uses CAMetalLayer and CVMetalTextureCache to render CVPixelBuffer frames with zero-copy.
    • Zero-Copy Data Flow: AVPacket data is wrapped into a CMSampleBuffer using kCFAllocatorNull to avoid copying, and CVPixelBuffer is converted to MTLTexture via CVMetalTextureCache.
    /* High-level flow for M1 acceleration */
    // 1. Demuxer pushes AVPacket
    // 2. VTDecoder parses SPS/PPS and creates VTDecompressionSession
    // 3. VTDecoder wraps AVPacket data into CMSampleBuffer (zero-copy)
    // 4. VTDecompressionSession decodes to CVPixelBuffer
    // 5. MetalVideoWindow renders CVPixelBuffer via CVMetalTextureCache
  7. Configure mouse movement mapping (mouseMoveMap)

    dev

    The mouseMoveMap type maps mouse movement to finger dragging operations. This is commonly used in FPS games to control character vision. When enabled, the mouse cursor is hidden and its movement range is restricted.

    Properties:

    • startPos: The starting point for the finger drag.
    • speedRatio: The ratio of mouse movement to finger drag. A higher value decreases sensitivity. The value must be > 0.00225. (Note: Y-axis is translated at a 2.25 ratio).
    • speedRatioX: Sensitivity for the X-axis. Must be at least 0.001.
    • speedRatioY: Sensitivity for the Y-axis. Must be at least 0.001.
    • smallEyes: A key that, when pressed, triggers a drag starting from the position defined in smallEyes.pos based on mouse movement direction.
  8. How the server handles video encoding and rotation

    dev

    The server manages encoding via the ScreenEncoder class using the Android MediaCodec API.

    • Input/Output: The codec takes input from a display-associated [surface] and writes the H.264 stream to the socket connected to the client.
    • Rotation: When the device rotates, the codec, surface, and display are reinitialized, and a new video stream is produced.
    • Frame Optimization: New frames are only produced when changes occur on the surface to save bandwidth. To prevent issues like missing the first frame on start or poor quality after fast motion, the KEY_REPEAT_PREVIOUS_FRAME_AFTER flag is used.
  9. How input events are injected into the device

    dev

    The server's Controller thread listens for control messages (keycode, text, mouse motion/click, mouse scroll, or commands like switching the screen on) from the client.

    To inject these into the Android system, the server uses the hidden InputManager.injectInputEvent method, which is exposed through an InputManager wrapper class.

  10. Connect to an Android device via USB or WiFi

    dev

    USB Connection

    1. Connect your Android device to your computer via USB.
    2. Run QtScrcpy.
    3. Click USB connect.

    Wireless (WiFi) Connection

    Ensure both the mobile phone and PC are on the same Local Area Network (LAN).

    1. Enable USB debugging in the Android developer options.
    2. Connect the device to the computer via USB.
    3. In QtScrcpy, click update device to see the device list.
    4. Click get device IP.
    5. Click start adbd.
    6. Click wireless connect.
    7. Click update device again. Select the device that appears with an IP address.
    8. Click start service.

    Note: You do not need to keep the USB cable connected after starting adbd.

  11. Connect to Android devices via USB or WiFi

    dev

    USB Connection

    1. Connect your Android device to your computer via USB.
    2. In QtScrcpy, click 一键USB连接 (One-click USB connection).

    Wireless (WiFi) Connection

    1. Ensure both the phone and computer are on the same local network.
    2. Enable USB Debugging in the Android device's Developer Options.
    3. Connect the phone to the computer via USB temporarily.
    4. In QtScrcpy, click Refresh Device List (刷新设备列表) to see the device.
    5. Click Get Device IP (获取设备IP).
    6. Click Start adbd (启动 adbd).
    7. Click Wireless Connection (无线连接).
    8. Click Refresh Device List again; select the device entry starting with an IP address.
    9. Click Start Service (启动服务).

    Note: Once adbd is started, you can disconnect the USB cable. The wireless connection will persist unless adbd stops running.

  12. Install QtScrcpy on Windows, macOS, or Linux

    dev

    You can download pre-compiled binaries for your platform or install via package managers where available.

    Windows and macOS

    Download the executable directly from the release pages:

    Linux

    • Arch Linux: Install via AUR using yay -Syu qtscrcpy.
    • Other Distributions: Download pre-compiled binaries from GitHub Releases or obtain the latest builds from GitHub Actions.

    Requirements

    • Android devices must support at least API 21 (Android 5.0).
    • USB Debugging must be enabled on the Android device via Developer Options.