hlskit 0.1.3

HlsKit provides strong HLS video conversion features backed by ffmpeg. Prepare your mp4 files for streaming
Documentation
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::Stdio;
use tempfile::NamedTempFile;
use tokio::process::Command;

use crate::models::hls_video::HlsVideoSegment;
use crate::{
    models::hls_video::HlsVideoResolution,
    tools::{ffmpeg_command_builder::build_simple_hls, hlskit_error::HlsKitError},
};

pub async fn process_video_profile(
    input_bytes: Vec<u8>,
    resolution: (i32, i32),
    crf: i32,
    preset: &str,
    output_dir: &Path,
    stream_index: i32,
) -> Result<HlsVideoResolution, HlsKitError> {
    let (width, height) = resolution;
    let segment_filename = format!(
        "{}/data_{}_%03d.ts",
        output_dir.to_str().unwrap(),
        stream_index
    );
    let playlist_filename = format!(
        "{}/playlist_{}.m3u8",
        output_dir.to_str().unwrap(),
        stream_index
    );

    let mut temp_file = NamedTempFile::new()?;

    temp_file.write_all(&input_bytes)?;

    temp_file.flush()?;

    temp_file.as_file().sync_all()?;

    let input_path = temp_file.path().to_str().unwrap();

    let command = build_simple_hls(
        width,
        height,
        crf,
        preset,
        &segment_filename,
        &playlist_filename,
        None,
        input_path,
    )
    .await;

    let process = Command::new(&command[0])
        .args(&command[1..])
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .map_err(|error| HlsKitError::FfmpegError {
            error: error.to_string(),
        })?;

    let output = process
        .wait_with_output()
        .await
        .map_err(|error| HlsKitError::FfmpegError {
            error: format!("Failed to write to ffmpeg output: {}", error),
        })?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(HlsKitError::FfmpegError {
            error: format!(
                "FFmpeg error for resolution {}x{}: {}",
                width, height, stderr
            ),
        });
    }

    let mut hls_resolution = HlsVideoResolution {
        resolution,
        playlist_name: format!("playlist_{}.m3u8", stream_index),
        playlist_data: vec![],
        segments: vec![],
    };

    let mut playlist_handler = File::open(&playlist_filename)?;
    playlist_handler.read_to_end(&mut hls_resolution.playlist_data)?;

    let mut segment_index = 0;

    loop {
        let segment_path = segment_filename.replace("%03d", &format!("{:03}", segment_index));

        if !PathBuf::from(&segment_path).exists() {
            break;
        }

        let mut segment_file_handler = File::open(segment_path)?;
        let mut segment_file_read_buffer: Vec<u8> = Vec::new();
        segment_file_handler.read_to_end(&mut segment_file_read_buffer)?;

        let segment_name = format!("data_{}_%03d.ts", stream_index)
            .replace("%03d", &format!("{:03}", segment_index));

        let segment = HlsVideoSegment {
            segment_name,
            segment_data: segment_file_read_buffer,
        };

        hls_resolution.segments.push(segment);

        segment_index += 1;
    }

    Ok(hls_resolution)
}