use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone)]
pub enum RecordingCommand {
Start,
Pause,
Resume,
Stop,
Cancel,
SetOutputPath(PathBuf),
SetQuality(f32),
SelectDevice(String),
}
#[derive(Debug, Clone)]
pub enum AudioRecordingCommand {
Base(RecordingCommand),
SetSampleRate(u32),
SetChannels(u16),
SetFormat(AudioFormat),
SetNoiseReduction(bool),
SetGain(f32),
}
#[derive(Debug, Clone)]
pub enum VideoRecordingCommand {
Base(RecordingCommand),
SetResolution { width: u32, height: u32 },
SetFrameRate(u32),
SetFormat(VideoFormat),
SetRecordAudio(bool),
SelectCamera(String),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum RecordingState {
#[default]
Idle,
Recording,
Paused,
Processing,
Completed,
Failed,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum AudioFormat {
#[default]
Wav,
Mp3,
Ogg,
Flac,
WebM,
Aac,
}
impl AudioFormat {
pub fn extension(&self) -> &'static str {
match self {
Self::Wav => "wav",
Self::Mp3 => "mp3",
Self::Ogg => "ogg",
Self::Flac => "flac",
Self::WebM => "webm",
Self::Aac => "aac",
}
}
pub fn mime_type(&self) -> &'static str {
match self {
Self::Wav => "audio/wav",
Self::Mp3 => "audio/mpeg",
Self::Ogg => "audio/ogg",
Self::Flac => "audio/flac",
Self::WebM => "audio/webm",
Self::Aac => "audio/aac",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum VideoFormat {
#[default]
Mp4H264,
WebMVp8,
WebMVp9,
Avi,
Mov,
}
impl VideoFormat {
pub fn extension(&self) -> &'static str {
match self {
Self::Mp4H264 => "mp4",
Self::WebMVp8 | Self::WebMVp9 => "webm",
Self::Avi => "avi",
Self::Mov => "mov",
}
}
pub fn mime_type(&self) -> &'static str {
match self {
Self::Mp4H264 => "video/mp4",
Self::WebMVp8 | Self::WebMVp9 => "video/webm",
Self::Avi => "video/avi",
Self::Mov => "video/quicktime",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AudioRecorderState {
pub state: RecordingState,
pub duration: Duration,
pub level: f32,
pub peak_level: f32,
pub device: Option<String>,
pub output_path: Option<PathBuf>,
pub format: AudioFormat,
pub sample_rate: u32,
pub channels: u16,
pub error: Option<String>,
}
impl AudioRecorderState {
pub fn new() -> Self {
Self {
sample_rate: 44100,
channels: 2,
..Default::default()
}
}
pub fn is_recording(&self) -> bool {
self.state == RecordingState::Recording
}
pub fn is_paused(&self) -> bool {
self.state == RecordingState::Paused
}
}
#[derive(Debug, Clone, Default)]
pub struct VideoRecorderState {
pub state: RecordingState,
pub duration: Duration,
pub camera: Option<String>,
pub output_path: Option<PathBuf>,
pub format: VideoFormat,
pub width: u32,
pub height: u32,
pub frame_rate: u32,
pub recording_audio: bool,
pub error: Option<String>,
}
impl VideoRecorderState {
pub fn new() -> Self {
Self {
width: 1280,
height: 720,
frame_rate: 30,
recording_audio: true,
..Default::default()
}
}
pub fn is_recording(&self) -> bool {
self.state == RecordingState::Recording
}
pub fn is_paused(&self) -> bool {
self.state == RecordingState::Paused
}
}
#[derive(Debug, Clone)]
pub enum RecordingEvent {
Started,
Paused,
Resumed,
Completed { path: PathBuf, duration: Duration },
Cancelled,
LevelUpdate(f32),
DurationUpdate(Duration),
Error(String),
DevicesChanged,
}
#[derive(Debug, Clone)]
pub struct InputDevice {
pub id: String,
pub name: String,
pub is_default: bool,
}
pub trait AudioRecorderBackend: Send {
fn send(&self, command: AudioRecordingCommand) -> Result<(), RecordingError>;
fn state(&self) -> AudioRecorderState;
fn list_devices(&self) -> Vec<InputDevice>;
}
pub trait VideoRecorderBackend: Send {
fn send(&self, command: VideoRecordingCommand) -> Result<(), RecordingError>;
fn state(&self) -> VideoRecorderState;
fn list_cameras(&self) -> Vec<InputDevice>;
fn preview_frame(&self) -> Option<PreviewFrame>;
}
#[derive(Debug, Clone)]
pub struct PreviewFrame {
pub data: Vec<u8>,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone)]
pub enum RecordingError {
NoDeviceAvailable,
DeviceNotFound(String),
PermissionDenied,
DeviceBusy,
EncodingError(String),
IoError(String),
BackendUnavailable,
Other(String),
}
impl std::fmt::Display for RecordingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoDeviceAvailable => write!(f, "No recording device available"),
Self::DeviceNotFound(id) => write!(f, "Recording device not found: {}", id),
Self::PermissionDenied => write!(f, "Permission denied for recording"),
Self::DeviceBusy => write!(f, "Recording device is busy"),
Self::EncodingError(msg) => write!(f, "Encoding error: {}", msg),
Self::IoError(msg) => write!(f, "IO error: {}", msg),
Self::BackendUnavailable => write!(f, "Recording backend unavailable"),
Self::Other(msg) => write!(f, "Recording error: {}", msg),
}
}
}
impl std::error::Error for RecordingError {}
#[derive(Debug, Default)]
pub struct NoOpAudioRecorder {
state: AudioRecorderState,
}
impl NoOpAudioRecorder {
pub fn new() -> Self {
Self {
state: AudioRecorderState::new(),
}
}
}
impl AudioRecorderBackend for NoOpAudioRecorder {
fn send(&self, _command: AudioRecordingCommand) -> Result<(), RecordingError> {
Ok(())
}
fn state(&self) -> AudioRecorderState {
self.state.clone()
}
fn list_devices(&self) -> Vec<InputDevice> {
Vec::new()
}
}
#[derive(Debug, Default)]
pub struct NoOpVideoRecorder {
state: VideoRecorderState,
}
impl NoOpVideoRecorder {
pub fn new() -> Self {
Self {
state: VideoRecorderState::new(),
}
}
}
impl VideoRecorderBackend for NoOpVideoRecorder {
fn send(&self, _command: VideoRecordingCommand) -> Result<(), RecordingError> {
Ok(())
}
fn state(&self) -> VideoRecorderState {
self.state.clone()
}
fn list_cameras(&self) -> Vec<InputDevice> {
Vec::new()
}
fn preview_frame(&self) -> Option<PreviewFrame> {
None
}
}