From c39bf970be5c7031788eb156861765a7b26e60d6 Mon Sep 17 00:00:00 2001 From: Cameron Cordes Date: Mon, 13 Jul 2020 22:39:00 -0400 Subject: [PATCH] Generating a HLS playlist through ffmpeg I might be able to streamline the requests to cut down on the endpoints. This also will likely take some time if the file is large and could time out, that may be a concern for another day. --- src/main.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- src/video.rs | 24 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/video.rs diff --git a/src/main.rs b/src/main.rs index 992dc18..38a0fc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,10 +13,12 @@ use std::path::PathBuf; use crate::data::{Claims, CreateAccountRequest, Token}; use crate::database::{create_user, get_user, user_exists}; use crate::files::list_files; +use crate::video::create_playlist; mod data; mod database; mod files; +mod video; #[post("/register")] async fn register(user: Json) -> impl Responder { @@ -119,16 +121,58 @@ async fn image( } } +#[post("/video/generate")] +async fn generate_video(_claims: Claims, body: web::Json) -> impl Responder { + let filename = PathBuf::from(&body.path); + + if let Some(name) = filename.file_stem() { + let filename = name.to_str().expect("Filename should conver to string"); + create_playlist(&body.path, &format!("tmp/{}.m3u8", filename)); + + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } +} + +#[get("/video/stream")] +async fn stream_video(request: HttpRequest, path: web::Query) -> impl Responder { + let playlist = &path.path; + println!("Playlist: {}", playlist); + + if let Ok(file) = NamedFile::open(playlist) { + file.into_response(&request).unwrap() + } else { + HttpResponse::NotFound().finish() + } +} + +#[get("/video/{path}")] +async fn get_video_part(request: HttpRequest, path: web::Path) -> impl Responder { + let playlist = &path.path; + println!("Video part: {}", playlist); + + if let Ok(file) = NamedFile::open(String::from("tmp/") + playlist) { + file.into_response(&request).unwrap() + } else { + HttpResponse::NotFound().finish() + } +} + #[actix_rt::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() + .service(register) .service(login) .service(list_photos) - .service(register) .service(image) + .service(generate_video) + .service(stream_video) + .service(get_video_part) }) - .bind("127.0.0.1:8088")? + .bind("192.168.10.23:8088")? + .bind("localhost:8088")? .run() .await } diff --git a/src/video.rs b/src/video.rs new file mode 100644 index 0000000..a0cddab --- /dev/null +++ b/src/video.rs @@ -0,0 +1,24 @@ +use std::process::Command; + +// ffmpeg -i test.mp4 -c:v h264 -flags +cgop -g 30 -hls_time 3 out.m3u8 + +pub fn create_playlist(video_path: &str, playlist_file: &str) { + let result = Command::new("ffmpeg") + .arg("-i") + .arg(video_path) + .arg("-c:v") + .arg("h264") + .arg("-flags") + .arg("+cgop") + .arg("-g") + .arg("30") + .arg("-hls_time") + .arg("5") + .arg(playlist_file) + .output() + .expect("Expected this to work.."); + + println!("{:?}", result); + println!("Status: {}", String::from_utf8(result.stdout).unwrap()) +} +