refactor: convert to workspace

This commit is contained in:
2025-01-29 11:48:57 +00:00
parent 20c9d107b7
commit 9045bb93e4
56 changed files with 6215 additions and 1123 deletions

View File

@ -0,0 +1,79 @@
use ffmpeg_rs_raw::ffmpeg_sys_the_third::av_get_sample_fmt;
use ffmpeg_rs_raw::{cstr, Encoder};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use uuid::Uuid;
use crate::variant::{StreamMapping, VariantMapping};
/// Information related to variant streams for a given egress
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AudioVariant {
/// Id, Src, Dst
pub mapping: VariantMapping,
/// Bitrate of this stream
pub bitrate: u64,
/// Codec name
pub codec: String,
/// Number of channels
pub channels: u16,
/// Sample rate
pub sample_rate: usize,
/// Sample format as ffmpeg sample format string
pub sample_fmt: String,
}
impl Display for AudioVariant {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Audio #{}->{}: {}, {}kbps",
self.mapping.src_index,
self.mapping.dst_index,
self.codec,
self.bitrate / 1000
)
}
}
impl StreamMapping for AudioVariant {
fn id(&self) -> Uuid {
self.mapping.id
}
fn src_index(&self) -> usize {
self.mapping.src_index
}
fn dst_index(&self) -> usize {
self.mapping.dst_index
}
fn set_dst_index(&mut self, dst: usize) {
self.mapping.dst_index = dst;
}
fn group_id(&self) -> usize {
self.mapping.group_id
}
}
impl TryInto<Encoder> for &AudioVariant {
type Error = anyhow::Error;
fn try_into(self) -> Result<Encoder, Self::Error> {
unsafe {
let enc = Encoder::new_with_name(&self.codec)?
.with_sample_rate(self.sample_rate as _)?
.with_bitrate(self.bitrate as _)
.with_default_channel_layout(self.channels as _)
.with_sample_format(av_get_sample_fmt(cstr!(self.sample_fmt.as_bytes())))
.open(None)?;
Ok(enc)
}
}
}

View File

@ -0,0 +1,48 @@
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use uuid::Uuid;
use crate::variant::StreamMapping;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
pub struct VariantMapping {
/// Unique ID of this variant
pub id: Uuid,
/// Source video stream to use for this variant
pub src_index: usize,
/// Index of this variant stream in the output
pub dst_index: usize,
/// Stream group, groups one or more streams into a variant
pub group_id: usize,
}
impl Display for VariantMapping {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Copy #{}->{}", self.src_index, self.dst_index)
}
}
impl StreamMapping for VariantMapping {
fn id(&self) -> Uuid {
self.id
}
fn src_index(&self) -> usize {
self.src_index
}
fn dst_index(&self) -> usize {
self.dst_index
}
fn set_dst_index(&mut self, dst: usize) {
self.dst_index = dst;
}
fn group_id(&self) -> usize {
self.group_id
}
}

View File

@ -0,0 +1,113 @@
use crate::variant::audio::AudioVariant;
use crate::variant::mapping::VariantMapping;
use crate::variant::video::VideoVariant;
use anyhow::Error;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use uuid::Uuid;
pub mod audio;
pub mod mapping;
pub mod video;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum VariantStream {
/// Video stream mapping
Video(VideoVariant),
/// Audio stream mapping
Audio(AudioVariant),
Subtitle(VariantMapping),
/// Copy stream src<>dst stream
CopyVideo(VariantMapping),
/// Copy stream src<>dst stream
CopyAudio(VariantMapping),
}
impl StreamMapping for VariantStream {
fn id(&self) -> Uuid {
match self {
VariantStream::Video(v) => v.id(),
VariantStream::Audio(v) => v.id(),
VariantStream::Subtitle(v) => v.id(),
VariantStream::CopyAudio(v) => v.id(),
VariantStream::CopyVideo(v) => v.id(),
}
}
fn src_index(&self) -> usize {
match self {
VariantStream::Video(v) => v.src_index(),
VariantStream::Audio(v) => v.src_index(),
VariantStream::Subtitle(v) => v.src_index(),
VariantStream::CopyAudio(v) => v.src_index(),
VariantStream::CopyVideo(v) => v.src_index(),
}
}
fn dst_index(&self) -> usize {
match self {
VariantStream::Video(v) => v.dst_index(),
VariantStream::Audio(v) => v.dst_index(),
VariantStream::Subtitle(v) => v.dst_index(),
VariantStream::CopyAudio(v) => v.dst_index(),
VariantStream::CopyVideo(v) => v.dst_index(),
}
}
fn set_dst_index(&mut self, dst: usize) {
match self {
VariantStream::Video(v) => v.set_dst_index(dst),
VariantStream::Audio(v) => v.set_dst_index(dst),
VariantStream::Subtitle(v) => v.set_dst_index(dst),
VariantStream::CopyAudio(v) => v.set_dst_index(dst),
VariantStream::CopyVideo(v) => v.set_dst_index(dst),
}
}
fn group_id(&self) -> usize {
match self {
VariantStream::Video(v) => v.group_id(),
VariantStream::Audio(v) => v.group_id(),
VariantStream::Subtitle(v) => v.group_id(),
VariantStream::CopyAudio(v) => v.group_id(),
VariantStream::CopyVideo(v) => v.group_id(),
}
}
}
impl Display for VariantStream {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
VariantStream::Video(v) => write!(f, "{}", v),
VariantStream::Audio(a) => write!(f, "{}", a),
VariantStream::Subtitle(s) => write!(f, "{}", s),
VariantStream::CopyVideo(c) => write!(f, "{}", c),
VariantStream::CopyAudio(c) => write!(f, "{}", c),
}
}
}
pub trait StreamMapping {
fn id(&self) -> Uuid;
fn src_index(&self) -> usize;
fn dst_index(&self) -> usize;
fn set_dst_index(&mut self, dst: usize);
fn group_id(&self) -> usize;
}
/// Find a stream by ID in a vec of streams
pub fn find_stream<'a>(
config: &'a Vec<VariantStream>,
id: &Uuid,
) -> Result<&'a VariantStream, Error> {
config
.iter()
.find(|x| match x {
VariantStream::Video(v) => v.id() == *id,
VariantStream::Audio(a) => a.id() == *id,
VariantStream::Subtitle(v) => v.id() == *id,
VariantStream::CopyVideo(c) => c.id() == *id,
VariantStream::CopyAudio(c) => c.id() == *id,
})
.ok_or(Error::msg("Variant does not exist"))
}

View File

@ -0,0 +1,111 @@
use ffmpeg_rs_raw::ffmpeg_sys_the_third::AVColorSpace::AVCOL_SPC_BT709;
use ffmpeg_rs_raw::Encoder;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::intrinsics::transmute;
use uuid::Uuid;
use crate::variant::{StreamMapping, VariantMapping};
/// Information related to variant streams for a given egress
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct VideoVariant {
/// Id, Src, Dst
pub mapping: VariantMapping,
/// Width of this video stream
pub width: u16,
/// Height of this video stream
pub height: u16,
/// FPS for this stream
pub fps: f32,
/// Bitrate of this stream
pub bitrate: u64,
/// Codec name
pub codec: String,
/// Codec profile
pub profile: usize,
/// Codec level
pub level: usize,
/// Keyframe interval in frames
pub keyframe_interval: u16,
/// Pixel Format
pub pixel_format: u32,
}
impl Display for VideoVariant {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Video #{}->{}: {}, {}x{}, {}fps, {}kbps",
self.mapping.src_index,
self.mapping.dst_index,
self.codec,
self.width,
self.height,
self.fps,
self.bitrate / 1000
)
}
}
impl StreamMapping for VideoVariant {
fn id(&self) -> Uuid {
self.mapping.id
}
fn src_index(&self) -> usize {
self.mapping.src_index
}
fn dst_index(&self) -> usize {
self.mapping.dst_index
}
fn set_dst_index(&mut self, dst: usize) {
self.mapping.dst_index = dst;
}
fn group_id(&self) -> usize {
self.mapping.group_id
}
}
impl TryInto<Encoder> for &VideoVariant {
type Error = anyhow::Error;
fn try_into(self) -> Result<Encoder, Self::Error> {
unsafe {
let mut opt = HashMap::new();
if self.codec == "x264" {
opt.insert("preset".to_string(), "fast".to_string());
//opt.insert("tune".to_string(), "zerolatency".to_string());
}
let enc = Encoder::new_with_name(&self.codec)?
.with_bitrate(self.bitrate as _)
.with_width(self.width as _)
.with_height(self.height as _)
.with_pix_fmt(transmute(self.pixel_format))
.with_profile(transmute(self.profile as i32))
.with_level(transmute(self.level as i32))
.with_framerate(self.fps)?
.with_options(|ctx| {
(*ctx).gop_size = self.keyframe_interval as _;
(*ctx).keyint_min = self.keyframe_interval as _;
(*ctx).max_b_frames = 3;
(*ctx).colorspace = AVCOL_SPC_BT709;
})
.open(Some(opt))?;
Ok(enc)
}
}
}