Created
January 24, 2020 14:56
-
-
Save rust-play/d21f060eeff08023994fa177058e7b09 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::os::raw::c_int; | |
use std::sync::mpsc::sync_channel; | |
use std::{cmp, slice}; | |
use reqwest::{Client, Method, StatusCode}; | |
use tokio::runtime::Handle; | |
pub struct Context<'a> { | |
client: &'a Client, | |
endpoint: &'a str, | |
pos: i64, | |
end: Option<i64>, | |
_rope: (), | |
} | |
enum Whence { | |
Start, | |
Current, | |
End, | |
Invalid, | |
} | |
impl Whence { | |
fn from(w: isize) -> Whence { | |
match w { | |
0 => Whence::Start, | |
1 => Whence::Current, | |
2 => Whence::End, | |
_ => Whence::Invalid, | |
} | |
} | |
} | |
impl Context<'_> { | |
async fn read(&mut self, buf: &[u8]) -> Result<isize, &'static str> { | |
let lastbyte = self.offset(buf.len() as i64, Whence::Current) - 1; | |
let range = format!("bytes={}-{}", self.pos, lastbyte); | |
let res = match self | |
.client | |
.request(Method::GET, self.endpoint) | |
.header("Accept-Range", range) | |
.send() | |
.await | |
{ | |
Ok(res) => res, | |
Err(_) => return Err("error executing request"), | |
}; | |
match res.status() { | |
StatusCode::OK => | |
// whoops, all body. | |
{ | |
Err("") | |
} | |
StatusCode::PARTIAL_CONTENT => { | |
// That good shit. | |
Ok(0) | |
} | |
_ => Err("unexpected status code"), | |
} | |
} | |
async fn seek(&mut self, off: i64, w: Whence) -> Result<i64, &'static str> { | |
if self.end.is_none() { | |
self.rangefind().await?; | |
} | |
self.pos = self.offset(off, w); | |
Ok(self.pos) | |
} | |
fn offset(&self, off: i64, w: Whence) -> i64 { | |
match w { | |
Whence::Start => off, | |
Whence::Current => cmp::max(self.pos + off, 0), | |
Whence::End => cmp::max(self.end.unwrap() + off, 0), | |
Whence::Invalid => panic!("passed invalid Whence"), | |
} | |
} | |
async fn rangefind(&mut self) -> Result<(), &'static str> { | |
let req = self | |
.client | |
.request(Method::GET, self.endpoint) | |
.header("Accept-Range", "bytes=-0"); | |
let res = match req.send().await { | |
Ok(res) => res, | |
Err(_) => return Err("error executing request"), | |
}; | |
match res.status() { | |
StatusCode::OK => { | |
if let Some(l) = res.content_length() { | |
self.end = Some(l as i64); | |
drop(res); | |
return Ok(()); | |
} | |
} | |
StatusCode::PARTIAL_CONTENT => (), | |
_ => return Err("unexpected status code"), | |
} | |
Ok(()) | |
} | |
async fn _request(req: reqwest::RequestBuilder) -> Result<reqwest::Response, reqwest::Error> { | |
req.send().await | |
} | |
} | |
/// Read is a function suitable for using as an ffmpeg AVIOContext "read" callback. | |
/// | |
/// The IOContext must be configured such that the opaque pointer is of type *mut Context. | |
#[no_mangle] | |
pub extern "C" fn read(ctx: *mut Context, buf: *mut u8, sz: c_int) -> c_int { | |
assert!(!ctx.is_null(), "null *Context passed to read"); | |
let ctx = unsafe { &mut *ctx }; | |
assert!(!buf.is_null(), "null *u8 passed to read"); | |
let buf: &[u8] = unsafe { slice::from_raw_parts_mut(buf, sz as usize) }; | |
let h = Handle::current(); | |
let (tx, rx) = sync_channel(0); | |
h.spawn(async { | |
let res = ctx.read(buf).await.unwrap_or(-1) as c_int; | |
tx.send(res).unwrap(); | |
}); | |
rx.recv().unwrap() | |
} | |
/// Seek is a function suitable for using as an ffmpeg AVIOContext "seek" callback. | |
/// | |
/// The IOContext must be configured such that the opaque pointer is of type *mut Context. | |
#[no_mangle] | |
pub extern "C" fn seek(ctx: *mut Context, off: i64, whence: c_int) -> i64 { | |
assert!(!ctx.is_null(), "null *Context passed to seek"); | |
let ctx = unsafe { &mut *ctx }; | |
let w = Whence::from(whence as isize); | |
let h = Handle::current(); | |
let (tx, rx) = sync_channel(0); | |
h.spawn(async { | |
let res = ctx.seek(off, w).await.unwrap_or(-1); | |
tx.send(res).unwrap(); | |
}); | |
rx.recv().unwrap() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment