mirror of
https://github.com/v0l/zap-stream-core.git
synced 2025-06-20 10:47:02 +00:00
refactor: convert to workspace
This commit is contained in:
79
crates/core/src/variant/audio.rs
Normal file
79
crates/core/src/variant/audio.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
48
crates/core/src/variant/mapping.rs
Normal file
48
crates/core/src/variant/mapping.rs
Normal 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
|
||||
}
|
||||
}
|
113
crates/core/src/variant/mod.rs
Normal file
113
crates/core/src/variant/mod.rs
Normal 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"))
|
||||
}
|
111
crates/core/src/variant/video.rs
Normal file
111
crates/core/src/variant/video.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user