use std::path::Path;
use rgb::RGBA8;
use serde::{Serialize, Serializer, ser::SerializeStruct};
use time::Duration;
use crate::common::FontFlags;
pub const SOURCE_COREAUDIO_INPUT_CAPTURE: &str = "coreaudio_input_capture";
pub const SOURCE_COREAUDIO_OUTPUT_CAPTURE: &str = "coreaudio_output_capture";
pub const SOURCE_BROWSER_SOURCE: &str = "browser_source";
pub const SOURCE_COLOR_SOURCE_V3: &str = "color_source_v3";
pub const SOURCE_DISPLAY_CAPTURE: &str = "display_capture";
pub const SOURCE_IMAGE_SOURCE: &str = "image_source";
pub const SOURCE_SLIDESHOW: &str = "slideshow";
pub const SOURCE_FFMPEG_SOURCE: &str = "ffmpeg_source";
pub const SOURCE_TEXT_FT2_SOURCE_V2: &str = "text_ft2_source_v2";
pub const SOURCE_VLC_SOURCE: &str = "vlc_source";
pub const SOURCE_AV_CAPTURE_INPUT_V2: &str = "av_capture_input_v2";
pub const SOURCE_WINDOW_CAPTURE: &str = "window_capture";
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct CoreaudioInputCapture<'a> {
pub device_id: &'a str,
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct CoreaudioOutputCapture<'a> {
pub device_id: &'a str,
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct BrowserSource<'a> {
pub is_local_file: bool,
pub local_file: &'a Path,
pub url: &'a str,
pub width: u32,
pub height: u32,
pub fps_custom: bool,
pub fps: u16,
pub reroute_audio: bool,
pub css: &'a str,
pub shutdown: bool,
pub restart_when_active: bool,
}
impl Default for BrowserSource<'_> {
fn default() -> Self {
Self {
is_local_file: false,
local_file: Path::new(""),
url: "https://obsproject.com/browser-source",
width: 800,
height: 600,
fps_custom: false,
fps: 30,
reroute_audio: false,
css: "body { background-color: rgba(0, 0, 0, 0); margin: 0px auto; overflow: hidden; }",
shutdown: false,
restart_when_active: false,
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct ColorSourceV3 {
#[serde(with = "crate::serde::rgba8_inverse")]
pub color: RGBA8,
pub width: u32,
pub height: u32,
}
impl Default for ColorSourceV3 {
fn default() -> Self {
Self {
color: RGBA8::new(209, 209, 209, 255),
width: 0,
height: 0,
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct DisplayCapture<'a> {
pub display: u8,
pub show_cursor: bool,
#[serde(flatten)]
pub crop_mode: CropMode<'a>,
}
#[non_exhaustive]
pub enum CropMode<'a> {
None,
Manual {
left: f64,
top: f64,
right: f64,
bottom: f64,
},
ToWindow {
owner_name: &'a str,
window_name: &'a str,
window: u32,
show_empty_names: bool,
},
ToWindowAndManual {
owner_name: &'a str,
window_name: &'a str,
window: u32,
show_empty_names: bool,
left: f64,
top: f64,
right: f64,
bottom: f64,
},
}
impl Serialize for CropMode<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::None => {
let mut s = serializer.serialize_struct("CropMode", 1)?;
s.serialize_field("crop_mode", &0u8)?;
s.end()
}
Self::Manual {
left,
top,
right,
bottom,
} => {
let mut s = serializer.serialize_struct("CropMode", 5)?;
s.serialize_field("crop_mode", &1u8)?;
s.serialize_field("manual.origin.x", left)?;
s.serialize_field("manual.origin.y", top)?;
s.serialize_field("manual.size.width", right)?;
s.serialize_field("manual.size.height", bottom)?;
s.end()
}
Self::ToWindow {
owner_name,
window_name,
window,
show_empty_names,
} => {
let mut s = serializer.serialize_struct("CropMode", 5)?;
s.serialize_field("crop_mode", &2u8)?;
s.serialize_field("owner_name", owner_name)?;
s.serialize_field("window_name", window_name)?;
s.serialize_field("window", window)?;
s.serialize_field("show_empty_names", show_empty_names)?;
s.end()
}
CropMode::ToWindowAndManual {
owner_name,
window_name,
window,
show_empty_names,
left,
top,
right,
bottom,
} => {
let mut s = serializer.serialize_struct("CropMode", 9)?;
s.serialize_field("crop_mode", &3u8)?;
s.serialize_field("owner_name", owner_name)?;
s.serialize_field("window_name", window_name)?;
s.serialize_field("window", window)?;
s.serialize_field("show_empty_names", show_empty_names)?;
s.serialize_field("window.origin.x", left)?;
s.serialize_field("window.origin.y", top)?;
s.serialize_field("window.size.width", right)?;
s.serialize_field("window.size.height", bottom)?;
s.end()
}
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct ImageSource<'a> {
pub file: &'a Path,
pub unload: bool,
}
impl Default for ImageSource<'_> {
fn default() -> Self {
Self {
file: Path::new(""),
unload: false,
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct Slideshow<'a> {
pub playback_behavior: PlaybackBehavior,
pub slide_mode: SlideMode,
pub transition: Transition,
#[serde(with = "crate::serde::duration_millis")]
pub slide_time: Duration,
#[serde(with = "crate::serde::duration_millis")]
pub transition_speed: Duration,
#[serde(rename = "loop")]
pub loop_: bool,
pub hide: bool,
pub randomize: bool,
pub use_custom_size: CustomSize,
pub files: &'a [SlideshowFile<'a>],
}
impl Default for Slideshow<'_> {
fn default() -> Self {
Self {
playback_behavior: PlaybackBehavior::AlwaysPlay,
slide_mode: SlideMode::default(),
transition: Transition::default(),
slide_time: Duration::seconds(8),
transition_speed: Duration::milliseconds(700),
loop_: true,
hide: false,
randomize: false,
use_custom_size: CustomSize::default(),
files: &[],
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct SlideshowFile<'a> {
pub value: &'a Path,
pub hidden: bool,
pub selected: bool,
}
impl Default for SlideshowFile<'_> {
fn default() -> Self {
Self {
value: Path::new(""),
hidden: false,
selected: false,
}
}
}
#[derive(Clone, Copy, Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum PlaybackBehavior {
AlwaysPlay,
StopRestart,
PauseUnpause,
}
#[derive(Clone, Copy, Default, Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum SlideMode {
#[default]
ModeAuto,
ModeManual,
}
#[derive(Default, Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Transition {
Cut,
#[default]
Fade,
Swipe,
Slide,
}
#[derive(Clone, Copy, Default, Serialize)]
#[serde(into = "String")]
#[non_exhaustive]
pub enum CustomSize {
#[default]
Automatic,
SixteenToNine,
SixteenToTen,
FourToThree,
OneToOne,
CustomRatio(u32, u32),
CustomSize(u32, u32),
}
impl From<CustomSize> for String {
fn from(s: CustomSize) -> Self {
match s {
CustomSize::Automatic => "Automatic".to_owned(),
CustomSize::SixteenToNine => "16:9".to_owned(),
CustomSize::SixteenToTen => "16:10".to_owned(),
CustomSize::FourToThree => "4:3".to_owned(),
CustomSize::OneToOne => "1:1".to_owned(),
CustomSize::CustomRatio(w, h) => format!("{w}:{h}"),
CustomSize::CustomSize(w, h) => format!("{w}x{h}"),
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct FfmpegSource<'a> {
pub is_local_file: bool,
pub local_file: &'a Path,
pub looping: bool,
pub buffering_mb: u8,
pub input: &'a str,
pub input_format: &'a str,
pub reconnect_delay_sec: u8,
pub restart_on_activate: bool,
pub clear_on_media_end: bool,
pub close_when_inactive: bool,
pub speed_percent: u8,
pub color_range: ColorRange,
pub seekable: bool,
}
#[derive(Clone, Copy, Default, Serialize)]
#[serde(into = "u8")]
#[repr(u8)]
#[non_exhaustive]
pub enum ColorRange {
#[default]
Auto = 0,
Partial = 1,
Full = 2,
}
impl From<ColorRange> for u8 {
fn from(value: ColorRange) -> Self {
value as Self
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct TextFt2SourceV2<'a> {
pub antialiasing: bool,
#[serde(with = "crate::serde::rgba8_inverse")]
pub color1: RGBA8,
#[serde(with = "crate::serde::rgba8_inverse")]
pub color2: RGBA8,
pub custom_width: u32,
pub drop_shadow: bool,
pub font: Font<'a>,
pub from_file: bool,
pub log_lines: u32,
pub log_mode: bool,
pub outline: bool,
pub text: &'a str,
pub text_file: &'a Path,
pub word_wrap: bool,
}
impl Default for TextFt2SourceV2<'_> {
fn default() -> Self {
Self {
antialiasing: true,
color1: RGBA8::new(255, 255, 255, 255),
color2: RGBA8::new(255, 255, 255, 255),
custom_width: 0,
drop_shadow: false,
font: Font::default(),
from_file: false,
log_lines: 6,
log_mode: false,
outline: false,
text: "",
text_file: Path::new(""),
word_wrap: false,
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct Font<'a> {
pub face: &'a str,
pub flags: FontFlags,
pub size: u32,
pub style: &'a str,
}
impl Default for Font<'_> {
fn default() -> Self {
Self {
face: "Helvetica",
flags: FontFlags::empty(),
size: 256,
style: "Regular",
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct VlcSource<'a> {
#[serde(rename = "bool")]
pub loop_: bool,
pub shuffle: bool,
pub playback_behavior: PlaybackBehavior,
pub playlist: &'a [SlideshowFile<'a>],
#[serde(with = "crate::serde::duration_millis")]
pub network_caching: Duration,
pub track: u32,
pub subtitle_enable: bool,
pub subtitle: u32,
}
impl Default for VlcSource<'_> {
fn default() -> Self {
Self {
loop_: true,
shuffle: false,
playback_behavior: PlaybackBehavior::StopRestart,
playlist: &[],
network_caching: Duration::milliseconds(400),
track: 1,
subtitle_enable: false,
subtitle: 1,
}
}
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct AvCaptureInputV2<'a> {
pub buffering: bool,
pub color_space: ColorSpace,
pub device: &'a str,
pub device_name: &'a str,
pub frame_rate: FrameRate,
pub input_format: u32,
pub preset: AvPreset,
#[serde(with = "crate::serde::json_string")]
pub resolution: Resolution,
pub use_preset: bool,
pub video_range: VideoRange,
}
#[derive(Clone, Copy, Default, Serialize)]
#[serde(into = "i8")]
#[repr(i8)]
#[non_exhaustive]
pub enum ColorSpace {
#[default]
Auto = -1,
Rec601 = 1,
Rec709 = 2,
}
impl From<ColorSpace> for i8 {
fn from(value: ColorSpace) -> Self {
value as Self
}
}
#[derive(Clone, Copy, Default, Serialize)]
#[serde(into = "i8")]
#[repr(i8)]
#[non_exhaustive]
pub enum VideoRange {
#[default]
Auto = -1,
Partial = 1,
Full = 2,
}
impl From<VideoRange> for i8 {
fn from(value: VideoRange) -> Self {
value as Self
}
}
#[derive(Serialize)]
#[non_exhaustive]
pub enum AvPreset {
#[serde(rename = "AVCaptureSessionPreset3840x2160")]
Res3840x2160,
#[serde(rename = "AVCaptureSessionPreset1920x1080")]
Res1920x1080,
#[serde(rename = "AVCaptureSessionPreset1280x720")]
Res1280x720,
#[serde(rename = "AVCaptureSessionPreset960x540")]
Res960x540,
#[serde(rename = "AVCaptureSessionPreset640x480")]
Res640x480,
#[serde(rename = "AVCaptureSessionPreset352x288")]
Res352x288,
#[serde(rename = "AVCaptureSessionPreset320x240")]
Res320x240,
#[serde(rename = "AVCaptureSessionPresetHigh")]
High,
#[serde(rename = "AVCaptureSessionPresetMedium")]
Medium,
#[serde(rename = "AVCaptureSessionPresetLow")]
Low,
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct FrameRate {
pub numerator: u64,
pub denominator: u64,
}
#[derive(Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct Resolution {
pub width: u32,
pub height: u32,
}
#[derive(Default, Serialize)]
#[cfg_attr(feature = "builder", derive(bon::Builder))]
pub struct WindowCapture<'a> {
pub owner_name: &'a str,
pub window_name: &'a str,
pub window: u16,
pub show_empty_names: bool,
pub show_shadow: bool,
}