use core::ptr;
use std::ffi::CString;
use std::path::PathBuf;
use ffmpeg_sys_next::{
av_dump_format, av_write_trailer, avformat_alloc_output_context2, avformat_free_context,
avformat_write_header, avio_open, AVFormatContext,
};
use super::WrapperType;
use crate::error::VideoEncoderError;
use crate::status::FfmpegSysStatus;
use crate::util::path_to_cstring;
pub struct FormatContext {
out_path: CString,
raw: *mut AVFormatContext,
}
impl FormatContext {
pub fn new(out_path: PathBuf) -> Result<Self, VideoEncoderError> {
let path_str = path_to_cstring(&out_path)?;
let mut fmt = ptr::null_mut();
let err = unsafe {
avformat_alloc_output_context2(
&mut fmt,
ptr::null_mut(),
ptr::null(),
path_str.as_ptr(),
)
};
let status = FfmpegSysStatus::from_ffmpeg_sys_error(err);
if status.is_error() {
let err = VideoEncoderError::FfmpegSysError {
message: "Failed to set codec parameters.",
error_code: status,
};
println!("{err:?}");
}
if fmt.is_null() {
let mpeg = CString::new(&b"mpeg"[..]).unwrap();
let err = unsafe {
avformat_alloc_output_context2(
&mut fmt,
ptr::null_mut(),
mpeg.as_ptr(),
path_str.as_ptr(),
)
};
let status = FfmpegSysStatus::from_ffmpeg_sys_error(err);
if status.is_error() {
let err = VideoEncoderError::FfmpegSysError {
message: "Failed to set codec parameters.",
error_code: status,
};
println!("{err:?}");
}
}
if fmt.is_null() {
return Err(VideoEncoderError::FfmpegSysError {
message: "Unable to create the output context.",
error_code: FfmpegSysStatus::Unknown,
});
}
Ok(FormatContext {
out_path: path_str,
raw: fmt,
})
}
pub fn print_format(&self) {
unsafe {
av_dump_format(
self.get_inner() as *mut AVFormatContext,
0,
self.out_path.as_ptr(),
1,
)
}
}
pub fn open_output_file(&mut self) -> Result<(), VideoEncoderError> {
let err = unsafe { avio_open(&mut (*self.get_inner_mut()).pb, self.out_path.as_ptr(), 2) };
let status = FfmpegSysStatus::from_ffmpeg_sys_error(err);
if status.is_error() {
return Err(VideoEncoderError::FfmpegSysError {
message: "Failed to open the output media file.",
error_code: status,
});
}
let err = unsafe { avformat_write_header(self.get_inner_mut(), ptr::null_mut()) };
let status = FfmpegSysStatus::from_ffmpeg_sys_error(err);
if status.is_error() {
return Err(VideoEncoderError::FfmpegSysError {
message: "Failed to write the stream header to the output media file.",
error_code: status,
});
}
Ok(())
}
pub fn write_trailer(&mut self) -> Result<(), VideoEncoderError> {
let err = unsafe { av_write_trailer(self.get_inner_mut()) };
let status = FfmpegSysStatus::from_ffmpeg_sys_error(err);
if status.is_error() {
return Err(VideoEncoderError::FfmpegSysError {
message: "Error writing trailer.",
error_code: status,
});
}
Ok(())
}
}
impl WrapperType for FormatContext {
type OUT = AVFormatContext;
fn get_inner(&self) -> *const Self::OUT {
self.raw
}
fn get_inner_mut(&mut self) -> *mut Self::OUT {
self.raw
}
}
impl Drop for FormatContext {
fn drop(&mut self) {
unsafe {
if ffmpeg_sys_next::avio_closep(&mut (*self.raw).pb) < 0 {
println!("Warning: failed closing output file");
}
avformat_free_context(self.get_inner_mut());
}
}
}