[vlc-devel] [RFC: PATCH 1/2] libvlc: Add Rust API for writing modules in rust
Kartik Ohri
kartikohri13 at gmail.com
Thu Sep 10 18:47:29 CEST 2020
The API is written in two layers. vlccore-sys
crate contains one to one bindings of libvlc
functions. vlccore-rs crate wraps vlccore-sys and
exposes a Rust idiomatic API for writing modules
in rust. As of now, the API exposes only a subset
of the vlc_stream API. More functionality can be
added to Rust API as required in the future.
---
.gitignore | 2 +-
configure.ac | 13 ++++
src/vlccore-rs/Cargo.toml | 10 +++
src/vlccore-rs/src/lib.rs | 1 +
src/vlccore-rs/src/stream.rs | 119 ++++++++++++++++++++++++++++++++++
src/vlccore-sys/Cargo.toml | 10 +++
src/vlccore-sys/src/lib.rs | 2 +
src/vlccore-sys/src/stream.rs | 24 +++++++
8 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 src/vlccore-rs/Cargo.toml
create mode 100644 src/vlccore-rs/src/lib.rs
create mode 100644 src/vlccore-rs/src/stream.rs
create mode 100644 src/vlccore-sys/Cargo.toml
create mode 100644 src/vlccore-sys/src/lib.rs
create mode 100644 src/vlccore-sys/src/stream.rs
diff --git a/.gitignore b/.gitignore
index fc368212c8..6c049a80b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,7 +46,7 @@ wxvlc
vlc_install_dir/*
plugins.dat
patches/*
-
+**/Cargo.lock
include/vlc/libvlc_version.h
# Ignore build dirs
diff --git a/configure.ac b/configure.ac
index 99e4669942..53d56a725c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1896,6 +1896,19 @@ AS_IF([test "${enable_sout}" != "no"], [
])
AM_CONDITIONAL([ENABLE_SOUT], [test "${enable_sout}" != "no"])
+dnl Rust Modules
+AC_ARG_ENABLE([rust],
+ AS_HELP_STRING([--enable-rust], [enable building Rust modules (default disabled)]))
+AS_IF([test "${enable_rust}" = "yes"],
+ [AC_DEFINE(ENABLE_RUST, 1, [Define to 1 for building rust modules.])])
+AM_CONDITIONAL([BUILD_RUST], [test "${enable_rust}" = "yes"])
+if test "${enable_rust}" = "yes"
+then
+ AC_CHECK_PROG(CARGO, [cargo], [yes], [no])
+ AS_IF([test "x$CARGO" = "xno"],
+ AC_MSG_ERROR([cargo not found. cargo is required to build rust modules]))
+fi
+
dnl Lua modules
AC_ARG_ENABLE([lua],
AS_HELP_STRING([--disable-lua],
diff --git a/src/vlccore-rs/Cargo.toml b/src/vlccore-rs/Cargo.toml
new file mode 100644
index 0000000000..108c849f80
--- /dev/null
+++ b/src/vlccore-rs/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "vlccore-rs"
+version = "0.1.0"
+authors = ["Kartik Ohri <kartikohri13 at gmail.com>"]
+edition = "2018"
+license = "LGPL-2.1-or-later"
+
+[dependencies]
+libc = "0.2"
+vlccore-sys = {path="../vlccore-sys"}
\ No newline at end of file
diff --git a/src/vlccore-rs/src/lib.rs b/src/vlccore-rs/src/lib.rs
new file mode 100644
index 0000000000..bb01fe9161
--- /dev/null
+++ b/src/vlccore-rs/src/lib.rs
@@ -0,0 +1 @@
+pub mod stream;
\ No newline at end of file
diff --git a/src/vlccore-rs/src/stream.rs b/src/vlccore-rs/src/stream.rs
new file mode 100644
index 0000000000..ea2ab4fc52
--- /dev/null
+++ b/src/vlccore-rs/src/stream.rs
@@ -0,0 +1,119 @@
+use vlccore_sys::stream as ffi;
+use libc::{c_char, c_void};
+use std::ffi::{CStr, CString};
+use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};
+use std::ptr::null_mut;
+
+pub struct Stream {
+ stream: *mut ffi::stream_t,
+ is_owned: bool
+}
+
+impl Read for Stream {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let bytes_read: isize = unsafe {
+ ffi::vlc_stream_Read(self.stream, buf.as_mut_ptr() as *mut c_void, buf.len())
+ };
+ if bytes_read < 0 {
+ Err(Error::new(ErrorKind::Other, "Error while reading from stream"))
+ } else {
+ Ok(bytes_read as usize)
+ }
+ }
+}
+
+impl Seek for Stream {
+ fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
+ let offset = match pos {
+ SeekFrom::Start(n) => n,
+ SeekFrom::Current(n) => {
+ let len = self.position();
+ if n >= 0 {
+ len.checked_add(n as u64)
+ } else {
+ len.checked_sub(n.wrapping_neg() as u64)
+ }.ok_or(Error::new(ErrorKind::Other, "Position Overflow"))?
+ }
+ SeekFrom::End(n) => {
+ self.len().and_then(|position| {
+ if n >= 0 {
+ position.checked_add(n as u64)
+ } else {
+ position.checked_sub(n.wrapping_neg() as u64)
+ }.ok_or(Error::new(ErrorKind::Other, "Position Overflow"))
+ })?
+ }
+ };
+
+ let result = unsafe { ffi::vlc_stream_Seek(self.stream, offset) };
+ if result != 0 {
+ return Err(Error::new(ErrorKind::Other, "Could not seek stream"))
+ }
+ Ok(offset)
+ }
+}
+
+impl Drop for Stream {
+ fn drop(&mut self) {
+ if self.is_owned {
+ unsafe {ffi::vlc_stream_Delete(self.stream)}
+ }
+ }
+}
+
+impl From<*mut ffi::stream_t> for Stream {
+ fn from(stream: *mut ffi::stream_t) -> Self {
+ Stream {stream : stream, is_owned: false}
+ }
+}
+
+impl Stream {
+
+ pub fn len(&self) -> std::result::Result<u64, Error> {
+ let size : u64 = 0;
+ const STREAM_GET_SIZE: i32 = 6;
+ let result = unsafe { ffi::vlc_stream_vaControl(self.stream, STREAM_GET_SIZE, &size) };
+ if result != 0 {
+ return Err(Error::new(ErrorKind::Other, "Could not determine stream size"))
+ }
+ Ok(size)
+ }
+
+ pub fn position(&self) -> u64 {
+ unsafe { ffi::vlc_stream_Tell(self.stream) }
+ }
+
+ pub fn eof(&self) -> Result<bool> {
+ Ok(unsafe { ffi::vlc_stream_Eof(self.stream) })
+ }
+
+ pub fn mimetype(&self) -> Option<String> {
+ self.contenttype().map(|content_type| {
+ let index = content_type.chars().position(|c| c == ';').unwrap();
+ let mime_type: String = content_type[..index].to_owned();
+ mime_type
+ })
+ }
+
+ pub fn contenttype(&self) -> Option<String> {
+ // FIXME: STREAM_GET_CONTENT_TYPE does not have a fixed value
+ const STREAM_GET_CONTENT_TYPE: i32 = 0;
+ let result: *mut c_char = null_mut();
+ unsafe {
+ if ffi::vlc_stream_vaControl(self.stream, STREAM_GET_CONTENT_TYPE, result) != 0 {
+ return None
+ } else if result.is_null() {
+ return None
+ }
+ // FIXME: Free the C pointer received but retain copy of data in Rust
+ CStr::from_ptr(result).to_str().map(|s| s.to_string()).ok()
+ }
+ }
+
+ pub fn new_url(&mut self, url: &str) -> Self {
+ let c_url = CString::new(url).unwrap().as_ptr();
+ let stream = unsafe {ffi::vlc_stream_NewURL(self.stream, c_url)};
+ Stream {stream: stream, is_owned: true}
+ }
+
+}
diff --git a/src/vlccore-sys/Cargo.toml b/src/vlccore-sys/Cargo.toml
new file mode 100644
index 0000000000..0dde7b2e7d
--- /dev/null
+++ b/src/vlccore-sys/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "vlccore-sys"
+version = "0.1.0"
+authors = ["Kartik Ohri <kartikohri13 at gmail.com>"]
+edition = "2018"
+license = "LGPL-2.1-or-later"
+
+[dependencies]
+libc = "0.2"
+
diff --git a/src/vlccore-sys/src/lib.rs b/src/vlccore-sys/src/lib.rs
new file mode 100644
index 0000000000..42a75452cd
--- /dev/null
+++ b/src/vlccore-sys/src/lib.rs
@@ -0,0 +1,2 @@
+#![allow(non_camel_case_types)]
+pub mod stream;
diff --git a/src/vlccore-sys/src/stream.rs b/src/vlccore-sys/src/stream.rs
new file mode 100644
index 0000000000..cad6bba655
--- /dev/null
+++ b/src/vlccore-sys/src/stream.rs
@@ -0,0 +1,24 @@
+use libc::{c_char, c_int, c_void, size_t, ssize_t};
+
+#[repr(C)]
+pub struct stream_t {
+ _private : [u8; 0]
+}
+
+pub type vlc_object_t = stream_t;
+
+extern "C" {
+ pub fn vlc_stream_Read(s : *mut stream_t, buf: *mut c_void, len: size_t) -> ssize_t;
+
+ pub fn vlc_stream_Tell(s: *const stream_t) -> u64;
+
+ pub fn vlc_stream_Eof(s: *const stream_t) -> bool;
+
+ pub fn vlc_stream_Seek(s: *mut stream_t, offset: u64) -> c_int;
+
+ pub fn vlc_stream_vaControl(s: *mut stream_t, query: c_int, ...) -> c_int;
+
+ pub fn vlc_stream_Delete(s: *mut stream_t);
+
+ pub fn vlc_stream_NewURL(obj: *mut vlc_object_t, url: *const c_char) -> *mut stream_t;
+}
--
2.25.1
More information about the vlc-devel
mailing list