sysaudio: load libpipewire-0.3.so dynamically

This commit is contained in:
Ali Chraghi 2023-01-21 00:30:50 +03:30 committed by Stephen Gutekanst
parent 655e061d50
commit 05968f0496
3 changed files with 69 additions and 21 deletions

View file

@ -34,7 +34,6 @@ pub fn Sdk(comptime deps: anytype) type {
step.linkFramework("CoreAudio"); step.linkFramework("CoreAudio");
} else if (step.target.toTarget().os.tag == .linux) { } else if (step.target.toTarget().os.tag == .linux) {
step.addCSourceFile(sdkPath("/src/pipewire/sysaudio.c"), &.{"-std=gnu99"}); step.addCSourceFile(sdkPath("/src/pipewire/sysaudio.c"), &.{"-std=gnu99"});
step.linkSystemLibrary("pipewire-0.3");
step.linkLibC(); step.linkLibC();
} }
} }

View file

@ -7,6 +7,52 @@ const main = @import("main.zig");
const backends = @import("backends.zig"); const backends = @import("backends.zig");
const util = @import("util.zig"); const util = @import("util.zig");
const lib = struct {
var handle: std.DynLib = undefined;
var pw_init: *const fn ([*c]c_int, [*c][*c][*c]u8) callconv(.C) void = undefined;
var pw_deinit: *const fn () callconv(.C) void = undefined;
var pw_thread_loop_new: *const fn ([*c]const u8, [*c]const c.spa_dict) callconv(.C) ?*c.pw_thread_loop = undefined;
var pw_thread_loop_destroy: *const fn (?*c.pw_thread_loop) callconv(.C) void = undefined;
var pw_thread_loop_start: *const fn (?*c.pw_thread_loop) callconv(.C) c_int = undefined;
var pw_thread_loop_stop: *const fn (?*c.pw_thread_loop) callconv(.C) void = undefined;
var pw_thread_loop_signal: *const fn (?*c.pw_thread_loop, bool) callconv(.C) void = undefined;
var pw_thread_loop_wait: *const fn (?*c.pw_thread_loop) callconv(.C) void = undefined;
var pw_thread_loop_lock: *const fn (?*c.pw_thread_loop) callconv(.C) void = undefined;
var pw_thread_loop_unlock: *const fn (?*c.pw_thread_loop) callconv(.C) void = undefined;
var pw_thread_loop_get_loop: *const fn (?*c.pw_thread_loop) callconv(.C) [*c]c.pw_loop = undefined;
var pw_properties_new: *const fn ([*c]const u8, ...) callconv(.C) [*c]c.pw_properties = undefined;
var pw_stream_new_simple: *const fn ([*c]c.pw_loop, [*c]const u8, [*c]c.pw_properties, [*c]const c.pw_stream_events, ?*anyopaque) callconv(.C) ?*c.pw_stream = undefined;
var pw_stream_destroy: *const fn (?*c.pw_stream) callconv(.C) void = undefined;
var pw_stream_connect: *const fn (?*c.pw_stream, c.spa_direction, u32, c.pw_stream_flags, [*c][*c]const c.spa_pod, u32) callconv(.C) c_int = undefined;
var pw_stream_queue_buffer: *const fn (?*c.pw_stream, [*c]c.pw_buffer) callconv(.C) c_int = undefined;
var pw_stream_dequeue_buffer: *const fn (?*c.pw_stream) callconv(.C) [*c]c.pw_buffer = undefined;
var pw_stream_get_state: *const fn (?*c.pw_stream, [*c][*c]const u8) callconv(.C) c.pw_stream_state = undefined;
pub fn load() !void {
handle = std.DynLib.openZ("libpipewire-0.3.so") catch return error.LibraryNotFound;
pw_init = handle.lookup(@TypeOf(pw_init), "pw_init") orelse return error.SymbolLookup;
pw_deinit = handle.lookup(@TypeOf(pw_deinit), "pw_deinit") orelse return error.SymbolLookup;
pw_thread_loop_new = handle.lookup(@TypeOf(pw_thread_loop_new), "pw_thread_loop_new") orelse return error.SymbolLookup;
pw_thread_loop_destroy = handle.lookup(@TypeOf(pw_thread_loop_destroy), "pw_thread_loop_destroy") orelse return error.SymbolLookup;
pw_thread_loop_start = handle.lookup(@TypeOf(pw_thread_loop_start), "pw_thread_loop_start") orelse return error.SymbolLookup;
pw_thread_loop_stop = handle.lookup(@TypeOf(pw_thread_loop_stop), "pw_thread_loop_stop") orelse return error.SymbolLookup;
pw_thread_loop_signal = handle.lookup(@TypeOf(pw_thread_loop_signal), "pw_thread_loop_signal") orelse return error.SymbolLookup;
pw_thread_loop_wait = handle.lookup(@TypeOf(pw_thread_loop_wait), "pw_thread_loop_wait") orelse return error.SymbolLookup;
pw_thread_loop_lock = handle.lookup(@TypeOf(pw_thread_loop_lock), "pw_thread_loop_lock") orelse return error.SymbolLookup;
pw_thread_loop_unlock = handle.lookup(@TypeOf(pw_thread_loop_unlock), "pw_thread_loop_unlock") orelse return error.SymbolLookup;
pw_thread_loop_get_loop = handle.lookup(@TypeOf(pw_thread_loop_get_loop), "pw_thread_loop_get_loop") orelse return error.SymbolLookup;
pw_properties_new = handle.lookup(@TypeOf(pw_properties_new), "pw_properties_new") orelse return error.SymbolLookup;
pw_stream_new_simple = handle.lookup(@TypeOf(pw_stream_new_simple), "pw_stream_new_simple") orelse return error.SymbolLookup;
pw_stream_destroy = handle.lookup(@TypeOf(pw_stream_destroy), "pw_stream_destroy") orelse return error.SymbolLookup;
pw_stream_connect = handle.lookup(@TypeOf(pw_stream_connect), "pw_stream_connect") orelse return error.SymbolLookup;
pw_stream_queue_buffer = handle.lookup(@TypeOf(pw_stream_queue_buffer), "pw_stream_queue_buffer") orelse return error.SymbolLookup;
pw_stream_dequeue_buffer = handle.lookup(@TypeOf(pw_stream_dequeue_buffer), "pw_stream_dequeue_buffer") orelse return error.SymbolLookup;
pw_stream_get_state = handle.lookup(@TypeOf(pw_stream_get_state), "pw_stream_get_state") orelse return error.SymbolLookup;
}
};
const default_playback = main.Device{ const default_playback = main.Device{
.id = "default-playback", .id = "default-playback",
.name = "Default Device", .name = "Default Device",
@ -45,7 +91,9 @@ pub const Context = struct {
}; };
pub fn init(allocator: std.mem.Allocator, options: main.Context.Options) !backends.BackendContext { pub fn init(allocator: std.mem.Allocator, options: main.Context.Options) !backends.BackendContext {
c.pw_init(null, null); try lib.load();
lib.pw_init(null, null);
var self = try allocator.create(Context); var self = try allocator.create(Context);
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@ -82,8 +130,9 @@ pub const Context = struct {
for (self.devices_info.list.items) |d| for (self.devices_info.list.items) |d|
freeDevice(self.allocator, d); freeDevice(self.allocator, d);
self.devices_info.list.deinit(self.allocator); self.devices_info.list.deinit(self.allocator);
c.pw_deinit(); lib.pw_deinit();
self.allocator.destroy(self); self.allocator.destroy(self);
lib.handle.close();
} }
pub fn refresh(self: *Context) !void { pub fn refresh(self: *Context) !void {
@ -130,7 +179,7 @@ pub const Context = struct {
}; };
pub fn createPlayer(self: *Context, device: main.Device, writeFn: main.WriteFn, options: main.Player.Options) !backends.BackendPlayer { pub fn createPlayer(self: *Context, device: main.Device, writeFn: main.WriteFn, options: main.Player.Options) !backends.BackendPlayer {
const thread = c.pw_thread_loop_new(device.id, null) orelse return error.SystemResources; const thread = lib.pw_thread_loop_new(device.id, null) orelse return error.SystemResources;
const media_role = switch (options.media_role) { const media_role = switch (options.media_role) {
.default => "Screen", .default => "Screen",
@ -143,7 +192,7 @@ pub const Context = struct {
var buf: [8]u8 = undefined; var buf: [8]u8 = undefined;
const audio_rate = std.fmt.bufPrintZ(&buf, "{d}", .{options.sample_rate}) catch unreachable; const audio_rate = std.fmt.bufPrintZ(&buf, "{d}", .{options.sample_rate}) catch unreachable;
const props = c.pw_properties_new( const props = lib.pw_properties_new(
c.PW_KEY_MEDIA_TYPE, c.PW_KEY_MEDIA_TYPE,
"Audio", "Audio",
@ -165,8 +214,8 @@ pub const Context = struct {
var player = try self.allocator.create(Player); var player = try self.allocator.create(Player);
errdefer self.allocator.destroy(player); errdefer self.allocator.destroy(player);
const stream = c.pw_stream_new_simple( const stream = lib.pw_stream_new_simple(
c.pw_thread_loop_get_loop(thread), lib.pw_thread_loop_get_loop(thread),
"audio-src", "audio-src",
props, props,
&stream_events, &stream_events,
@ -196,7 +245,7 @@ pub const Context = struct {
sysaudio_spa_format_audio_raw_build(&pod_builder, c.SPA_PARAM_EnumFormat, &info), sysaudio_spa_format_audio_raw_build(&pod_builder, c.SPA_PARAM_EnumFormat, &info),
}; };
if (c.pw_stream_connect( if (lib.pw_stream_connect(
stream, stream,
c.PW_DIRECTION_OUTPUT, c.PW_DIRECTION_OUTPUT,
c.PW_ID_ANY, c.PW_ID_ANY,
@ -243,16 +292,16 @@ pub const Player = struct {
var self = @ptrCast(*Player, @alignCast(@alignOf(*Player), self_opaque.?)); var self = @ptrCast(*Player, @alignCast(@alignOf(*Player), self_opaque.?));
if (state == c.PW_STREAM_STATE_STREAMING or state == c.PW_STREAM_STATE_ERROR) { if (state == c.PW_STREAM_STATE_STREAMING or state == c.PW_STREAM_STATE_ERROR) {
c.pw_thread_loop_signal(self.thread, false); lib.pw_thread_loop_signal(self.thread, false);
} }
} }
pub fn processCb(self_opaque: ?*anyopaque) callconv(.C) void { pub fn processCb(self_opaque: ?*anyopaque) callconv(.C) void {
var self = @ptrCast(*Player, @alignCast(@alignOf(*Player), self_opaque.?)); var self = @ptrCast(*Player, @alignCast(@alignOf(*Player), self_opaque.?));
const buf = c.pw_stream_dequeue_buffer(self.stream) orelse unreachable; const buf = lib.pw_stream_dequeue_buffer(self.stream) orelse unreachable;
if (buf.*.buffer.*.datas[0].data == null) return; if (buf.*.buffer.*.datas[0].data == null) return;
defer _ = c.pw_stream_queue_buffer(self.stream, buf); defer _ = lib.pw_stream_queue_buffer(self.stream, buf);
const stride = self.format.frameSize(self.channels.len); const stride = self.format.frameSize(self.channels.len);
const n_frames = std.math.min( const n_frames = std.math.min(
@ -276,20 +325,20 @@ pub const Player = struct {
} }
pub fn deinit(self: *Player) void { pub fn deinit(self: *Player) void {
c.pw_thread_loop_stop(self.thread); lib.pw_thread_loop_stop(self.thread);
c.pw_thread_loop_destroy(self.thread); lib.pw_thread_loop_destroy(self.thread);
c.pw_stream_destroy(self.stream); lib.pw_stream_destroy(self.stream);
self.allocator.destroy(self); self.allocator.destroy(self);
} }
pub fn start(self: *Player) !void { pub fn start(self: *Player) !void {
if (c.pw_thread_loop_start(self.thread) < 0) return error.SystemResources; if (lib.pw_thread_loop_start(self.thread) < 0) return error.SystemResources;
c.pw_thread_loop_lock(self.thread); lib.pw_thread_loop_lock(self.thread);
c.pw_thread_loop_wait(self.thread); lib.pw_thread_loop_wait(self.thread);
c.pw_thread_loop_unlock(self.thread); lib.pw_thread_loop_unlock(self.thread);
if (c.pw_stream_get_state(self.stream, null) == c.PW_STREAM_STATE_ERROR) { if (lib.pw_stream_get_state(self.stream, null) == c.PW_STREAM_STATE_ERROR) {
return error.CannotPlay; return error.CannotPlay;
} }
} }

View file

@ -1,5 +1,5 @@
#include <pipewire-0.3/pipewire/core.h> #include <pipewire/core.h>
#include <spa-0.2/spa/param/audio/format-utils.h> #include <spa/param/audio/format-utils.h>
struct spa_pod *sysaudio_spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_raw *info) struct spa_pod *sysaudio_spa_format_audio_raw_build(struct spa_pod_builder *builder, uint32_t id, struct spa_audio_info_raw *info)
{ {