#ffmpeg #codec #decoding #muxing #encoding

video-rs-adder-dep

基于ffmpeg的高级视频工具包。略微修改以向ADDER项目公开内部信息。

1个不稳定版本

0.4.1 2023年11月29日

#351视频

33 每月下载量
3 个组件(2直接)中使用

MIT/Apache

125KB
2K SLoC

video-rs

基于ffmpeg的高级视频工具包。

🎬 简介

video-rs 是一个通用的Rust视频库,它使用了来自 ffmpeglibav 家族库。它的目标是提供一个稳定且Rust风格的接口,用于许多常见的视频任务,如读取、写入、复用、编码和解码。

🛠 状态

⚠️ 此项目仍在进行中,可能包含错误。API的一些部分尚未完善。请谨慎使用。

还可以查看我们的其他视频/音频项目 raverave 仍在开发中,但其最终目标是取代 video-rs 并提供一个功能齐全的媒体库,而不依赖于ffmpeg。

📦 设置

首先,安装 ffmpeg 库。 ffmpeg-next 项目在这方面有出色的说明video-rs 依赖于 ffmpeg-next 包)。

然后,将以下内容添加到您的 Cargo.toml 依赖项中

video-rs = "0.4"

使用 ndarray 功能来使用原始帧与 ndarray

video-rs = { version = "0.4", features = ["ndarray"] }

📖 示例

解码视频并打印左上角的RGB值

use video_rs::{self, Decoder, Locator};

fn main() {
    video_rs::init().unwrap();

    let source = Locator::Url(
        "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
            .parse()
            .unwrap(),
    );

    let mut decoder = Decoder::new(&source)
        .expect("failed to create decoder");

    for frame in decoder.decode_iter() {
        if let Ok((_, frame)) = frame {
            let rgb = frame
                .slice(ndarray::s![0, 0, ..])
                .to_slice()
                .unwrap();
            println!(
                "pixel at 0, 0: {}, {}, {}",
                rgb[0],
                rgb[1],
                rgb[2],
            );
        } else {
            break;
        }
    }
}

使用 ndarray 创建每个帧来编码 🌈 视频

use std::path::PathBuf;

use ndarray::Array3;

use video_rs::{Encoder, EncoderSettings, Locator, Time};

fn main() {
    video_rs::init().unwrap();

    let destination: Locator = PathBuf::from("rainbow.mp4").into();
    let settings = EncoderSettings::for_h264_yuv420p(1280, 720, false);

    let mut encoder = Encoder::new(&destination, settings)
        .expect("failed to create encoder");

    let duration: Time = Time::from_nth_of_a_second(24);
    let mut position = Time::zero();
    for i in 0..256 {
        // This will create a smooth rainbow animation video!
        let frame = rainbow_frame(i as f32 / 256.0);

        encoder
            .encode(&frame, &position)
            .expect("failed to encode frame");

        // Update the current position and add the inter-frame
        // duration to it.
        position = position.aligned_with(&duration).add();
    }

    encoder.finish().expect("failed to finish encoder");
}

fn rainbow_frame(p: f32) -> Array3<u8> {
    // This is what generated the rainbow effect! We loop through
    // the HSV color spectrum and convert to RGB.
    let rgb = hsv_to_rgb(p * 360.0, 100.0, 100.0);

    // This creates a frame with height 720, width 1280 and three
    // channels. The RGB values for each pixel are equal, and
    // determined by the `rgb` we chose above.
    Array3::from_shape_fn((720, 1280, 3), |(_y, _x, c)| rgb[c])
}

fn hsv_to_rgb(h: f32, s: f32, v: f32) -> [u8; 3] {
    let s = s / 100.0;
    let v = v / 100.0;
    let c = s * v;
    let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs());
    let m = v - c;
    let (r, g, b) = if (0.0..60.0).contains(&h) {
        (c, x, 0.0)
    } else if (60.0..120.0).contains(&h) {
        (x, c, 0.0)
    } else if (120.0..180.0).contains(&h) {
        (0.0, c, x)
    } else if (180.0..240.0).contains(&h) {
        (0.0, x, c)
    } else if (240.0..300.0).contains(&h) {
        (x, 0.0, c)
    } else if (300.0..360.0).contains(&h) {
        (c, 0.0, x)
    } else {
        (0.0, 0.0, 0.0)
    };
    [
        ((r + m) * 255.0) as u8,
        ((g + m) * 255.0) as u8,
        ((b + m) * 255.0) as u8,
    ]
}

🪲 调试

FFmpeg并不总是直接产生有用的错误信息。如果在运行过程中遇到问题,建议开启跟踪功能,以查看日志信息中是否包含额外的信息。

请将以下包添加到Cargo.toml

[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"

并在主函数中添加以下内容

fn main() {
    tracing_subscriber::fmt::init();

    // ...
}

设置RUST_LOG环境变量以显示跟踪信息

RUST_LOG=video=debug cargo run

✨ 致谢

video-rs的存在离不开以下组织和个人

⚖️ 许可证

根据以下任一许可证授权

任选其一。

贡献

除非你明确表示,否则根据Apache-2.0许可证定义的,任何有意提交以包含在作品中的贡献,都应按照上述方式双许可,而不附加任何额外条款或条件。

依赖项

~2–5MB
~118K SLoC