use bytes::Bytes;
use super::encoder::{Codec, Config, Kind};
use crate::Error;
use crate::frame::Frame;
mod openh264;
#[cfg(target_os = "macos")]
mod videotoolbox;
#[cfg(target_os = "windows")]
mod mediafoundation;
#[cfg(all(target_os = "linux", feature = "nvenc"))]
mod nvenc;
#[cfg(all(target_os = "linux", feature = "vaapi"))]
mod vaapi;
pub(crate) trait Backend: Send {
fn encode(&mut self, frame: &Frame, keyframe: bool) -> Result<Vec<Bytes>, Error>;
fn finish(&mut self) -> Result<Vec<Bytes>, Error>;
fn set_bitrate(&mut self, bitrate: u64) -> Result<(), Error>;
fn name(&self) -> &str;
}
struct Candidate {
name: &'static str,
codecs: &'static [Codec],
open: fn(&Config) -> Result<Box<dyn Backend>, Error>,
}
const HARDWARE: &[Candidate] = &[
#[cfg(target_os = "macos")]
Candidate {
name: videotoolbox::NAME,
codecs: &[Codec::H264, Codec::H265],
open: videotoolbox::VideoToolbox::open,
},
#[cfg(target_os = "windows")]
Candidate {
name: mediafoundation::NAME,
codecs: &[Codec::H264, Codec::H265],
open: mediafoundation::MediaFoundation::open,
},
#[cfg(all(target_os = "linux", feature = "nvenc"))]
Candidate {
name: nvenc::NAME,
codecs: &[Codec::H264, Codec::H265],
open: nvenc::Nvenc::open,
},
#[cfg(all(target_os = "linux", feature = "vaapi"))]
Candidate {
name: vaapi::NAME,
codecs: &[Codec::H264],
open: vaapi::Vaapi::open,
},
];
const SOFTWARE: &[Candidate] = &[Candidate {
name: openh264::NAME,
codecs: &[Codec::H264],
open: openh264::Openh264::open,
}];
pub(crate) fn open(config: &Config) -> Result<Box<dyn Backend>, Error> {
let codec = config.codec;
let supports = move |c: &&Candidate| c.codecs.contains(&codec);
let hardware = HARDWARE.iter().filter(supports);
let software = SOFTWARE.iter().filter(supports);
let candidates: Vec<&Candidate> = match &config.kind {
Kind::Auto => hardware.chain(software).collect(),
Kind::Hardware => hardware.collect(),
Kind::Software => software.collect(),
Kind::Named(name) => HARDWARE
.iter()
.chain(SOFTWARE.iter())
.filter(supports)
.filter(|c| c.name == name)
.collect(),
};
let mut tried = Vec::new();
for candidate in candidates {
tried.push(candidate.name);
match (candidate.open)(config) {
Ok(backend) => return Ok(backend),
Err(e) => tracing::debug!(encoder = candidate.name, error = %e, "encoder unavailable, trying next"),
}
}
Err(Error::NoEncoder(tried.join(", ")))
}