extern crate ffmpeg_next as ffmpeg;
use std::collections::HashMap;
use ffmpeg::{
codec::Id as AvCodecId,
Rational as AvRational,
Error as AvError,
};
use super::{
Error,
Locator,
Sps,
Pps,
Packet,
StreamInfo,
};
use super::io::{
Reader,
Write,
Writer,
BufWriter,
PacketizedBufWriter,
};
use super::extradata::extract_parameter_sets_h264;
use super::ffi::extradata;
use super::options::Options;
type Result<T> = std::result::Result<T, Error>;
pub struct Muxer<W: Write> {
pub(crate) writer: W,
mapping: HashMap<usize, StreamDescription>,
interleaved: bool,
have_written_header: bool,
}
pub type FileMuxer = Muxer<Writer>;
pub type BufMuxer = Muxer<BufWriter>;
pub type PacketizedBufMuxer = Muxer<PacketizedBufWriter>;
impl Muxer<Writer> {
pub fn new_to_file(dest: &Locator) -> Result<Self> {
Self::new(Writer::new(dest)?)
}
pub fn new_to_file_with_format(
dest: &Locator,
format: &str,
) -> Result<Self> {
Self::new(Writer::new_with_format(dest, format)?)
}
}
impl Muxer<PacketizedBufWriter> {
pub fn new_to_packetized_buf(
format: &str,
) -> Result<Self> {
Self::new(PacketizedBufWriter::new(format)?)
}
pub fn new_to_packetized_buf_with_options(
format: &str,
options: Options<'static>,
) -> Result<Self> {
Self::new(PacketizedBufWriter::new_with(format, options)?)
}
}
impl Muxer<BufWriter> {
pub fn new_to_buf(
format: &str,
) -> Result<Self> {
Self::new(BufWriter::new(format)?)
}
pub fn new_to_buf_with_options(
format: &str,
options: Options<'static>,
) -> Result<Self> {
Self::new(BufWriter::new_with(format, options)?)
}
}
impl<W: Write> Muxer<W> {
fn new(writer: W) -> Result<Self> {
Ok(Self {
writer,
mapping: HashMap::new(),
interleaved: false,
have_written_header: false,
})
}
pub fn interleaved(mut self) -> Self {
self.interleaved = true;
self
}
pub fn with_stream(
mut self,
stream_info: StreamInfo,
) -> Result<Self> {
let (index, codec_parameters, reader_stream_time_base) =
stream_info.into_parts();
let mut writer_stream = self
.writer
.output_mut()
.add_stream(ffmpeg::encoder::find(codec_parameters.id()))?;
writer_stream.set_parameters(codec_parameters);
let stream_description = StreamDescription {
index: writer_stream.index(),
source_time_base: reader_stream_time_base,
};
self
.mapping
.insert(index, stream_description);
Ok(self)
}
pub fn with_streams(
mut self,
reader: &Reader,
) -> Result<Self> {
for stream in reader.input.streams() {
self = self.with_stream(reader.stream_info(stream.index())?)?;
}
Ok(self)
}
pub fn mux(
&mut self,
packet: Packet,
) -> Result<W::Out> {
if self.have_written_header {
let mut packet = packet.into_inner();
let stream_description = self
.mapping
.get(&packet.stream())
.ok_or_else(|| AvError::StreamNotFound)?;
let destination_stream = self
.writer
.output()
.stream(stream_description.index)
.ok_or_else(|| AvError::StreamNotFound)?;
packet.set_stream(destination_stream.index());
packet.set_position(-1);
packet.rescale_ts(
stream_description.source_time_base,
destination_stream.time_base(),
);
Ok({
if self.interleaved {
self.writer.write_interleaved(&mut packet)?
} else {
self.writer.write(&mut packet)?
}
})
} else {
self.have_written_header = true;
self.writer.write_header()?;
self.mux(packet)
}
}
pub fn parameter_sets_h264<'param>(
&'param self,
) -> Vec<Result<(Sps<'param>, Pps<'param>)>> {
self
.writer
.output()
.streams()
.map(|stream| {
if stream.codec().id() == AvCodecId::H264 {
extract_parameter_sets_h264(
extradata(
&self.writer.output(),
stream.index()
)?
)
} else {
Err(Error::UnsupportedCodecParameterSets)
}
})
.collect::<Vec<_>>()
}
pub fn finish(&mut self) -> Result<W::Out> {
self.writer.write_trailer()
}
}
struct StreamDescription {
index: usize,
source_time_base: AvRational,
}