use std::collections::VecDeque;
use std::sync::Arc;
use orfail::OrFail;
use shiguredo_mp4::{
Uint,
boxes::{SampleEntry, Vp08Box, Vp09Box, VpccBox},
};
use crate::{
encoder::VideoEncoderOptions,
types::CodecName,
video::{self, VideoFormat, VideoFrame},
};
pub const DEFAULT_CQ_LEVEL: &str = "30";
pub const DEFAULT_MIN_Q: &str = "10";
pub const DEFAULT_MAX_Q: &str = "50";
const CHROMA_SUBSAMPLING_I420: Uint<u8, 3, 1> = Uint::new(1); const BIT_DEPTH: Uint<u8, 4, 4> = Uint::new(8);
const LEGAL_RANGE: Uint<u8, 1> = Uint::new(0); const BT_709: u8 = 1;
#[derive(Debug)]
pub struct LibvpxEncoder {
inner: shiguredo_libvpx::Encoder,
format: VideoFormat,
sample_entry: Option<SampleEntry>,
input_queue: VecDeque<Arc<VideoFrame>>,
output_queue: VecDeque<VideoFrame>,
}
impl LibvpxEncoder {
pub fn new_vp8(options: &VideoEncoderOptions) -> orfail::Result<Self> {
let width = options.width.get();
let height = options.height.get();
let config = shiguredo_libvpx::EncoderConfig {
width,
height,
fps_numerator: options.frame_rate.numerator.get(),
fps_denominator: options.frame_rate.denumerator.get(),
target_bitrate: options.bitrate,
..options.encode_params.libvpx_vp8.clone()
};
log::debug!("libvpx vp8 encoder config: {config:?}");
let inner = shiguredo_libvpx::Encoder::new_vp8(&config).or_fail()?;
let sample_entry = vp8_sample_entry(width, height);
Ok(Self {
inner,
format: VideoFormat::Vp8,
sample_entry: Some(sample_entry),
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
})
}
pub fn new_vp9(options: &VideoEncoderOptions) -> orfail::Result<Self> {
let width = options.width.get();
let height = options.height.get();
let config = shiguredo_libvpx::EncoderConfig {
width,
height,
fps_numerator: options.frame_rate.numerator.get(),
fps_denominator: options.frame_rate.denumerator.get(),
target_bitrate: options.bitrate,
..options.encode_params.libvpx_vp9.clone()
};
log::debug!("libvpx vp9 encoder config: {config:?}");
let inner = shiguredo_libvpx::Encoder::new_vp9(&config).or_fail()?;
let sample_entry = vp9_sample_entry(width, height);
Ok(Self {
inner,
format: VideoFormat::Vp9,
sample_entry: Some(sample_entry),
input_queue: VecDeque::new(),
output_queue: VecDeque::new(),
})
}
pub fn codec(&self) -> CodecName {
if self.format == VideoFormat::Vp8 {
CodecName::Vp8
} else {
CodecName::Vp9
}
}
pub fn encode(&mut self, frame: Arc<VideoFrame>) -> orfail::Result<()> {
(frame.format == VideoFormat::I420).or_fail()?;
let (y_plane, u_plane, v_plane) = frame.as_yuv_planes().or_fail()?;
self.inner.encode(y_plane, u_plane, v_plane).or_fail()?;
self.input_queue.push_back(frame);
self.handle_encoded_frames().or_fail()?;
Ok(())
}
pub fn finish(&mut self) -> orfail::Result<()> {
self.inner.finish().or_fail()?;
self.handle_encoded_frames().or_fail()?;
Ok(())
}
fn handle_encoded_frames(&mut self) -> orfail::Result<()> {
while let Some(frame) = self.inner.next_frame() {
let input_frame = self.input_queue.pop_front().or_fail()?;
self.output_queue.push_back(VideoFrame {
source_id: None,
sample_entry: self.sample_entry.take(),
data: frame.data().to_vec(),
format: self.format,
keyframe: frame.is_keyframe(),
width: frame.width() as usize,
height: frame.height() as usize,
timestamp: input_frame.timestamp,
duration: input_frame.duration,
});
}
Ok(())
}
pub fn next_encoded_frame(&mut self) -> Option<VideoFrame> {
self.output_queue.pop_front()
}
}
fn vp8_sample_entry(width: usize, height: usize) -> SampleEntry {
SampleEntry::Vp08(Vp08Box {
visual: video::sample_entry_visual_fields(width, height),
vpcc_box: VpccBox {
bit_depth: BIT_DEPTH,
chroma_subsampling: CHROMA_SUBSAMPLING_I420,
video_full_range_flag: LEGAL_RANGE,
colour_primaries: BT_709,
transfer_characteristics: BT_709,
matrix_coefficients: BT_709,
profile: 0,
level: 0,
codec_initialization_data: Vec::new(),
},
unknown_boxes: Vec::new(),
})
}
fn vp9_sample_entry(width: usize, height: usize) -> SampleEntry {
SampleEntry::Vp09(Vp09Box {
visual: video::sample_entry_visual_fields(width, height),
vpcc_box: VpccBox {
profile: 0, level: 0,
bit_depth: BIT_DEPTH,
chroma_subsampling: CHROMA_SUBSAMPLING_I420,
video_full_range_flag: LEGAL_RANGE,
colour_primaries: BT_709,
transfer_characteristics: BT_709,
matrix_coefficients: BT_709,
codec_initialization_data: Vec::new(),
},
unknown_boxes: Vec::new(),
})
}