extern crate ffmpeg_next as ffmpeg;
use std::path::{PathBuf, Path};
use std::mem;
use ffmpeg::{
ffi::AV_TIME_BASE_Q,
format::context::Input as AvInput,
format::context::Output as AvOutput,
codec::packet::Packet as AvPacket,
media::Type as AvMediaType,
Error as AvError,
};
use super::StreamInfo;
use super::Packet;
use super::Error;
use super::options::Options;
use super::ffi;
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<'static>,
}
impl BufWriter {
pub fn new(format: &str) -> Result<Self> {
Self::new_with(format, Default::default())
}
pub fn new_with(format: &str, options: Options<'static>) -> 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<'static>,
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<'static>) -> 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 {
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::{
AvOutput,
AvPacket,
Writer,
BufWriter,
PacketizedBufWriter,
Buf,
Bufs,
Error,
ffi,
};
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
}
}
}