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
33 changes: 27 additions & 6 deletions src/capture/video/linux/linux_pipewire_dma_capture.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ const vk = @import("vulkan");

const types = @import("../../../types.zig");
const util = @import("../../../util.zig");
const TokenStorage = @import("../../../common/linux/token_storage.zig");
const Vulkan = @import("../../../vulkan/vulkan.zig").Vulkan;
const Pipewire = @import("./pipewire/pipewire.zig").Pipewire;
const Chan = @import("../../../channel.zig").Chan;
const ChanError = @import("../../../channel.zig").ChanError;
const VideoCaptureSourceType = @import("../video_capture.zig").VideoCaptureSourceType;
const VideoCaptureSelection = @import("../video_capture.zig").VideoCaptureSelection;
const VideoCapture = @import("../video_capture.zig").VideoCapture;
const VideoCaptureError = @import("../video_capture.zig").VideoCaptureError;
const VulkanImageBuffer = @import("../../../vulkan/vulkan_image_buffer.zig").VulkanImageBuffer;
const rc = @import("zigrc");

pub const LinuxPipewireDmaCapture = struct {
const Self = @This();
const log = std.log.scoped(.LinuxPipewireDmaCapture);

allocator: std.mem.Allocator,
vulkan: *Vulkan,
Expand All @@ -31,7 +33,7 @@ pub const LinuxPipewireDmaCapture = struct {
return self;
}

pub fn selectSource(context: *anyopaque, source_type: VideoCaptureSourceType) (VideoCaptureError || anyerror)!void {
pub fn selectSource(context: *anyopaque, selection: VideoCaptureSelection) (VideoCaptureError || anyerror)!void {
const self: *Self = @ptrCast(@alignCast(context));
if (self.pipewire) |pipewire| {
// TODO: Probably don't have to destroy all of pipewire
Expand All @@ -45,10 +47,26 @@ pub const LinuxPipewireDmaCapture = struct {
self.vulkan,
);
errdefer {
self.pipewire.?.deinit();
self.pipewire = null;
if (self.pipewire) |pipewire| {
pipewire.deinit();
self.pipewire = null;
}
}
try self.pipewire.?.selectSource(source_type);
try self.pipewire.?.selectSource(selection);
}

pub fn shouldRestoreCaptureSession(context: *anyopaque) !bool {
const self: *Self = @ptrCast(@alignCast(context));
const restore_token = TokenStorage.loadToken(self.allocator, "restore_token") catch |err| {
log.err("failed to load restore token: {}", .{err});
return false;
};
if (restore_token == null) {
return false;
}
defer self.allocator.free(restore_token.?);

return restore_token.?.len > 0;
}

pub fn nextFrame(context: *anyopaque) ChanError!void {
Expand All @@ -72,7 +90,8 @@ pub const LinuxPipewireDmaCapture = struct {

pub fn size(context: *anyopaque) ?types.Size {
const self: *Self = @ptrCast(@alignCast(context));
return if (self.pipewire.?.info) |info|
const pipewire = self.pipewire orelse return null;
return if (pipewire.info) |info|
.{
.width = info.size.width,
.height = info.size.height,
Expand All @@ -95,6 +114,7 @@ pub const LinuxPipewireDmaCapture = struct {
const self: *Self = @ptrCast(@alignCast(context));
if (self.pipewire) |pipewire| {
pipewire.deinit();
self.pipewire = null;
}
self.allocator.destroy(self);
}
Expand All @@ -104,6 +124,7 @@ pub const LinuxPipewireDmaCapture = struct {
.ptr = self,
.vtable = &.{
.selectSource = selectSource,
.shouldRestoreCaptureSession = shouldRestoreCaptureSession,
.nextFrame = nextFrame,
.closeAllChannels = closeAllChannels,
.waitForFrame = waitForFrame,
Expand Down
44 changes: 29 additions & 15 deletions src/capture/video/linux/pipewire/pipewire.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const ChanError = @import("../../../../channel.zig").ChanError;
const VulkanImageBufferChan = @import("./vulkan_image_buffer_chan.zig").VulkanImageBufferChan;
const Vulkan = @import("../../../../vulkan/vulkan.zig").Vulkan;
const VideoCaptureError = @import("../../video_capture.zig").VideoCaptureError;
const VideoCaptureSourceType = @import("../../video_capture.zig").VideoCaptureSourceType;
const VideoCaptureSelection = @import("../../video_capture.zig").VideoCaptureSelection;
const c = @import("../../../../common/linux/pipewire_include.zig").c;
const c_def = @import("../../../../common/linux/pipewire_include.zig").c_def;
const Portal = @import("./portal.zig").Portal;
Expand Down Expand Up @@ -49,15 +49,25 @@ pub const Pipewire = struct {
vulkan: *Vulkan,
) (VideoCaptureError || anyerror)!*Self {
const self = try allocator.create(Self);
errdefer allocator.destroy(self);
self.* = Self{
.allocator = allocator,
.rx_chan = try .init(allocator),
.tx_chan = try .init(allocator),
.portal = try .init(allocator),
.rx_chan = undefined,
.tx_chan = undefined,
.portal = undefined,
.vulkan = vulkan,
.vulkan_image_buffer_chan = try VulkanImageBufferChan.init(allocator),
.vulkan_image_buffer_chan = undefined,
};

self.rx_chan = try .init(allocator);
errdefer self.rx_chan.deinit();
self.tx_chan = try .init(allocator);
errdefer self.tx_chan.deinit();
self.portal = try .init(allocator);
errdefer self.portal.deinit();
self.vulkan_image_buffer_chan = try .init(allocator);
errdefer self.vulkan_image_buffer_chan.deinit();

return self;
}

Expand Down Expand Up @@ -111,14 +121,14 @@ pub const Pipewire = struct {
self.allocator.destroy(self);
}

pub fn selectSource(
self: *Self,
source_type: VideoCaptureSourceType,
) (VideoCaptureError || anyerror)!void {
const pipewire_node = try self.portal.selectSource(source_type);
pub fn selectSource(self: *Self, selection: VideoCaptureSelection) (VideoCaptureError || anyerror)!void {
const pipewire_node = try self.portal.selectSource(selection);
const pipewire_fd = try self.portal.openPipewireRemote();
errdefer _ = pw.close(pipewire_fd);

self.has_format = false;
self.info = null;

self.thread_loop = pw.pw_thread_loop_new(
"spacecap-pipewire-capture-video",
null,
Expand Down Expand Up @@ -166,19 +176,23 @@ pub const Pipewire = struct {

try self.startStream(pipewire_node);

while (!self.has_format) {
while (!self.hasValidInfo()) {
pw.pw_thread_loop_wait(self.thread_loop);
}

pw.pw_thread_loop_unlock(self.thread_loop);

if (self.info == null or self.info.?.format < 0) {
return error.bad_format;
}

self.worker_thread = try std.Thread.spawn(.{}, workerMain, .{self});
}

fn hasValidInfo(self: *const Self) bool {
const info = self.info orelse return false;
return self.has_format and
info.format >= 0 and
info.size.width > 0 and
info.size.height > 0;
}

fn build_format(self: *const Self, b: ?*pw.spa_pod_builder, format: u32, modifiers: []const u64) ?*pw.spa_pod {
_ = self;
var format_frame = std.mem.zeroes(pw.spa_pod_frame);
Expand Down
Loading
Loading