You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
9.1 KiB
Rust

use crate::av::as_ptr::{AsMutPtr, AsMutVoidPtr, AsPtr, AsVoidPtr};
use crate::av::avio::{AVIOWriter, AVIO};
use crate::av::dictionary::Dictionary;
use crate::av::options::Options;
use crate::av::packet::Packet;
use crate::av::stream::Stream;
use crate::av::{decoder_codec_from_name, verify_response, Rational};
use ffmpeg_sys_next::{
av_find_best_stream, av_interleaved_write_frame, av_read_frame, av_seek_frame, av_write_frame,
av_write_trailer, avformat_alloc_context, avformat_alloc_output_context2,
avformat_find_stream_info, avformat_free_context, avformat_init_output, avformat_new_stream,
avformat_open_input, avformat_write_header, avio_context_free, avio_open2, AVFormatContext,
AVMediaType, AVIO_FLAG_WRITE, AVSEEK_FLAG_BACKWARD, AV_TIME_BASE,
};
use std::ffi::CString;
use std::fmt::{Debug, Error, Formatter};
use std::os::raw::{c_int, c_void};
use std::ptr::{null, null_mut};
pub struct Format<T> {
pub ctx: *mut AVFormatContext,
pub avio: Option<AVIO<T>>,
}
impl<T> Debug for Format<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_struct("Format<_>").field("ctx", &self.ctx).finish()
}
}
impl<T> Format<T> {
pub fn flags(&self) -> c_int {
unsafe { (*self.as_ptr()).flags }
}
pub fn set_flags(&self, flags: c_int) {
unsafe { (*self.as_mut_ptr()).flags = flags }
}
}
impl<T> Format<T> {
pub fn seek(&self, seconds: u32, stream: Option<&Stream>) -> Result<(), String> {
let seconds: i32 = seconds as i32;
let time_base = stream.map(|s| s.time_base().den()).unwrap_or(AV_TIME_BASE);
verify_response("Failed to seek", unsafe {
av_seek_frame(
self.ctx,
stream.map(|s| s.index()).unwrap_or(-1),
(seconds * time_base) as i64,
AVSEEK_FLAG_BACKWARD,
)
})?;
Ok(())
}
pub fn stream(
&self,
stream_type: AVMediaType,
index: Option<i32>,
related: Option<i32>,
) -> Result<Option<Stream>, String> {
unsafe {
let ctx = self.ctx;
let mut stream = (*ctx).streams;
let index = if let Some(index) = index {
index
} else {
verify_response(
"Failed finding best stream",
av_find_best_stream(ctx, stream_type, -1, related.unwrap_or(-1), null_mut(), 0),
)?
};
for _ in 0..(*ctx).nb_streams {
let curr_stream = *stream;
if (*(*curr_stream).codecpar).codec_type == stream_type {
if (*curr_stream).index == index {
return Ok(Some(Stream(curr_stream)));
}
}
stream = stream.add(1)
}
}
Ok(None)
}
pub fn as_mut_ptr(&self) -> *mut AVFormatContext {
self.ctx
}
pub fn next_packet(&self) -> Option<Packet> {
let packet = Packet::alloc();
return if unsafe { av_read_frame(self.as_mut_ptr(), packet.as_mut_ptr()) } >= 0 {
Some(packet)
} else {
None
};
}
pub fn duration(&self) -> i64 {
unsafe { (*self.ctx).duration }
}
pub fn write_header(&self, options: Option<Dictionary>) -> Result<(), String> {
verify_response("failed to write header to output", unsafe {
avformat_write_header(
self.as_mut_ptr(),
if let Some(mut options) = options {
&mut options.as_mut_ptr()
} else {
null_mut()
},
)
})?;
Ok(())
}
pub fn write_trailer(&self) -> Result<(), String> {
verify_response("failed to write trailer to output", unsafe {
av_write_trailer(self.as_mut_ptr())
})?;
Ok(())
}
pub fn write_packet_null(&self) -> Result<(), String> {
verify_response("failed to write to output", unsafe {
av_write_frame(self.as_mut_ptr(), null_mut())
})?;
Ok(())
}
pub fn write_packet_interleaved(&self, packet: &Packet) -> Result<(), String> {
verify_response("failed to write interleaved to output", unsafe {
av_interleaved_write_frame(self.as_mut_ptr(), packet.as_mut_ptr())
})?;
Ok(())
}
pub fn write_packet(&self, packet: &Packet) -> Result<(), String> {
verify_response("failed to write to output", unsafe {
av_write_frame(self.as_mut_ptr(), packet.as_mut_ptr())
})?;
Ok(())
}
pub fn new_stream(&self, codec: &str) -> Result<Stream, String> {
let codec =
decoder_codec_from_name(codec).ok_or(format!("No codec found with name {}", codec))?;
let stream = unsafe { avformat_new_stream(self.ctx, codec) };
Ok(Stream(stream))
}
pub fn avio_inner(&self) -> Option<&T> {
self.avio.as_ref().map(|x| x.inner())
}
pub fn avio_inner_mut(&mut self) -> Option<&mut T> {
self.avio.as_mut().map(|x| x.inner_mut())
}
pub fn init_output(&self, mut options: Dictionary) -> Result<(), String> {
verify_response("Failed to init output", unsafe {
avformat_init_output(self.as_mut_ptr(), &mut options.as_mut_ptr())
})?;
Ok(())
}
}
impl<T: AVIOWriter> Format<T> {
pub fn output_avio<'a>(avio: AVIO<T>, format: &str) -> Result<Format<T>, String> {
let mut context = unsafe { avformat_alloc_context() };
let format_c = CString::new(format).unwrap();
verify_response("Failed to open output with AVIO", unsafe {
avformat_alloc_output_context2(&mut context, null_mut(), format_c.as_ptr(), null_mut())
})?;
unsafe {
(*context).pb = avio.as_mut_ptr();
}
Ok(Format {
ctx: context,
avio: Some(avio),
})
}
}
type FS = ();
impl Format<FS> {
pub fn output(
path: &str,
format: Option<&str>,
options: Option<Dictionary>,
) -> Result<Format<FS>, String> {
let mut context = unsafe { avformat_alloc_context() };
let path_c = CString::new(path).unwrap();
let format_c = if let Some(fmt) = format {
CString::new(fmt).unwrap()
} else {
CString::default()
};
let resp = unsafe {
avformat_alloc_output_context2(
&mut context,
null_mut(),
if format.is_none() {
null()
} else {
format_c.as_ptr()
},
path_c.as_ptr(),
)
};
if resp < 0 {
return Err(format!(
"Failed to open output (path: {}, format: {:?})",
path, format
));
}
verify_response("Failed to open output", unsafe {
avio_open2(
&mut (*context).pb,
path_c.as_ptr(),
AVIO_FLAG_WRITE,
null(),
if let Some(mut options) = options {
options.disown();
&mut options.as_mut_ptr()
} else {
null_mut()
},
)
})?;
Ok(Format {
ctx: context,
avio: None,
})
}
pub fn open(path: &str, options: Option<Dictionary>) -> Result<Format<FS>, String> {
unsafe {
let mut context = avformat_alloc_context();
let c_path = CString::new(path).unwrap();
verify_response(
"failed to open file",
avformat_open_input(
&mut context,
c_path.as_ptr(),
null_mut(),
if let Some(mut options) = options {
&mut options.as_mut_ptr()
} else {
null_mut()
},
),
)?;
verify_response(
"failed to find stream info",
avformat_find_stream_info(context, null_mut()),
)?;
Ok(Format {
ctx: context,
avio: None,
})
}
}
}
impl<T> AsPtr<AVFormatContext> for Format<T> {
#[inline]
fn as_ptr(&self) -> *const AVFormatContext {
self.ctx
}
}
impl<T> AsMutPtr<AVFormatContext> for Format<T> {
#[inline]
fn as_mut_ptr(&self) -> *mut AVFormatContext {
self.ctx
}
}
impl<T> AsVoidPtr for Format<T> {
#[inline]
fn as_void_ptr(&self) -> *const c_void {
self.as_ptr().cast()
}
}
impl<T> AsMutVoidPtr for Format<T> {
#[inline]
fn as_mut_void_ptr(&self) -> *mut c_void {
self.as_mut_ptr().cast()
}
}
impl<T> Options for Format<T> {}
impl<T> Drop for Format<T> {
fn drop(&mut self) {
unsafe {
let mut pb = (*self.ctx).pb;
avformat_free_context(self.ctx);
if self.avio.is_none() {
avio_context_free(&mut pb);
}
}
}
}