#![warn(missing_docs)]
#[cfg(any(
test,
target_os = "android",
target_os = "linux",
target_os = "windows"
))]
mod bitstream;
mod color;
#[cfg(any(
test,
target_vendor = "apple",
target_os = "android",
target_os = "windows"
))]
mod config;
mod frame;
mod image;
#[cfg(target_vendor = "apple")]
mod image_apple;
mod software;
mod sys;
#[cfg(any(
test,
target_os = "android",
target_os = "linux",
target_os = "windows"
))]
pub use bitstream::{ConvertedProtectedSample, NalStreamConverter};
pub use color::{
ColorOutputTarget, SDR_REFERENCE_WHITE_NITS, VideoColorUniform, YUV_COLOR_SHADER_WGSL,
video_color_uniform,
};
pub use frame::{
DecodedFrame, DecodedFrameUploader, DecodedPixelLayout, GpuFrame, LinearRgbaConverter,
};
pub use image::{DecodedImage, DecodedPixelFormat, decode_image, decode_image_platform};
use shaderloom::CompiledShader;
const YUV_COLOR_SHADER: CompiledShader = include!(concat!(env!("OUT_DIR"), "/yuv_color.rs"));
use std::{time::Duration, vec::IntoIter};
use thiserror::Error;
#[derive(Debug, Clone, Error)]
pub enum CodecError {
#[error("unsupported codec or format: {0}")]
Unsupported(String),
#[error("initialization failed: {0}")]
InitializationFailed(String),
#[error("encoding failed: {0}")]
EncodingFailed(String),
#[error("decoding failed: {0}")]
DecodingFailed(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodecType {
H264,
H265,
Av1,
}
#[derive(Debug, Clone, Copy)]
pub struct DecodePacket<'a> {
data: &'a [u8],
presentation_time: Duration,
}
impl<'a> DecodePacket<'a> {
#[must_use]
pub const fn new(data: &'a [u8], presentation_time: Duration) -> Self {
Self {
data,
presentation_time,
}
}
#[must_use]
pub const fn data(self) -> &'a [u8] {
self.data
}
#[must_use]
pub const fn presentation_time(self) -> Duration {
self.presentation_time
}
}
#[derive(Debug, Clone, Copy)]
pub struct FrameInfo {
pub width: u32,
pub height: u32,
pub timestamp: std::time::Duration,
pub y_offset: usize,
pub uv_offset: usize,
pub total_bytes: usize,
}
pub struct DecodeStream {
inner: DecodeStreamInner,
}
enum DecodeStreamInner {
Frames(IntoIter<DecodedFrame>),
Error(Option<CodecError>),
}
impl Iterator for DecodeStream {
type Item = Result<DecodedFrame, CodecError>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
DecodeStreamInner::Frames(iter) => iter.next().map(Ok),
DecodeStreamInner::Error(err) => err.take().map(Err),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
DecodeStreamInner::Frames(iter) => iter.size_hint(),
DecodeStreamInner::Error(Some(_)) => (1, Some(1)),
DecodeStreamInner::Error(None) => (0, Some(0)),
}
}
}
impl std::fmt::Debug for DecodeStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecodeStream").finish_non_exhaustive()
}
}
pub struct DecodeIntoStream {
inner: DecodeIntoStreamInner,
}
enum DecodeIntoStreamInner {
Infos(IntoIter<FrameInfo>),
Error(Option<CodecError>),
}
impl Iterator for DecodeIntoStream {
type Item = Result<FrameInfo, CodecError>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
DecodeIntoStreamInner::Infos(iter) => iter.next().map(Ok),
DecodeIntoStreamInner::Error(err) => err.take().map(Err),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
DecodeIntoStreamInner::Infos(iter) => iter.size_hint(),
DecodeIntoStreamInner::Error(Some(_)) => (1, Some(1)),
DecodeIntoStreamInner::Error(None) => (0, Some(0)),
}
}
}
impl std::fmt::Debug for DecodeIntoStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecodeIntoStream").finish_non_exhaustive()
}
}
pub struct EncodeStream {
inner: EncodeStreamInner,
}
enum EncodeStreamInner {
Packet(Option<Vec<u8>>),
Error(Option<CodecError>),
}
impl Iterator for EncodeStream {
type Item = Result<Vec<u8>, CodecError>;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
EncodeStreamInner::Packet(packet) => packet.take().map(Ok),
EncodeStreamInner::Error(err) => err.take().map(Err),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.inner {
EncodeStreamInner::Packet(Some(_)) | EncodeStreamInner::Error(Some(_)) => (1, Some(1)),
EncodeStreamInner::Packet(None) | EncodeStreamInner::Error(None) => (0, Some(0)),
}
}
}
impl std::fmt::Debug for EncodeStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EncodeStream").finish_non_exhaustive()
}
}
pub struct Decoder {
inner: DecoderInner,
}
enum DecoderInner {
#[cfg(target_arch = "wasm32")]
Unsupported,
#[cfg(target_vendor = "apple")]
Apple(sys::apple::AppleDecoder),
#[cfg(target_os = "android")]
Android(sys::android::AndroidDecoder),
#[cfg(target_os = "windows")]
Windows(sys::windows::WindowsDecoder),
#[cfg(target_os = "linux")]
Linux(sys::linux::LinuxDecoder),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
Av1(software::av1::Av1Decoder),
}
impl std::fmt::Debug for Decoder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Decoder").finish_non_exhaustive()
}
}
impl Decoder {
pub fn new(
codec: CodecType,
config: Option<&[u8]>,
width: u32,
height: u32,
) -> Result<Self, CodecError> {
#[cfg(target_arch = "wasm32")]
{
let _ = (config, width, height);
let _unsupported = DecoderInner::Unsupported;
return Err(CodecError::Unsupported(format!(
"{codec:?} decoding is not supported by waterkit-codec on WebAssembly"
)));
}
#[cfg(not(target_arch = "wasm32"))]
{
let inner = match codec {
#[cfg(target_vendor = "apple")]
CodecType::H264 | CodecType::H265 => {
let apple_codec = match codec {
CodecType::H264 => sys::apple::CodecType::H264,
CodecType::H265 => sys::apple::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
DecoderInner::Apple(sys::apple::AppleDecoder::new(
apple_codec,
config,
width,
height,
)?)
}
#[cfg(target_os = "android")]
CodecType::H264 | CodecType::H265 => {
let android_codec = match codec {
CodecType::H264 => sys::android::CodecType::H264,
CodecType::H265 => sys::android::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
DecoderInner::Android(sys::android::AndroidDecoder::new(
android_codec,
config,
width,
height,
)?)
}
#[cfg(target_os = "windows")]
CodecType::H264 | CodecType::H265 => {
let windows_codec = match codec {
CodecType::H264 => sys::windows::CodecType::H264,
CodecType::H265 => sys::windows::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
DecoderInner::Windows(sys::windows::WindowsDecoder::new(
windows_codec,
config,
width,
height,
)?)
}
#[cfg(target_os = "linux")]
CodecType::H264 | CodecType::H265 => {
let linux_codec = match codec {
CodecType::H264 => sys::linux::CodecType::H264,
CodecType::H265 => sys::linux::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
DecoderInner::Linux(sys::linux::LinuxDecoder::new(
linux_codec,
config,
width,
height,
)?)
}
#[cfg(not(any(
target_vendor = "apple",
target_os = "android",
target_os = "windows",
target_os = "linux"
)))]
CodecType::H264 | CodecType::H265 => {
return Err(CodecError::Unsupported(format!(
"{codec:?} hardware decoding not available on this platform"
)));
}
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
CodecType::Av1 => DecoderInner::Av1(software::av1::Av1Decoder::new()?),
#[cfg(not(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
)))]
CodecType::Av1 => {
return Err(CodecError::Unsupported(
"AV1 software decoding not available on this platform".into(),
));
}
};
Ok(Self { inner })
}
}
pub fn decode(&mut self, packet: DecodePacket<'_>) -> DecodeStream {
let result = self.decode_inner(packet);
match result {
Ok(frames) => DecodeStream {
inner: DecodeStreamInner::Frames(frames.into_iter()),
},
Err(e) => DecodeStream {
inner: DecodeStreamInner::Error(Some(e)),
},
}
}
fn decode_inner(&mut self, packet: DecodePacket<'_>) -> Result<Vec<DecodedFrame>, CodecError> {
#[cfg(target_arch = "wasm32")]
let _ = packet;
match &mut self.inner {
#[cfg(target_arch = "wasm32")]
DecoderInner::Unsupported => Err(CodecError::Unsupported(String::from(
"video decoding is not supported by waterkit-codec on WebAssembly",
))),
#[cfg(target_vendor = "apple")]
DecoderInner::Apple(dec) => {
let surfaces = dec.decode_to_iosurface(packet)?;
let mut frames = Vec::with_capacity(surfaces.len());
for surface in surfaces {
let frame = DecodedFrame::from_iosurface(
surface.surface,
surface.pixel_buffer,
surface.width,
surface.height,
surface.timestamp_ns,
surface.layout,
);
frames.push(frame);
}
Ok(frames)
}
#[cfg(target_os = "android")]
DecoderInner::Android(dec) => {
let android_frames = dec.decode(packet)?;
let mut frames = Vec::with_capacity(android_frames.len());
for android_frame in android_frames {
let frame = DecodedFrame::from_biplanar_data(
android_frame.data,
android_frame.width,
android_frame.height,
android_frame.timestamp_ns,
android_frame.layout,
);
frames.push(frame);
}
Ok(frames)
}
#[cfg(target_os = "windows")]
DecoderInner::Windows(dec) => {
let windows_frames = dec.decode(packet)?;
let mut frames = Vec::with_capacity(windows_frames.len());
for windows_frame in windows_frames {
let frame = DecodedFrame::from_biplanar_data(
windows_frame.data,
windows_frame.width,
windows_frame.height,
windows_frame.timestamp_ns,
windows_frame.layout,
);
frames.push(frame);
}
Ok(frames)
}
#[cfg(target_os = "linux")]
DecoderInner::Linux(dec) => {
let linux_frames = dec.decode(packet)?;
let mut frames = Vec::with_capacity(linux_frames.len());
for linux_frame in linux_frames {
let frame = DecodedFrame::from_biplanar_data(
linux_frame.data,
linux_frame.width,
linux_frame.height,
linux_frame.timestamp_ns,
linux_frame.layout,
);
frames.push(frame);
}
Ok(frames)
}
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
DecoderInner::Av1(dec) => {
let cpu_frames = dec.decode(packet)?;
let mut frames = Vec::with_capacity(cpu_frames.len());
for cpu_frame in cpu_frames {
let frame = DecodedFrame::from_biplanar_data(
cpu_frame.data,
cpu_frame.width,
cpu_frame.height,
cpu_frame.timestamp_ns,
cpu_frame.layout,
);
frames.push(frame);
}
Ok(frames)
}
}
}
pub fn drain(&mut self) -> DecodeStream {
match self.drain_inner() {
Ok(frames) => DecodeStream {
inner: DecodeStreamInner::Frames(frames.into_iter()),
},
Err(error) => DecodeStream {
inner: DecodeStreamInner::Error(Some(error)),
},
}
}
fn drain_inner(&mut self) -> Result<Vec<DecodedFrame>, CodecError> {
match &mut self.inner {
#[cfg(target_arch = "wasm32")]
DecoderInner::Unsupported => Err(CodecError::Unsupported(String::from(
"video decoding is not supported by waterkit-codec on WebAssembly",
))),
#[cfg(target_vendor = "apple")]
DecoderInner::Apple(decoder) => Ok(decoder
.drain()?
.into_iter()
.map(|surface| {
DecodedFrame::from_iosurface(
surface.surface,
surface.pixel_buffer,
surface.width,
surface.height,
surface.timestamp_ns,
surface.layout,
)
})
.collect()),
#[cfg(target_os = "android")]
DecoderInner::Android(decoder) => Ok(decoder
.drain()?
.into_iter()
.map(|frame| {
DecodedFrame::from_biplanar_data(
frame.data,
frame.width,
frame.height,
frame.timestamp_ns,
frame.layout,
)
})
.collect()),
#[cfg(target_os = "windows")]
DecoderInner::Windows(decoder) => Ok(decoder
.drain()?
.into_iter()
.map(|frame| {
DecodedFrame::from_biplanar_data(
frame.data,
frame.width,
frame.height,
frame.timestamp_ns,
frame.layout,
)
})
.collect()),
#[cfg(target_os = "linux")]
DecoderInner::Linux(decoder) => Ok(decoder
.drain()?
.into_iter()
.map(|frame| {
DecodedFrame::from_biplanar_data(
frame.data,
frame.width,
frame.height,
frame.timestamp_ns,
frame.layout,
)
})
.collect()),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
DecoderInner::Av1(decoder) => Ok(decoder
.drain()?
.into_iter()
.map(|frame| {
DecodedFrame::from_biplanar_data(
frame.data,
frame.width,
frame.height,
frame.timestamp_ns,
frame.layout,
)
})
.collect()),
}
}
pub fn decode_into(&mut self, packet: DecodePacket<'_>, output: &mut [u8]) -> DecodeIntoStream {
let result = self.decode_into_inner(packet, output);
match result {
Ok(infos) => DecodeIntoStream {
inner: DecodeIntoStreamInner::Infos(infos.into_iter()),
},
Err(e) => DecodeIntoStream {
inner: DecodeIntoStreamInner::Error(Some(e)),
},
}
}
fn decode_into_inner(
&mut self,
packet: DecodePacket<'_>,
output: &mut [u8],
) -> Result<Vec<FrameInfo>, CodecError> {
#[cfg(target_arch = "wasm32")]
let _ = (packet, output);
match &mut self.inner {
#[cfg(target_arch = "wasm32")]
DecoderInner::Unsupported => Err(CodecError::Unsupported(String::from(
"video decoding is not supported by waterkit-codec on WebAssembly",
))),
#[cfg(target_vendor = "apple")]
DecoderInner::Apple(dec) => {
let surfaces = dec.decode_to_iosurface(packet)?;
let mut infos = Vec::with_capacity(surfaces.len());
let mut offset = 0;
for surface in &surfaces {
let width = surface.width;
let height = surface.height;
let y_size = surface.layout.bytes_per_row(width) * height as usize;
let total_bytes = surface.layout.packed_len(width, height);
if offset + total_bytes > output.len() {
return Err(CodecError::DecodingFailed(format!(
"buffer too small: need {total_bytes} more bytes at offset {offset}"
)));
}
let frame = DecodedFrame::from_iosurface(
surface.surface.clone(),
surface.pixel_buffer.clone(),
width,
height,
surface.timestamp_ns,
surface.layout,
);
frame.copy_to_buffer(&mut output[offset..offset + total_bytes]);
infos.push(FrameInfo {
width,
height,
timestamp: std::time::Duration::from_nanos(surface.timestamp_ns),
y_offset: offset,
uv_offset: offset + y_size,
total_bytes,
});
offset += total_bytes;
}
Ok(infos)
}
#[cfg(target_os = "android")]
DecoderInner::Android(dec) => {
let android_frames = dec.decode(packet)?;
copy_frames_to_buffer(
android_frames
.into_iter()
.map(|f| (f.data, f.width, f.height, f.timestamp_ns, f.layout)),
output,
)
}
#[cfg(target_os = "windows")]
DecoderInner::Windows(dec) => {
let windows_frames = dec.decode(packet)?;
copy_frames_to_buffer(
windows_frames
.into_iter()
.map(|f| (f.data, f.width, f.height, f.timestamp_ns, f.layout)),
output,
)
}
#[cfg(target_os = "linux")]
DecoderInner::Linux(dec) => {
let linux_frames = dec.decode(packet)?;
copy_frames_to_buffer(
linux_frames
.into_iter()
.map(|f| (f.data, f.width, f.height, f.timestamp_ns, f.layout)),
output,
)
}
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
DecoderInner::Av1(dec) => {
let cpu_frames = dec.decode(packet)?;
copy_frames_to_buffer(
cpu_frames
.into_iter()
.map(|f| (f.data, f.width, f.height, f.timestamp_ns, f.layout)),
output,
)
}
}
}
}
#[cfg(all(waterkit_software_frames, not(target_arch = "wasm32")))]
fn copy_frames_to_buffer(
frames: impl Iterator<Item = (Vec<u8>, u32, u32, u64, DecodedPixelLayout)>,
output: &mut [u8],
) -> Result<Vec<FrameInfo>, CodecError> {
let mut infos = Vec::new();
let mut offset = 0;
for (data, width, height, timestamp_ns, layout) in frames {
let y_size = layout.bytes_per_row(width) * height as usize;
let total_bytes = data.len();
if offset + total_bytes > output.len() {
return Err(CodecError::DecodingFailed(format!(
"buffer too small: need {total_bytes} more bytes at offset {offset}"
)));
}
output[offset..offset + total_bytes].copy_from_slice(&data);
infos.push(FrameInfo {
width,
height,
timestamp: std::time::Duration::from_nanos(timestamp_ns),
y_offset: offset,
uv_offset: offset + y_size,
total_bytes,
});
offset += total_bytes;
}
Ok(infos)
}
pub struct Encoder {
inner: EncoderInner,
}
enum EncoderInner {
#[cfg(target_arch = "wasm32")]
Unsupported,
#[cfg(target_vendor = "apple")]
Apple(sys::apple::AppleEncoder),
#[cfg(target_os = "android")]
Android(sys::android::AndroidEncoder),
#[cfg(target_os = "windows")]
Windows(sys::windows::WindowsEncoder),
#[cfg(target_os = "linux")]
Linux(sys::linux::LinuxEncoder),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
Av1(Box<software::av1::Av1Encoder>),
}
impl std::fmt::Debug for Encoder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Encoder").finish_non_exhaustive()
}
}
impl Encoder {
pub fn new(codec: CodecType, width: u32, height: u32) -> Result<Self, CodecError> {
#[cfg(target_arch = "wasm32")]
{
let _ = (width, height);
let _unsupported = EncoderInner::Unsupported;
return Err(CodecError::Unsupported(format!(
"{codec:?} encoding is not supported by waterkit-codec on WebAssembly"
)));
}
#[cfg(not(target_arch = "wasm32"))]
{
let inner = match codec {
#[cfg(target_vendor = "apple")]
CodecType::H264 | CodecType::H265 => {
let apple_codec = match codec {
CodecType::H264 => sys::apple::CodecType::H264,
CodecType::H265 => sys::apple::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
EncoderInner::Apple(sys::apple::AppleEncoder::with_size(
apple_codec,
width,
height,
)?)
}
#[cfg(target_os = "android")]
CodecType::H264 | CodecType::H265 => {
let android_codec = match codec {
CodecType::H264 => sys::android::CodecType::H264,
CodecType::H265 => sys::android::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
EncoderInner::Android(sys::android::AndroidEncoder::new(
android_codec,
width,
height,
)?)
}
#[cfg(target_os = "windows")]
CodecType::H264 | CodecType::H265 => {
let windows_codec = match codec {
CodecType::H264 => sys::windows::CodecType::H264,
CodecType::H265 => sys::windows::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
EncoderInner::Windows(sys::windows::WindowsEncoder::new(
windows_codec,
width,
height,
)?)
}
#[cfg(target_os = "linux")]
CodecType::H264 | CodecType::H265 => {
let linux_codec = match codec {
CodecType::H264 => sys::linux::CodecType::H264,
CodecType::H265 => sys::linux::CodecType::H265,
CodecType::Av1 => unreachable!(),
};
EncoderInner::Linux(sys::linux::LinuxEncoder::new(linux_codec, width, height)?)
}
#[cfg(not(any(
target_vendor = "apple",
target_os = "android",
target_os = "windows",
target_os = "linux"
)))]
CodecType::H264 | CodecType::H265 => {
return Err(CodecError::Unsupported(format!(
"{codec:?} hardware encoding not available on this platform"
)));
}
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
CodecType::Av1 => EncoderInner::Av1(Box::new(software::av1::Av1Encoder::new(
width as usize,
height as usize,
)?)),
#[cfg(not(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
)))]
CodecType::Av1 => {
return Err(CodecError::Unsupported(
"AV1 software encoding not available on this platform".into(),
));
}
};
Ok(Self { inner })
}
}
pub fn encode_nv12(&mut self, data: &[u8]) -> EncodeStream {
let result = self.encode_nv12_inner(data);
match result {
Ok(packet) => EncodeStream {
inner: EncodeStreamInner::Packet(Some(packet)),
},
Err(e) => EncodeStream {
inner: EncodeStreamInner::Error(Some(e)),
},
}
}
fn encode_nv12_inner(&mut self, data: &[u8]) -> Result<Vec<u8>, CodecError> {
#[cfg(target_arch = "wasm32")]
let _ = data;
match &mut self.inner {
#[cfg(target_arch = "wasm32")]
EncoderInner::Unsupported => Err(CodecError::Unsupported(String::from(
"video encoding is not supported by waterkit-codec on WebAssembly",
))),
#[cfg(target_vendor = "apple")]
EncoderInner::Apple(enc) => enc.encode_nv12(data),
#[cfg(target_os = "android")]
EncoderInner::Android(enc) => enc.encode_nv12(data),
#[cfg(target_os = "windows")]
EncoderInner::Windows(enc) => enc.encode_nv12(data),
#[cfg(target_os = "linux")]
EncoderInner::Linux(enc) => enc.encode_nv12(data),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
EncoderInner::Av1(enc) => enc.encode_nv12(data),
}
}
#[cfg(target_vendor = "apple")]
pub fn encode_iosurface(&mut self, iosurface_ptr: u64) -> EncodeStream {
let result = self.encode_iosurface_inner(iosurface_ptr);
match result {
Ok(packet) => EncodeStream {
inner: EncodeStreamInner::Packet(Some(packet)),
},
Err(e) => EncodeStream {
inner: EncodeStreamInner::Error(Some(e)),
},
}
}
#[cfg(target_vendor = "apple")]
fn encode_iosurface_inner(&mut self, iosurface_ptr: u64) -> Result<Vec<u8>, CodecError> {
match &mut self.inner {
EncoderInner::Apple(enc) => enc.encode_iosurface(iosurface_ptr),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
EncoderInner::Av1(_) => Err(CodecError::Unsupported(
"IOSurface encoding not supported for AV1".into(),
)),
}
}
#[must_use]
pub fn codec_config(&self) -> Option<Vec<u8>> {
match &self.inner {
#[cfg(target_arch = "wasm32")]
EncoderInner::Unsupported => None,
#[cfg(target_vendor = "apple")]
EncoderInner::Apple(enc) => enc.get_codec_config(),
#[cfg(target_os = "android")]
EncoderInner::Android(enc) => enc.get_codec_config(),
#[cfg(target_os = "windows")]
EncoderInner::Windows(enc) => enc.get_codec_config(),
#[cfg(target_os = "linux")]
EncoderInner::Linux(enc) => enc.get_codec_config(),
#[cfg(all(
feature = "software-fallback",
not(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))
))]
EncoderInner::Av1(_) => None, }
}
}