Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created January 24, 2020 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rust-play/d21f060eeff08023994fa177058e7b09 to your computer and use it in GitHub Desktop.
Save rust-play/d21f060eeff08023994fa177058e7b09 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
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