extern crate ffmpeg_next as ffmpeg;
use std::path::{Path, PathBuf};
use ffmpeg::codec::packet::Packet as AvPacket;
use ffmpeg::ffi::AV_TIME_BASE_Q;
use ffmpeg::format::context::{Input as AvInput, Output as AvOutput};
use ffmpeg::media::Type as AvMediaType;
use ffmpeg::Error as AvError;
use crate::ffi;
use crate::options::Options;
use crate::{Error, Packet, StreamInfo};
type Result<T> = std::result::Result<T, Error>;
pub use url::Url;
pub struct Reader {
pub source: Locator,
pub input: AvInput,
}
impl Reader {
pub fn new(source: &Locator) -> Result<Self> {
let input = ffmpeg::format::input(&source.resolve())?;
Ok(Self {
source: source.clone(),
input,
})
}
pub fn new_with_options(source: &Locator, options: &Options) -> Result<Self> {
let input = ffmpeg::format::input_with_dictionary(&source.resolve(), options.to_dict())?;
Ok(Self {
source: source.clone(),
input,
})
}
pub fn read(&mut self, stream_index: usize) -> Result<Packet> {
let mut error_count = 0;
loop {
match self.input.packets().next() {
Some((stream, packet)) => {
if stream.index() == stream_index {
return Ok(Packet::new(packet, stream.time_base()));
}
}
None => {
error_count += 1;
if error_count > 3 {
return Err(Error::ReadExhausted);
}
}
}
}
}
pub fn stream_info(&self, stream_index: usize) -> Result<StreamInfo> {
StreamInfo::from_reader(self, stream_index)
}
pub fn seek(&mut self, timestamp_milliseconds: i64) -> Result<()> {
const CONVERSION_FACTOR: i64 = (AV_TIME_BASE_Q.den / 1000) as i64;
const LEEWAY: i64 = AV_TIME_BASE_Q.den as i64;
let timestamp = CONVERSION_FACTOR * timestamp_milliseconds;
let range = timestamp - LEEWAY..timestamp + LEEWAY;
self.input
.seek(timestamp, range)
.map_err(Error::BackendError)
}
pub fn seek_to_start(&mut self) -> Result<()> {
self.input
.seek(i64::min_value(), ..)
.map_err(Error::BackendError)
}
pub fn best_video_stream_index(&self) -> Result<usize> {
Ok(self
.input
.streams()
.best(AvMediaType::Video)
.ok_or(AvError::StreamNotFound)?
.index())
}
}
unsafe impl Send for Reader {}
unsafe impl Sync for Reader {}
pub trait Write: private::Write + private::Output {}
pub struct Writer {
pub dest: Locator,
pub(crate) output: AvOutput,
}
impl Writer {
pub fn new(dest: &Locator) -> Result<Self> {
let output = ffmpeg::format::output(&dest.resolve())?;
Ok(Self {
dest: dest.clone(),
output,
})
}
pub fn new_with_format(dest: &Locator, format: &str) -> Result<Self> {
let output = ffmpeg::format::output_as(&dest.resolve(), format)?;
Ok(Self {
dest: dest.clone(),
output,
})
}
pub fn new_with_options(dest: &Locator, options: &Options) -> Result<Self> {
let output = ffmpeg::format::output_with(&dest.resolve(), options.to_dict())?;
Ok(Self {
dest: dest.clone(),
output,
})
}
pub fn new_with_format_and_options(
dest: &Locator,
format: &str,
options: &Options,
) -> Result<Self> {
let output = ffmpeg::format::output_as_with(&dest.resolve(), format, options.to_dict())?;
Ok(Self {
dest: dest.clone(),
output,
})
}
}
impl Write for Writer {}
unsafe impl Send for Writer {}
unsafe impl Sync for Writer {}
pub type Buf = Vec<u8>;
pub type Bufs = Vec<Buf>;
pub struct BufWriter {
pub(crate) output: AvOutput,
options: Options,
}
impl BufWriter {
pub fn new(format: &str) -> Result<Self> {
Self::new_with(format, Default::default())
}
pub fn new_with(format: &str, options: Options) -> Result<Self> {
let output = ffi::output_raw(format)?;
Ok(Self { output, options })
}
fn begin_write(&mut self) {
ffi::output_raw_buf_start(&mut self.output);
}
fn end_write(&mut self) -> Buf {
ffi::output_raw_buf_end(&mut self.output)
}
}
impl Write for BufWriter {}
impl Drop for BufWriter {
fn drop(&mut self) {
let _ = ffi::output_raw_buf_end(&mut self.output);
}
}
unsafe impl Send for BufWriter {}
unsafe impl Sync for BufWriter {}
pub struct PacketizedBufWriter {
pub(crate) output: AvOutput,
options: Options,
buffers: Bufs,
}
impl PacketizedBufWriter {
const PACKET_SIZE: usize = 1024;
pub fn new(format: &str) -> Result<Self> {
Self::new_with(format, Default::default())
}
pub fn new_with(format: &str, options: Options) -> Result<Self> {
let output = ffi::output_raw(format)?;
Ok(Self {
output,
options,
buffers: Vec::new(),
})
}
fn begin_write(&mut self) {
ffi::output_raw_packetized_buf_start(
&mut self.output,
&mut self.buffers,
Self::PACKET_SIZE,
);
}
fn end_write(&mut self) {
ffi::output_raw_packetized_buf_end(&mut self.output);
}
#[inline]
fn take_buffers(&mut self) -> Bufs {
std::mem::take(&mut self.buffers)
}
}
impl Write for PacketizedBufWriter {}
unsafe impl Send for PacketizedBufWriter {}
unsafe impl Sync for PacketizedBufWriter {}
#[derive(Clone)]
pub enum Locator {
Path(PathBuf),
Url(Url),
}
impl Locator {
fn resolve(&self) -> &Path {
match self {
Locator::Path(path) => path.as_path(),
Locator::Url(url) => Path::new(url.as_str()),
}
}
}
impl From<PathBuf> for Locator {
fn from(path: PathBuf) -> Locator {
Locator::Path(path)
}
}
impl From<Url> for Locator {
fn from(url: Url) -> Locator {
Locator::Url(url)
}
}
impl std::fmt::Display for Locator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Locator::Path(ref path) => write!(f, "{}", path.display()),
Locator::Url(ref url) => write!(f, "{url}"),
}
}
}
pub(crate) mod private {
use super::*;
type Result<T> = std::result::Result<T, Error>;
pub trait Write {
type Out;
fn write_header(&mut self) -> Result<Self::Out>;
fn write(&mut self, packet: &mut AvPacket) -> Result<Self::Out>;
fn write_interleaved(&mut self, packet: &mut AvPacket) -> Result<Self::Out>;
fn write_trailer(&mut self) -> Result<Self::Out>;
}
impl Write for Writer {
type Out = ();
fn write_header(&mut self) -> Result<()> {
Ok(self.output.write_header()?)
}
fn write(&mut self, packet: &mut AvPacket) -> Result<()> {
packet.write(&mut self.output)?;
Ok(())
}
fn write_interleaved(&mut self, packet: &mut AvPacket) -> Result<()> {
packet.write_interleaved(&mut self.output)?;
Ok(())
}
fn write_trailer(&mut self) -> Result<()> {
Ok(self.output.write_trailer()?)
}
}
impl Write for BufWriter {
type Out = Buf;
fn write_header(&mut self) -> Result<Buf> {
self.begin_write();
self.output.write_header_with(self.options.to_dict())?;
Ok(self.end_write())
}
fn write(&mut self, packet: &mut AvPacket) -> Result<Buf> {
self.begin_write();
packet.write(&mut self.output)?;
ffi::flush_output(&mut self.output)?;
Ok(self.end_write())
}
fn write_interleaved(&mut self, packet: &mut AvPacket) -> Result<Buf> {
self.begin_write();
packet.write_interleaved(&mut self.output)?;
ffi::flush_output(&mut self.output)?;
Ok(self.end_write())
}
fn write_trailer(&mut self) -> Result<Buf> {
self.begin_write();
self.output.write_trailer()?;
Ok(self.end_write())
}
}
impl Write for PacketizedBufWriter {
type Out = Bufs;
fn write_header(&mut self) -> Result<Bufs> {
self.begin_write();
self.output.write_header_with(self.options.to_dict())?;
self.end_write();
Ok(self.take_buffers())
}
fn write(&mut self, packet: &mut AvPacket) -> Result<Bufs> {
self.begin_write();
packet.write(&mut self.output)?;
ffi::flush_output(&mut self.output)?;
self.end_write();
Ok(self.take_buffers())
}
fn write_interleaved(&mut self, packet: &mut AvPacket) -> Result<Bufs> {
self.begin_write();
packet.write_interleaved(&mut self.output)?;
ffi::flush_output(&mut self.output)?;
self.end_write();
Ok(self.take_buffers())
}
fn write_trailer(&mut self) -> Result<Bufs> {
self.begin_write();
self.output.write_trailer()?;
self.end_write();
Ok(self.take_buffers())
}
}
pub trait Output {
fn output(&self) -> &AvOutput;
fn output_mut(&mut self) -> &mut AvOutput;
}
impl Output for Writer {
fn output(&self) -> &AvOutput {
&self.output
}
fn output_mut(&mut self) -> &mut AvOutput {
&mut self.output
}
}
impl Output for BufWriter {
fn output(&self) -> &AvOutput {
&self.output
}
fn output_mut(&mut self) -> &mut AvOutput {
&mut self.output
}
}
impl Output for PacketizedBufWriter {
fn output(&self) -> &AvOutput {
&self.output
}
fn output_mut(&mut self) -> &mut AvOutput {
&mut self.output
}
}
}