Merge branch 'rust-for-vlc.fourcc' into 'master'

rust: add abstraction for fourcc_t

See merge request videolan/vlc!4164
This commit is contained in:
Loïc 2024-04-28 07:10:44 +00:00
commit 0db6c57aa0
6 changed files with 299 additions and 1 deletions

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"vlcrs-fourcc",
"vlcrs-messages",
"vlcrs-sys-generator"
]

View File

@ -0,0 +1,7 @@
[package]
name = "vlcrs-fourcc"
version = "0.0.0"
edition = "2021"
license = "LGPL-2.1-or-later"
[dependencies]

View File

@ -0,0 +1,55 @@
use std::env;
use std::fs::File;
use std::io::{BufWriter, Result, Write};
use std::path::{Path, PathBuf};
fn main() {
let vlc_fourcc_h_path = Path::new("../../../include/vlc_fourcc.h");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
println!("cargo:rerun-if-changed={}", vlc_fourcc_h_path.display());
generate_fourcc(&vlc_fourcc_h_path, out_path.join("fourcc.rs"))
.expect("coundn't generate the fourcc bindings!");
}
fn generate_fourcc(from: &Path, to: PathBuf) -> Result<()> {
let mut output_file = BufWriter::new(File::create(to).expect("cannot open vlc_fourcc.h"));
let fourcc_include_contents = std::fs::read_to_string(from).unwrap();
writeln!(
output_file,
"// These constants have been automaticly generated by vlcrs-fourcc"
)?;
writeln!(
output_file,
"// in it's build.rs, do not modify them manually!\n"
)?;
writeln!(output_file, "{}", "impl FourCC {")?;
for line in fourcc_include_contents.lines() {
if line.starts_with("#define VLC_CODEC") {
// VLC_CODEC_....
let name = line
.strip_prefix("#define ")
.unwrap()
.split_whitespace()
.next()
.unwrap();
// ('u','n','d','f')
let fourcc = line.rsplit("VLC_FOURCC").next().unwrap();
writeln!(
output_file,
" pub const {}: FourCC = fourcc!{};",
name, fourcc
)
.unwrap();
}
}
writeln!(output_file, "{}", "}")?;
Ok(())
}

View File

@ -0,0 +1,164 @@
//! FourCC.
use core::ffi::c_char;
use core::slice;
use std::num::NonZeroU32;
#[allow(dead_code)]
mod sys;
use sys::*;
/// FourCC ("four-character code") is a sequence of four bytes (typically ASCII)
/// used to uniquely identify data formats.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[doc(alias = "vlc_fourcc_t")]
#[repr(transparent)]
pub struct FourCC(vlc_fourcc_t);
/// FourCC macro-creation
///
/// ```
/// # use vlcrs_fourcc::fourcc;
/// let mp4 = fourcc!('m', 'p', '4', 'v');
/// assert_eq!(mp4.as_u32().get(), 0x7634706d);
/// ```
#[macro_export]
macro_rules! fourcc {
($a:expr, $b:expr, $c:expr, $d:expr) => {{
$crate::FourCC::from_u32(u32::from_ne_bytes([$a as u8, $b as u8, $c as u8, $d as u8]))
}};
}
/// Internal function to convert a raw [u32] to a [Option<FourCC>]
fn crf(fourcc: NonZeroU32) -> Option<FourCC> {
if fourcc == FourCC::VLC_CODEC_UNKNOWN.0 {
None
} else {
Some(FourCC(fourcc))
}
}
impl FourCC {
// Only exist because we cannot yet define `From::from` as const.
#[doc(hidden)]
pub const fn from_u32(value: u32) -> FourCC {
FourCC(if let Some(nz) = NonZeroU32::new(value) {
nz
} else {
panic!("fourcc: non-zero value passed")
})
}
/// Return the inner representation of a fourcc
#[inline]
pub fn as_u32(&self) -> NonZeroU32 {
self.0
}
/// It returns the codec associated to a fourcc within an ES category.
///
/// Returns [Option::None] if unknown.
#[inline]
#[doc(alias = "vlc_fourcc_GetCodec")]
pub fn get_codec(cat: i32, fourcc: FourCC) -> Option<FourCC> {
// SAFETY: Allways valid to call and always return a fourcc
crf(unsafe { vlc_fourcc_GetCodec(cat, fourcc.0) })
}
/// It converts the given fourcc to an audio codec when possible.
///
/// Returns [Option::None] if unknown.
#[inline]
#[doc(alias = "vlc_fourcc_GetCodecAudio")]
pub fn get_codec_audio(fourcc: FourCC, bits: i32) -> Option<FourCC> {
// SAFETY: Allways valid to call and always return a fourcc
crf(unsafe { vlc_fourcc_GetCodecAudio(fourcc.0, bits) })
}
/// It converts the given fourcc to an audio codec when possible.
///
/// Returns [Option::None] if unknown.
#[doc(alias = "vlc_fourcc_GetCodecAudio")]
pub fn get_codec_from_string(cat: i32, s: &str) -> Option<FourCC> {
if s.len() != 4 {
return None;
}
let mut bytes = s.bytes();
// SAFETY: We just verified that the length is indeed 4
let buf: [c_char; 4] = unsafe {
[
bytes.next().unwrap_unchecked() as c_char,
bytes.next().unwrap_unchecked() as c_char,
bytes.next().unwrap_unchecked() as c_char,
bytes.next().unwrap_unchecked() as c_char,
]
};
// SAFETY: Always valid to call and always return a fourcc
crf(unsafe { vlc_fourcc_GetCodecFromString(cat, buf.as_ptr()) })
}
/// Returns a slice of YUV fourccs in decreasing priority order for the given chroma
pub fn get_yuv_fallback(&self) -> Option<&[FourCC]> {
// SAFETY: Always valid to call and always return a list fourcc
let fallback = unsafe { vlc_fourcc_GetYUVFallback(self.0) };
// SAFETY: The pointer points to a valid fourcc list
unsafe { fourcc_list(fallback as *const u32) }
}
/// Returns a slice of RGB fourccs in decreasing priority order for the given chroma
pub fn get_rgb_fallback(&self) -> Option<&[FourCC]> {
// SAFETY: Always valid to call and always return a list fourcc
let fallback = unsafe { vlc_fourcc_GetRGBFallback(self.0) };
// SAFETY: The pointer points to a valid fourcc list
unsafe { fourcc_list(fallback as *const u32) }
}
/// Returns a slice of YUV fourccs in decreasing priority order for the given chroma
pub fn get_fallback(&self) -> Option<&[FourCC]> {
// SAFETY: Always valid to call and always return a list fourcc
let fallback = unsafe { vlc_fourcc_GetFallback(self.0) };
// SAFETY: The pointer points to a valid fourcc list
unsafe { fourcc_list(fallback as *const u32) }
}
/// Returns true if the given fourcc is YUV and false otherwise.
#[inline]
pub fn is_yuv(&self) -> bool {
// SAFETY: No specific requirements, other than passing a valid FourCC.
unsafe { vlc_fourcc_IsYUV(self.0) }
}
/// Returns true if the two fourccs are equivalent if their U&V planes are swapped
#[inline]
pub fn are_uvplanes_swapped(&self, rhs: FourCC) -> bool {
// SAFETY: No specific requirements, other than passing a valid FourCC.
unsafe { vlc_fourcc_AreUVPlanesSwapped(self.0, rhs.0) }
}
}
/// Convert a fourcc C "vector" to a optional Rust slice.
unsafe fn fourcc_list(fallback: *const u32) -> Option<&'static [FourCC]> {
if fallback.is_null() {
None
} else {
let mut i = 0;
let mut ptr = fallback;
// SAFETY: The pointer is valid and not out-of-bounds
while unsafe { *ptr != 0 } {
// SAFETY: The pointer is valid and not out-of-bounds
ptr = unsafe { ptr.add(1) };
i += 1;
}
// SAFETY: The pointer is valid, not-null and the length as just been calculated
Some(unsafe { slice::from_raw_parts(fallback as *const FourCC, i) })
}
}
include!(concat!(env!("OUT_DIR"), "/fourcc.rs"));

View File

@ -0,0 +1,60 @@
/* automatically generated by rust-bindgen 0.66.1 */
#![allow(rustdoc::bare_urls)]
#![allow(rustdoc::broken_intra_doc_links)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::num::NonZeroU32;
#[doc = " Basic types definitions\n/\n/**\n The vlc_fourcc_t type.\n\n See http://www.webartz.com/fourcc/ for a very detailed list."]
pub type vlc_fourcc_t = NonZeroU32;
extern "C" {
#[doc = " It returns the codec associated to a fourcc within an ES category.\n\n If not found, it will return the given fourcc.\n If found, it will always be one of the VLC_CODEC_ defined above.\n\n You may use UNKNOWN_ES for the ES category if you don't have the information."]
pub fn vlc_fourcc_GetCodec(
i_cat: ::std::os::raw::c_int,
i_fourcc: vlc_fourcc_t,
) -> vlc_fourcc_t;
}
extern "C" {
#[doc = " It returns the codec associated to a fourcc stored in a zero terminated\n string.\n\n If the string is NULL or does not have exactly 4 characters, it will\n return 0, otherwise it behaves like vlc_fourcc_GetCodec.\n\n Provided for convenience."]
pub fn vlc_fourcc_GetCodecFromString(
i_cat: ::std::os::raw::c_int,
arg1: *const ::std::os::raw::c_char,
) -> vlc_fourcc_t;
}
extern "C" {
#[doc = " It converts the given fourcc to an audio codec when possible.\n\n The fourccs converted are aflt, araw/pcm , twos, sowt. When an incompatible i_bits\n is detected, 0 is returned.\n The other fourccs go through vlc_fourcc_GetCodec and i_bits is not checked."]
pub fn vlc_fourcc_GetCodecAudio(
i_fourcc: vlc_fourcc_t,
i_bits: ::std::os::raw::c_int,
) -> vlc_fourcc_t;
}
extern "C" {
#[doc = " It returns the description of the given fourcc or NULL if not found.\n\n You may use UNKNOWN_ES for the ES category if you don't have the information."]
pub fn vlc_fourcc_GetDescription(
i_cat: ::std::os::raw::c_int,
i_fourcc: vlc_fourcc_t,
) -> *const ::std::os::raw::c_char;
}
extern "C" {
#[doc = " It returns a list (terminated with the value 0) of YUV fourccs in\n decreasing priority order for the given chroma.\n\n It will always return a non NULL pointer that must not be freed."]
pub fn vlc_fourcc_GetYUVFallback(arg1: vlc_fourcc_t) -> *const vlc_fourcc_t;
}
extern "C" {
#[doc = " It returns a list (terminated with the value 0) of RGB fourccs in\n decreasing priority order for the given chroma.\n\n It will always return a non NULL pointer that must not be freed."]
pub fn vlc_fourcc_GetRGBFallback(arg1: vlc_fourcc_t) -> *const vlc_fourcc_t;
}
extern "C" {
#[doc = " It returns a list (terminated with the value 0) of fourccs in decreasing\n priority order for the given chroma. It will return either YUV or RGB\n fallbacks depending on whether or not the fourcc given is YUV.\n\n It will always return a non NULL pointer that must not be freed."]
pub fn vlc_fourcc_GetFallback(arg1: vlc_fourcc_t) -> *const vlc_fourcc_t;
}
extern "C" {
#[doc = " It returns true if the given fourcc is YUV and false otherwise."]
pub fn vlc_fourcc_IsYUV(arg1: vlc_fourcc_t) -> bool;
}
extern "C" {
#[doc = " It returns true if the two fourccs are equivalent if their U&V planes are\n swapped."]
pub fn vlc_fourcc_AreUVPlanesSwapped(arg1: vlc_fourcc_t, arg2: vlc_fourcc_t) -> bool;
}

View File

@ -62,7 +62,11 @@ impl BindingsGenerator {
.raw_line("#![allow(non_snake_case)]")
// Since we are generating the input header content at runtime,
// specify wrapper.h as a fake name.
.header_contents("wrapper.h", &header_contents);
.header_contents("wrapper.h", &header_contents)
// And since the generated wrappers should be target-independent,
// we cannot have layout tests, since they are by definition
// target-dependent.
.layout_tests(false);
// Apply "user" configurations, ie allowlist_function, allowlist_type, ...
// So that they can pit-point what they want.
@ -85,6 +89,13 @@ fn main() {
include_path: env::var("INCLUDE_PATH").unwrap_or_else(|_| "../../include".to_string()),
};
bindings_gen.generate_bindings_for("vlcrs-fourcc", &["vlc_fourcc.h"], |builder| {
builder
.allowlist_function("vlc_fourcc_.*")
.blocklist_function("vlc_fourcc_GetChromaDescription")
.blocklist_type("vlc_chroma_.*")
.blocklist_type("vlc_rational_.*")
});
bindings_gen.generate_bindings_for("vlcrs-messages", &["vlc_messages.h"], |builder| {
builder
.allowlist_function("vlc_Log")