mirror of https://github.com/wayvr-org/wayvr.git
wgui: <Video> tag (wip), Duck IVF parser (video container), add `video` feature
This commit is contained in:
parent
00d4e35dd8
commit
3c2ac4b5f1
|
|
@ -43,7 +43,7 @@ dependencies = [
|
|||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
"zerocopy 0.8.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -312,6 +312,12 @@ 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"
|
||||
|
|
@ -489,6 +495,26 @@ 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"
|
||||
|
|
@ -609,6 +635,19 @@ 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"
|
||||
|
|
@ -771,6 +810,12 @@ 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"
|
||||
|
|
@ -2222,7 +2267,7 @@ dependencies = [
|
|||
"bytemuck",
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
"zerocopy",
|
||||
"zerocopy 0.8.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3237,6 +3282,16 @@ 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"
|
||||
|
|
@ -4201,7 +4256,7 @@ version = "0.2.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.8.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4545,6 +4600,28 @@ 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"
|
||||
|
|
@ -4595,6 +4672,15 @@ 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"
|
||||
|
|
@ -5802,6 +5888,12 @@ 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"
|
||||
|
|
@ -6640,6 +6732,7 @@ name = "wgui"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"cosmic-text",
|
||||
"etagere",
|
||||
"flate2",
|
||||
|
|
@ -6649,6 +6742,7 @@ dependencies = [
|
|||
"lru",
|
||||
"ouroboros",
|
||||
"parking_lot",
|
||||
"rav1d",
|
||||
"regex",
|
||||
"resvg",
|
||||
"roxmltree 0.21.1",
|
||||
|
|
@ -7467,13 +7561,34 @@ 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",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
18
Cargo.toml
18
Cargo.toml
|
|
@ -14,6 +14,7 @@ members = [
|
|||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.100"
|
||||
bytes = { version = "1.11.1" }
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
glam = { version = "0.30.9", features = ["mint", "serde"] }
|
||||
idmap = "0.2.2"
|
||||
|
|
@ -26,16 +27,23 @@ serde_json = "1.0.145"
|
|||
slotmap = "1.1.1"
|
||||
smol = "2.0.2"
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
uuid = { version = "1.19.0", features = ["fast-rng", "v4", "serde"] }
|
||||
vulkano = { version = "0.35.2", default-features = false, features = [
|
||||
"macros",
|
||||
] }
|
||||
uuid = { version = "1.19.0", features = ["fast-rng", "serde", "v4"] }
|
||||
vulkano = {
|
||||
version = "0.35.2",
|
||||
default-features = false,
|
||||
features = [
|
||||
"macros",
|
||||
]
|
||||
}
|
||||
vulkano-shaders = "0.35.0"
|
||||
wayland-client = { version = "0.31.11" }
|
||||
xdg = "3.0.0"
|
||||
|
||||
[patch.crates-io]
|
||||
vulkano = { git = "https://github.com/galister/vulkano.git", rev = "cf7f92867928a56ce16b376037c1120f2b167678" }
|
||||
vulkano = {
|
||||
git = "https://github.com/galister/vulkano.git",
|
||||
rev = "cf7f92867928a56ce16b376037c1120f2b167678"
|
||||
}
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::tab::settings::{
|
||||
SettingType, SettingsMountParams, SettingsTab,
|
||||
macros::{options_category, options_checkbox, options_range_f32, options_slider_f32},
|
||||
macros::{options_category, options_checkbox, options_range_f32},
|
||||
};
|
||||
|
||||
pub struct State {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<layout>
|
||||
<elements>
|
||||
<rectangle position="absolute" width="10000" height="10000"/>
|
||||
<div margin="16" gap="8" flex_direction="column">
|
||||
<label text="aaa"/>
|
||||
<Video src="test.ivf" width="320" height="240"/>
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -8,18 +8,18 @@ authors = ["galister", "oo8dev"]
|
|||
repository = "https://github.com/wlx-team/wayvr"
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.11.1"
|
||||
smallvec = "1.13.2"
|
||||
serde.workspace = true
|
||||
anyhow = "1.0.93"
|
||||
bytes.workspace = true
|
||||
log = "0.4.22"
|
||||
serde.workspace = true
|
||||
smallvec = "1.13.2"
|
||||
|
||||
# client-only deps
|
||||
interprocess = { version = "2.2.2", features = ["tokio"], optional = true }
|
||||
serde_json.workspace = true
|
||||
tokio = { version = "1.43.1", features = ["macros"], optional = true }
|
||||
tokio-util = { version = "0.7.13", optional = true }
|
||||
serde_json.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["client"]
|
||||
client = ["dep:tokio", "dep:tokio-util", "dep:interprocess"]
|
||||
client = ["dep:interprocess", "dep:tokio", "dep:tokio-util"]
|
||||
|
|
|
|||
|
|
@ -43,28 +43,41 @@ vulkano-shaders.workspace = true
|
|||
xdg.workspace = true
|
||||
|
||||
ash = "^0.38.0" # must match vulkano
|
||||
bytes = { version = "1.11.1" }
|
||||
bytes = { workspace = true }
|
||||
chrono = { version = "0.4.42", features = ["unstable-locales"] }
|
||||
chrono-tz = "0.10.4"
|
||||
config = "0.15.19"
|
||||
dbus = { version = "0.9.9" }
|
||||
futures = "0.3.31"
|
||||
image_dds = { version = "0.7.2", default-features = false, features = [
|
||||
"ddsfile",
|
||||
] }
|
||||
image_dds = {
|
||||
version = "0.7.2",
|
||||
default-features = false,
|
||||
features = [
|
||||
"ddsfile",
|
||||
]
|
||||
}
|
||||
input-linux = "0.7.1"
|
||||
interprocess = { version = "2.2.3" }
|
||||
json = { version = "0.12.4", optional = true }
|
||||
json5 = "1.3.0"
|
||||
libc = "0.2.178"
|
||||
libmonado = { git = "https://github.com/wayvr-org/libmonado-rs.git", rev = "6f66b26930c24a8a2fc57ddcd85704784894c750", optional = true }
|
||||
libmonado = {
|
||||
git = "https://github.com/wayvr-org/libmonado-rs.git",
|
||||
rev = "6f66b26930c24a8a2fc57ddcd85704784894c750",
|
||||
optional = true
|
||||
}
|
||||
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
|
||||
mint = "0.5.9"
|
||||
openxr = { version = "0.21.0", features = ["linked", "mint"], optional = true }
|
||||
ovr_overlay = { git = "https://github.com/galister/ovr_overlay_oyasumi", rev = "e477bd2a9e04293ea68c1e7529ef2cb131f32acc", features = [
|
||||
"ovr_input",
|
||||
"ovr_system",
|
||||
], optional = true }
|
||||
ovr_overlay = {
|
||||
git = "https://github.com/galister/ovr_overlay_oyasumi",
|
||||
rev = "e477bd2a9e04293ea68c1e7529ef2cb131f32acc",
|
||||
features = [
|
||||
"ovr_input",
|
||||
"ovr_system",
|
||||
],
|
||||
optional = true
|
||||
}
|
||||
prost = { version = "0.14.3", optional = true }
|
||||
pure-rust-locales = "0.8.2"
|
||||
rosc = { version = "0.11.4", optional = true }
|
||||
|
|
@ -72,12 +85,16 @@ serde_json5 = "0.2.1"
|
|||
serde_yaml = "0.9.34"
|
||||
signal-hook = "0.3.18"
|
||||
smallvec = "1.15.1"
|
||||
smithay = { version = "0.7.0", default-features = false, features = [
|
||||
"backend_vulkan",
|
||||
"desktop",
|
||||
"wayland_frontend",
|
||||
"xwayland",
|
||||
] }
|
||||
smithay = {
|
||||
version = "0.7.0",
|
||||
default-features = false,
|
||||
features = [
|
||||
"backend_vulkan",
|
||||
"desktop",
|
||||
"wayland_frontend",
|
||||
"xwayland",
|
||||
]
|
||||
}
|
||||
smol = { workspace = true }
|
||||
sysinfo = { version = "0.37" }
|
||||
thiserror = "2.0"
|
||||
|
|
@ -86,10 +103,16 @@ tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
|||
uuid = { workspace = true }
|
||||
wayland-client = { workspace = true }
|
||||
winit = { version = "0.30.12", optional = true }
|
||||
xcb = { version = "1.6.0", features = [
|
||||
"as-raw-xcb-connection",
|
||||
], optional = true }
|
||||
xkbcommon = { version = "0.8.0" } # 0.9.0 breaks keymap import on some distros
|
||||
xcb = {
|
||||
version = "1.6.0",
|
||||
features = [
|
||||
"as-raw-xcb-connection",
|
||||
],
|
||||
optional = true
|
||||
}
|
||||
xkbcommon = {
|
||||
version = "0.8.0"
|
||||
} # 0.9.0 breaks keymap import on some distros
|
||||
|
||||
[build-dependencies]
|
||||
regex.workspace = true
|
||||
|
|
|
|||
|
|
@ -11,14 +11,19 @@ repository = "https://github.com/wlx-team/wayvr"
|
|||
anyhow.workspace = true
|
||||
cosmic-text = "0.15.0"
|
||||
etagere = "0.2.15"
|
||||
flate2 = "1.1.5"
|
||||
glam.workspace = true
|
||||
image = { version = "0.25.9", default-features = false, features = [
|
||||
"gif",
|
||||
"jpeg",
|
||||
"png",
|
||||
"rayon",
|
||||
"webp",
|
||||
] }
|
||||
image = {
|
||||
version = "0.25.9",
|
||||
default-features = false,
|
||||
features = [
|
||||
"gif",
|
||||
"jpeg",
|
||||
"png",
|
||||
"rayon",
|
||||
"webp",
|
||||
]
|
||||
}
|
||||
log.workspace = true
|
||||
lru = "0.16.2"
|
||||
ouroboros = "0.18.5"
|
||||
|
|
@ -26,6 +31,7 @@ parking_lot = "0.12.5"
|
|||
regex.workspace = true
|
||||
resvg = { version = "0.45.1", default-features = false }
|
||||
roxmltree = "0.21.1"
|
||||
rust-embed.workspace = true
|
||||
rustc-hash = "2.1.1"
|
||||
serde_json.workspace = true
|
||||
slotmap.workspace = true
|
||||
|
|
@ -33,5 +39,17 @@ smallvec = "1.15.1"
|
|||
taffy = "0.9.2"
|
||||
vulkano.workspace = true
|
||||
vulkano-shaders.workspace = true
|
||||
rust-embed.workspace = true
|
||||
flate2 = "1.1.5"
|
||||
|
||||
# `video` wgui feature
|
||||
bytes = { workspace = true, optional = true }
|
||||
rav1d = {
|
||||
git = "https://github.com/memorysafety/rav1d.git",
|
||||
rev = "2a734dc9cd52190146796fb24b29a8c7e3d2af1e",
|
||||
default-features = false,
|
||||
features = ["bitdepth_8"],
|
||||
optional = true
|
||||
}
|
||||
|
||||
[features]
|
||||
default = ["video"]
|
||||
video = ["dep:bytes", "dep:rav1d"]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ pub mod slider;
|
|||
pub mod tabs;
|
||||
pub mod tooltip;
|
||||
|
||||
#[cfg(feature = "video")]
|
||||
pub mod video;
|
||||
|
||||
pub struct RefreshData<'a> {
|
||||
pub layout: &'a mut Layout,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
use crate::{
|
||||
assets::AssetPath,
|
||||
components::{Component, ComponentBase, ComponentTrait, RefreshData},
|
||||
drawing::Color,
|
||||
layout::{WidgetID, WidgetPair},
|
||||
widget::{
|
||||
ConstructEssentials,
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
},
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Params<'a> {
|
||||
pub style: taffy::Style,
|
||||
pub src: Option<AssetPath<'a>>,
|
||||
}
|
||||
|
||||
struct State {}
|
||||
|
||||
struct Data {
|
||||
#[allow(dead_code)]
|
||||
id_container: WidgetID,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ComponentVideo {
|
||||
base: ComponentBase,
|
||||
data: Rc<Data>,
|
||||
state: Rc<RefCell<State>>,
|
||||
}
|
||||
|
||||
impl ComponentTrait for ComponentVideo {
|
||||
fn base(&self) -> &ComponentBase {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn base_mut(&mut self) -> &mut ComponentBase {
|
||||
&mut self.base
|
||||
}
|
||||
|
||||
fn refresh(&self, _data: &mut RefreshData) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentVideo {}
|
||||
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentVideo>)> {
|
||||
let style = params.style;
|
||||
|
||||
let (root, _) = ess.layout.add_child(
|
||||
ess.parent,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: Color::new(0.1, 0.1, 0.1, 1.0),
|
||||
..Default::default()
|
||||
}),
|
||||
style,
|
||||
)?;
|
||||
|
||||
let id_container = root.id;
|
||||
let data = Rc::new(Data { id_container });
|
||||
|
||||
let state = Rc::new(RefCell::new(State {}));
|
||||
|
||||
let base = ComponentBase {
|
||||
id: root.id,
|
||||
lhandles: Default::default(),
|
||||
};
|
||||
|
||||
let video = Rc::new(ComponentVideo { base, data, state });
|
||||
|
||||
ess.layout.defer_component_refresh(Component(video.clone()));
|
||||
Ok((root, video))
|
||||
}
|
||||
|
|
@ -44,6 +44,9 @@ pub mod theme;
|
|||
pub mod widget;
|
||||
pub mod windowing;
|
||||
|
||||
#[cfg(feature = "video")]
|
||||
pub mod video_dec;
|
||||
|
||||
// re-exported libs
|
||||
pub use cosmic_text;
|
||||
pub use taffy;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
use crate::{
|
||||
assets::AssetPath,
|
||||
components::{self, Component},
|
||||
layout::WidgetID,
|
||||
parser::{
|
||||
AttribPair, ParserContext, ParserFile, get_asset_path_from_kv,
|
||||
helpers::{TooltipAttribs, parse_attrib_tooltip},
|
||||
parse_children, process_component,
|
||||
style::parse_style,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn parse_component_video<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'a, 'a>,
|
||||
parent_id: WidgetID,
|
||||
attribs: &[AttribPair],
|
||||
tag_name: &str,
|
||||
) -> anyhow::Result<WidgetID> {
|
||||
let mut tooltip = TooltipAttribs::default();
|
||||
let mut src: Option<AssetPath> = None;
|
||||
|
||||
let style = parse_style(ctx, attribs, tag_name);
|
||||
|
||||
for pair in attribs {
|
||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||
match key {
|
||||
"src" | "src_ext" | "src_builtin" | "src_internal" => {
|
||||
let asset_path = get_asset_path_from_kv("", key, value);
|
||||
|
||||
if !value.is_empty() {
|
||||
src = Some(asset_path);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
parse_attrib_tooltip(ctx, tag_name, pair, &mut tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (widget, video) = components::video::construct(
|
||||
&mut ctx.get_construct_essentials(parent_id),
|
||||
components::video::Params { style, src },
|
||||
)?;
|
||||
|
||||
process_component(ctx, Component(video), widget.id, attribs);
|
||||
parse_children(file, ctx, node, widget.id)?;
|
||||
|
||||
Ok(widget.id)
|
||||
}
|
||||
|
|
@ -6,6 +6,10 @@ mod component_editbox;
|
|||
mod component_radio_group;
|
||||
mod component_slider;
|
||||
mod component_tabs;
|
||||
|
||||
#[cfg(feature = "video")]
|
||||
mod component_video;
|
||||
|
||||
mod helpers;
|
||||
mod style;
|
||||
mod widget_div;
|
||||
|
|
@ -1084,6 +1088,14 @@ fn parse_child<'a>(
|
|||
file, ctx, child_node, parent_id, &attribs, tag_name,
|
||||
)?);
|
||||
}
|
||||
#[cfg(feature = "video")]
|
||||
"Video" => {
|
||||
use crate::parser::component_video::parse_component_video;
|
||||
|
||||
new_widget_id = Some(parse_component_video(
|
||||
file, ctx, child_node, parent_id, &attribs, tag_name,
|
||||
)?);
|
||||
}
|
||||
"Slider" => {
|
||||
new_widget_id = Some(parse_component_slider(ctx, parent_id, &attribs, tag_name)?);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
use bytes::{Buf, Bytes};
|
||||
use rav1d::include::dav1d::dav1d::{Dav1dContext, Dav1dSettings};
|
||||
|
||||
struct IvfReader {
|
||||
file_data: Vec<u8>,
|
||||
reader: Bytes, // uses &file_data ptr
|
||||
pub cur_frame: u32,
|
||||
pub num_frames: u32,
|
||||
pub framerate: f32,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
// Duck IVF video container. Documentation:
|
||||
// https://wiki.multimedia.cx/index.php/Duck_IVF
|
||||
// Yes, it's just as simple as that.
|
||||
//
|
||||
// Header:
|
||||
// bytes 0-3 signature: 'DKIF'
|
||||
// bytes 4-5 version (should be 0)
|
||||
// bytes 6-7 length of header in bytes
|
||||
// bytes 8-11 codec FourCC (e.g., 'VP80')
|
||||
// bytes 12-13 width in pixels
|
||||
// bytes 14-15 height in pixels
|
||||
// bytes 16-19 time base denominator
|
||||
// bytes 20-23 time base numerator
|
||||
// bytes 24-27 number of `Frame`s in file
|
||||
// bytes 28-31 unused
|
||||
//
|
||||
// Frame:
|
||||
// bytes 0-3 size of frame in bytes (not including the 12-byte header)
|
||||
// bytes 4-11 64-bit presentation timestamp
|
||||
// bytes 12.. frame data
|
||||
|
||||
const IVF_MAGIC: [u8; 4] = [0x44, 0x4B, 0x49, 0x46];
|
||||
|
||||
impl IvfReader {
|
||||
pub fn new(file_data: Vec<u8>) -> anyhow::Result<IvfReader> {
|
||||
// safety: both reader and file_data are located in the same struct
|
||||
// file_data won't move at all.
|
||||
let mut reader = Bytes::from_static(unsafe { std::mem::transmute::<&[u8], &'static [u8]>(&file_data) });
|
||||
|
||||
// read ivf magic
|
||||
let mut header_magic: [u8; 4] = [0; 4];
|
||||
reader.try_copy_to_slice(&mut header_magic)?;
|
||||
|
||||
if header_magic != IVF_MAGIC {
|
||||
anyhow::bail!("invalid magic");
|
||||
}
|
||||
|
||||
let header_version = reader.try_get_u16_le()?;
|
||||
|
||||
if header_version != 0 {
|
||||
anyhow::bail!("unsupported version"); // there's no other version than 0
|
||||
}
|
||||
|
||||
let header_len = reader.try_get_u16_le()?;
|
||||
if header_len != 32 {
|
||||
anyhow::bail!("header length mismatching");
|
||||
}
|
||||
|
||||
let mut header_fourcc: [u8; 4] = [0; 4];
|
||||
reader.try_copy_to_slice(&mut header_fourcc)?;
|
||||
|
||||
let header_width = reader.try_get_u16_le()?;
|
||||
let header_height = reader.try_get_u16_le()?;
|
||||
let header_timebase_den = reader.try_get_u32_le()?;
|
||||
let header_timebase_num = reader.try_get_u32_le()?;
|
||||
let header_num_frames = reader.try_get_u32_le()?;
|
||||
|
||||
let framerate = header_timebase_den as f32 / header_timebase_num as f32;
|
||||
|
||||
let mut padding: [u8; 4] = [0; 4];
|
||||
reader.try_copy_to_slice(&mut padding)?;
|
||||
|
||||
log::info!("IvfReader: width {header_width}, height {header_height}, framerate {framerate}");
|
||||
|
||||
Ok(IvfReader {
|
||||
file_data,
|
||||
reader,
|
||||
cur_frame: 0,
|
||||
width: header_width,
|
||||
height: header_height,
|
||||
framerate,
|
||||
num_frames: header_num_frames,
|
||||
})
|
||||
}
|
||||
|
||||
// Read demuxed video packet to a chunk
|
||||
pub fn read_frame(&mut self) -> anyhow::Result<&[u8]> {
|
||||
let frame_size = self.reader.try_get_u32_le()? as usize;
|
||||
let _frame_pts = self.reader.try_get_u64_le()?;
|
||||
|
||||
if frame_size > 8 * 1024 * 1024 {
|
||||
// something went really wrong
|
||||
anyhow::bail!("Invalid frame size");
|
||||
}
|
||||
|
||||
// SAFETY: reader.chunk() slice lifetime is the same as Self, no risk here.
|
||||
let chunk_a /* 'a */ = unsafe /* it's safe */ {
|
||||
let Some(chunk) = self.reader.chunk().get(0..(frame_size)) else {
|
||||
anyhow::bail!("chunk read error");
|
||||
};
|
||||
std::mem::transmute::<&[u8], &'static [u8]>(chunk)
|
||||
};
|
||||
|
||||
self.reader.advance(frame_size);
|
||||
|
||||
self.cur_frame += 1;
|
||||
|
||||
Ok(chunk_a)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RgbFrame {
|
||||
width: u16,
|
||||
height: u16,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct YuvFrame {
|
||||
width: u16,
|
||||
height: u16,
|
||||
stride_luma: u32,
|
||||
stride_chroma: u32,
|
||||
data_y: Vec<u8>,
|
||||
data_u: Vec<u8>,
|
||||
data_v: Vec<u8>,
|
||||
}
|
||||
|
||||
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 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))
|
||||
}
|
||||
|
||||
impl YuvFrame {
|
||||
// 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 {
|
||||
unsafe {
|
||||
let channels = 3; // rgb
|
||||
|
||||
let rgb_size = self.width as usize * self.height as usize * channels;
|
||||
let mut rgb = Vec::<u8>::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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
RgbFrame {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
data: rgb,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Av1Decoder {}
|
||||
|
||||
impl Av1Decoder {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn read_frame(&mut self, reader: &mut IvfReader) -> anyhow::Result<RgbFrame> {
|
||||
let packet = reader.read_frame()?;
|
||||
|
||||
let mut decoder = rav1d::Decoder::new()?;
|
||||
|
||||
todo!();
|
||||
|
||||
// rav1d::Ok(RgbFrame {})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue