diff --git a/Cargo.lock b/Cargo.lock index 76fff5dc..d3200744 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.50", + "zerocopy", ] [[package]] @@ -312,12 +312,6 @@ dependencies = [ "zbus", ] -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-broadcast" version = "0.7.2" @@ -495,26 +489,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" -[[package]] -name = "atomig" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0f41f4bb89f5c6450325e283fb78c4a3d042181b54f3855ee2f872919f9863" -dependencies = [ - "atomig-macro", -] - -[[package]] -name = "atomig-macro" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c98dba06b920588de7d63f6acc23f1e6a9fade5fd6198e564506334fb5a4f5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "atty" version = "0.2.14" @@ -635,19 +609,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "av-data" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca67ba5d317924c02180c576157afd54babe48a76ebc66ce6d34bb8ba08308e" -dependencies = [ - "byte-slice-cast", - "bytes", - "num-derive", - "num-rational", - "num-traits", -] - [[package]] name = "av-scenechange" version = "0.14.1" @@ -810,12 +771,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "byte-slice-cast" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" - [[package]] name = "bytemuck" version = "1.25.0" @@ -1514,6 +1469,15 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +[[package]] +name = "dav1d-sys" +version = "0.8.3" +source = "git+https://github.com/rust-av/dav1d-rs.git?rev=03477a36c3de4f2aacbab81a8951150ffdb9c4c3#03477a36c3de4f2aacbab81a8951150ffdb9c4c3" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "dbus" version = "0.9.11" @@ -2267,7 +2231,7 @@ dependencies = [ "bytemuck", "cfg-if", "crunchy", - "zerocopy 0.8.50", + "zerocopy", ] [[package]] @@ -3282,16 +3246,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" -[[package]] -name = "nasm-rs" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149" -dependencies = [ - "jobserver", - "log", -] - [[package]] name = "native-tls" version = "0.2.18" @@ -4256,7 +4210,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.50", + "zerocopy", ] [[package]] @@ -4600,28 +4554,6 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" -[[package]] -name = "rav1d" -version = "1.1.0" -source = "git+https://github.com/memorysafety/rav1d.git?rev=2a734dc9cd52190146796fb24b29a8c7e3d2af1e#2a734dc9cd52190146796fb24b29a8c7e3d2af1e" -dependencies = [ - "assert_matches", - "atomig", - "av-data", - "bitflags 2.12.1", - "cc", - "cfg-if", - "libc", - "nasm-rs", - "parking_lot", - "paste", - "raw-cpuid", - "static_assertions", - "strum", - "to_method", - "zerocopy 0.7.35", -] - [[package]] name = "rav1e" version = "0.8.1" @@ -4672,15 +4604,6 @@ dependencies = [ "rgb", ] -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags 2.12.1", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -5888,12 +5811,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - [[package]] name = "tokio" version = "1.52.3" @@ -6734,6 +6651,7 @@ dependencies = [ "anyhow", "bytes", "cosmic-text", + "dav1d-sys", "etagere", "flate2", "glam", @@ -6742,7 +6660,6 @@ dependencies = [ "lru", "ouroboros", "parking_lot", - "rav1d", "regex", "resvg", "roxmltree 0.21.1", @@ -7561,34 +7478,13 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ - "zerocopy-derive 0.8.50", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "zerocopy-derive", ] [[package]] diff --git a/wgui/Cargo.toml b/wgui/Cargo.toml index b67fab4f..72dedebe 100644 --- a/wgui/Cargo.toml +++ b/wgui/Cargo.toml @@ -42,14 +42,12 @@ vulkano-shaders.workspace = true # `video` wgui feature bytes = { workspace = true, optional = true } -rav1d = { - git = "https://github.com/memorysafety/rav1d.git", - rev = "2a734dc9cd52190146796fb24b29a8c7e3d2af1e", - default-features = false, - features = ["bitdepth_8"], +dav1d-sys = { + git = "https://github.com/rust-av/dav1d-rs.git", + rev = "03477a36c3de4f2aacbab81a8951150ffdb9c4c3", optional = true } [features] default = ["video"] -video = ["dep:bytes", "dep:rav1d"] +video = ["dep:bytes", "dep:dav1d-sys"] diff --git a/wgui/src/video_dec.rs b/wgui/src/video_dec.rs index 42adf5e9..f55d1253 100644 --- a/wgui/src/video_dec.rs +++ b/wgui/src/video_dec.rs @@ -1,9 +1,14 @@ -use bytes::{Buf, Bytes}; -use rav1d::include::dav1d::dav1d::{Dav1dContext, Dav1dSettings}; +use std::ptr::null_mut; -struct IvfReader { - file_data: Vec, - reader: Bytes, // uses &file_data ptr +use bytes::{Buf, Bytes}; +use dav1d_sys::{ + Dav1dContext, Dav1dData, Dav1dPicture, Dav1dSettings, dav1d_close, dav1d_data_unref, dav1d_default_settings, + dav1d_get_picture, dav1d_open, dav1d_picture_unref, dav1d_send_data, +}; + +pub struct IvfReader { + _file_data: Vec, // Used by `reader`, do not remove! + reader: Bytes, pub cur_frame: u32, pub num_frames: u32, pub framerate: f32, @@ -76,7 +81,7 @@ impl IvfReader { log::info!("IvfReader: width {header_width}, height {header_height}, framerate {framerate}"); Ok(IvfReader { - file_data, + _file_data: file_data, reader, cur_frame: 0, width: header_width, @@ -87,9 +92,9 @@ impl IvfReader { } // Read demuxed video packet to a chunk - pub fn read_frame(&mut self) -> anyhow::Result<&[u8]> { + pub fn read_frame(&mut self) -> anyhow::Result<(&[u8], u64 /* pts */)> { let frame_size = self.reader.try_get_u32_le()? as usize; - let _frame_pts = self.reader.try_get_u64_le()?; + let frame_pts = self.reader.try_get_u64_le()?; if frame_size > 8 * 1024 * 1024 { // something went really wrong @@ -108,7 +113,7 @@ impl IvfReader { self.cur_frame += 1; - Ok(chunk_a) + Ok((chunk_a, frame_pts)) } } @@ -118,90 +123,140 @@ pub struct RgbFrame { data: Vec, } -pub struct YuvFrame { - width: u16, - height: u16, - stride_luma: u32, - stride_chroma: u32, - data_y: Vec, - data_u: Vec, - data_v: Vec, -} - -fn clamp_u8(v: i32) -> u8 { - v.max(0).min(255) as u8 -} - fn yuv_to_rgb(y: u8, u: u8, v: u8) -> (u8, u8, u8) { - let y_i = y as i32; - let u_i = u as i32 - 128; - let v_i = v as i32 - 128; + let y_i = i32::from(y); + let u_i = i32::from(u) - 128; + let v_i = i32::from(v) - 128; let c = (1192 * (y_i - 16)).max(0); let out_r = (c + 1634 * v_i) >> 10; let out_g = (c - 401 * u_i - 834 * v_i) >> 10; let out_b = (c + 2066 * u_i) >> 10; - (clamp_u8(out_r), clamp_u8(out_g), clamp_u8(out_b)) + ( + out_r.clamp(0, 255) as u8, + out_g.clamp(0, 255) as u8, + out_b.clamp(0, 255) as u8, + ) } -impl YuvFrame { +impl RgbFrame { // Simple, temporary, best-effort yuv420->rgb converter. Not performant at all, but at least it works. // Temporary till we get native YUV support in shaders (as 3 vulkan textures). // SAFETY: this function expects YUV420 data only. - fn to_rgb(&self) -> RgbFrame { + #[allow(clippy::uninit_vec)] + fn from_yuv( + width: u16, + height: u16, + data_y: *const u8, + data_u: *const u8, + data_v: *const u8, + stride_luma: usize, + stride_chroma: usize, + ) -> RgbFrame { unsafe { let channels = 3; // rgb - let rgb_size = self.width as usize * self.height as usize * channels; + let rgb_size = width as usize * height as usize * channels; let mut rgb = Vec::::with_capacity(rgb_size); rgb.set_len(rgb_size); let ptr_rgb = rgb.as_mut_ptr(); - let ptr_y = self.data_y.as_ptr(); - let ptr_u = self.data_u.as_ptr(); - let ptr_v = self.data_v.as_ptr(); - let stride_luma = self.stride_luma as usize; - let stride_chroma = self.stride_chroma as usize; - let width = self.width as usize; - let height = self.height as usize; - - for y in 0..height { - for x in 0..width { - let val_y = *ptr_y.offset((y * stride_luma + x) as isize); - let val_u = *ptr_u.offset(((y / 2) * stride_chroma + (x / 2)) as isize); - let val_v = *ptr_v.offset(((y / 2) * stride_chroma + (x / 2)) as isize); + for y in 0..height as usize { + for x in 0..width as usize { + let val_y = *data_y.add(y * stride_luma + x); + let val_u = *data_u.add((y / 2) * stride_chroma + (x / 2)); + let val_v = *data_v.add((y / 2) * stride_chroma + (x / 2)); let (r, g, b) = yuv_to_rgb(val_y, val_u, val_v); - let pos = y * (width * channels) + x * channels; - *ptr_rgb.offset((pos + 0) as isize) = r; - *ptr_rgb.offset((pos + 1) as isize) = g; - *ptr_rgb.offset((pos + 2) as isize) = b; + let pos = y * (width as usize * channels) + x * channels; + *ptr_rgb.add(pos) = r; + *ptr_rgb.add(pos + 1) = g; + *ptr_rgb.add(pos + 2) = b; } } RgbFrame { - width: self.width, - height: self.height, + width, + height, data: rgb, } } } } -pub struct Av1Decoder {} +pub struct Av1Decoder { + ctx: *mut Dav1dContext, +} + +impl Drop for Av1Decoder { + fn drop(&mut self) { + unsafe { + dav1d_close(&raw mut self.ctx); + } + } +} impl Av1Decoder { - pub fn new() -> Self { - Self {} + pub fn new() -> anyhow::Result { + unsafe { + let mut set: Dav1dSettings = std::mem::zeroed(); + dav1d_default_settings(&raw mut set); + + let mut ctx = null_mut(); + let ret = dav1d_open(&raw mut ctx, &raw mut set); + if ret < 0 { + anyhow::bail!("dav1d_open failed: {ret}"); + } + Ok(Self { ctx }) + } } pub fn read_frame(&mut self, reader: &mut IvfReader) -> anyhow::Result { - let packet = reader.read_frame()?; + let (packet, pts) = reader.read_frame()?; - let mut decoder = rav1d::Decoder::new()?; + let mut ret; - todo!(); + unsafe { + let mut data: Dav1dData = std::mem::zeroed(); + data.data = packet.as_ptr(); + data.sz = packet.len(); + data.m.timestamp = pts as i64; + data.m.size = packet.len(); + data.m.offset = -1; + data.m.duration = 0; - // rav1d::Ok(RgbFrame {}) + ret = dav1d_send_data(self.ctx, &raw mut data); + dav1d_data_unref(&raw mut data); + if ret < 0 { + anyhow::bail!("dav1d_send_data failed: {ret}"); + } + + let mut picture: Dav1dPicture = std::mem::zeroed(); + ret = dav1d_get_picture(self.ctx, &raw mut picture); + if ret < 0 { + dav1d_picture_unref(&raw mut picture); + anyhow::bail!("dav1d_get_picture failure: {ret}") + } + + let pic_y = picture.data[0]; // luma Y + let pic_u = picture.data[1]; // chroma U + let pic_v = picture.data[2]; // chroma V + let stride_luma = picture.stride[0]; + let stride_chroma = picture.stride[1]; + + let rgb = RgbFrame::from_yuv( + reader.width, + reader.height, + pic_y as *const u8, + pic_u as *const u8, + pic_v as *const u8, + stride_luma as usize, + stride_chroma as usize, + ); + + dav1d_picture_unref(&raw mut picture); + + Ok(rgb) + } } }