Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 37 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
</p>
<h1 align="center">Spacecap</h1>

A hardware accelerated screen recording/replay application focused on performance.
Currently only supports Linux. Still in early development (see roadmap below).
A hardware accelerated screen recording tool for Linux. _Still in development
(see features/roadmap below)_.

- Written in [Zig](https://ziglang.org/).
- Written in [Zig](https://ziglang.org/) (0.16.0).
- Video encoding with Vulkan Video ([vulkan-zig](https://github.com/Snektron/vulkan-zig)).
- UI built with [imgui](https://github.com/ocornut/imgui)/[SDL3](https://github.com/allyourcodebase/SDL3).
- Muxing/Audio encoding with [FFmpeg](https://www.ffmpeg.org/).

---

![screenshot2](./docs/screenshot_4.png)

## Installation

```sh
# Spacecap will be installed to ~/.local/bin/spacecap

# Install
curl -LsSf https://spacecap.org/install | sh

Expand All @@ -39,27 +39,44 @@ curl -LsSf https://spacecap.org/install | sh -s -- --uninstall
- Video player/editor.
- Simple video editor (trim start/end).
- File browser to select videos to edit.
- Additional video output formats (mp4, mov, mkv, gif, etc.).
- Windows support.

## Requirements

- A GPU that supports Vulkan Video.
- A GPU that supports Vulkan Video encoding.

**NOTE:** Only tested on an Nvidia GPU (RTX 3080) so far. AMD will be supported, I just
have no way of testing at this time.
**NOTE:** So far this has only been tested on an Nvidia GPU (RTX 3080). AMD will
be supported, I just have no way of testing at this time.

### Linux

- vulkan
- pipewire
- pipewire-pulse
- Wayland
- Pipewire

#### Global Keybinds
### Windows

If your version of Linux supports [xdg-desktop-portal global shortcuts](https://wiki.archlinux.org/title/XDG_Desktop_Portal#List_of_backends_and_interfaces)
then they can be configured that way. Alternatively, Spacecap runs an IPC
server, which can be communicated with via Spacecap CLI.
- Windows is not yet supported. Spacecap is architected in such a way that it
can be cross platform. For Windows support, the audio/video capture interfaces
need to be implemented. It's on the roadmap, but is not currently a priority.

## Global Keybinds

### Linux

[xdg-desktop-portal global
shortcuts](https://wiki.archlinux.org/title/XDG_Desktop_Portal#List_of_backends_and_interfaces)
can be used if your desktop environment supports it, otherwise the Spacecap CLI
can be used to send commands.

e.g.

```sh
# Save replay
spacecap -s save-replay

# List available commands
spacecap -h
```

For example, here is what a config in [niri](https://github.com/YaLTeR/niri) would look like:

Expand All @@ -69,20 +86,9 @@ binds {
}
```

Use `spacecap -h` to see available commands.

### Windows

Windows is not yet supported. Spacecap is architected in such a way
that it can be cross platform. For Windows support, the audio/video capture
interfaces need to be implemented. It's on the roadmap, but is not currently
a priority.

## Development

[Nix](https://nixos.org/download/#download-nix) is required for development,
unless you want to install all dependencies manually. See `flake.nix` if you'd
like to do so.
[Nix](https://nixos.org/download/#download-nix) is required for development.

```sh
# Build
Expand All @@ -98,15 +104,10 @@ nix develop -c zig build test -Dnix
## Logging

By default, Spacecap only writes error logs to `error.log`. Set the
`SPACECAP_LOG_LEVEL` environment variable to one of the following to change this
behavior:

- debug
- info
- warning
- error
`SPACECAP_LOG_LEVEL` environment variable to `debug`, `info`, `warning`, or
`error`.

Crash logs are written to `crash.log`. This happens when a panic occurs.
Crash logs are written to `crash.log`, which happens when a panic occurs.

#### Log Location

Expand Down
11 changes: 11 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,14 @@ else
fi
echo "Installed the desktop entry to $DESKTOP_PATH."
echo "Installed the app icon to $ICON_PATH."

case ":$PATH:" in
*:"$INSTALL_DIR":*)
;;
*)
echo
echo "$INSTALL_DIR is not on your PATH."
echo "To use the Spacecap CLI, add it to your shell profile, then restart your shell:"
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
;;
esac
2 changes: 1 addition & 1 deletion src/audio/audio_encoder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const ffmpeg = @import("../ffmpeg.zig").ffmpeg;
const checkErr = @import("../ffmpeg.zig").check_err;

// TODO: Make this a user setting.
const AUDIO_BIT_RATE: i64 = 320_000;
const AUDIO_BIT_RATE: i64 = 128_000;

pub const EncodedAudioPacketNode = struct {
data: [*c]const ffmpeg.AVPacket,
Expand Down
6 changes: 5 additions & 1 deletion src/capture/video/linux/pipewire/pipewire_video.zig
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,11 @@ pub const PipewireVideo = struct {
if (copy_data.vulkan_image_buffer) |vulkan_image_buffer| {
self.vulkan_image_buffer_chan.drain();
self.vulkan_image_buffer_chan.send(vulkan_image_buffer.clone()) catch |err| {
log.err("[stream_process_callback] vulkan image buffer chan send err: {}", .{err});
if (err == ChanError.Closed) {
log.debug("[stream_process_callback] vulkan image buffer chan closed", .{});
} else {
log.err("[stream_process_callback] vulkan image buffer chan send err: {}", .{err});
}
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub const VulkanImageBufferChan = struct {

/// Increment the buffer ref count, set to in use, then send on the channel.
/// On send error, release the buffer and return the error.
/// Takes ownership of vulkan_image_buffer. Clone before passing in.
pub fn send(self: *Self, vulkan_image_buffer: Arc(VulkanImageBuffer)) ChanError!void {
defer vulkan_image_buffer.deinit();
errdefer vulkan_image_buffer.as_ptr().in_use.store(false, .release);
Expand Down
2 changes: 2 additions & 0 deletions src/vulkan/vulkan_image_buffer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub const VulkanImageBuffer = struct {

width: u32,
height: u32,
/// This should be true until the image buffer has been released from the
/// encode pipeline.
in_use: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),

pub const InitArgs = struct {
Expand Down
Loading