mod bitstream;
mod header;
mod motion;
mod quantization;
mod vlc;
pub use header::{AdvancedModes, PictureFormat};
use crate::{
traits::{DecoderConfig, EncodedPacket, EncoderConfig, VideoDecoder, VideoEncoder},
CodecError, CodecResult, FrameType, Plane, VideoFrame,
};
use bitstream::{BitReader, BitWriter};
use header::{GobHeader, MacroblockHeader, MacroblockType, PictureCodingType, PictureHeader};
use motion::{LoopFilter, MotionEstimator, MotionVector, MotionVectorPredictor, SearchAlgorithm};
use oximedia_core::{CodecId, PixelFormat};
use quantization::{dequantize_block, quantize_block};
use vlc::{
decode_cbpy, decode_mcbpc_i, decode_mcbpc_p, decode_mvd, encode_cbpy, encode_mcbpc_i,
encode_mcbpc_p, encode_mvd,
};
pub struct H263Decoder {
config: DecoderConfig,
width: Option<u32>,
height: Option<u32>,
format: Option<PictureFormat>,
reference_frame: Option<VideoFrame>,
pending_frames: Vec<VideoFrame>,
quantizer: u8,
loop_filter_enabled: bool,
advanced_modes: AdvancedModes,
}
impl H263Decoder {
pub fn new(config: DecoderConfig) -> CodecResult<Self> {
Ok(Self {
config,
width: None,
height: None,
format: None,
reference_frame: None,
pending_frames: Vec::new(),
quantizer: quantization::DEFAULT_QP,
loop_filter_enabled: false,
advanced_modes: AdvancedModes::default(),
})
}
fn decode_picture(&mut self, data: &[u8], pts: i64) -> CodecResult<VideoFrame> {
let mut reader = BitReader::new(data);
let header = PictureHeader::parse(&mut reader)?;
let (width, height) = header.dimensions()?;
self.width = Some(width);
self.height = Some(height);
self.format = Some(header.source_format);
self.quantizer = header.quantizer;
self.advanced_modes.umv = header.umv_mode;
self.advanced_modes.sac = header.sac_mode;
self.advanced_modes.ap = header.ap_mode;
self.advanced_modes.pb_frames = header.pb_frames_mode;
let y_stride = ((width + 15) / 16) * 16; let c_stride = ((width / 2 + 15) / 16) * 16;
let y_size = (y_stride * height) as usize;
let c_size = (c_stride * (height / 2)) as usize;
let mut y_plane = vec![0u8; y_size];
let mut cb_plane = vec![0u8; c_size];
let mut cr_plane = vec![0u8; c_size];
let mb_width = ((width + 15) / 16) as usize;
let mb_height = ((height + 15) / 16) as usize;
let mut mv_predictor = MotionVectorPredictor::new(mb_width);
for mb_y in 0..mb_height {
for mb_x in 0..mb_width {
self.decode_macroblock(
&mut reader,
&header,
&mut y_plane,
&mut cb_plane,
&mut cr_plane,
y_stride as usize,
c_stride as usize,
&mut mv_predictor,
mb_x,
mb_y,
)?;
}
mv_predictor.next_row();
}
if self.loop_filter_enabled {
let filter = LoopFilter::new(self.quantizer / 2);
for mb_y in 0..mb_height {
for mb_x in 0..mb_width {
filter.filter_mb_edge(&mut y_plane, y_stride as usize, mb_x, mb_y, true);
filter.filter_mb_edge(&mut y_plane, y_stride as usize, mb_x, mb_y, false);
}
}
}
let mut frame = VideoFrame::new(PixelFormat::Yuv420p, width, height);
frame.timestamp = oximedia_core::Timestamp::new(pts, oximedia_core::Rational::new(1, 1000));
frame.frame_type = if header.is_intra() {
FrameType::Key
} else {
FrameType::Inter
};
frame.planes.push(Plane::new(y_plane, y_stride as usize));
frame.planes.push(Plane::new(cb_plane, c_stride as usize));
frame.planes.push(Plane::new(cr_plane, c_stride as usize));
if !header.is_intra() {
self.reference_frame = Some(frame.clone());
} else {
self.reference_frame = Some(frame.clone());
}
Ok(frame)
}
#[allow(clippy::too_many_arguments)]
fn decode_macroblock(
&mut self,
reader: &mut BitReader<'_>,
header: &PictureHeader,
y_plane: &mut [u8],
cb_plane: &mut [u8],
cr_plane: &mut [u8],
y_stride: usize,
c_stride: usize,
mv_predictor: &mut MotionVectorPredictor,
mb_x: usize,
mb_y: usize,
) -> CodecResult<()> {
if header.is_inter() {
let cod = reader.read_bit()?;
if !cod {
return Ok(());
}
}
let (mb_type_code, cbpc) = if header.is_intra() {
self.decode_mcbpc_i(reader)?
} else {
self.decode_mcbpc_p(reader)?
};
let mb_type = match mb_type_code {
0 => MacroblockType::Inter,
1 => MacroblockType::InterQ,
2 => MacroblockType::Inter4V,
3 => MacroblockType::Intra,
4 => MacroblockType::IntraQ,
_ => return Err(CodecError::InvalidData("Invalid MB type".into())),
};
let cbpy = self.decode_cbpy(reader, mb_type.is_intra())?;
let mut current_quant = self.quantizer;
if mb_type.has_quant() {
let dquant = reader.read_signed_bits(2)?;
current_quant = ((current_quant as i32) + dquant).clamp(1, 31) as u8;
self.quantizer = current_quant;
}
if mb_type.is_inter() {
let mv = self.decode_motion_vector(reader, mv_predictor, mb_x, mb_y)?;
mv_predictor.update(mb_x, mv);
}
let mb_header = MacroblockHeader::new(mb_type);
for block_idx in 0..6 {
if mb_header.is_block_coded(block_idx) {
let block =
self.decode_block(reader, current_quant, block_idx == 0 && mb_type.is_intra())?;
let idct_block = idct_8x8(&block);
self.write_block_to_planes(
&idct_block,
y_plane,
cb_plane,
cr_plane,
y_stride,
c_stride,
mb_x,
mb_y,
block_idx,
);
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn write_block_to_planes(
&self,
block: &[[i16; 8]; 8],
y_plane: &mut [u8],
cb_plane: &mut [u8],
cr_plane: &mut [u8],
y_stride: usize,
c_stride: usize,
mb_x: usize,
mb_y: usize,
block_idx: usize,
) {
let (plane, stride, bx, by) = if block_idx < 4 {
let bx = (block_idx % 2) * 8;
let by = (block_idx / 2) * 8;
(y_plane, y_stride, mb_x * 16 + bx, mb_y * 16 + by)
} else if block_idx == 4 {
(cb_plane, c_stride, mb_x * 8, mb_y * 8)
} else {
(cr_plane, c_stride, mb_x * 8, mb_y * 8)
};
for y in 0..8 {
for x in 0..8 {
let offset = (by + y) * stride + (bx + x);
if offset < plane.len() {
plane[offset] = block[y][x].clamp(0, 255) as u8;
}
}
}
}
fn decode_mcbpc_i(&self, reader: &mut BitReader<'_>) -> CodecResult<(u8, u8)> {
for bits in 1..=8 {
if let Ok(code) = reader.peek_bits(bits) {
if let Some((mb_type, cbpc)) = decode_mcbpc_i(code, bits) {
reader.skip_bits(bits as usize)?;
return Ok((mb_type, cbpc));
}
}
}
Err(CodecError::InvalidData("Invalid MCBPC code".into()))
}
fn decode_mcbpc_p(&self, reader: &mut BitReader<'_>) -> CodecResult<(u8, u8)> {
for bits in 1..=11 {
if let Ok(code) = reader.peek_bits(bits) {
if let Some((mb_type, cbpc)) = decode_mcbpc_p(code, bits) {
reader.skip_bits(bits as usize)?;
return Ok((mb_type, cbpc));
}
}
}
Err(CodecError::InvalidData("Invalid MCBPC code".into()))
}
fn decode_cbpy(&self, reader: &mut BitReader<'_>, intra: bool) -> CodecResult<u8> {
for bits in 4..=5 {
if let Ok(code) = reader.peek_bits(bits) {
if let Some(cbpy) = decode_cbpy(code, bits, intra) {
reader.skip_bits(bits as usize)?;
return Ok(cbpy);
}
}
}
Err(CodecError::InvalidData("Invalid CBPY code".into()))
}
fn decode_motion_vector(
&self,
reader: &mut BitReader<'_>,
mv_predictor: &MotionVectorPredictor,
mb_x: usize,
mb_y: usize,
) -> CodecResult<MotionVector> {
let pred_mv = mv_predictor.predict(mb_x, mb_y);
let mvd_x = self.decode_mvd(reader)?;
let mvd_y = self.decode_mvd(reader)?;
let mv = MotionVector::new(pred_mv.x + mvd_x, pred_mv.y + mvd_y);
Ok(mv)
}
fn decode_mvd(&self, reader: &mut BitReader<'_>) -> CodecResult<i16> {
for bits in 1..=13 {
if let Ok(code) = reader.peek_bits(bits) {
if let Some(mvd) = decode_mvd(code, bits) {
reader.skip_bits(bits as usize)?;
return Ok(mvd as i16);
}
}
}
Err(CodecError::InvalidData("Invalid MVD code".into()))
}
fn decode_block(
&self,
reader: &mut BitReader<'_>,
qp: u8,
is_dc_intra: bool,
) -> CodecResult<[[i16; 8]; 8]> {
let mut block = [[0i16; 8]; 8];
if is_dc_intra {
let dc_size = self.decode_intra_dc_size(reader)?;
if dc_size > 0 {
let dc_diff = reader.read_signed_bits(dc_size)?;
block[0][0] = dc_diff as i16;
}
}
let mut coeff_idx = if is_dc_intra { 1 } else { 0 };
loop {
let (run, level, last) = self.decode_tcoef(reader)?;
coeff_idx += run as usize;
if coeff_idx >= 64 {
break;
}
let row = coeff_idx / 8;
let col = coeff_idx % 8;
block[row][col] = level;
coeff_idx += 1;
if last || coeff_idx >= 64 {
break;
}
}
let dequantized = dequantize_block(&block, qp);
Ok(dequantized)
}
fn decode_intra_dc_size(&self, reader: &mut BitReader<'_>) -> CodecResult<u8> {
let size = reader.read_bits(3)?;
Ok(size as u8)
}
fn decode_tcoef(&self, reader: &mut BitReader<'_>) -> CodecResult<(u8, i16, bool)> {
let escape = reader.peek_bits(7)?;
if escape == 0b0000011 {
reader.skip_bits(7)?;
let last = reader.read_bit()?;
let run = reader.read_bits(6)? as u8;
let level = reader.read_signed_bits(8)? as i16;
return Ok((run, level, last));
}
for bits in 2..=13 {
if let Ok(code) = reader.peek_bits(bits) {
if let Some(entry) = vlc::find_tcoef_entry(code, bits) {
reader.skip_bits(bits as usize)?;
let sign = reader.read_bit()?;
let level = if sign { -entry.level } else { entry.level };
return Ok((entry.run, level, entry.last));
}
}
}
Err(CodecError::InvalidData("Invalid TCOEF code".into()))
}
}
impl VideoDecoder for H263Decoder {
fn codec(&self) -> CodecId {
CodecId::H263
}
fn send_packet(&mut self, data: &[u8], pts: i64) -> CodecResult<()> {
let frame = self.decode_picture(data, pts)?;
self.pending_frames.push(frame);
Ok(())
}
fn receive_frame(&mut self) -> CodecResult<Option<VideoFrame>> {
if self.pending_frames.is_empty() {
return Ok(None);
}
Ok(Some(self.pending_frames.remove(0)))
}
fn flush(&mut self) -> CodecResult<()> {
self.pending_frames.clear();
self.reference_frame = None;
Ok(())
}
fn reset(&mut self) {
self.pending_frames.clear();
self.reference_frame = None;
self.width = None;
self.height = None;
self.format = None;
}
fn output_format(&self) -> Option<PixelFormat> {
Some(PixelFormat::Yuv420p)
}
fn dimensions(&self) -> Option<(u32, u32)> {
self.width.and_then(|w| self.height.map(|h| (w, h)))
}
}
pub struct H263Encoder {
config: EncoderConfig,
frame_count: u64,
reference_frame: Option<VideoFrame>,
motion_estimator: MotionEstimator,
quantizer: u8,
frames_since_keyframe: u32,
}
impl H263Encoder {
pub fn new(config: EncoderConfig) -> CodecResult<Self> {
let quantizer = match &config.bitrate {
crate::traits::BitrateMode::Crf(crf) => (*crf as u8).clamp(1, 31),
crate::traits::BitrateMode::Cbr(_) => 10,
crate::traits::BitrateMode::Vbr { .. } => 10,
crate::traits::BitrateMode::Lossless => 1,
};
Ok(Self {
config,
frame_count: 0,
reference_frame: None,
motion_estimator: MotionEstimator::new(SearchAlgorithm::Diamond, 16),
quantizer,
frames_since_keyframe: 0,
})
}
fn encode_frame(&mut self, frame: &VideoFrame) -> CodecResult<EncodedPacket> {
let is_keyframe = self.frame_count % u64::from(self.config.keyint) == 0;
self.frames_since_keyframe = if is_keyframe {
0
} else {
self.frames_since_keyframe + 1
};
let mut writer = BitWriter::new();
self.write_picture_header(&mut writer, frame, is_keyframe)?;
let mb_width = ((frame.width + 15) / 16) as usize;
let mb_height = ((frame.height + 15) / 16) as usize;
let mut mv_predictor = MotionVectorPredictor::new(mb_width);
for mb_y in 0..mb_height {
for mb_x in 0..mb_width {
self.encode_macroblock(
&mut writer,
frame,
&mut mv_predictor,
mb_x,
mb_y,
is_keyframe,
)?;
}
mv_predictor.next_row();
}
self.reference_frame = Some(frame.clone());
self.frame_count += 1;
Ok(EncodedPacket {
data: writer.into_vec(),
pts: frame.timestamp.pts,
dts: frame.timestamp.pts,
keyframe: is_keyframe,
duration: None,
})
}
fn write_picture_header(
&mut self,
writer: &mut BitWriter,
frame: &VideoFrame,
is_keyframe: bool,
) -> CodecResult<()> {
writer.write_bits(0x20, 22);
let tr = (self.frame_count % 256) as u8;
writer.write_bits(u32::from(tr), 8);
writer.write_bit(true); writer.write_bit(false); writer.write_bit(false); writer.write_bit(false);
let format = self.determine_format(frame.width, frame.height);
writer.write_bits(u32::from(format.to_code()), 3);
writer.write_bit(!is_keyframe);
writer.write_bit(false); writer.write_bit(false); writer.write_bit(false); writer.write_bit(false); writer.write_bit(false);
writer.write_bit(false);
writer.write_bits(u32::from(self.quantizer), 5);
writer.write_bit(false);
Ok(())
}
fn determine_format(&self, width: u32, height: u32) -> PictureFormat {
match (width, height) {
(128, 96) => PictureFormat::SubQcif,
(176, 144) => PictureFormat::Qcif,
(352, 288) => PictureFormat::Cif,
(704, 576) => PictureFormat::FourCif,
(1408, 1152) => PictureFormat::SixteenCif,
_ => PictureFormat::Extended,
}
}
#[allow(clippy::too_many_arguments)]
fn encode_macroblock(
&mut self,
writer: &mut BitWriter,
frame: &VideoFrame,
mv_predictor: &mut MotionVectorPredictor,
mb_x: usize,
mb_y: usize,
is_keyframe: bool,
) -> CodecResult<()> {
if is_keyframe {
self.encode_intra_macroblock(writer, frame, mb_x, mb_y)?;
} else {
self.encode_inter_macroblock(writer, frame, mv_predictor, mb_x, mb_y)?;
}
Ok(())
}
fn encode_intra_macroblock(
&mut self,
writer: &mut BitWriter,
frame: &VideoFrame,
mb_x: usize,
mb_y: usize,
) -> CodecResult<()> {
if let Some(vlc) = encode_mcbpc_i(3, 0) {
writer.write_vlc(vlc.code, vlc.bits);
}
if let Some(vlc) = encode_cbpy(15, true) {
writer.write_vlc(vlc.code, vlc.bits);
}
for block_idx in 0..6 {
let block = self.extract_block(frame, mb_x, mb_y, block_idx);
self.encode_block(writer, &block, true)?;
}
Ok(())
}
fn encode_inter_macroblock(
&mut self,
writer: &mut BitWriter,
frame: &VideoFrame,
mv_predictor: &mut MotionVectorPredictor,
mb_x: usize,
mb_y: usize,
) -> CodecResult<()> {
writer.write_bit(true);
if let Some(vlc) = encode_mcbpc_p(0, 0) {
writer.write_vlc(vlc.code, vlc.bits);
}
if let Some(vlc) = encode_cbpy(15, false) {
writer.write_vlc(vlc.code, vlc.bits);
}
let mv = self.estimate_motion(frame, mb_x, mb_y);
self.encode_motion_vector(writer, mv_predictor, mb_x, mb_y, mv)?;
mv_predictor.update(mb_x, mv);
for block_idx in 0..6 {
let block = self.extract_block(frame, mb_x, mb_y, block_idx);
self.encode_block(writer, &block, false)?;
}
Ok(())
}
fn estimate_motion(&self, frame: &VideoFrame, mb_x: usize, mb_y: usize) -> MotionVector {
if let Some(ref_frame) = &self.reference_frame {
if !frame.planes.is_empty() && !ref_frame.planes.is_empty() {
let cur_plane = &frame.planes[0];
let ref_plane = &ref_frame.planes[0];
return self.motion_estimator.estimate(
&cur_plane.data,
cur_plane.stride,
&ref_plane.data,
ref_plane.stride,
mb_x,
mb_y,
frame.width as usize,
frame.height as usize,
);
}
}
MotionVector::zero()
}
fn encode_motion_vector(
&self,
writer: &mut BitWriter,
mv_predictor: &MotionVectorPredictor,
mb_x: usize,
mb_y: usize,
mv: MotionVector,
) -> CodecResult<()> {
let pred_mv = mv_predictor.predict(mb_x, mb_y);
let mvd = mv.sub(&pred_mv);
if let Some(vlc) = encode_mvd(mvd.x as i32) {
writer.write_vlc(vlc.code, vlc.bits);
}
if let Some(vlc) = encode_mvd(mvd.y as i32) {
writer.write_vlc(vlc.code, vlc.bits);
}
Ok(())
}
fn extract_block(
&self,
frame: &VideoFrame,
mb_x: usize,
mb_y: usize,
block_idx: usize,
) -> [[i16; 8]; 8] {
let mut block = [[0i16; 8]; 8];
let (plane_idx, bx, by) = if block_idx < 4 {
let bx = (block_idx % 2) * 8;
let by = (block_idx / 2) * 8;
(0, mb_x * 16 + bx, mb_y * 16 + by)
} else {
let plane_idx = block_idx - 3;
(plane_idx, mb_x * 8, mb_y * 8)
};
if plane_idx < frame.planes.len() {
let plane = &frame.planes[plane_idx];
for y in 0..8 {
for x in 0..8 {
let offset = (by + y) * plane.stride + (bx + x);
if offset < plane.data.len() {
block[y][x] = plane.data[offset] as i16;
}
}
}
}
block
}
fn encode_block(
&self,
writer: &mut BitWriter,
block: &[[i16; 8]; 8],
intra: bool,
) -> CodecResult<()> {
let dct_block = dct_8x8(block);
let quant_block = quantize_block(&dct_block, self.quantizer);
if intra {
let dc = quant_block[0][0];
writer.write_bits(3, 3); writer.write_signed_bits(dc as i32, 8);
}
let mut last_idx = 63;
for i in (0..64).rev() {
let row = i / 8;
let col = i % 8;
if quant_block[row][col] != 0 {
last_idx = i;
break;
}
}
let mut idx = if intra { 1 } else { 0 };
let mut run = 0u8;
while idx <= last_idx {
let row = idx / 8;
let col = idx % 8;
let level = quant_block[row][col];
if level == 0 {
run += 1;
} else {
let last = idx == last_idx;
let entry = vlc::TcoefEntry::new(run, level, last);
if let Some(vlc) = vlc::find_tcoef_vlc(&entry) {
writer.write_vlc(vlc.code, vlc.bits);
writer.write_bit(level < 0);
} else {
writer.write_bits(0b0000011, 7);
writer.write_bit(last);
writer.write_bits(u32::from(run), 6);
writer.write_signed_bits(level as i32, 8);
}
if last {
break;
}
run = 0;
}
idx += 1;
}
Ok(())
}
}
impl VideoEncoder for H263Encoder {
fn codec(&self) -> CodecId {
CodecId::H263
}
fn send_frame(&mut self, frame: &VideoFrame) -> CodecResult<()> {
Ok(())
}
fn receive_packet(&mut self) -> CodecResult<Option<EncodedPacket>> {
Ok(None)
}
fn flush(&mut self) -> CodecResult<()> {
self.reference_frame = None;
Ok(())
}
fn config(&self) -> &EncoderConfig {
&self.config
}
}
#[must_use]
fn dct_8x8(input: &[[i16; 8]; 8]) -> [[i16; 8]; 8] {
let mut output = [[0i16; 8]; 8];
let mut temp = [[0f32; 8]; 8];
for i in 0..8 {
for j in 0..8 {
let mut sum = 0.0f32;
for k in 0..8 {
sum += (input[i][k] as f32)
* ((2 * k + 1) as f32 * j as f32 * std::f32::consts::PI / 16.0).cos();
}
let c = if j == 0 { 1.0 / (2.0_f32).sqrt() } else { 1.0 };
temp[i][j] = c * sum / 2.0;
}
}
for j in 0..8 {
for i in 0..8 {
let mut sum = 0.0f32;
for k in 0..8 {
sum += temp[k][j]
* ((2 * k + 1) as f32 * i as f32 * std::f32::consts::PI / 16.0).cos();
}
let c = if i == 0 { 1.0 / (2.0_f32).sqrt() } else { 1.0 };
output[i][j] = ((c * sum / 2.0).round()) as i16;
}
}
output
}
#[must_use]
fn idct_8x8(input: &[[i16; 8]; 8]) -> [[i16; 8]; 8] {
let mut output = [[0i16; 8]; 8];
let mut temp = [[0f32; 8]; 8];
for i in 0..8 {
for j in 0..8 {
let mut sum = 0.0f32;
for k in 0..8 {
let c = if k == 0 { 1.0 / (2.0_f32).sqrt() } else { 1.0 };
sum += c
* (input[i][k] as f32)
* ((2 * j + 1) as f32 * k as f32 * std::f32::consts::PI / 16.0).cos();
}
temp[i][j] = sum / 2.0;
}
}
for j in 0..8 {
for i in 0..8 {
let mut sum = 0.0f32;
for k in 0..8 {
let c = if k == 0 { 1.0 / (2.0_f32).sqrt() } else { 1.0 };
sum += c
* temp[k][j]
* ((2 * i + 1) as f32 * k as f32 * std::f32::consts::PI / 16.0).cos();
}
output[i][j] = ((sum / 2.0).round().clamp(-256.0, 255.0)) as i16;
}
}
output
}