8 releases

Uses new Rust 2021

new 0.1.7 Dec 1, 2022
0.1.6 Nov 29, 2022
0.1.3 Sep 13, 2022
0.1.2 Aug 31, 2022

#50 in Video

Download history 37/week @ 2022-08-14 12/week @ 2022-08-21 26/week @ 2022-08-28 15/week @ 2022-09-04 28/week @ 2022-09-11 16/week @ 2022-09-18 3/week @ 2022-09-25 14/week @ 2022-10-02 9/week @ 2022-10-09 3/week @ 2022-10-16 7/week @ 2022-10-23 8/week @ 2022-10-30 13/week @ 2022-11-06 16/week @ 2022-11-13 84/week @ 2022-11-20 65/week @ 2022-11-27

180 downloads per month

MIT/Apache

100KB
2K SLoC

video-rs

High-level video toolkit based on ffmpeg.

🎬 Introduction

video-rs is a general-purpose video library for Rust that uses the libav-family libraries from ffmpeg. It aims to provide a stable and Rusty interface to many common video tasks such as reading, writing, muxing, encoding and decoding.

🛠 S️️tatus

⚠️ This project is still a work-in-progress, and will contain bugs. Some parts of the API have not been flushed out yet. Use with caution.

📦 Setup

First, install the ffmpeg libraries. The ffmpeg-next project has excellent instructions on this (video-rs depends on the ffmpeg-next crate).

Then, add the following to your dependencies in Cargo.toml:

video-rs = "0.1"

Use the ndarray feature to be able to use raw frames with the ndarray crate:

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

📖 Examples

Decode a video and print the RGB value for the top left pixel:

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

fn main() {
  video_rs::init();
  
  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;
    }
  }

}

Encode a 🌈 video, using ndarray to create each frame:

use std::path::PathBuf;
use std::time::Duration;

use ndarray::Array3;

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

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

  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");

  // By determining the duration of each frame, we are essentially determing
  // the true frame rate of the output video. We choose 24 here.
  let duration: Time = Duration::from_nanos(1_000_000_000 / 24).into();

  // Keep track of the current video timestamp.
  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 `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 h >= 0.0 && h < 60.0 {
      (c, x, 0.0)
    } else if h >= 60.0 && h < 120.0 {
      (x, c, 0.0)
    } else if h >= 120.0 && h < 180.0 {
      (0.0, c, x)
    } else if h >= 180.0 && h < 240.0 {
      (0.0, x, c)
    } else if h >= 240.0 && h < 300.0 {
      (x, 0.0, c)
    } else if h >= 300.0 && h < 360.0 {
      (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,
  ]
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~2.2–4.5MB
~119K SLoC