also looking at this
feat(media): add bounded PCM capture, owned snapshots and A/V streaming
#917
11 files
+683
-0
| @@ -0,0 +1,41 @@ | ||
| 1 | //! Home opaque, header-checked ALSA capture. Build C boundary and link libasound. | |
| 2 | //! Calls serialize; close only after read returns. S16LE mono/stereo, BOOTTIME ns. | |
| 3 | const Handle = opaque {}; | |
| 4 | pub const Config = extern struct { sample_rate: u32, channels: u32, block_frames: u32 }; | |
| 5 | extern fn home_audio_open(*const Config, [*:0]const u8, *?*Handle) c_int; | |
| 6 | extern fn home_audio_read(*Handle, [*]u8, u32, u32, *u32, *u64, *i64) c_int; | |
| 7 | extern fn home_audio_close(*Handle) void; | |
| 8 | pub const Block = struct { frames: u32, timestamp_ns: u64, clock_error_ns: i64 }; | |
| 9 | pub const Capture = struct { | |
| 10 | handle: *Handle, | |
| 11 | config: Config, | |
| 12 | failed: bool = false, | |
| 13 | pub fn init(cfg: Config, device: [:0]const u8) !Capture { | |
| 14 | var out: ?*Handle = null; | |
| 15 | try check(home_audio_open(&cfg, device.ptr, &out)); | |
| 16 | return .{ .handle = out.?, .config = cfg }; | |
| 17 | } | |
| 18 | pub fn deinit(s: *Capture) void { | |
| 19 | home_audio_close(s.handle); | |
| 20 | } | |
| 21 | pub fn read(s: *Capture, data: []u8, timeout_ms: u32) !?Block { | |
| 22 | if (s.failed) return error.CaptureFailed; | |
| 23 | if (data.len < @as(u64, s.config.block_frames) * s.config.channels * 2 or timeout_ms > 1000) return error.InvalidConfiguration; | |
| 24 | var block: Block = undefined; | |
| 25 | const rc = home_audio_read(s.handle, data.ptr, @intCast(s.config.block_frames), timeout_ms, &block.frames, &block.timestamp_ns, &block.clock_error_ns); | |
| 26 | if (rc == 1) return null; | |
| 27 | check(rc) catch |err| { | |
| 28 | s.failed = true; | |
| 29 | return err; | |
| 30 | }; | |
| 31 | return block; | |
| 32 | } | |
| 33 | }; | |
| 34 | fn check(rc: c_int) !void { | |
| 35 | return switch (rc) { | |
| 36 | 0 => {}, | |
| 37 | -1 => error.InvalidConfiguration, | |
| 38 | -2 => error.OutOfMemory, | |
| 39 | else => error.CaptureFailed, | |
| 40 | }; | |
| 41 | } | |