{"id":13753359,"url":"https://github.com/EHfive/pw-capture","last_synced_at":"2025-05-09T20:35:33.178Z","repository":{"id":115562202,"uuid":"600069514","full_name":"EHfive/pw-capture","owner":"EHfive","description":"Vulkan/OpenGL (game) capture for PipeWire","archived":false,"fork":false,"pushed_at":"2024-12-09T14:06:02.000Z","size":226,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-09T15:20:58.786Z","etag":null,"topics":["linux","opengl","pipewire","vulkan"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EHfive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-10T14:13:21.000Z","updated_at":"2024-12-09T14:06:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"1c6bb0e2-6f18-4078-a8b5-d384e8dec84b","html_url":"https://github.com/EHfive/pw-capture","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EHfive%2Fpw-capture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EHfive%2Fpw-capture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EHfive%2Fpw-capture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EHfive%2Fpw-capture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EHfive","download_url":"https://codeload.github.com/EHfive/pw-capture/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253321875,"owners_count":21890485,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linux","opengl","pipewire","vulkan"],"created_at":"2024-08-03T09:01:20.917Z","updated_at":"2025-05-09T20:35:28.150Z","avatar_url":"https://github.com/EHfive.png","language":"Rust","readme":"# pw-capture\n\nVulkan/OpenGL layer that captures render images to PipeWire server.\n\n```mermaid\nflowchart LR\n    subgraph pw-capture Layer\n        subgraph src_app[Vulkan/OpenGL App/Game]\n            src_frame(frame to be present)\n        end\n        src_frame --\u003e|copy \u0026 export| src_buf[(DMA-BUF fd 0..n)] \u003c==\u003e src_queue\n        subgraph src_client[PipeWire Client]\n            src_queue[(buffer queue)]\n        end\n    end\n\n    gst[gst-launch-1.0 pipewiresrc ! ..]\n    other[Other PipeWire video sinks/clients]\n\n    src_client -.-\u003e link1 -.-\u003e gst\n    src_client -.-\u003e link2 -.-\u003e other\n    subgraph server[PipeWire Server]\n        link1{{Link}}\n        link2{{Link}}\n    end\n```\n\n| Crate                          |                                                                                           |\n| ------------------------------ | ----------------------------------------------------------------------------------------- |\n| [pw-capture-client](./client/) | A PipeWire client library specialized for queued video capture                            |\n| [pw-capture-vk](./vulkan/)     | A Vulkan layer that can export copies of presented images in DMA-BUF                      |\n| [pw-capture-gl](./gl/)         | An OpenGL (GLX/EGL) intercept layer that can export copies of presented images in DMA-BUF |\n\nInspired by [obs-vkcapture](https://github.com/nowrep/obs-vkcapture).\n\n## Usage\n\nJust launch Vulkan/OpenGL apps with `pw-capture` wrapper, the capture node would now registered on PipeWire graph and waiting for connection from sink nodes (see section [Pipe image datas to GStreamer](#pipe-image-datas-to-gstreamer)).\n\n```bash\npw-capture vkcube\n# X11\npw-capture glxgears\npw-capture eglgears_x11\npw-capture glxgears32\n# Wayland\npw-capture eglgears_wayland\n# Wine apps using DXVK\npw-capture wine some_game.exe\n```\n\nOr for OpenGL app:\n\n```bash\n# `$LIB` is a dynamic string tokens of ld.so and would\n# expands to `lib64` or `lib32` depending on the architecture\nenv LD_LIBRARY_PATH=\"/usr/\\$LIB\" \\\n    LD_PRELOAD=libpw-capture-gl.so \\\n    glxgears\n```\n\nfor Vulkan app:\n\n```bash\nENABLE_PW_CAPTURE=1 vkcube\n```\n\n`pw-capture` script is just a combination of two above.\n\n**Note**: use `pw-dump` to inspect the node info and use tools like [pw-viz](https://github.com/Ax9D/pw-viz) or [qpwgraph](https://gitlab.freedesktop.org/rncbc/qpwgraph) to view the node in graph.\n\n### Requirements\n\n- pipewire: `\u003e=0.3.41`\n- libffi: Wayland event dispatching\n\nBelow are implicit dependencies and would be loaded on demand\n\n- libx11, libxcb: DRI3 buffer export and X11/XCB cursor query\n- libwayland-client: Wayland cursor interception\n- libglvnd: libEGL, libGLX/libGL interception\n\n### Installation\n\n| Repo       | Package                                                                         |\n| ---------- | ------------------------------------------------------------------------------- |\n| AUR (Arch) | [pw-capture-git](https://aur.archlinux.org/packages/pw-capture-git)             |\n|            | [lib32-pw-capture-git](https://aur.archlinux.org/packages/lib32-pw-capture-git) |\n\n#### Install Manually\n\nWe have set up a meson script to make installation more \\*unix idiomatic, you could instead follow [Development](#development).\n\n```bash\nmeson setup builddir --prefix /usr -Dprofile=release\n# avoid running cargo in root\nmeson install -C builddir --destdir destdir\ntree builddir/destdir\nsudo cp -r builddir/destdir/usr/. /usr\n```\n\nOptionally, build layers for 32-bit:\n\n```\nexport PKG_CONFIG=i686-pc-linux-gnu-pkg-config\nmeson setup builddir32 --prefix /usr --libdir lib32 \\\n    -Dprofile=release -Dtarget=i686-unknown-linux-gnu\nmeson install -C builddir32 --destdir destdir\ntree builddir32/destdir\nsudo cp -r builddir32/destdir/usr/lib32/. /usr/lib32\n```\n\n### Pipe image datas to GStreamer\n\nWith latest PipeWire(at least 0.3.66) gst plugins installed, you can pipe the node to other sinks with `pipewiresrc`. Currently it only supports `video/x-raw(memory:DMABuf)`, so you would have to use `gl*` plugins as intermediary.\n\n```bash\n# find the node `target-object` with command below\ngst-device-monitor-1.0 Video/Source\n# or use jq to filter \"object.serial\" property\npw-dump | jq '.[] | select(.info.props.\"media.software\" == \"pw-capture\") | .info.props.\"object.serial\"'\n\n# make GL plugins use EGL so it can import DMA-BUF as EGL image than to GL texture,\n# not required on Wayland as it uses EGL by default\nexport GST_GL_PLATFORM=egl\n\n# launch the pipeline, presuming \"object.serial\" of the layer node is 999\ngst-launch-1.0 -e pipewiresrc target-object=999 ! glimagesink ignore-alpha=0\n\n# force GRAY8 format, the color conversion is performed inside layer with `vkCmdBlitImage`\ngst-launch-1.0 -e pipewiresrc target-object=999 \\\n    ! 'video/x-raw(memory:DMABuf),format=GRAY8' ! glimagesink ignore-alpha=0\n\n# to convert `video/x-raw(memory:DMABuf)` to `video/x-raw`, use `glupload ! glcolorconvert ! gldownload`\ngst-launch-1.0 -e pipewiresrc target-object=999 \\\n    ! glupload ! glcolorconvert ! gldownload \\\n    ! queue ! 'video/x-raw' ! autovideosink\n\n# you can also pipe the src to `v4l2sink` (with v4l2loopback)\n# the videorate filter is required as `v4l2sink` does not accept variable framerate\ngst-launch-1.0 -e pipewiresrc target-object=999 min-buffers=64 \\\n    ! glupload ! glcolorconvert ! gldownload \\\n    ! videorate ! 'video/x-raw,format=YUY2,framerate=60/1' \\\n    ! v4l2sink device=/dev/video1\n\n# to encode with VA-API\ngst-launch-1.0 -e pipewiresrc target-object=999 min-buffers=64 \\\n    ! glupload ! glcolorconvert ! gldownload \\\n    ! videorate ! 'video/x-raw,framerate=60/1' ! queue \\\n    ! vah264enc ! h264parse ! mp4mux ! filesink location=test.mp4\n```\n\n## TODO\n\n- [x] Installation script\n- [x] OpenGL support\n- [x] Passing cursor position \u0026 bitmap in buffer meta (X11)\n- [x] Wayland cursor capture (by intercepting libwayland-client)\n- [x] Better handling of node description \u0026 Wine application node name\n- [ ] Support export image that maps or copies to memfd as fallback of DMA-BUF export\n- [ ] Add more control options (via env vars or config file)\n- [ ] Support color conversion to common YUV formats with render pipeline\n- [ ] Renegotiate stream format on Vulkan swapchain recreation\n- [ ] Allows single buffer display mode\n- [ ] Saner error handling, make sure dangling resources are freed before return\n- [ ] Support alternative server protocol (may be obs-vkcapture)\n\n## Development\n\n### Vulkan\n\nFirst build the layer.\n\n```bash\ncargo build -p pw-capture-vk\nstat ./target/debug/libpw_capture_vk.so\n```\n\nThen add layer [manifest](./vulkan/layer.json) to Vulkan loader lookup path and enable it, you can just source the [.envrc](./vulkan/.envrc) file.\n\n```bash\nsource ./vulkan/.envrc\nvulkaninfo | grep pwcapture\n```\n\nNow the layer would be loaded by Vulkan loader when Vulkan app launches. We would use `vkcube` (in `vulkan-tools`) here. You would see layer logs if it successfully loaded.\n\n```bash\nvkcube\n```\n\nYou can also find info of the layer created node with `pw-dump`.\n\n```bash\npw-dump | jq '.[] | select(.info.props.\"media.software\" == \"pw-capture\")'\n```\n\n### OpenGL\n\nFirst build the intercept library.\n\n```bash\ncargo build -p pw-capture-gl\nstat ./target/debug/libpw_capture_gl.so\n```\n\nSet LD_PRELOAD to the path of built library so it can hook onto GLX/EGL functions.\n\n```\nexport LD_PRELOAD=\"$(pwd)/target/debug/libpw_capture_gl.so\"\n```\n\nThe intercept layer supports both GLX and EGL, try it out with `glxgears`, `eglgears_x11` or `eglgears_wayland`.\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEHfive%2Fpw-capture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEHfive%2Fpw-capture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEHfive%2Fpw-capture/lists"}