wip
parent
fe171a56be
commit
2684e58bbf
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,256 @@
|
||||
use crate::transcoder::Transcoder;
|
||||
use async_std::sync::{Arc, Receiver, RecvError, RwLock, RwLockReadGuard, Sender, TryRecvError};
|
||||
use async_std::task;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::option::Option::Some;
|
||||
use std::thread::Builder;
|
||||
use std::thread::JoinHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TranscoderInstance {
|
||||
id: Uuid,
|
||||
channel: Channel<Message>,
|
||||
state: RwLock<TranscoderInstanceState>,
|
||||
command: TranscoderCommand,
|
||||
}
|
||||
|
||||
impl TranscoderInstance {
|
||||
pub async fn state<T>(&self, block: fn(RwLockReadGuard<TranscoderInstanceState>) -> T) -> T {
|
||||
block(self.state.read().await)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TranscoderInstanceState {
|
||||
thread: Option<JoinHandle<()>>,
|
||||
duration_secs: f64,
|
||||
segments: HashSet<u32>,
|
||||
init_written: bool,
|
||||
}
|
||||
|
||||
impl TranscoderInstanceState {
|
||||
#[inline]
|
||||
pub fn duration(&self) -> f64 {
|
||||
self.duration_secs
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn segments(&self) -> &HashSet<u32> {
|
||||
&self.segments
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn init_written(&self) -> bool {
|
||||
self.init_written
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TranscoderManager {
|
||||
instances: HashMap<Uuid, Arc<TranscoderInstance>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Channel<T> {
|
||||
receiver: Receiver<T>,
|
||||
sender: Sender<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
async fn send(&self, msg: T) {
|
||||
self.sender.send(msg).await
|
||||
}
|
||||
|
||||
fn send_blocking(&self, msg: T) {
|
||||
task::block_on(self.sender.send(msg))
|
||||
}
|
||||
|
||||
async fn recv(&self) -> Result<T, RecvError> {
|
||||
self.receiver.recv().await
|
||||
}
|
||||
|
||||
fn recv_blocking(&self) -> Result<T, RecvError> {
|
||||
task::block_on(self.receiver.recv())
|
||||
}
|
||||
|
||||
fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
self.receiver.try_recv()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_channel() -> (Channel<Message>, Channel<Message>) {
|
||||
let (sender_a, receiver_a) = async_std::sync::channel(25);
|
||||
let (sender_b, receiver_b) = async_std::sync::channel(25);
|
||||
|
||||
(
|
||||
Channel {
|
||||
sender: sender_a,
|
||||
receiver: receiver_b,
|
||||
},
|
||||
Channel {
|
||||
sender: sender_b,
|
||||
receiver: receiver_a,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TranscoderCommand {
|
||||
input: String,
|
||||
output: String,
|
||||
audio: Option<i32>,
|
||||
video: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
Info { duration_secs: f64 },
|
||||
SegmentReady(u32),
|
||||
Seek(u32),
|
||||
Failure(String),
|
||||
Stop,
|
||||
Finished,
|
||||
}
|
||||
|
||||
fn transcoder_thread_main(command: TranscoderCommand, channel: Channel<Message>) {
|
||||
if let Err(err) = transcoder_thread(command, &channel) {
|
||||
channel.send_blocking(Message::Failure(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn transcoder_thread(command: TranscoderCommand, channel: &Channel<Message>) -> Result<(), String> {
|
||||
let transcoder = Transcoder::create(
|
||||
&command.input,
|
||||
&command.output,
|
||||
command.video,
|
||||
command.audio,
|
||||
);
|
||||
|
||||
let mut transcoder = match transcoder {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
channel.send_blocking(Message::Failure(e));
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
transcoder.open()?;
|
||||
|
||||
channel.send_blocking(Message::Info {
|
||||
duration_secs: transcoder.duration_secs(),
|
||||
});
|
||||
|
||||
let mut last_segment = transcoder.segment();
|
||||
while transcoder.transcode()? {
|
||||
while let Ok(msg) = channel.try_recv() {
|
||||
match msg {
|
||||
Message::Seek(segment) => {
|
||||
transcoder.seek(segment, Some(transcoder.video_stream()))?;
|
||||
}
|
||||
Message::Stop => {
|
||||
transcoder.stop();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if last_segment < transcoder.segment() {
|
||||
channel.send_blocking(Message::SegmentReady(last_segment));
|
||||
last_segment = transcoder.segment();
|
||||
}
|
||||
}
|
||||
|
||||
transcoder.finish()?;
|
||||
channel.send_blocking(Message::SegmentReady(transcoder.segment()));
|
||||
transcoder.stop();
|
||||
channel.send_blocking(Message::Finished);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl TranscoderManager {
|
||||
pub fn start(
|
||||
&mut self,
|
||||
input: String,
|
||||
audio: Option<i32>,
|
||||
video: Option<i32>,
|
||||
) -> Result<Uuid, std::io::Error> {
|
||||
let uuid = Uuid::new_v4();
|
||||
let command = TranscoderCommand {
|
||||
input: input.clone(),
|
||||
output: format!("/tmp/transotf/{}/", uuid),
|
||||
audio,
|
||||
video,
|
||||
};
|
||||
let (thread_channel, own_channel) = new_channel();
|
||||
let thread = {
|
||||
let command = command.clone();
|
||||
Builder::new()
|
||||
.name(format!("t#{}", uuid))
|
||||
.spawn(|| transcoder_thread_main(command, thread_channel))?
|
||||
};
|
||||
|
||||
let instance = TranscoderInstance {
|
||||
id: uuid,
|
||||
command,
|
||||
state: RwLock::new(TranscoderInstanceState {
|
||||
thread: Some(thread),
|
||||
init_written: false,
|
||||
segments: HashSet::new(),
|
||||
duration_secs: 0.0,
|
||||
}),
|
||||
channel: own_channel,
|
||||
};
|
||||
|
||||
let instance = Arc::new(instance);
|
||||
self.instances.insert(uuid, instance.clone());
|
||||
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
let fut = instance.channel.recv();
|
||||
if let Ok(x) = fut.await {
|
||||
match x {
|
||||
Message::Finished => {
|
||||
let mut state = instance.state.write().await;
|
||||
if let Some(join_handle) = std::mem::replace(&mut state.thread, None) {
|
||||
join_handle.join().unwrap();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Message::Info { duration_secs } => {
|
||||
let mut state = instance.state.write().await;
|
||||
state.duration_secs = duration_secs
|
||||
}
|
||||
|
||||
Message::SegmentReady(segment) => {
|
||||
let mut state = instance.state.write().await;
|
||||
if segment == 0 {
|
||||
state.init_written = true
|
||||
}
|
||||
|
||||
state.segments.insert(segment);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
println!("> {:?}", x);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(uuid)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: Uuid) -> Option<Arc<TranscoderInstance>> {
|
||||
self.instances.get(&id).cloned()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue