pub mod audio;
pub mod bsf;
pub mod video;
use std::{
ffi::{CStr, CString},
fmt::{self, Display, Formatter},
os::raw::{c_char, c_int, c_void},
ptr, slice,
};
use crate::{
codec::{
audio::{ChannelLayoutRef, SampleFormat},
video::PixelFormat,
},
packet::Packet,
Error,
};
extern "C" {
fn ffw_audio_codec_parameters_new(codec: *const c_char) -> *mut c_void;
fn ffw_video_codec_parameters_new(codec: *const c_char) -> *mut c_void;
fn ffw_subtitle_codec_parameters_new(codec: *const c_char) -> *mut c_void;
fn ffw_codec_parameters_clone(params: *const c_void) -> *mut c_void;
fn ffw_codec_parameters_is_audio_codec(params: *const c_void) -> c_int;
fn ffw_codec_parameters_is_video_codec(params: *const c_void) -> c_int;
fn ffw_codec_parameters_is_subtitle_codec(params: *const c_void) -> c_int;
fn ffw_codec_parameters_get_decoder_name(params: *const c_void) -> *const c_char;
fn ffw_codec_parameters_get_encoder_name(params: *const c_void) -> *const c_char;
fn ffw_codec_parameters_get_bit_rate(params: *const c_void) -> i64;
fn ffw_codec_parameters_get_format(params: *const c_void) -> c_int;
fn ffw_codec_parameters_get_width(params: *const c_void) -> c_int;
fn ffw_codec_parameters_get_height(params: *const c_void) -> c_int;
fn ffw_codec_parameters_get_sample_rate(params: *const c_void) -> c_int;
fn ffw_codec_parameters_get_channel_layout(params: *const c_void) -> *const c_void;
fn ffw_codec_parameters_get_codec_tag(params: *const c_void) -> u32;
fn ffw_codec_parameters_get_extradata(params: *mut c_void) -> *mut c_void;
fn ffw_codec_parameters_get_extradata_size(params: *const c_void) -> c_int;
fn ffw_codec_parameters_set_bit_rate(params: *mut c_void, bit_rate: i64);
fn ffw_codec_parameters_set_format(params: *mut c_void, format: c_int);
fn ffw_codec_parameters_set_width(params: *mut c_void, width: c_int);
fn ffw_codec_parameters_set_height(params: *mut c_void, height: c_int);
fn ffw_codec_parameters_set_sample_rate(params: *mut c_void, rate: c_int);
fn ffw_codec_parameters_set_channel_layout(params: *mut c_void, layout: *const c_void)
-> c_int;
fn ffw_codec_parameters_set_codec_tag(params: *mut c_void, codec_tag: u32);
fn ffw_codec_parameters_set_extradata(
params: *mut c_void,
extradata: *const u8,
size: c_int,
) -> c_int;
fn ffw_codec_parameters_free(params: *mut c_void);
fn ffw_decoder_new(codec: *const c_char) -> *mut c_void;
fn ffw_decoder_from_codec_parameters(params: *const c_void) -> *mut c_void;
fn ffw_decoder_set_extradata(decoder: *mut c_void, extradata: *const u8, size: c_int) -> c_int;
fn ffw_decoder_set_initial_option(
decoder: *mut c_void,
key: *const c_char,
value: *const c_char,
) -> c_int;
fn ffw_decoder_set_pkt_timebase(decoder: *mut c_void, num: c_int, den: c_int);
fn ffw_decoder_open(decoder: *mut c_void) -> c_int;
fn ffw_decoder_push_packet(decoder: *mut c_void, packet: *const c_void) -> c_int;
fn ffw_decoder_take_frame(decoder: *mut c_void, frame: *mut *mut c_void) -> c_int;
fn ffw_decoder_get_codec_parameters(decoder: *const c_void) -> *mut c_void;
fn ffw_decoder_free(decoder: *mut c_void);
fn ffw_encoder_new(codec: *const c_char) -> *mut c_void;
fn ffw_encoder_from_codec_parameters(params: *const c_void) -> *mut c_void;
fn ffw_encoder_get_codec_parameters(encoder: *const c_void) -> *mut c_void;
fn ffw_encoder_get_pixel_format(encoder: *const c_void) -> c_int;
fn ffw_encoder_get_width(encoder: *const c_void) -> c_int;
fn ffw_encoder_get_height(encoder: *const c_void) -> c_int;
fn ffw_encoder_get_sample_format(encoder: *const c_void) -> c_int;
fn ffw_encoder_get_sample_rate(encoder: *const c_void) -> c_int;
fn ffw_encoder_get_channel_layout(encoder: *const c_void) -> *const c_void;
fn ffw_encoder_get_frame_size(encoder: *const c_void) -> c_int;
fn ffw_encoder_set_time_base(encoder: *mut c_void, num: c_int, den: c_int);
fn ffw_encoder_set_bit_rate(encoder: *mut c_void, bit_rate: i64);
fn ffw_encoder_set_pixel_format(encoder: *mut c_void, format: c_int);
fn ffw_encoder_set_width(encoder: *mut c_void, width: c_int);
fn ffw_encoder_set_height(encoder: *mut c_void, height: c_int);
fn ffw_encoder_set_sample_format(encoder: *mut c_void, format: c_int);
fn ffw_encoder_set_sample_rate(encoder: *mut c_void, sample_rate: c_int);
fn ffw_encoder_set_channel_layout(encoder: *mut c_void, layout: *const c_void) -> c_int;
fn ffw_encoder_set_codec_tag(encoder: *mut c_void, codec_tag: u32);
fn ffw_encoder_set_initial_option(
encoder: *mut c_void,
key: *const c_char,
value: *const c_char,
) -> c_int;
fn ffw_encoder_open(encoder: *mut c_void) -> c_int;
fn ffw_encoder_push_frame(encoder: *mut c_void, frame: *const c_void) -> c_int;
fn ffw_encoder_take_packet(encoder: *mut c_void, packet: *mut *mut c_void) -> c_int;
fn ffw_encoder_free(encoder: *mut c_void);
}
#[derive(Debug, Clone)]
enum CodecErrorVariant {
Error(Error),
Again(&'static str),
}
#[derive(Debug, Clone)]
pub struct CodecError {
variant: CodecErrorVariant,
}
impl CodecError {
fn error<T>(msg: T) -> Self
where
T: ToString,
{
Self {
variant: CodecErrorVariant::Error(Error::new(msg)),
}
}
fn from_raw_error_code(code: c_int) -> Self {
Self::from(Error::from_raw_error_code(code))
}
fn again(msg: &'static str) -> Self {
Self {
variant: CodecErrorVariant::Again(msg),
}
}
pub fn is_again(&self) -> bool {
matches!(&self.variant, CodecErrorVariant::Again(_))
}
pub fn into_inner(self) -> Option<Error> {
if let CodecErrorVariant::Error(err) = self.variant {
Some(err)
} else {
None
}
}
pub fn unwrap_inner(self) -> Error {
match self.variant {
CodecErrorVariant::Error(err) => err,
CodecErrorVariant::Again(msg) => panic!("{}", msg),
}
}
}
impl Display for CodecError {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match &self.variant {
CodecErrorVariant::Again(msg) => write!(f, "{}", msg),
CodecErrorVariant::Error(err) => write!(f, "{}", err),
}
}
}
impl std::error::Error for CodecError {}
impl From<Error> for CodecError {
fn from(err: Error) -> Self {
Self {
variant: CodecErrorVariant::Error(err),
}
}
}
struct InnerCodecParameters {
ptr: *mut c_void,
}
impl InnerCodecParameters {
unsafe fn from_raw_ptr(ptr: *mut c_void) -> Self {
Self { ptr }
}
fn as_ptr(&self) -> *const c_void {
self.ptr
}
fn is_audio_codec(&self) -> bool {
unsafe { ffw_codec_parameters_is_audio_codec(self.ptr) != 0 }
}
fn is_video_codec(&self) -> bool {
unsafe { ffw_codec_parameters_is_video_codec(self.ptr) != 0 }
}
fn is_subtitle_codec(&self) -> bool {
unsafe { ffw_codec_parameters_is_subtitle_codec(self.ptr) != 0 }
}
fn decoder_name(&self) -> Option<&'static str> {
unsafe {
let ptr = ffw_codec_parameters_get_decoder_name(self.ptr);
if ptr.is_null() {
None
} else {
let name = CStr::from_ptr(ptr as _);
Some(name.to_str().unwrap())
}
}
}
fn encoder_name(&self) -> Option<&'static str> {
unsafe {
let ptr = ffw_codec_parameters_get_encoder_name(self.ptr);
if ptr.is_null() {
None
} else {
let name = CStr::from_ptr(ptr as _);
Some(name.to_str().unwrap())
}
}
}
pub fn codec_tag(&self) -> CodecTag {
let codec_tag = unsafe { ffw_codec_parameters_get_codec_tag(self.ptr) };
codec_tag.into()
}
}
impl Drop for InnerCodecParameters {
fn drop(&mut self) {
unsafe { ffw_codec_parameters_free(self.ptr) }
}
}
impl Clone for InnerCodecParameters {
fn clone(&self) -> Self {
let ptr = unsafe { ffw_codec_parameters_clone(self.ptr) };
if ptr.is_null() {
panic!("unable to clone codec parameters");
}
Self { ptr }
}
}
unsafe impl Send for InnerCodecParameters {}
unsafe impl Sync for InnerCodecParameters {}
#[derive(Clone)]
enum CodecParametersVariant {
Audio(AudioCodecParameters),
Video(VideoCodecParameters),
Subtitle(SubtitleCodecParameters),
Other(OtherCodecParameters),
}
impl CodecParametersVariant {
unsafe fn from_raw_ptr(ptr: *mut c_void) -> Self {
let inner = InnerCodecParameters::from_raw_ptr(ptr);
if inner.is_audio_codec() {
Self::Audio(AudioCodecParameters::from(inner))
} else if inner.is_video_codec() {
Self::Video(VideoCodecParameters::from(inner))
} else if inner.is_subtitle_codec() {
Self::Subtitle(SubtitleCodecParameters::from(inner))
} else {
Self::Other(OtherCodecParameters::from(inner))
}
}
}
impl AsRef<InnerCodecParameters> for CodecParametersVariant {
fn as_ref(&self) -> &InnerCodecParameters {
match self {
Self::Audio(audio) => audio.as_ref(),
Self::Video(video) => video.as_ref(),
Self::Subtitle(subtitle) => subtitle.as_ref(),
Self::Other(other) => other.as_ref(),
}
}
}
#[derive(Clone)]
pub struct CodecParameters {
inner: CodecParametersVariant,
}
impl CodecParameters {
pub(crate) unsafe fn from_raw_ptr(ptr: *mut c_void) -> Self {
Self {
inner: CodecParametersVariant::from_raw_ptr(ptr),
}
}
pub(crate) fn as_ptr(&self) -> *const c_void {
self.inner.as_ref().as_ptr()
}
pub fn is_audio_codec(&self) -> bool {
self.inner.as_ref().is_audio_codec()
}
pub fn is_video_codec(&self) -> bool {
self.inner.as_ref().is_video_codec()
}
pub fn is_subtitle_codec(&self) -> bool {
self.inner.as_ref().is_subtitle_codec()
}
pub fn decoder_name(&self) -> Option<&'static str> {
self.inner.as_ref().decoder_name()
}
pub fn encoder_name(&self) -> Option<&'static str> {
self.inner.as_ref().encoder_name()
}
pub fn as_audio_codec_parameters(&self) -> Option<&AudioCodecParameters> {
if let CodecParametersVariant::Audio(params) = &self.inner {
Some(params)
} else {
None
}
}
pub fn as_video_codec_parameters(&self) -> Option<&VideoCodecParameters> {
if let CodecParametersVariant::Video(params) = &self.inner {
Some(params)
} else {
None
}
}
pub fn as_subtitle_codec_parameters(&self) -> Option<&SubtitleCodecParameters> {
if let CodecParametersVariant::Subtitle(params) = &self.inner {
Some(params)
} else {
None
}
}
pub fn into_audio_codec_parameters(self) -> Option<AudioCodecParameters> {
if let CodecParametersVariant::Audio(params) = self.inner {
Some(params)
} else {
None
}
}
pub fn into_video_codec_parameters(self) -> Option<VideoCodecParameters> {
if let CodecParametersVariant::Video(params) = self.inner {
Some(params)
} else {
None
}
}
pub fn into_subtitle_codec_parameters(self) -> Option<SubtitleCodecParameters> {
if let CodecParametersVariant::Subtitle(params) = self.inner {
Some(params)
} else {
None
}
}
}
impl From<AudioCodecParameters> for CodecParameters {
fn from(params: AudioCodecParameters) -> Self {
Self {
inner: CodecParametersVariant::Audio(params),
}
}
}
impl From<VideoCodecParameters> for CodecParameters {
fn from(params: VideoCodecParameters) -> Self {
Self {
inner: CodecParametersVariant::Video(params),
}
}
}
impl From<SubtitleCodecParameters> for CodecParameters {
fn from(params: SubtitleCodecParameters) -> Self {
Self {
inner: CodecParametersVariant::Subtitle(params),
}
}
}
pub struct AudioCodecParametersBuilder {
inner: InnerCodecParameters,
}
impl AudioCodecParametersBuilder {
fn new(codec: &str) -> Result<Self, Error> {
let codec = CString::new(codec).expect("invalid codec name");
let ptr = unsafe { ffw_audio_codec_parameters_new(codec.as_ptr() as *const _) };
if ptr.is_null() {
return Err(Error::new("unknown codec"));
}
let params = unsafe { InnerCodecParameters::from_raw_ptr(ptr) };
let res = AudioCodecParametersBuilder { inner: params };
Ok(res)
}
pub fn bit_rate(self, bit_rate: u64) -> Self {
unsafe {
ffw_codec_parameters_set_bit_rate(self.inner.ptr, bit_rate as _);
}
self
}
pub fn sample_format(self, format: SampleFormat) -> Self {
unsafe {
ffw_codec_parameters_set_format(self.inner.ptr, format.into_raw());
}
self
}
pub fn sample_rate(self, rate: u32) -> Self {
assert!(rate > 0);
unsafe {
ffw_codec_parameters_set_sample_rate(self.inner.ptr, rate as _);
}
self
}
pub fn channel_layout(self, layout: &ChannelLayoutRef) -> Self {
let ret =
unsafe { ffw_codec_parameters_set_channel_layout(self.inner.ptr, layout.as_ptr()) };
if ret != 0 {
panic!("unable to copy channel layout");
}
self
}
pub fn codec_tag(self, codec_tag: impl Into<CodecTag>) -> Self {
unsafe {
ffw_codec_parameters_set_codec_tag(self.inner.ptr, codec_tag.into().into());
}
self
}
pub fn extradata<T>(self, data: Option<T>) -> Self
where
T: AsRef<[u8]>,
{
let data = data.as_ref().map(|d| d.as_ref());
let ptr;
let size;
if let Some(data) = data {
ptr = data.as_ptr();
size = data.len();
} else {
ptr = ptr::null();
size = 0;
}
let res = unsafe { ffw_codec_parameters_set_extradata(self.inner.ptr, ptr, size as _) };
if res < 0 {
panic!("unable to allocate extradata");
}
self
}
pub fn build(self) -> AudioCodecParameters {
AudioCodecParameters { inner: self.inner }
}
}
impl From<AudioCodecParameters> for AudioCodecParametersBuilder {
fn from(params: AudioCodecParameters) -> Self {
Self {
inner: params.inner,
}
}
}
#[derive(Clone)]
pub struct AudioCodecParameters {
inner: InnerCodecParameters,
}
impl AudioCodecParameters {
pub fn builder(codec: &str) -> Result<AudioCodecParametersBuilder, Error> {
AudioCodecParametersBuilder::new(codec)
}
pub(crate) fn as_ptr(&self) -> *const c_void {
self.inner.ptr
}
pub fn decoder_name(&self) -> Option<&'static str> {
self.inner.decoder_name()
}
pub fn encoder_name(&self) -> Option<&'static str> {
self.inner.encoder_name()
}
pub fn bit_rate(&self) -> u64 {
unsafe { ffw_codec_parameters_get_bit_rate(self.inner.ptr) as _ }
}
pub fn sample_format(&self) -> SampleFormat {
unsafe { SampleFormat::from_raw(ffw_codec_parameters_get_format(self.inner.ptr)) }
}
pub fn sample_rate(&self) -> u32 {
unsafe { ffw_codec_parameters_get_sample_rate(self.inner.ptr) as _ }
}
pub fn channel_layout(&self) -> &ChannelLayoutRef {
unsafe {
ChannelLayoutRef::from_raw_ptr(ffw_codec_parameters_get_channel_layout(self.inner.ptr))
}
}
pub fn codec_tag(&self) -> CodecTag {
self.inner.codec_tag()
}
pub fn extradata(&self) -> Option<&[u8]> {
unsafe {
let data = ffw_codec_parameters_get_extradata(self.inner.ptr) as *const u8;
let size = ffw_codec_parameters_get_extradata_size(self.inner.ptr) as usize;
if data.is_null() {
None
} else {
Some(slice::from_raw_parts(data, size))
}
}
}
}
impl AsRef<InnerCodecParameters> for AudioCodecParameters {
fn as_ref(&self) -> &InnerCodecParameters {
&self.inner
}
}
impl From<InnerCodecParameters> for AudioCodecParameters {
fn from(params: InnerCodecParameters) -> Self {
Self { inner: params }
}
}
pub struct VideoCodecParametersBuilder {
inner: InnerCodecParameters,
}
impl VideoCodecParametersBuilder {
fn new(codec: &str) -> Result<Self, Error> {
let codec = CString::new(codec).expect("invalid codec name");
let ptr = unsafe { ffw_video_codec_parameters_new(codec.as_ptr() as *const _) };
if ptr.is_null() {
return Err(Error::new("unknown codec"));
}
let params = unsafe { InnerCodecParameters::from_raw_ptr(ptr) };
let res = VideoCodecParametersBuilder { inner: params };
Ok(res)
}
pub fn bit_rate(self, bit_rate: u64) -> Self {
unsafe {
ffw_codec_parameters_set_bit_rate(self.inner.ptr, bit_rate as _);
}
self
}
pub fn pixel_format(self, format: PixelFormat) -> Self {
unsafe {
ffw_codec_parameters_set_format(self.inner.ptr, format.into_raw());
}
self
}
pub fn width(self, width: usize) -> Self {
unsafe {
ffw_codec_parameters_set_width(self.inner.ptr, width as _);
}
self
}
pub fn height(self, height: usize) -> Self {
unsafe {
ffw_codec_parameters_set_height(self.inner.ptr, height as _);
}
self
}
pub fn codec_tag(self, codec_tag: impl Into<CodecTag>) -> Self {
unsafe {
ffw_codec_parameters_set_codec_tag(self.inner.ptr, codec_tag.into().into());
}
self
}
pub fn extradata<T>(self, data: Option<T>) -> Self
where
T: AsRef<[u8]>,
{
let data = data.as_ref().map(|d| d.as_ref());
let ptr;
let size;
if let Some(data) = data {
ptr = data.as_ptr();
size = data.len();
} else {
ptr = ptr::null();
size = 0;
}
let res = unsafe { ffw_codec_parameters_set_extradata(self.inner.ptr, ptr, size as _) };
if res < 0 {
panic!("unable to allocate extradata");
}
self
}
pub fn build(self) -> VideoCodecParameters {
VideoCodecParameters { inner: self.inner }
}
}
impl From<VideoCodecParameters> for VideoCodecParametersBuilder {
fn from(params: VideoCodecParameters) -> VideoCodecParametersBuilder {
VideoCodecParametersBuilder {
inner: params.inner,
}
}
}
#[derive(Clone)]
pub struct VideoCodecParameters {
inner: InnerCodecParameters,
}
impl VideoCodecParameters {
pub fn builder(codec: &str) -> Result<VideoCodecParametersBuilder, Error> {
VideoCodecParametersBuilder::new(codec)
}
pub(crate) fn as_ptr(&self) -> *const c_void {
self.inner.ptr
}
pub fn decoder_name(&self) -> Option<&'static str> {
self.inner.decoder_name()
}
pub fn encoder_name(&self) -> Option<&'static str> {
self.inner.encoder_name()
}
pub fn bit_rate(&self) -> u64 {
unsafe { ffw_codec_parameters_get_bit_rate(self.inner.ptr) as _ }
}
pub fn pixel_format(&self) -> PixelFormat {
unsafe { PixelFormat::from_raw(ffw_codec_parameters_get_format(self.inner.ptr)) }
}
pub fn width(&self) -> usize {
unsafe { ffw_codec_parameters_get_width(self.inner.ptr) as _ }
}
pub fn height(&self) -> usize {
unsafe { ffw_codec_parameters_get_height(self.inner.ptr) as _ }
}
pub fn codec_tag(&self) -> CodecTag {
self.inner.codec_tag()
}
pub fn extradata(&self) -> Option<&[u8]> {
unsafe {
let data = ffw_codec_parameters_get_extradata(self.inner.ptr) as *const u8;
let size = ffw_codec_parameters_get_extradata_size(self.inner.ptr) as usize;
if data.is_null() {
None
} else {
Some(slice::from_raw_parts(data, size))
}
}
}
}
impl AsRef<InnerCodecParameters> for VideoCodecParameters {
fn as_ref(&self) -> &InnerCodecParameters {
&self.inner
}
}
impl From<InnerCodecParameters> for VideoCodecParameters {
fn from(params: InnerCodecParameters) -> Self {
Self { inner: params }
}
}
#[derive(Clone)]
pub struct SubtitleCodecParameters {
inner: InnerCodecParameters,
}
impl SubtitleCodecParameters {
pub fn new(codec: &str) -> Result<Self, Error> {
let codec = CString::new(codec).expect("invalid codec name");
let ptr = unsafe { ffw_subtitle_codec_parameters_new(codec.as_ptr() as *const _) };
if ptr.is_null() {
return Err(Error::new("unknown codec"));
}
let params = unsafe { InnerCodecParameters::from_raw_ptr(ptr) };
let res = SubtitleCodecParameters { inner: params };
Ok(res)
}
pub fn decoder_name(&self) -> Option<&'static str> {
self.inner.decoder_name()
}
pub fn encoder_name(&self) -> Option<&'static str> {
self.inner.encoder_name()
}
}
impl AsRef<InnerCodecParameters> for SubtitleCodecParameters {
fn as_ref(&self) -> &InnerCodecParameters {
&self.inner
}
}
impl From<InnerCodecParameters> for SubtitleCodecParameters {
fn from(params: InnerCodecParameters) -> Self {
Self { inner: params }
}
}
#[derive(Clone)]
struct OtherCodecParameters {
inner: InnerCodecParameters,
}
impl AsRef<InnerCodecParameters> for OtherCodecParameters {
fn as_ref(&self) -> &InnerCodecParameters {
&self.inner
}
}
impl From<InnerCodecParameters> for OtherCodecParameters {
fn from(params: InnerCodecParameters) -> Self {
Self { inner: params }
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct CodecTag(u32);
impl From<u32> for CodecTag {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<CodecTag> for u32 {
fn from(value: CodecTag) -> Self {
value.0
}
}
impl From<&[u8; 4]> for CodecTag {
fn from(value: &[u8; 4]) -> Self {
Self(u32::from_le_bytes(*value))
}
}
pub trait Decoder {
type CodecParameters;
type Frame;
fn codec_parameters(&self) -> Self::CodecParameters;
fn push(&mut self, packet: Packet) -> Result<(), Error> {
self.try_push(packet).map_err(|err| err.unwrap_inner())
}
fn try_push(&mut self, packet: Packet) -> Result<(), CodecError>;
fn flush(&mut self) -> Result<(), Error> {
self.try_flush().map_err(|err| err.unwrap_inner())
}
fn try_flush(&mut self) -> Result<(), CodecError>;
fn take(&mut self) -> Result<Option<Self::Frame>, Error>;
}
pub trait Encoder {
type CodecParameters;
type Frame;
fn codec_parameters(&self) -> Self::CodecParameters;
fn push(&mut self, frame: Self::Frame) -> Result<(), Error> {
self.try_push(frame).map_err(|err| err.unwrap_inner())
}
fn try_push(&mut self, frame: Self::Frame) -> Result<(), CodecError>;
fn flush(&mut self) -> Result<(), Error> {
self.try_flush().map_err(|err| err.unwrap_inner())
}
fn try_flush(&mut self) -> Result<(), CodecError>;
fn take(&mut self) -> Result<Option<Packet>, Error>;
}