Răsfoiți Sursa

app/vid: make video decoding fast now. turns out i just need release mode for rav1d

darkfi 7 luni în urmă
părinte
comite
0e56d14013

+ 9 - 4
bin/app/Cargo.toml

@@ -20,10 +20,6 @@ freetype-rs = { version = "0.37.0", features = ["bundled"] }
 image = "0.25.9"
 image = "0.25.9"
 qoi = "0.4.1"
 qoi = "0.4.1"
 
 
-# AV1 video decoding - rav1d with Rust API from leo030303 fork
-# Disable default features to avoid NASM dependency (asm)
-rav1d = { git = "https://github.com/leo030303/rav1d", branch = "add-rust-api", default-features = false, features = ["bitdepth_8"] }
-
 tracing = "0.1.44"
 tracing = "0.1.44"
 glam = "0.30.9"
 glam = "0.30.9"
 #zmq = "0.10.0"
 #zmq = "0.10.0"
@@ -69,6 +65,15 @@ fluent = "0.17.0"
 unic-langid = { version = "0.9.6", features = ["unic-langid-macros"] }
 unic-langid = { version = "0.9.6", features = ["unic-langid-macros"] }
 indoc = "2.0.7"
 indoc = "2.0.7"
 
 
+# AV1 video decoding - rav1d with Rust API from leo030303 fork
+# Disable default features to avoid NASM dependency (asm)
+rav1d = { git = "https://github.com/leo030303/rav1d", branch = "add-rust-api", features = ["bitdepth_8"] }
+
+# This makes a HUGE difference to decoding speed.
+# Over 160s to like 2s.
+[profile.dev.package.rav1d]
+opt-level = 3
+
 [features]
 [features]
 emulate-android = []
 emulate-android = []
 enable-plugins = []
 enable-plugins = []

+ 0 - 1
bin/app/src/main.rs

@@ -59,7 +59,6 @@ mod text;
 mod text2;
 mod text2;
 mod ui;
 mod ui;
 mod util;
 mod util;
-mod video;
 
 
 use crate::{
 use crate::{
     app::{App, AppPtr},
     app::{App, AppPtr},

+ 0 - 0
bin/app/src/video/ivf.rs → bin/app/src/ui/vid/ivf.rs


+ 0 - 145
bin/app/src/video/decoder.rs

@@ -1,145 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! rav1d AV1 video decoder wrapper
-//!
-//! This module provides a Rust wrapper around the rav1d AV1 decoder.
-
-use rav1d::{Decoder as Rav1dDecoderInner, Picture, PlanarImageComponent, Rav1dError};
-
-pub type DecoderResult<T> = Result<T, Rav1dError>;
-
-macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui:video", $($arg)*); } }
-
-/// A decoded frame containing RGBA data
-#[derive(Debug, Clone)]
-pub struct DecodedFrame {
-    /// Frame width in pixels
-    pub width: u32,
-    /// Frame height in pixels
-    pub height: u32,
-    /// RGBA pixel data (width * height * 4 bytes)
-    pub data: Vec<u8>,
-}
-
-/// rav1d AV1 video decoder wrapper
-///
-/// This wraps the rav1d decoder and provides automatic planar GBR to RGBA conversion.
-pub struct Rav1dDecoder {
-    /// Inner decoder from rav1d
-    decoder: Rav1dDecoderInner,
-}
-
-impl Rav1dDecoder {
-    pub fn new() -> Self {
-        Self { decoder: Rav1dDecoderInner::new().unwrap() }
-    }
-
-    /// Send AV1 bitstream data to the decoder without getting a frame
-    pub fn send_data(&mut self, data: &[u8]) -> DecoderResult<()> {
-        let data = data.to_vec();
-        match self.decoder.send_data(data, None, None, None) {
-            Ok(_) => {}
-            Err(Rav1dError::TryAgain) => {
-                while let Err(Rav1dError::TryAgain) = self.decoder.send_pending_data() {
-                    // Continue sending pending data
-                }
-            }
-            Err(err) => return Err(err),
-        }
-        Ok(())
-    }
-
-    /// Get the next decoded frame from the decoder
-    pub fn get_pic(&mut self) -> DecoderResult<DecodedFrame> {
-        let now = std::time::Instant::now();
-        let pix = self.decoder.get_picture();
-        t!("decoder get pix: {:?}", now.elapsed());
-
-        let now = std::time::Instant::now();
-        let res = pix.map(|pic| Self::conv(pic));
-        t!("decoder conv: {:?}", now.elapsed());
-        res
-    }
-
-    /// Decode AV1 bitstream data and get all available frames
-    /// Returns a vector of frames (may be empty if decoder needs more data)
-    pub fn decode(&mut self, data: &[u8]) -> DecoderResult<Vec<DecodedFrame>> {
-        self.send_data(data)?;
-
-        let mut frames = Vec::new();
-        loop {
-            match self.get_pic() {
-                Ok(frame) => frames.push(frame),
-                Err(Rav1dError::TryAgain) => break,
-                Err(e) => return Err(e),
-            }
-        }
-        Ok(frames)
-    }
-
-    /// Convert a rav1d Picture from planar GBR to RGBA
-    fn conv(pic: Picture) -> DecodedFrame {
-        let g_plane = pic.plane(PlanarImageComponent::Y);
-        let b_plane = pic.plane(PlanarImageComponent::U);
-        let r_plane = pic.plane(PlanarImageComponent::V);
-
-        let g_stride = pic.stride(PlanarImageComponent::Y) as usize;
-        let b_stride = pic.stride(PlanarImageComponent::U) as usize;
-        let r_stride = pic.stride(PlanarImageComponent::V) as usize;
-
-        let width = pic.width() as usize;
-        let height = pic.height() as usize;
-
-        let mut rgba = vec![0u8; width * height * 4];
-
-        for y in 0..height {
-            for x in 0..width {
-                let g_idx = y * g_stride + x;
-                let b_idx = y * b_stride + x;
-                let r_idx = y * r_stride + x;
-
-                let r = r_plane[r_idx];
-                let g = g_plane[g_idx];
-                let b = b_plane[b_idx];
-
-                let out_idx = (y * width + x) * 4;
-                rgba[out_idx] = r;
-                rgba[out_idx + 1] = g;
-                rgba[out_idx + 2] = b;
-                rgba[out_idx + 3] = 255;
-            }
-        }
-
-        DecodedFrame { width: width as u32, height: height as u32, data: rgba }
-    }
-
-    /// Flush the decoder to get any remaining frames
-    pub fn flush(&mut self) -> DecoderResult<Vec<DecodedFrame>> {
-        self.decoder.flush();
-        let mut frames = Vec::new();
-        loop {
-            match self.get_pic() {
-                Ok(frame) => frames.push(frame),
-                Err(Rav1dError::TryAgain) => break,
-                Err(e) => return Err(e),
-            }
-        }
-        Ok(frames)
-    }
-}

+ 0 - 25
bin/app/src/video/mod.rs

@@ -1,25 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-mod decoder;
-mod ivf;
-mod yuv_conv;
-
-pub use decoder::{DecodedFrame, Rav1dDecoder};
-pub use ivf::{IvfHeader, IvfStreamingDemuxer};
-pub use yuv_conv::yuv420p_to_rgba;

+ 0 - 175
bin/app/src/video/yuv_conv.rs

@@ -1,175 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! YUV to RGBA color space conversion
-//!
-//! This module provides functions to convert YUV420P planar format
-//! to RGBA format for GPU rendering.
-
-/// Convert YUV420P planar format to RGBA
-///
-/// # Arguments
-/// * `y_plane` - Y (luminance) plane data
-/// * `u_plane` - U (chrominance) plane data
-/// * `v_plane` - V (chrominance) plane data
-/// * `width` - Frame width in pixels
-/// * `height` - Frame height in pixels
-/// * `y_stride` - Y plane stride (bytes per row)
-/// * `u_stride` - U plane stride (bytes per row)
-/// * `v_stride` - V plane stride (bytes per row)
-///
-/// # Returns
-/// A Vec<u8> containing RGBA data (width * height * 4 bytes)
-///
-/// # Color Conversion
-/// Uses BT.601 standard for YUV to RGB conversion:
-/// ```text
-/// R = Y + 1.402 * (V - 128)
-/// G = Y - 0.344 * (U - 128) - 0.714 * (V - 128)
-/// B = Y + 1.772 * (U - 128)
-/// ```
-pub fn yuv420p_to_rgba(
-    y_plane: &[u8],
-    u_plane: &[u8],
-    v_plane: &[u8],
-    width: usize,
-    height: usize,
-    y_stride: usize,
-    u_stride: usize,
-    v_stride: usize,
-) -> Vec<u8> {
-    let mut rgba = vec![0u8; width * height * 4];
-
-    for y in 0..height {
-        for x in 0..width {
-            let y_idx = y * y_stride + x;
-            let u_idx = (y / 2) * u_stride + (x / 2);
-            let v_idx = (y / 2) * v_stride + (x / 2);
-
-            let y_val = y_plane[y_idx] as i32;
-            let u_val = u_plane[u_idx] as i32 - 128;
-            let v_val = v_plane[v_idx] as i32 - 128;
-
-            // BT.601 YUV to RGB conversion
-            let r = clamp((y_val as f32) + 1.402 * (v_val as f32));
-            let g = clamp((y_val as f32) - 0.344136 * (u_val as f32) - 0.714136 * (v_val as f32));
-            let b = clamp((y_val as f32) + 1.772 * (u_val as f32));
-
-            let out_idx = (y * width + x) * 4;
-            rgba[out_idx] = r;
-            rgba[out_idx + 1] = g;
-            rgba[out_idx + 2] = b;
-            // Alpha
-            rgba[out_idx + 3] = 255;
-        }
-    }
-
-    rgba
-}
-
-/// Clamp a floating point value to 0-255 range and convert to u8
-#[inline]
-fn clamp(value: f32) -> u8 {
-    value.round().clamp(0.0, 255.0) as u8
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_yuv420p_to_rgba_basic() {
-        // Create a small test frame (4x4)
-        let width = 4;
-        let height = 4;
-
-        // Y plane - full resolution
-        let y_plane: Vec<u8> = (0..16).map(|i| (i * 16) as u8).collect();
-
-        // U and V planes - half resolution (2x2)
-        let u_plane: Vec<u8> = vec![128, 128, 128, 128];
-        let v_plane: Vec<u8> = vec![128, 128, 128, 128];
-
-        let y_stride = width;
-        let u_stride = width / 2;
-        let v_stride = width / 2;
-
-        let rgba = yuv420p_to_rgba(
-            &y_plane, &u_plane, &v_plane, width, height, y_stride, u_stride, v_stride,
-        );
-
-        // Check output size
-        assert_eq!(rgba.len(), width * height * 4);
-
-        // With U=V=128 (neutral chroma), RGB should equal Y
-        for i in 0..16 {
-            let expected_y = (i * 16) as u8;
-            // R
-            assert_eq!(rgba[i * 4], expected_y);
-            // G
-            assert_eq!(rgba[i * 4 + 1], expected_y);
-            // B
-            assert_eq!(rgba[i * 4 + 2], expected_y);
-            // A
-            assert_eq!(rgba[i * 4 + 3], 255);
-        }
-    }
-
-    #[test]
-    fn test_clamp() {
-        assert_eq!(clamp(-10.0), 0);
-        assert_eq!(clamp(0.0), 0);
-        assert_eq!(clamp(127.5), 128);
-        assert_eq!(clamp(255.0), 255);
-        assert_eq!(clamp(300.0), 255);
-    }
-
-    #[test]
-    fn test_yuv_to_rgb_conversion() {
-        // Test specific color conversions
-        // Black: Y=0, U=128, V=128
-        let black = yuv_to_rgb(0, 128, 128);
-        assert_eq!(black, [0, 0, 0]);
-
-        // White: Y=255, U=128, V=128
-        let white = yuv_to_rgb(255, 128, 128);
-        assert_eq!(white, [255, 255, 255]);
-
-        // Red: Y=76, U=85, V=255 (approximate pure red in BT.601)
-        let red = yuv_to_rgb(76, 85, 255);
-        // R should be high
-        assert!(red[0] > 200);
-        // G should be low
-        assert!(red[1] < 50);
-        // B should be low
-        assert!(red[2] < 50);
-    }
-
-    /// Helper function to convert a single YUV pixel to RGB
-    fn yuv_to_rgb(y: u8, u: u8, v: u8) -> [u8; 3] {
-        let y_val = y as i32;
-        let u_val = u as i32 - 128;
-        let v_val = v as i32 - 128;
-
-        let r = clamp((y_val as f32) + 1.402 * (v_val as f32));
-        let g = clamp((y_val as f32) - 0.344136 * (u_val as f32) - 0.714136 * (v_val as f32));
-        let b = clamp((y_val as f32) + 1.772 * (u_val as f32));
-
-        [r, g, b]
-    }
-}