use crate::{ Image, PixelFormat };
use anyhow::Result;
use rsmpeg::avcodec::{ AVCodec, AVCodecContext };
use rsmpeg::avformat::AVFormatContextOutput;
use rsmpeg::avutil::{ AVFrame, ra };
use rsmpeg::error::RsmpegError;
use rsmpeg::ffi;
use rsmpeg::swscale::SwsContext;
const DEFAULT_BIT_RATE: i64 = 400_000;
const DEFAULT_GOP_SIZE: i32 = 10;
const DEFAULT_MAX_B_FRAMES: i32 = 1;
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
pub struct Options {
pub size: (u32, u32),
pub frame_rate: i32,
pub pix_fmt: PixelFormat,
pub bit_rate: Option<i64>,
pub gop_size: Option<i32>,
pub max_b_frames: Option<i32>,
}
pub fn encode(
output_ctx: &mut AVFormatContextOutput,
frames: impl Iterator<Item = impl Image>,
codec: ffi::AVCodecID,
options: Options,
) -> Result<()> {
let encode_codec = AVCodec::find_encoder(codec)
.ok_or(anyhow!("Failed to find encoder"))?;
let mut encode_ctx = AVCodecContext::new(&encode_codec);
encode_ctx.set_width(options.size.0 as i32);
encode_ctx.set_height(options.size.1 as i32);
encode_ctx.set_framerate(ra(options.frame_rate, 1));
encode_ctx.set_time_base(ra(1, options.frame_rate));
encode_ctx.set_pix_fmt(encode_codec.pix_fmts().unwrap()[0]);
encode_ctx.set_bit_rate(options.bit_rate.unwrap_or(DEFAULT_BIT_RATE));
encode_ctx.set_gop_size(options.gop_size.unwrap_or(DEFAULT_GOP_SIZE));
encode_ctx.set_max_b_frames(options.max_b_frames.unwrap_or(DEFAULT_MAX_B_FRAMES));
encode_ctx.open(None)?;
let mut stream = output_ctx.new_stream();
stream.set_codecpar(encode_ctx.extract_codecpar());
stream.set_time_base(encode_ctx.time_base);
drop(stream);
let mut rescale_ctx = SwsContext::get_context(
encode_ctx.width,
encode_ctx.height,
options.pix_fmt.into(),
encode_ctx.width,
encode_ctx.height,
encode_ctx.pix_fmt,
ffi::SWS_FAST_BILINEAR,
).ok_or(anyhow!("Failed to init sws"))?;
output_ctx.write_header()?;
for (i, image) in frames.enumerate() {
let raw_frame = image.to_avframe()
.ok_or(anyhow!("Unable to create frame"))?;
let mut frame = AVFrame::new();
frame.set_format(encode_ctx.pix_fmt);
frame.set_width(encode_ctx.width);
frame.set_height(encode_ctx.height);
frame.alloc_buffer()?;
rescale_ctx.scale_frame(&raw_frame, 0, raw_frame.height, &mut frame)?;
frame.set_pts(i as i64);
encode_video_frame(Some(frame), output_ctx, &mut encode_ctx)?;
}
encode_video_frame(None, output_ctx, &mut encode_ctx)?;
output_ctx.write_trailer()?;
Ok(())
}
fn encode_video_frame(
frame: Option<AVFrame>,
output_ctx: &mut AVFormatContextOutput,
encode_ctx: &mut AVCodecContext,
) -> Result<()> {
encode_ctx.send_frame(frame.as_ref())?;
let streams = output_ctx.streams();
let stream_time_base = streams.get(streams.num() - 1)
.unwrap().time_base;
loop {
let mut packet = match encode_ctx.receive_packet() {
Ok(packet) => packet,
Err(
RsmpegError::EncoderDrainError |
RsmpegError::EncoderFlushedError
) => break,
Err(err) => bail!(err),
};
packet.rescale_ts(
encode_ctx.time_base,
stream_time_base,
);
output_ctx.write_frame(&mut packet)?;
}
Ok(())
}