Adjust structure for libdvdcss bindings.
- Add libdvdcss as a submodule. - Add libdvdcss.h for structure definitions. - Path to headers provided by libdvdcss. - Add meson to build config.h for libdvdcss.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
[submodule "external/libdvdcss"]
|
||||
path = external/libdvdcss
|
||||
url = https://code.videolan.org/videolan/libdvdcss.git
|
||||
+2
-1
@@ -16,7 +16,8 @@ publish = false
|
||||
# Workspace member crates
|
||||
#
|
||||
dvd = { path = "crates/dvd" }
|
||||
libdvdcss = { path = "crates/libdvdcss" }
|
||||
dvdcss-sys = { path = "crates/dvdcss-sys" }
|
||||
dvdcss = { path = "crates/dvdcss" }
|
||||
mapping = { path = "crates/mapping" }
|
||||
media = { path = "crates/media" }
|
||||
plugins = { path = "crates/plugins" }
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use media::Media;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub type DVD = libdvdcss::DVD;
|
||||
|
||||
// Probably wipe and restart this.
|
||||
// Not thought out yet.
|
||||
|
||||
impl TryFrom<PathBuf> for DVD {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
|
||||
libdvdcss::DVD::new(value).ok_or(Error::LibDvdCssOpenError)
|
||||
}
|
||||
}
|
||||
|
||||
impl Media for DVD {}
|
||||
|
||||
mod error {
|
||||
use derive_more::{Display, From};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
LibDvdCssOpenError,
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "dvd"
|
||||
version.workspace = true
|
||||
name = "dvdcss-sys"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
libdvdcss.workspace = true
|
||||
media.workspace = true
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
meson = "1.0"
|
||||
@@ -0,0 +1,3 @@
|
||||
# dvdcss-sys
|
||||
|
||||
A crate for pure bindings against the system libdvdcss.
|
||||
@@ -1,16 +1,25 @@
|
||||
use std::env;
|
||||
use std::env::{self, current_dir};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Path from which to search for shared libraries.
|
||||
// Is there no better (automated), platform-specific way?
|
||||
println!("cargo:rustc-link-search=/usr/lib");
|
||||
|
||||
// Locate and link the shared library.
|
||||
println!("cargo:rustc-link-lib=libdvdcss");
|
||||
println!("cargo:rustc-link-lib=dvdcss");
|
||||
|
||||
// Because clang wants an absolute path and using canonicalize() fails.
|
||||
let libdir_path = current_dir()
|
||||
.expect("Cannot get CWD.")
|
||||
.join("../../external/libdvdcss");
|
||||
|
||||
// Run the meson build to produce build/config.h
|
||||
meson::build(
|
||||
libdir_path.to_str().unwrap(),
|
||||
libdir_path.join("build").to_str().unwrap(),
|
||||
);
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/wrapper.h")
|
||||
.clang_arg(format!("-I{}/src", libdir_path.to_str().unwrap()))
|
||||
.clang_arg(format!("-I{}/build", libdir_path.to_str().unwrap()))
|
||||
// Invalidate the build if a header has changed.
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
||||
.generate()
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "dvdcss/dvdcss.h"
|
||||
#include "libdvdcss.h"
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "libdvdcss"
|
||||
name = "dvdcss"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
@@ -10,6 +10,8 @@ publish.workspace = true
|
||||
[dependencies]
|
||||
derive_more.workspace = true
|
||||
semver.workspace = true
|
||||
dvdcss-sys.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
[features]
|
||||
semver = []
|
||||
encryption-type = []
|
||||
@@ -0,0 +1,11 @@
|
||||
# libdvdcss Rust Wrapper
|
||||
|
||||
This crate assumes a baseline version of `libdvdcss` 1.5.0.
|
||||
|
||||
If you wish to use the `dvdcss_get_encryption_type()` function introduced in
|
||||
`libdvdcss` version 1.6.0, add the `encryption-type` feature.
|
||||
|
||||
For convenience, the `semver` feature can be added for a `semver::Version`
|
||||
representation of `DVDCSS_VERSION` instead of a tuple.
|
||||
|
||||
`dvdcss_open_stream()` is not implemented. At least not yet.
|
||||
@@ -0,0 +1,180 @@
|
||||
use std::ffi::{CStr, c_int, c_void};
|
||||
use std::io::{self, Error, Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
use derive_more::From;
|
||||
use dvdcss_sys::*;
|
||||
|
||||
pub const BLOCK_SIZE: u32 = DVDCSS_BLOCK_SIZE;
|
||||
|
||||
#[cfg(feature = "semver")]
|
||||
pub const DVDCSS_VERSION: semver::Version = semver::Version::new(
|
||||
DVDCSS_VERSION_MAJOR as u64,
|
||||
DVDCSS_VERSION_MINOR as u64,
|
||||
DVDCSS_VERSION_MICRO as u64,
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "semver"))]
|
||||
pub const DVDCSS_VERSION: (u32, u32, u32) = (
|
||||
DVDCSS_VERSION_MAJOR,
|
||||
DVDCSS_VERSION_MINOR,
|
||||
DVDCSS_VERSION_MICRO,
|
||||
);
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum SeekFlag {
|
||||
#[default]
|
||||
None = DVDCSS_NOFLAGS,
|
||||
Key = DVDCSS_SEEK_KEY,
|
||||
Mpeg = DVDCSS_SEEK_MPEG,
|
||||
}
|
||||
|
||||
// I dislike this as technically, 2 should be Unknown/Reserved and 3 as CPRM.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Default, From)]
|
||||
pub enum EncryptionScheme {
|
||||
#[default]
|
||||
None = 0,
|
||||
CssCppm = 1,
|
||||
//Reserved,
|
||||
Cprm = 2,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub struct Dvd {
|
||||
ptr: dvdcss_t,
|
||||
decrypt: bool,
|
||||
}
|
||||
|
||||
impl Dvd {
|
||||
/// Open a DVD.
|
||||
/// If libdvdcss returns a null pointer, this returns None.
|
||||
/// No more about the error can be discerned than this.
|
||||
pub fn open<P: AsRef<Path>>(src: P) -> Option<Self> {
|
||||
// Cast the path to a CStr (*const char),
|
||||
// returning early if there's any issue.
|
||||
let c_path = CStr::from_bytes_with_nul(src.as_ref().as_os_str().as_encoded_bytes())
|
||||
.ok()?
|
||||
.as_ptr();
|
||||
|
||||
let ptr = unsafe { dvdcss_open(c_path) };
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Self {
|
||||
ptr,
|
||||
decrypt: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Dvd` from a raw pointer to `dvdcss_t`.
|
||||
pub unsafe fn from_raw_pointer(ptr: dvdcss_t) -> Self {
|
||||
Self {
|
||||
ptr,
|
||||
decrypt: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt(&mut self, decrypt: bool) {
|
||||
self.decrypt = decrypt;
|
||||
}
|
||||
|
||||
/// Return the error message string from dvdcss_error
|
||||
pub fn error(&self) -> &CStr {
|
||||
unsafe { CStr::from_ptr(dvdcss_error(self.ptr)) }
|
||||
}
|
||||
|
||||
/// Return the error message string from dvdcss_error with lossy conversion.
|
||||
pub fn error_lossy(&self) -> String {
|
||||
self.error().to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
/// Detect whether or not the content is scrambled.
|
||||
pub fn is_scrambled(&self) -> bool {
|
||||
match unsafe { dvdcss_is_scrambled(self.ptr) } {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The length in bytes of the Dvd.
|
||||
pub fn len(&self) {
|
||||
self.ptr.i_fd
|
||||
}
|
||||
|
||||
/// Return the encryption scheme in use.
|
||||
#[cfg(feature = "encryption-type")]
|
||||
pub fn encryption_type(&self) -> EncryptionScheme {
|
||||
unsafe { dvdcss_get_encryption_type(self.ptr) }
|
||||
.try_into()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Dvd {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let i_flags = if self.decrypt {
|
||||
DVDCSS_READ_DECRYPT
|
||||
} else {
|
||||
DVDCSS_NOFLAGS
|
||||
} as i32;
|
||||
|
||||
let retval = unsafe {
|
||||
dvdcss_read(
|
||||
self.ptr,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
buf.len() as i32 / BLOCK_SIZE as c_int,
|
||||
i_flags,
|
||||
)
|
||||
};
|
||||
|
||||
if retval < 0 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
Ok(retval.unsigned_abs() as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Dvd {
|
||||
/// Seek to some posititon in the disc.
|
||||
///
|
||||
/// Contrary to Rust's standard for Seek implementations,
|
||||
/// this is limited to seeking in multiples of BLOCK_SIZE (2048),
|
||||
/// as defined by `dvdcss_seek()`.
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
// dvdcss_seek reports in blocks, rather than bytes as expected by Rust.
|
||||
//
|
||||
// As for what purpose the flags serve... I haven't a clue.
|
||||
|
||||
let bytes = match pos {
|
||||
SeekFrom::Start(p) => p as i64,
|
||||
SeekFrom::Current(p) => self.stream_position()? as i64 + p,
|
||||
SeekFrom::End(p) => self.stream_len()? as i64 - p,
|
||||
} * BLOCK_SIZE as i64;
|
||||
|
||||
// Although this call generally returns a negative value on fail, as of writing
|
||||
// it can only return -1, so there's no point in expanding this fail result into
|
||||
// a proper enum.
|
||||
let retval = unsafe { dvdcss_seek(self.ptr, bytes as c_int, SeekFlag::None as c_int) };
|
||||
|
||||
if retval < 0 {
|
||||
Err(Error::last_os_error())
|
||||
} else {
|
||||
Ok((retval.unsigned_abs() * BLOCK_SIZE) as u64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Dvd {
|
||||
fn drop(&mut self) {
|
||||
// Right... so drop can't handle return values.
|
||||
// So... best option is ...panic?
|
||||
let retval = unsafe { dvdcss_close(self.ptr) };
|
||||
assert!(retval == 0);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# libdvdcss Rust Wrapper
|
||||
|
||||
Minimum `libdvdcss` version: 1.6.0
|
||||
@@ -1,17 +0,0 @@
|
||||
use derive_more::{Display, From};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, From, Display)]
|
||||
pub enum Error {
|
||||
#[display("I32ToEnumCastError {{ {}, {} }}", value, enum_)]
|
||||
I32ToEnumCastError {
|
||||
value: i32,
|
||||
enum_: String,
|
||||
},
|
||||
InsufficientBufferLength,
|
||||
ReadError {
|
||||
value: i32,
|
||||
},
|
||||
SeekError,
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
use std::ffi::{CStr, c_int, c_void};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
mod dvdcss;
|
||||
mod error;
|
||||
|
||||
use dvdcss::*;
|
||||
use semver::Version;
|
||||
|
||||
pub const BLOCK_SIZE: u32 = DVDCSS_BLOCK_SIZE;
|
||||
pub const DVDCSS_VERSION: semver::Version = Version::new(
|
||||
DVDCSS_VERSION_MAJOR as u64,
|
||||
DVDCSS_VERSION_MINOR as u64,
|
||||
DVDCSS_VERSION_MICRO as u64,
|
||||
);
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum SeekFlag {
|
||||
#[default]
|
||||
None = DVDCSS_NOFLAGS,
|
||||
Key = DVDCSS_SEEK_KEY,
|
||||
Mpeg = DVDCSS_SEEK_MPEG,
|
||||
}
|
||||
|
||||
// I dislike this as technically, 2 should be Unknown/Reserved and 3 as CPRM.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum EncryptionScheme {
|
||||
#[default]
|
||||
None = 0,
|
||||
CssCppm = 1,
|
||||
//Reserved,
|
||||
Cprm = 2,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for EncryptionScheme {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: i32) -> std::result::Result<Self, Self::Error> {
|
||||
if !(0..=2).contains(&value) {
|
||||
return Err(Error::I32ToEnumCastError {
|
||||
value,
|
||||
enum_: "EncryptionScheme".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(match value {
|
||||
0 => Self::None,
|
||||
1 => Self::CssCppm,
|
||||
2 => Self::Cprm,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct DVD {
|
||||
ptr: dvdcss_t,
|
||||
}
|
||||
|
||||
impl DVD {
|
||||
pub fn new<P: AsRef<Path>>(src: P) -> Option<Self> {
|
||||
// Cast the path to a CStr (*const char), returning early if there's any issue.
|
||||
let c_path = CStr::from_bytes_with_nul(src.as_ref().as_os_str().as_encoded_bytes())
|
||||
.ok()?
|
||||
.as_ptr();
|
||||
|
||||
// Make the FFI call.
|
||||
let ptr = unsafe { dvdcss_open(c_path) };
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Self { ptr })
|
||||
}
|
||||
}
|
||||
|
||||
// Seek in blocks (See: `BLOCK_SIZE`).
|
||||
pub fn seek(&self, blocks: u32, flag: SeekFlag) -> Result<u32> {
|
||||
// Although this call generally returns a negative value on fail, as of writing
|
||||
// it can only return -1, so there's no point in expanding this fail result into
|
||||
// a proper enum.
|
||||
let retval = unsafe { dvdcss_seek(self.ptr, blocks as c_int, flag as c_int) };
|
||||
|
||||
if retval < 0 {
|
||||
Err(Error::SeekError)
|
||||
} else {
|
||||
Ok(retval as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8], blocks: u32, decrypt: bool) -> Result<u32> {
|
||||
// If the user has provided a wonk buffer size that's not a multiple of BLOCK_SIZE,
|
||||
// I'm not fixing it for them.
|
||||
// But ensure that buffer is big enough to safely read the data.
|
||||
if buf.len() < (blocks * BLOCK_SIZE) as usize {
|
||||
return Err(Error::InsufficientBufferLength);
|
||||
}
|
||||
|
||||
let i_flags = if decrypt {
|
||||
DVDCSS_READ_DECRYPT
|
||||
} else {
|
||||
DVDCSS_NOFLAGS
|
||||
} as i32;
|
||||
|
||||
let retval = unsafe {
|
||||
dvdcss_read(
|
||||
self.ptr,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
blocks as c_int,
|
||||
i_flags,
|
||||
)
|
||||
};
|
||||
|
||||
if retval < 0 {
|
||||
// I can't track back the error return values for pf_read,
|
||||
// so this is good enough for now.
|
||||
return Err(Error::ReadError { value: retval });
|
||||
}
|
||||
|
||||
Ok(retval as u32)
|
||||
}
|
||||
|
||||
// Return the error message string from dvdcss_error
|
||||
pub fn error(&self) -> &CStr {
|
||||
unsafe { CStr::from_ptr(dvdcss_error(self.ptr)) }
|
||||
}
|
||||
|
||||
// Return the error message string from dvdcss_error with lossy conversion.
|
||||
pub fn error_lossy(&self) -> String {
|
||||
self.error().to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
pub fn is_scrambled(&self) -> bool {
|
||||
match unsafe { dvdcss_is_scrambled(self.ptr) } {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn encryption_type(&self) -> EncryptionScheme {
|
||||
unsafe { dvdcss_get_encryption_type(self.ptr) }
|
||||
.try_into()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl Drop for DVD {
|
||||
fn drop(&mut self) {
|
||||
// Right... so drop can't handle return values.
|
||||
// So... best option is ...panic?
|
||||
let retval = unsafe { dvdcss_close(self.ptr) };
|
||||
assert!(retval == 0);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include <dvdcss/dvdcss.h>
|
||||
+1
Submodule external/libdvdcss added at 2682a4a7ed
Reference in New Issue
Block a user