extern crate ffmpeg_next as ffmpeg;
use ffmpeg::{
format::flag::Flags as AvFormatFlags,
codec::{
Id as AvCodecId,
Context as AvContext,
codec::Codec as AvCodec,
packet::Packet as AvPacket,
encoder::video::Encoder as AvEncoder,
encoder::video::Video as AvVideo,
flag::Flags as AvCodecFlags,
},
software::scaling::{
context::Context as AvScaler,
flag::Flags as AvScalerFlags,
},
util::{
format::Pixel as AvPixel,
picture::Type as AvFrameType,
mathematics::rescale::TIME_BASE,
error::EAGAIN,
},
Rational as AvRational,
Error as AvError,
};
use super::{
Error,
Locator,
RawFrame,
io::{
Writer,
private::Write,
},
options::Options,
frame::FRAME_PIXEL_FORMAT,
ffi::{
get_encoder_time_base,
codec_context_as,
},
};
#[cfg(feature = "ndarray")]
use super::{
Frame,
Time,
ffi::convert_ndarray_to_frame_rgb24,
};
type Result<T> = std::result::Result<T, Error>;
pub struct Encoder {
writer: Writer,
writer_stream_index: usize,
encoder: AvEncoder,
encoder_time_base: AvRational,
interleaved: bool,
scaler: AvScaler,
scaler_width: u32,
scaler_height: u32,
frame_count: u64,
have_written_header: bool,
have_written_trailer: bool,
}
impl Encoder {
const KEY_FRAME_INTERVAL: u64 = 12;
pub fn new(
dest: &Locator,
settings: Settings,
) -> Result<Self> {
Self::from_writer(
Writer::new(dest)?,
settings,
)
}
pub fn new_with_options(
dest: &Locator,
settings: Settings,
options: &Options,
) -> Result<Self> {
Self::from_writer(
Writer::new_with_options(dest, options)?,
settings,
)
}
pub fn new_with_format(
dest: &Locator,
settings: Settings,
format: &str,
) -> Result<Self> {
Self::from_writer(
Writer::new_with_format(dest, format)?,
settings,
)
}
pub fn new_with_format_and_options(
dest: &Locator,
settings: Settings,
format: &str,
options: &Options,
) -> Result<Self> {
Self::from_writer(
Writer::new_with_format_and_options(dest, format, options)?,
settings,
)
}
pub fn interleaved(mut self) -> Self {
self.interleaved = true;
self
}
#[cfg(feature = "ndarray")]
pub fn encode(
&mut self,
frame: &Frame,
source_timestamp: &Time,
) -> Result<()> {
let (height, width, channels) = frame.dim();
if height != self.scaler_height as usize ||
width != self.scaler_width as usize ||
channels != 3 {
return Err(Error::InvalidFrameFormat);
}
let mut frame = convert_ndarray_to_frame_rgb24(frame)
.map_err(Error::BackendError)?;
frame.set_pts(
source_timestamp
.aligned(self.encoder_time_base)
.into_value());
self.encode_raw(frame)
}
pub fn encode_raw(&mut self, frame: RawFrame) -> Result<()> {
if frame.width() != self.scaler_width ||
frame.height() != self.scaler_height ||
frame.format() != FRAME_PIXEL_FORMAT {
return Err(Error::InvalidFrameFormat);
}
if !self.have_written_header {
let _ = self.writer.write_header()?;
self.have_written_header = true;
}
let mut frame = self.scale(frame)?;
if self.frame_count % Self::KEY_FRAME_INTERVAL == 0 {
frame.set_kind(AvFrameType::I);
}
self
.encoder
.send_frame(&*frame)
.map_err(Error::BackendError)?;
if let Some(packet) = self.encoder_receive_packet()? {
self.write(packet)?;
}
Ok(())
}
pub fn finish(&mut self) -> Result<()> {
if self.have_written_header && !self.have_written_trailer {
self.have_written_trailer = true;
self.flush()?;
self.writer.write_trailer()?;
}
Ok(())
}
fn from_writer(
mut writer: Writer,
settings: Settings,
) -> Result<Self> {
let global_header = writer
.output
.format()
.flags()
.contains(AvFormatFlags::GLOBAL_HEADER);
let mut writer_stream = writer
.output
.add_stream(settings.codec())?;
let writer_stream_index = writer_stream.index();
let mut encoder_context = match settings.codec() {
Some(codec) => codec_context_as(&codec)?,
None => AvContext::new(),
};
if global_header {
encoder_context.set_flags(AvCodecFlags::GLOBAL_HEADER);
}
let mut encoder = encoder_context.encoder().video()?;
settings.apply_to(&mut encoder);
encoder.set_time_base(TIME_BASE);
let encoder = encoder.open_with(settings.options().to_dict())?;
let encoder_time_base = get_encoder_time_base(&encoder);
writer_stream.set_parameters(&encoder);
let scaler_width = encoder.width();
let scaler_height = encoder.height();
let scaler = AvScaler::get(
FRAME_PIXEL_FORMAT,
scaler_width,
scaler_height,
encoder.format(),
scaler_width,
scaler_height,
AvScalerFlags::empty())?;
Ok(Self {
writer,
writer_stream_index,
encoder,
encoder_time_base,
interleaved: false,
scaler,
scaler_width,
scaler_height,
frame_count: 0,
have_written_header: false,
have_written_trailer: false,
})
}
fn scale(&mut self, frame: RawFrame) -> Result<RawFrame> {
let mut frame_scaled = RawFrame::empty();
self
.scaler
.run(&frame, &mut frame_scaled)
.map_err(Error::BackendError)?;
frame_scaled.set_pts(frame.pts());
Ok(frame_scaled)
}
fn encoder_receive_packet(&mut self) -> Result<Option<AvPacket>> {
let mut packet = AvPacket::empty();
let encode_result = self.encoder.receive_packet(&mut packet);
match encode_result {
Ok(())
=> Ok(Some(packet)),
Err(AvError::Other { errno }) if errno == EAGAIN
=> Ok(None),
Err(err)
=> Err(err.into()),
}
}
fn stream_time_base(&mut self) -> AvRational {
self
.writer
.output
.stream(self.writer_stream_index)
.unwrap()
.time_base()
}
fn write(&mut self, mut packet: AvPacket) -> Result<()> {
packet.set_stream(self.writer_stream_index);
packet.set_position(-1);
packet.rescale_ts(self.encoder_time_base, self.stream_time_base());
let _ =
if self.interleaved {
self.writer.write_interleaved(&mut packet)?;
} else {
self.writer.write(&mut packet)?;
};
self.frame_count += 1;
Ok(())
}
fn flush(&mut self) -> Result<()> {
const MAX_DRAIN_ITERATIONS: u32 = 100;
self.encoder.send_eof()?;
for _ in 0..MAX_DRAIN_ITERATIONS {
match self.encoder_receive_packet() {
Ok(Some(packet))
=> self.write(packet)?,
Ok(None)
=> continue,
Err(_)
=> break,
}
}
Ok(())
}
}
impl Drop for Encoder {
fn drop(&mut self) {
let _ = self.finish();
}
}
pub struct Settings<'o> {
width: u32,
height: u32,
pixel_format: AvPixel,
options: Options<'o>,
}
impl<'o> Settings<'o> {
const FRAME_RATE: i32 = 30;
pub fn for_h264_yuv420p(
width: usize,
height: usize,
realtime: bool,
) -> Settings<'o> {
let options = if realtime {
Options::new_h264_realtime()
} else {
Options::new_h264()
};
Self {
width: width as u32,
height: height as u32,
pixel_format: AvPixel::YUV420P,
options,
}
}
fn apply_to(&self, encoder: &mut AvVideo) {
encoder.set_width(self.width);
encoder.set_height(self.height);
encoder.set_format(self.pixel_format);
encoder.set_frame_rate(Some((Self::FRAME_RATE, 1)));
}
fn codec(&self) -> Option<AvCodec> {
Some(ffmpeg::encoder::find_by_name("libx264")
.unwrap_or(ffmpeg::encoder::find(AvCodecId::H264)?))
}
fn options(&self) -> &Options<'o> {
&self.options
}
}
unsafe impl Send for Encoder {}
unsafe impl Sync for Encoder {}