├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── examples └── fade.rs └── src ├── colorspace.rs ├── data.rs ├── encoder.rs ├── error.rs ├── image.rs ├── lib.rs ├── picture.rs └── setup ├── mod.rs ├── preset.rs └── tune.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x264" 3 | description = "Encoding H.264 video." 4 | keywords = ["x264", "encoding", "h264", "video", "encoder"] 5 | repository = "https://github.com/quadrupleslap/x264" 6 | documentation = "https://docs.rs/x264" 7 | license = "MIT" 8 | version = "0.3.0" 9 | authors = ["Ram "] 10 | 11 | [dependencies] 12 | x264-sys = "0.1" 13 | 14 | [build-dependencies] 15 | pkg-config = "0.3" 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # x264 2 | 3 | [![Crates.io](https://img.shields.io/crates/d/x264.svg)](https://crates.io/crates/x264) 4 | [![Docs.rs](https://docs.rs/x264/badge.svg)](https://docs.rs/x264) 5 | 6 | This crate gives you safe but still extremely fast H.264 encoding. 7 | 8 | ## Usage 9 | 10 | 1. Add `x264` to your `Cargo.toml`. 11 | 2. Look at an [example](examples) and copy it. 12 | 3. Read some [documentation](https://docs.rs/x264). 13 | 14 | ## Contributing 15 | 16 | You could... 17 | 18 | - star this repository. 19 | - open issues if you find anything that can be improved. 20 | - add some beautiful examples. 21 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate pkg_config; 2 | 3 | fn main() { 4 | let x264 = pkg_config::Config::new() 5 | .cargo_metadata(false) 6 | .probe("x264") 7 | .expect("Is x264 installed?"); 8 | 9 | let n: Option = x264.version 10 | .split('.') 11 | .nth(1) 12 | .and_then(|n| n.parse().ok()); 13 | 14 | if let Some(n) = n { 15 | if n >= 149 { 16 | println!("rustc-cfg=yuyv"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/fade.rs: -------------------------------------------------------------------------------- 1 | extern crate x264; 2 | 3 | use std::io::Write; 4 | use std::fs::File; 5 | use x264::{Colorspace, Encoder, Image}; 6 | 7 | fn main() { 8 | const WIDTH: usize = 480; 9 | const HEIGHT: usize = 360; 10 | 11 | // Initialize things. 12 | 13 | let mut encoder = 14 | Encoder::builder() 15 | .fps(60, 1) 16 | .build(Colorspace::RGB, WIDTH as _, HEIGHT as _) 17 | .unwrap(); 18 | let mut file = File::create("fade.h264").unwrap(); 19 | let mut canvas = vec![0; WIDTH * HEIGHT * 3]; 20 | 21 | println!("Initialized!"); 22 | 23 | // Write the headers. 24 | 25 | { 26 | let headers = encoder.headers().unwrap(); 27 | file.write_all(headers.entirety()).unwrap(); 28 | } 29 | 30 | // Queue each frame. 31 | 32 | for i in 0..300 { 33 | frame(i as f64 / 300.0, &mut canvas); 34 | let image = Image::rgb(WIDTH as _, HEIGHT as _, &canvas); 35 | let (data, _) = encoder.encode((60 * i) as _, image).unwrap(); 36 | file.write_all(data.entirety()).unwrap(); 37 | } 38 | 39 | // Finally, flush any delayed frames. 40 | 41 | { 42 | let mut flush = encoder.flush(); 43 | while let Some(result) = flush.next() { 44 | let (data, _) = result.unwrap(); 45 | file.write_all(data.entirety()).unwrap(); 46 | } 47 | } 48 | 49 | println!("Done! The output is at `fade.h264`."); 50 | println!("Good luck finding a H.264 viewer, though! ;)"); 51 | } 52 | 53 | fn frame(p: f64, f: &mut [u8]) { 54 | let lum = (255.0 * p).floor().min(255.0) as u8; 55 | for x in f { *x = lum; } 56 | } 57 | -------------------------------------------------------------------------------- /src/colorspace.rs: -------------------------------------------------------------------------------- 1 | use x264::*; 2 | 3 | #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)] 4 | #[repr(u32)] 5 | /// The colorspace of an image, which details how its colors are represented. 6 | pub enum Colorspace { 7 | /// A Y plane followed by 2x2 subsampled U and V planes. 8 | I420 = X264_CSP_I420, 9 | /// A Y plane followed by 2x2 subsampled V and U planes. 10 | YV12 = X264_CSP_YV12, 11 | /// A Y plane followed by a packed 2x2 subsampled UV plane. 12 | NV12 = X264_CSP_NV12, 13 | /// A Y plane followed by a packed 2x2 subsampled VU plane. 14 | NV21 = X264_CSP_NV21, 15 | /// A Y plane followed by 2x1 subsampled U and V planes. 16 | I422 = X264_CSP_I422, 17 | /// A Y plane followed by 2x1 subsampled V and U planes. 18 | YV16 = X264_CSP_YV16, 19 | /// A Y plane followed by a packed 2x1 subsampled UV plane. 20 | NV16 = X264_CSP_NV16, 21 | /// A single plane whose bytes follow the pattern YUYV pattern, which means 22 | /// the U and V parts are 2x1 subsampled. 23 | #[cfg(feature = "yuyv")] 24 | YUYV = X264_CSP_YUYV, 25 | /// A single plane whose bytes follow the pattern UYVY pattern, which means 26 | /// the U and V parts are 2x1 subsampled. 27 | #[cfg(feature = "yuyv")] 28 | UYVY = X264_CSP_UYVY, 29 | /// A packed 32-bit UYVY plane with 10-bit components, and 2 padding bits. 30 | V210 = X264_CSP_V210, 31 | /// A Y plane followed by U and V planes. 32 | I444 = X264_CSP_I444, 33 | /// A Y plane followed by V and U planes. 34 | YV24 = X264_CSP_YV24, 35 | /// A packed 24-bit BGR plane. 36 | BGR = X264_CSP_BGR, 37 | /// A packed 32-bit BGR plane, where the latter byte is padding. 38 | BGRA = X264_CSP_BGRA, 39 | /// A packed 24-bit RGB plane. 40 | RGB = X264_CSP_RGB, 41 | } 42 | 43 | #[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)] 44 | /// The image's colorspace plus some extra encoding options. 45 | pub struct Encoding { 46 | raw: i32, 47 | } 48 | 49 | impl Encoding { 50 | /// Add an encoding option. 51 | pub fn add(mut self, modifier: Modifier) -> Self { 52 | self.raw |= modifier as i32; 53 | self 54 | } 55 | 56 | /// Remove an encoding option. 57 | pub fn remove(mut self, modifier: Modifier) -> Self { 58 | self.raw &= !(modifier as i32); 59 | self 60 | } 61 | 62 | /// Check if an encoding option has been set. 63 | pub fn has(self, modifier: Modifier) -> bool { 64 | self.raw & modifier as i32 != 0 65 | } 66 | 67 | /// Gets the colorspace of the encoding. 68 | pub fn colorspace(self) -> Colorspace { 69 | use core::mem; 70 | unsafe { mem::transmute(self.raw as u32 % X264_CSP_MAX) } 71 | } 72 | 73 | #[doc(hidden)] 74 | pub fn into_raw(self) -> i32 { 75 | self.raw 76 | } 77 | 78 | #[doc(hidden)] 79 | pub unsafe fn from_raw(raw: i32) -> Self { 80 | Self { raw } 81 | } 82 | } 83 | 84 | impl From for Encoding { 85 | fn from(csp: Colorspace) -> Self { 86 | Self { raw: csp as i32 } 87 | } 88 | } 89 | 90 | #[repr(i32)] 91 | /// Some extra encoding options. 92 | pub enum Modifier { 93 | /// Doubles the pixel depth, from 8 to 16 bits per pixel. 94 | HighDepth = X264_CSP_HIGH_DEPTH as i32, 95 | /// Vertically flips the image. 96 | VerticalFlip = X264_CSP_VFLIP as i32, 97 | } 98 | -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | use core::slice; 3 | use x264::*; 4 | 5 | //TODO: Iterator over the units. 6 | 7 | /// The encoded data, to be used in chunks or in its entirety. 8 | pub struct Data<'a> { 9 | ptr: *mut x264_nal_t, 10 | len: usize, 11 | spooky: PhantomData<&'a [x264_nal_t]> 12 | } 13 | 14 | impl<'a> Data<'a> { 15 | #[doc(hidden)] 16 | pub unsafe fn from_raw_parts( 17 | ptr: *mut x264_nal_t, 18 | len: usize 19 | ) -> Self { 20 | Data { ptr, len, spooky: PhantomData } 21 | } 22 | 23 | /// The length (in NAL units, **not** in bytes) of this data sequence. 24 | pub fn len(&self) -> usize { 25 | self.len 26 | } 27 | 28 | //TODO: Use index trait(s) once IndexMove becomes a thing. 29 | 30 | /// The `i`th unit. 31 | /// 32 | /// # Panics 33 | /// 34 | /// Panics if `i` is out-of-bounds. In order to be within the bounds, 35 | /// `i` must be less than `len`. 36 | pub fn unit(&self, i: usize) -> Unit<'a> { 37 | const D: i32 = Priority::Disposable as i32; 38 | const L: i32 = Priority::Low as i32; 39 | const H: i32 = Priority::High as i32; 40 | 41 | assert!(i < self.len); 42 | 43 | let nal = unsafe { 44 | *self.ptr.offset(i as isize) 45 | }; 46 | 47 | Unit { 48 | priority: 49 | match nal.i_ref_idc { 50 | D => Priority::Disposable, 51 | L => Priority::Low, 52 | H => Priority::High, 53 | _ => Priority::Highest, 54 | }, 55 | payload: 56 | unsafe { 57 | slice::from_raw_parts( 58 | nal.p_payload, 59 | nal.i_payload as usize 60 | ) 61 | } 62 | } 63 | } 64 | 65 | /// The entire chunk of data, as one big byte-slice. 66 | pub fn entirety(&self) -> &[u8] { 67 | if self.len == 0 { 68 | &[] 69 | } else { 70 | let (a, b) = unsafe { 71 | let a = *self.ptr; 72 | let b = *self.ptr.offset((self.len - 1) as isize); 73 | (a, b) 74 | }; 75 | 76 | let start = a.p_payload; 77 | let length = b.p_payload as usize 78 | + b.i_payload as usize 79 | - start as usize; 80 | 81 | unsafe { slice::from_raw_parts(start, length) } 82 | } 83 | } 84 | } 85 | 86 | /// A single NAL unit. 87 | pub struct Unit<'a> { 88 | priority: Priority, 89 | payload: &'a [u8] 90 | } 91 | 92 | impl<'a> Unit<'a> { 93 | /// How crucial this unit is regarding the decoding of the video. 94 | pub fn priority(&self) -> Priority { 95 | self.priority 96 | } 97 | } 98 | 99 | impl<'a> AsRef<[u8]> for Unit<'a> { 100 | fn as_ref(&self) -> &[u8] { 101 | self.payload 102 | } 103 | } 104 | 105 | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] 106 | #[repr(i32)] 107 | /// The importance of a given unit. 108 | pub enum Priority { 109 | /// Not important at all. 110 | Disposable = nal_priority_e::NAL_PRIORITY_DISPOSABLE as i32, 111 | /// Not very important. 112 | Low = nal_priority_e::NAL_PRIORITY_LOW as i32, 113 | /// Pretty important. 114 | High = nal_priority_e::NAL_PRIORITY_HIGH as i32, 115 | /// Extremely important. 116 | Highest = nal_priority_e::NAL_PRIORITY_HIGHEST as i32, 117 | } 118 | -------------------------------------------------------------------------------- /src/encoder.rs: -------------------------------------------------------------------------------- 1 | use {Data, Encoding, Error, Image, Picture, Result, Setup}; 2 | use core::{mem, ptr}; 3 | use x264::*; 4 | 5 | /// Encodes video. 6 | pub struct Encoder { 7 | raw: *mut x264_t, 8 | params: x264_param_t, 9 | } 10 | 11 | impl Encoder { 12 | /// Creates a new builder with default options. 13 | /// 14 | /// For more options see `Setup::new`. 15 | pub fn builder() -> Setup { 16 | Setup::default() 17 | } 18 | 19 | #[doc(hidden)] 20 | pub unsafe fn from_raw(raw: *mut x264_t) -> Self { 21 | let mut params = mem::uninitialized(); 22 | x264_encoder_parameters(raw, &mut params); 23 | Self { raw, params } 24 | } 25 | 26 | /// Feeds a frame to the encoder. 27 | /// 28 | /// # Panics 29 | /// 30 | /// Panics if there is a mismatch between the image and the encoder 31 | /// regarding width, height or colorspace. 32 | pub fn encode(&mut self, pts: i64, image: Image) 33 | -> Result<(Data, Picture)> 34 | { 35 | assert_eq!(image.width(), self.width()); 36 | assert_eq!(image.height(), self.height()); 37 | assert_eq!(image.encoding(), self.encoding()); 38 | unsafe { self.encode_unchecked(pts, image) } 39 | } 40 | 41 | /// Feeds a frame to the encoder. 42 | /// 43 | /// # Unsafety 44 | /// 45 | /// The caller must ensure that the width, height *and* colorspace 46 | /// of the image are the same as that of the encoder. 47 | pub unsafe fn encode_unchecked(&mut self, pts: i64, image: Image) 48 | -> Result<(Data, Picture)> 49 | { 50 | let image = image.raw(); 51 | 52 | let mut picture = mem::uninitialized(); 53 | x264_picture_init(&mut picture); 54 | picture.i_pts = pts; 55 | picture.img = image; 56 | 57 | let mut len = 0; 58 | let mut stuff = mem::uninitialized(); 59 | let mut raw = mem::uninitialized(); 60 | 61 | let err = x264_encoder_encode( 62 | self.raw, 63 | &mut stuff, 64 | &mut len, 65 | &mut picture, 66 | &mut raw 67 | ); 68 | 69 | if err < 0 { 70 | Err(Error) 71 | } else { 72 | let data = Data::from_raw_parts(stuff, len as usize); 73 | let picture = Picture::from_raw(raw); 74 | Ok((data, picture)) 75 | } 76 | } 77 | 78 | /// Gets the video headers, which should be sent first. 79 | pub fn headers(&mut self) -> Result { 80 | let mut len = 0; 81 | let mut stuff = unsafe { mem::uninitialized() }; 82 | 83 | let err = unsafe { 84 | x264_encoder_headers( 85 | self.raw, 86 | &mut stuff, 87 | &mut len 88 | ) 89 | }; 90 | 91 | if err < 0 { 92 | Err(Error) 93 | } else { 94 | Ok(unsafe { Data::from_raw_parts(stuff, len as usize) }) 95 | } 96 | } 97 | 98 | /// Begins flushing the encoder, to handle any delayed frames. 99 | /// 100 | /// ```rust 101 | /// # use x264::{Colorspace, Setup}; 102 | /// # let encoder = Setup::default().build(Colorspace::RGB, 1920, 1080).unwrap(); 103 | /// # 104 | /// let mut flush = encoder.flush(); 105 | /// 106 | /// while let Some(result) = flush.next() { 107 | /// if let Ok((data, picture)) = result { 108 | /// // Handle data. 109 | /// } 110 | /// } 111 | /// ``` 112 | pub fn flush(self) -> Flush { 113 | Flush { encoder: self } 114 | } 115 | 116 | /// The width required of any input images. 117 | pub fn width(&self) -> i32 { self.params.i_width } 118 | /// The height required of any input images. 119 | pub fn height(&self) -> i32 { self.params.i_height } 120 | /// The encoding required of any input images. 121 | pub fn encoding(&self) -> Encoding { 122 | unsafe { Encoding::from_raw(self.params.i_csp) } 123 | } 124 | } 125 | 126 | impl Drop for Encoder { 127 | fn drop(&mut self) { 128 | unsafe { x264_encoder_close(self.raw); } 129 | } 130 | } 131 | 132 | /// Iterate through any delayed frames. 133 | pub struct Flush { 134 | encoder: Encoder, 135 | } 136 | 137 | impl Flush { 138 | /// Keeps flushing. 139 | pub fn next(&mut self) -> Option> { 140 | let enc = self.encoder.raw; 141 | 142 | if unsafe { x264_encoder_delayed_frames(enc) } == 0 { 143 | return None; 144 | } 145 | 146 | let mut len = 0; 147 | let mut stuff = unsafe { mem::uninitialized() }; 148 | let mut raw = unsafe { mem::uninitialized() }; 149 | 150 | let err = unsafe { 151 | x264_encoder_encode( 152 | enc, 153 | &mut stuff, 154 | &mut len, 155 | ptr::null_mut(), 156 | &mut raw 157 | ) 158 | }; 159 | 160 | Some(if err < 0 { 161 | Err(Error) 162 | } else { 163 | Ok(unsafe {( 164 | Data::from_raw_parts(stuff, len as usize), 165 | Picture::from_raw(raw), 166 | )}) 167 | }) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use core::result; 2 | 3 | #[derive(Copy, Clone, Hash, Debug, PartialEq, Eq)] 4 | /// Indicates that something bad happened. 5 | pub struct Error; 6 | 7 | /// A specialized Result type for video encoding operations. 8 | pub type Result = result::Result; 9 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | use {Colorspace, Encoding, Modifier}; 2 | use core::marker::PhantomData; 3 | use core::ptr; 4 | use x264::*; 5 | 6 | /// Input image data to be given to the encoder. 7 | pub struct Image<'a> { 8 | raw: x264_image_t, 9 | width: i32, 10 | height: i32, 11 | spooky: PhantomData<&'a [u8]>, 12 | } 13 | 14 | impl<'a> Image<'a> { 15 | /// Makes a new image with the given information. 16 | /// 17 | /// # Panics 18 | /// 19 | /// Panics if the plane is invalid. 20 | pub fn new>( 21 | format: E, 22 | width: i32, 23 | height: i32, 24 | planes: &[Plane<'a>], 25 | ) -> Self { 26 | //TODO: Get someone who knows what they're doing to verify this. 27 | 28 | use self::Colorspace::*; 29 | 30 | let format = format.into(); 31 | 32 | let (pc, wm, hm, ws, hs): (_, _, _, &[_], &[_]) = 33 | match format.colorspace() { 34 | I420 | YV12 => (3, 2, 2, &[2, 1, 1], &[2, 1, 1]), 35 | NV12 | NV21 => (2, 2, 2, &[2, 2], &[2, 1] ), 36 | I422 | YV16 => (3, 2, 1, &[2, 1, 1], &[1, 1, 1]), 37 | NV16 => (2, 2, 1, &[2, 2], &[1, 1] ), 38 | #[cfg(feature = "yuyv")] 39 | YUYV | UYVY => (1, 1, 1, &[2], &[1] ), 40 | V210 => (1, 1, 1, &[4], &[1] ), 41 | I444 | YV24 => (3, 1, 1, &[1, 1, 1], &[1, 1, 1]), 42 | BGR | RGB => (1, 1, 1, &[3], &[1] ), 43 | BGRA => (1, 1, 1, &[4], &[1] ), 44 | }; 45 | 46 | let (wq, wr) = (width / wm, width % wm); 47 | let (hq, hr) = (height / hm, height % hm); 48 | let depth = if format.has(Modifier::HighDepth) { 2 } else { 1 }; 49 | 50 | // Check that the number of planes matches pc. 51 | assert!(planes.len() == pc); 52 | // Check that the width and the height are multiples of wm and hm. 53 | assert!(wr == 0 && hr == 0); 54 | for (i, plane) in planes.iter().enumerate() { 55 | // Check that the plane's stride is at least depth * wq * ws[i]. 56 | assert!(depth * wq * ws[i] <= plane.stride); 57 | // Check that there are at least hq * hs[i] rows in the plane. 58 | assert!(hq * hs[i] <= plane.data.len() as i32 / plane.stride); 59 | } 60 | 61 | unsafe { 62 | Self::new_unchecked(format, width, height, planes) 63 | } 64 | } 65 | 66 | /// Makes a new packed BGR image. 67 | pub fn bgr(width: i32, height: i32, data: &'a [u8]) -> Self { 68 | let plane = Plane { stride: data.len() as i32 / height, data }; 69 | Self::new(Colorspace::BGR, width, height, &[plane]) 70 | } 71 | 72 | /// Makes a new packed RGB image. 73 | pub fn rgb(width: i32, height: i32, data: &'a [u8]) -> Self { 74 | let plane = Plane { stride: data.len() as i32 / height, data }; 75 | Self::new(Colorspace::RGB, width, height, &[plane]) 76 | } 77 | 78 | /// Makes a new packed BGRA image. 79 | pub fn bgra(width: i32, height: i32, data: &'a [u8]) -> Self { 80 | let plane = Plane { stride: data.len() as i32 / height, data }; 81 | Self::new(Colorspace::BGRA, width, height, &[plane]) 82 | } 83 | 84 | /// Makes a new image with the given planes and colorspace. 85 | /// 86 | /// # Unsafety 87 | /// 88 | /// The caller must ensure that the plane fulfils all the invariants that 89 | /// x264 expects it to fulfil. I don't actually know what all of those are, 90 | /// but the source of `Encoder::new` is my best guess. 91 | pub unsafe fn new_unchecked( 92 | format: Encoding, 93 | width: i32, 94 | height: i32, 95 | planes: &[Plane<'a>], 96 | ) -> Self { 97 | let mut strides = [0; 4]; 98 | let mut pointers = [ptr::null_mut(); 4]; 99 | 100 | for (i, &Plane { stride, data }) in planes.iter().enumerate() { 101 | strides[i] = stride; 102 | pointers[i] = data.as_ptr() as *mut u8; 103 | } 104 | 105 | let raw = x264_image_t { 106 | i_csp: format.into_raw(), 107 | i_plane: planes.len() as i32, 108 | i_stride: strides, 109 | plane: pointers, 110 | }; 111 | 112 | Self { raw, width, height, spooky: PhantomData } 113 | } 114 | 115 | // Getters 116 | 117 | /// The width of the image. 118 | pub fn width(&self) -> i32 { self.width } 119 | /// The height of the image. 120 | pub fn height(&self) -> i32 { self.height } 121 | /// The encoding of the image. 122 | pub fn encoding(&self) -> Encoding { 123 | unsafe { Encoding::from_raw(self.raw.i_csp) } 124 | } 125 | 126 | #[doc(hidden)] 127 | pub fn raw(&self) -> x264_image_t { self.raw } 128 | } 129 | 130 | /// A single plane of an image. 131 | pub struct Plane<'a> { 132 | /// The plane's stride (the number of bytes for each row). 133 | pub stride: i32, 134 | /// The plane's pixel data. 135 | pub data: &'a [u8], 136 | } 137 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An x264 wrapper, so that you can safely encode H.264 video. 2 | 3 | #![no_std] 4 | #![warn(missing_docs)] 5 | 6 | extern crate x264_sys; 7 | 8 | use x264_sys::x264; 9 | 10 | mod colorspace; 11 | mod data; 12 | mod encoder; 13 | mod error; 14 | mod image; 15 | mod picture; 16 | mod setup; 17 | 18 | pub use colorspace::*; 19 | pub use data::*; 20 | pub use encoder::*; 21 | pub use error::*; 22 | pub use image::*; 23 | pub use picture::*; 24 | pub use setup::*; 25 | -------------------------------------------------------------------------------- /src/picture.rs: -------------------------------------------------------------------------------- 1 | use x264::*; 2 | 3 | /// Output information about an encoded frame. 4 | pub struct Picture { 5 | raw: x264_picture_t 6 | } 7 | 8 | impl Picture { 9 | /// Whether the picture is a keyframe. 10 | pub fn keyframe(&self) -> bool { 11 | self.raw.b_keyframe != 0 12 | } 13 | 14 | /// The presentation timestamp. 15 | pub fn pts(&self) -> i64 { 16 | self.raw.i_pts 17 | } 18 | 19 | /// The decoding timestamp. 20 | pub fn dts(&self) -> i64 { 21 | self.raw.i_dts 22 | } 23 | 24 | #[doc(hidden)] 25 | pub unsafe fn from_raw(raw: x264_picture_t) -> Self { 26 | Self { raw } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/setup/mod.rs: -------------------------------------------------------------------------------- 1 | use {Encoder, Encoding, Error, Result}; 2 | use core::mem; 3 | use x264::*; 4 | 5 | mod preset; 6 | mod tune; 7 | 8 | pub use self::preset::*; 9 | pub use self::tune::*; 10 | 11 | /// Builds a new encoder. 12 | pub struct Setup { 13 | raw: x264_param_t, 14 | } 15 | 16 | impl Setup { 17 | /// Creates a new builder with the specified preset and tune. 18 | pub fn preset( 19 | preset: Preset, 20 | tune: Tune, 21 | fast_decode: bool, 22 | zero_latency: bool 23 | ) -> Self { 24 | let mut raw = unsafe { mem::uninitialized() }; 25 | 26 | // Name validity verified at compile-time. 27 | assert_eq!(0, unsafe { 28 | x264_param_default_preset( 29 | &mut raw, 30 | preset.to_cstr(), 31 | tune.to_cstr(fast_decode, zero_latency) 32 | ) 33 | }); 34 | 35 | Self { raw } 36 | } 37 | 38 | /// Makes the first pass faster. 39 | pub fn fastfirstpass(mut self) -> Self { 40 | unsafe { x264_param_apply_fastfirstpass(&mut self.raw); } 41 | self 42 | } 43 | 44 | /// The video's framerate, represented as a rational number. 45 | /// 46 | /// The value is in frames per second. 47 | pub fn fps(mut self, num: u32, den: u32) -> Self { 48 | self.raw.i_fps_num = num; 49 | self.raw.i_fps_den = den; 50 | self 51 | } 52 | 53 | /// The encoder's timebase, used in rate control with timestamps. 54 | /// 55 | /// The value is in seconds per tick. 56 | pub fn timebase(mut self, num: u32, den: u32) -> Self { 57 | self.raw.i_timebase_num = num; 58 | self.raw.i_timebase_den = den; 59 | self 60 | } 61 | 62 | /// Please file an issue if you know what this does, because I have no idea. 63 | pub fn annexb(mut self, annexb: bool) -> Self { 64 | self.raw.b_annexb = if annexb { 1 } else { 0 }; 65 | self 66 | } 67 | 68 | /// Approximately restricts the bitrate. 69 | /// 70 | /// The value is in metric kilobits per second. 71 | pub fn bitrate(mut self, bitrate: i32) -> Self { 72 | self.raw.rc.i_bitrate = bitrate; 73 | self 74 | } 75 | 76 | /// The lowest profile, with guaranteed compatibility with all decoders. 77 | pub fn baseline(mut self) -> Self { 78 | unsafe { 79 | x264_param_apply_profile( 80 | &mut self.raw, 81 | b"baseline\0" as *const u8 as *const i8 82 | ); 83 | } 84 | self 85 | } 86 | 87 | /// A useless middleground between the baseline and high profiles. 88 | pub fn main(mut self) -> Self { 89 | unsafe { 90 | x264_param_apply_profile( 91 | &mut self.raw, 92 | b"main\0" as *const u8 as *const i8 93 | ); 94 | } 95 | self 96 | } 97 | 98 | /// The highest profile, which almost all encoders support. 99 | pub fn high(mut self) -> Self { 100 | unsafe { 101 | x264_param_apply_profile( 102 | &mut self.raw, 103 | b"high\0" as *const u8 as *const i8 104 | ); 105 | } 106 | self 107 | } 108 | 109 | /// Build the encoder. 110 | pub fn build( 111 | mut self, 112 | csp: C, 113 | width: i32, 114 | height: i32, 115 | ) -> Result 116 | where 117 | C: Into, 118 | { 119 | self.raw.i_csp = csp.into().into_raw(); 120 | self.raw.i_width = width; 121 | self.raw.i_height = height; 122 | 123 | let raw = unsafe { x264_encoder_open(&mut self.raw) }; 124 | 125 | if raw.is_null() { 126 | Err(Error) 127 | } else { 128 | Ok(unsafe { Encoder::from_raw(raw) }) 129 | } 130 | } 131 | } 132 | 133 | impl Default for Setup { 134 | fn default() -> Self { 135 | let raw = unsafe { 136 | let mut raw = mem::uninitialized(); 137 | x264_param_default(&mut raw); 138 | raw 139 | }; 140 | 141 | Self { raw } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/setup/preset.rs: -------------------------------------------------------------------------------- 1 | #[allow(missing_docs)] 2 | #[derive(Clone, Copy, Hash, Debug, Eq, PartialEq)] 3 | /// An encoder preset, which should handle most of the configuration. 4 | pub enum Preset { 5 | Ultrafast, 6 | Superfast, 7 | Veryfast, 8 | Faster, 9 | Fast, 10 | Medium, 11 | Slow, 12 | Slower, 13 | Veryslow, 14 | Placebo, 15 | } 16 | 17 | impl Preset { 18 | #[doc(hidden)] 19 | pub fn to_cstr(self) -> *const i8 { 20 | use self::Preset::*; 21 | 22 | (match self { 23 | Ultrafast => b"ultrafast\0" as *const u8, 24 | Superfast => b"superfast\0" as *const u8, 25 | Veryfast => b"veryfast\0" as *const u8, 26 | Faster => b"faster\0" as *const u8, 27 | Fast => b"fast\0" as *const u8, 28 | Medium => b"medium\0" as *const u8, 29 | Slow => b"slow\0" as *const u8, 30 | Slower => b"slower\0" as *const u8, 31 | Veryslow => b"veryslow\0" as *const u8, 32 | Placebo => b"placebo\0" as *const u8, 33 | }) as *const i8 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/setup/tune.rs: -------------------------------------------------------------------------------- 1 | #[allow(missing_docs)] 2 | #[derive(Clone, Copy, Hash, Debug, Eq, PartialEq)] 3 | /// Tunes the encoder for a certain kind of video. 4 | pub enum Tune { 5 | None, 6 | Film, 7 | Animation, 8 | Grain, 9 | StillImage, 10 | Psnr, 11 | Ssim, 12 | } 13 | 14 | impl Tune { 15 | #[doc(hidden)] 16 | pub fn to_cstr( 17 | self, 18 | fast_decode: bool, 19 | zero_latency: bool, 20 | ) -> *const i8 { 21 | (if !fast_decode && !zero_latency { 22 | match self { 23 | Tune::None => b"\0" as *const u8, 24 | Tune::Film => b"film\0" as *const u8, 25 | Tune::Animation => b"animation\0" as *const u8, 26 | Tune::Grain => b"grain\0" as *const u8, 27 | Tune::StillImage => b"stillimage\0" as *const u8, 28 | Tune::Psnr => b"psnr\0" as *const u8, 29 | Tune::Ssim => b"ssim\0" as *const u8, 30 | } 31 | } else if fast_decode && !zero_latency { 32 | match self { 33 | Tune::None => b"fastdecode\0" as *const u8, 34 | Tune::Film => b"fastdecode,film\0" as *const u8, 35 | Tune::Animation => b"fastdecode,animation\0" as *const u8, 36 | Tune::Grain => b"fastdecode,grain\0" as *const u8, 37 | Tune::StillImage => b"fastdecode,stillimage\0" as *const u8, 38 | Tune::Psnr => b"fastdecode,psnr\0" as *const u8, 39 | Tune::Ssim => b"fastdecode,ssim\0" as *const u8, 40 | } 41 | } else if !fast_decode && zero_latency { 42 | match self { 43 | Tune::None => b"zerolatency\0" as *const u8, 44 | Tune::Film => b"zerolatency,film\0" as *const u8, 45 | Tune::Animation => b"zerolatency,animation\0" as *const u8, 46 | Tune::Grain => b"zerolatency,grain\0" as *const u8, 47 | Tune::StillImage => b"zerolatency,stillimage\0" as *const u8, 48 | Tune::Psnr => b"zerolatency,psnr\0" as *const u8, 49 | Tune::Ssim => b"zerolatency,ssim\0" as *const u8, 50 | } 51 | } else { 52 | match self { 53 | Tune::None => 54 | b"fastdecode,zerolatency\0" as *const u8, 55 | Tune::Film => 56 | b"fastdecode,zerolatency,film\0" as *const u8, 57 | Tune::Animation => 58 | b"fastdecode,zerolatency,animation\0" as *const u8, 59 | Tune::Grain => 60 | b"fastdecode,zerolatency,grain\0" as *const u8, 61 | Tune::StillImage => 62 | b"fastdecode,zerolatency,stillimage\0" as *const u8, 63 | Tune::Psnr => 64 | b"fastdecode,zerolatency,psnr\0" as *const u8, 65 | Tune::Ssim => 66 | b"fastdecode,zerolatency,ssim\0" as *const u8, 67 | } 68 | }) as *const i8 69 | } 70 | } 71 | --------------------------------------------------------------------------------