use std::collections::VecDeque;
use oxideav_core::frame::VideoPlane;
use oxideav_core::{
CodecCapabilities, CodecId, CodecInfo, CodecParameters, CodecRegistry, CodecTag,
ContainerRegistry, Decoder, Encoder, Error, Frame, MediaType, Packet, PixelFormat, Result,
RuntimeContext, TimeBase, VideoFrame,
};
use crate::container;
use crate::decoder::decode_jpeg;
use crate::encoder::{
encode_jpeg_cmyk, encode_jpeg_cmyk_progressive, encode_jpeg_grayscale_with_opts,
encode_jpeg_progressive, encode_jpeg_progressive_grayscale, encode_jpeg_rgb24_with_opts,
encode_jpeg_with_opts, encode_lossless_jpeg_grayscale, DEFAULT_QUALITY,
};
use crate::error::MjpegError;
use crate::image::{MjpegFrame, MjpegPixelFormat, MjpegPlane};
use crate::mjpeg_container;
use crate::CODEC_ID_STR;
impl From<MjpegError> for Error {
fn from(e: MjpegError) -> Self {
match e {
MjpegError::InvalidData(s) => Error::InvalidData(s),
MjpegError::Unsupported(s) => Error::Unsupported(s),
MjpegError::Other(s) => Error::Other(s),
MjpegError::Eof => Error::Eof,
MjpegError::NeedMore => Error::NeedMore,
}
}
}
impl From<MjpegPixelFormat> for PixelFormat {
fn from(p: MjpegPixelFormat) -> Self {
match p {
MjpegPixelFormat::Gray8 => PixelFormat::Gray8,
MjpegPixelFormat::Gray10Le => PixelFormat::Gray10Le,
MjpegPixelFormat::Gray12Le => PixelFormat::Gray12Le,
MjpegPixelFormat::Gray16Le => PixelFormat::Gray16Le,
MjpegPixelFormat::Cmyk => PixelFormat::Cmyk,
MjpegPixelFormat::Rgb24 => PixelFormat::Rgb24,
MjpegPixelFormat::Rgb48Le => PixelFormat::Rgb48Le,
MjpegPixelFormat::Gbrp10Le => PixelFormat::Gbrp10Le,
MjpegPixelFormat::Gbrp12Le => PixelFormat::Gbrp12Le,
MjpegPixelFormat::Gbrp14Le => PixelFormat::Gbrp14Le,
MjpegPixelFormat::Yuv411P => PixelFormat::Yuv411P,
MjpegPixelFormat::Yuv420P => PixelFormat::Yuv420P,
MjpegPixelFormat::Yuv422P => PixelFormat::Yuv422P,
MjpegPixelFormat::Yuv444P => PixelFormat::Yuv444P,
MjpegPixelFormat::Yuv420P12Le => PixelFormat::Yuv420P12Le,
MjpegPixelFormat::Yuv422P12Le => PixelFormat::Yuv422P12Le,
MjpegPixelFormat::Yuv444P12Le => PixelFormat::Yuv444P12Le,
}
}
}
fn pix_to_local(p: PixelFormat) -> Option<MjpegPixelFormat> {
Some(match p {
PixelFormat::Gray8 => MjpegPixelFormat::Gray8,
PixelFormat::Gray10Le => MjpegPixelFormat::Gray10Le,
PixelFormat::Gray12Le => MjpegPixelFormat::Gray12Le,
PixelFormat::Gray16Le => MjpegPixelFormat::Gray16Le,
PixelFormat::Cmyk => MjpegPixelFormat::Cmyk,
PixelFormat::Rgb24 => MjpegPixelFormat::Rgb24,
PixelFormat::Rgb48Le => MjpegPixelFormat::Rgb48Le,
PixelFormat::Gbrp10Le => MjpegPixelFormat::Gbrp10Le,
PixelFormat::Gbrp12Le => MjpegPixelFormat::Gbrp12Le,
PixelFormat::Gbrp14Le => MjpegPixelFormat::Gbrp14Le,
PixelFormat::Yuv411P => MjpegPixelFormat::Yuv411P,
PixelFormat::Yuv420P => MjpegPixelFormat::Yuv420P,
PixelFormat::Yuv422P => MjpegPixelFormat::Yuv422P,
PixelFormat::Yuv444P => MjpegPixelFormat::Yuv444P,
PixelFormat::Yuv420P12Le => MjpegPixelFormat::Yuv420P12Le,
PixelFormat::Yuv422P12Le => MjpegPixelFormat::Yuv422P12Le,
PixelFormat::Yuv444P12Le => MjpegPixelFormat::Yuv444P12Le,
_ => return None,
})
}
impl From<MjpegFrame> for VideoFrame {
fn from(f: MjpegFrame) -> Self {
VideoFrame {
pts: f.pts,
planes: f
.planes
.into_iter()
.map(|p| VideoPlane {
stride: p.stride,
data: p.data,
})
.collect(),
}
}
}
impl From<MjpegPlane> for VideoPlane {
fn from(p: MjpegPlane) -> Self {
VideoPlane {
stride: p.stride,
data: p.data,
}
}
}
pub fn register_codecs(reg: &mut CodecRegistry) {
let caps = CodecCapabilities::video("mjpeg_sw")
.with_lossy(true)
.with_intra_only(true)
.with_max_size(16384, 16384);
reg.register(
CodecInfo::new(CodecId::new(CODEC_ID_STR))
.capabilities(caps)
.decoder(make_decoder)
.encoder(make_encoder)
.tags([
CodecTag::fourcc(b"MJPG"),
CodecTag::fourcc(b"AVRN"),
CodecTag::fourcc(b"LJPG"),
CodecTag::fourcc(b"JPGL"),
]),
);
}
pub fn register_containers(reg: &mut ContainerRegistry) {
container::register(reg);
mjpeg_container::register(reg);
}
pub fn register(ctx: &mut RuntimeContext) {
register_codecs(&mut ctx.codecs);
register_containers(&mut ctx.containers);
}
oxideav_core::register!("mjpeg", register);
pub fn make_decoder(params: &CodecParameters) -> Result<Box<dyn Decoder>> {
let codec_id = params.codec_id.clone();
Ok(Box::new(MjpegDecoder {
codec_id,
pending: None,
eof: false,
}))
}
struct MjpegDecoder {
codec_id: CodecId,
pending: Option<Packet>,
eof: bool,
}
impl Decoder for MjpegDecoder {
fn codec_id(&self) -> &CodecId {
&self.codec_id
}
fn send_packet(&mut self, packet: &Packet) -> Result<()> {
if self.pending.is_some() {
return Err(Error::other(
"MJPEG decoder: receive_frame must be called before sending another packet",
));
}
self.pending = Some(packet.clone());
Ok(())
}
fn receive_frame(&mut self) -> Result<Frame> {
let Some(pkt) = self.pending.take() else {
return if self.eof {
Err(Error::Eof)
} else {
Err(Error::NeedMore)
};
};
let vf = decode_jpeg(&pkt.data, pkt.pts)?;
Ok(Frame::Video(vf))
}
fn flush(&mut self) -> Result<()> {
self.eof = true;
Ok(())
}
}
pub fn make_encoder(params: &CodecParameters) -> Result<Box<dyn Encoder>> {
Ok(MjpegEncoder::from_params(params)?)
}
pub struct MjpegEncoder {
output_params: CodecParameters,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) pix: MjpegPixelFormat,
quality: u8,
restart_interval: u16,
progressive: bool,
lossless: bool,
lossless_predictor: u8,
cmyk_adobe_transform: Option<u8>,
time_base: TimeBase,
pending: VecDeque<Packet>,
eof: bool,
}
impl MjpegEncoder {
pub fn from_params(params: &CodecParameters) -> Result<Box<Self>> {
let width = params
.width
.ok_or_else(|| Error::invalid("MJPEG encoder: missing width"))?;
let height = params
.height
.ok_or_else(|| Error::invalid("MJPEG encoder: missing height"))?;
let pix_core = params.pixel_format.unwrap_or(PixelFormat::Yuv420P);
let pix = pix_to_local(pix_core).ok_or_else(|| {
Error::unsupported(format!(
"MJPEG encoder: pixel format {pix_core:?} not supported"
))
})?;
match pix {
MjpegPixelFormat::Yuv420P | MjpegPixelFormat::Yuv422P | MjpegPixelFormat::Yuv444P => {}
MjpegPixelFormat::Gray8
| MjpegPixelFormat::Gray10Le
| MjpegPixelFormat::Gray12Le
| MjpegPixelFormat::Gray16Le => {}
MjpegPixelFormat::Cmyk => {}
MjpegPixelFormat::Rgb24 => {}
_ => {
return Err(Error::unsupported(format!(
"MJPEG encoder: pixel format {pix_core:?} not supported"
)))
}
}
let mut output_params = params.clone();
output_params.media_type = MediaType::Video;
output_params.codec_id = CodecId::new(CODEC_ID_STR);
output_params.width = Some(width);
output_params.height = Some(height);
output_params.pixel_format = Some(pix.into());
Ok(Box::new(Self {
output_params,
width,
height,
pix,
quality: DEFAULT_QUALITY,
restart_interval: 0,
progressive: false,
lossless: false,
lossless_predictor: 1,
cmyk_adobe_transform: None,
time_base: params
.frame_rate
.map_or(TimeBase::new(1, 90_000), |r| TimeBase::new(r.den, r.num)),
pending: VecDeque::new(),
eof: false,
}))
}
pub fn set_restart_interval(&mut self, mcus: u32) {
self.restart_interval = mcus.min(u16::MAX as u32) as u16;
}
pub fn restart_interval(&self) -> u16 {
self.restart_interval
}
pub fn set_progressive(&mut self, on: bool) {
self.progressive = on;
}
pub fn progressive(&self) -> bool {
self.progressive
}
pub fn set_lossless(&mut self, on: bool) {
self.lossless = on;
}
pub fn lossless(&self) -> bool {
self.lossless
}
pub fn set_lossless_predictor(&mut self, predictor: u8) {
self.lossless_predictor = if (1..=7).contains(&predictor) {
predictor
} else {
1
};
}
pub fn lossless_predictor(&self) -> u8 {
self.lossless_predictor
}
pub fn set_adobe_transform(&mut self, transform: Option<u8>) -> Result<()> {
if let Some(t) = transform {
if t != 0 && t != 2 {
return Err(Error::invalid(
"MJPEG encoder: Adobe APP14 transform must be 0 (CMYK) or 2 (YCCK)",
));
}
}
self.cmyk_adobe_transform = transform;
Ok(())
}
pub fn adobe_transform(&self) -> Option<u8> {
self.cmyk_adobe_transform
}
}
impl Encoder for MjpegEncoder {
fn codec_id(&self) -> &CodecId {
&self.output_params.codec_id
}
fn output_params(&self) -> &CodecParameters {
&self.output_params
}
fn send_frame(&mut self, frame: &Frame) -> Result<()> {
match frame {
Frame::Video(v) => {
let pix = self.pix.into();
let data = match (self.pix, self.lossless) {
(MjpegPixelFormat::Gray8, true)
| (MjpegPixelFormat::Gray10Le, true)
| (MjpegPixelFormat::Gray12Le, true)
| (MjpegPixelFormat::Gray16Le, true) => {
if v.planes.is_empty() {
return Err(Error::invalid(
"MJPEG encoder: grayscale frame missing plane 0",
));
}
let plane = &v.planes[0];
let precision: u8 = match self.pix {
MjpegPixelFormat::Gray8 => 8,
MjpegPixelFormat::Gray10Le => 10,
MjpegPixelFormat::Gray12Le => 12,
MjpegPixelFormat::Gray16Le => 16,
_ => unreachable!(),
};
encode_lossless_jpeg_grayscale(
self.width,
self.height,
&plane.data,
plane.stride,
precision,
self.lossless_predictor,
)?
}
(MjpegPixelFormat::Gray8, false) => {
if v.planes.is_empty() {
return Err(Error::invalid(
"MJPEG encoder: grayscale frame missing plane 0",
));
}
let plane = &v.planes[0];
if self.progressive {
encode_jpeg_progressive_grayscale(
self.width,
self.height,
&plane.data,
plane.stride,
self.quality,
)?
} else {
encode_jpeg_grayscale_with_opts(
self.width,
self.height,
&plane.data,
plane.stride,
self.quality,
self.restart_interval,
)?
}
}
(
MjpegPixelFormat::Gray10Le
| MjpegPixelFormat::Gray12Le
| MjpegPixelFormat::Gray16Le,
false,
) => {
return Err(Error::unsupported(
"MJPEG encoder: high-bit-depth grayscale input requires set_lossless(true)",
));
}
(MjpegPixelFormat::Rgb24, _) => {
if v.planes.is_empty() {
return Err(Error::invalid(
"MJPEG encoder: RGB24 frame missing plane 0",
));
}
let plane = &v.planes[0];
let min_stride = (self.width as usize) * 3;
if plane.stride < min_stride {
return Err(Error::invalid(
"MJPEG encoder: RGB24 plane stride must be at least width * 3",
));
}
encode_jpeg_rgb24_with_opts(
self.width,
self.height,
&plane.data,
plane.stride,
self.quality,
self.restart_interval,
)?
}
(MjpegPixelFormat::Cmyk, _) => {
if v.planes.is_empty() {
return Err(Error::invalid(
"MJPEG encoder: CMYK frame missing plane 0",
));
}
let plane = &v.planes[0];
let min_stride = (self.width as usize) * 4;
if plane.stride < min_stride {
return Err(Error::invalid(
"MJPEG encoder: CMYK plane stride must be at least width * 4",
));
}
if self.progressive {
encode_jpeg_cmyk_progressive(
self.width,
self.height,
&plane.data,
plane.stride,
self.quality,
self.cmyk_adobe_transform,
)?
} else {
encode_jpeg_cmyk(
self.width,
self.height,
&plane.data,
plane.stride,
self.quality,
self.cmyk_adobe_transform,
)?
}
}
_ => {
if self.progressive {
encode_jpeg_progressive(v, self.width, self.height, pix, self.quality)?
} else {
encode_jpeg_with_opts(
v,
self.width,
self.height,
pix,
self.quality,
self.restart_interval,
)?
}
}
};
let mut pkt = Packet::new(0, self.time_base, data);
pkt.pts = v.pts;
pkt.dts = v.pts;
pkt.flags.keyframe = true;
self.pending.push_back(pkt);
Ok(())
}
_ => Err(Error::invalid("MJPEG encoder: video frames only")),
}
}
fn receive_packet(&mut self) -> Result<Packet> {
self.pending.pop_front().ok_or(Error::NeedMore)
}
fn flush(&mut self) -> Result<()> {
self.eof = true;
Ok(())
}
}