From 9a59c451c92ca4283fb6f2274e399393d9a5f972 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Sun, 3 Oct 2021 18:37:30 +0200 Subject: [PATCH] add raw RGB support for AVI --- nihav-commonfmt/Cargo.toml | 3 +- nihav-commonfmt/src/codecs/mod.rs | 4 + nihav-commonfmt/src/codecs/rawvideo_ms.rs | 159 ++++++++++++++++++++++ nihav-commonfmt/src/demuxers/avi.rs | 38 +++++- nihav-registry/src/register.rs | 1 + 5 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 nihav-commonfmt/src/codecs/rawvideo_ms.rs diff --git a/nihav-commonfmt/Cargo.toml b/nihav-commonfmt/Cargo.toml index 7f4cc4f..acccd96 100644 --- a/nihav-commonfmt/Cargo.toml +++ b/nihav-commonfmt/Cargo.toml @@ -34,10 +34,11 @@ muxer_wav = ["muxers"] all_decoders = ["all_video_decoders", "all_audio_decoders"] -all_video_decoders = ["decoder_cinepak", "decoder_clearvideo", "decoder_rawvideo", "decoder_zmbv"] +all_video_decoders = ["decoder_cinepak", "decoder_clearvideo", "decoder_rawvideo", "decoder_rawvideo_ms", "decoder_zmbv"] decoder_cinepak = ["decoders"] decoder_clearvideo = ["decoders"] decoder_rawvideo = ["decoders"] +decoder_rawvideo_ms = ["decoders"] decoder_zmbv = ["decoders"] all_audio_decoders = ["decoder_pcm", "decoder_ts102366", "decoder_sipro", "decoder_atrac3", "decoder_aac"] diff --git a/nihav-commonfmt/src/codecs/mod.rs b/nihav-commonfmt/src/codecs/mod.rs index 2c9eef7..cc1cf74 100644 --- a/nihav-commonfmt/src/codecs/mod.rs +++ b/nihav-commonfmt/src/codecs/mod.rs @@ -10,6 +10,8 @@ mod cinepak; mod clearvideo; #[cfg(feature="decoder_rawvideo")] mod rawvideo; +#[cfg(feature="decoder_rawvideo_ms")] +mod rawvideo_ms; #[cfg(feature="decoder_zmbv")] mod zmbv; @@ -40,6 +42,8 @@ const DECODERS: &[DecoderInfo] = &[ DecoderInfo { name: "clearvideo_rm", get_decoder: clearvideo::get_decoder_rm }, #[cfg(feature="decoder_rawvideo")] DecoderInfo { name: "rawvideo", get_decoder: rawvideo::get_decoder }, +#[cfg(feature="decoder_rawvideo_ms")] + DecoderInfo { name: "rawvideo-ms", get_decoder: rawvideo_ms::get_decoder }, #[cfg(feature="decoder_zmbv")] DecoderInfo { name: "zmbv", get_decoder: zmbv::get_decoder }, diff --git a/nihav-commonfmt/src/codecs/rawvideo_ms.rs b/nihav-commonfmt/src/codecs/rawvideo_ms.rs new file mode 100644 index 0000000..0b35881 --- /dev/null +++ b/nihav-commonfmt/src/codecs/rawvideo_ms.rs @@ -0,0 +1,159 @@ +use nihav_core::codecs::*; + +struct RawDecoder { + info: NACodecInfoRef, + pal: [u8; 768], +} + +impl RawDecoder { + fn new() -> Self { + Self { + info: NACodecInfo::new_dummy(), + pal: [0; 768], + } + } +} + +impl NADecoder for RawDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() { + self.info = info.clone(); + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + if let NACodecTypeInfo::Video(ref vinfo) = self.info.get_properties() { + let src = pkt.get_buffer(); + let width = vinfo.width; + let height = vinfo.height; + if !vinfo.format.model.is_rgb() { + return Err(DecoderError::InvalidData); + } + let depth = if vinfo.format.is_paletted() { 8 } else { vinfo.format.get_total_depth() } as usize; + let buf = match depth { + 8 => { + for sd in pkt.side_data.iter() { + match *sd { + NASideData::Palette(_, ref pal) => { + for (dst, src) in self.pal.chunks_mut(3).zip(pal.chunks(4)) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + break; + }, + _ => {}, + }; + } + + let sstride = (vinfo.width + 3) & !3; + + let buf = alloc_video_buffer(vinfo.clone(), 0)?; + + let mut frm = buf.get_vbuf().unwrap(); + let dstride = frm.get_stride(0); + let paloff = frm.get_offset(1); + let dst = frm.get_data_mut().unwrap(); + for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(height) { + drow[..width].copy_from_slice(&srow[..width]); + } + dst[paloff..][..768].copy_from_slice(&self.pal); + + buf + }, + 15 | 16 => { + let sstride = (vinfo.width * 2 + 3) & !3; + + let buf = alloc_video_buffer(vinfo.clone(), 0)?; + + let mut frm = buf.get_vbuf16().unwrap(); + let dstride = frm.get_stride(0); + let dst = frm.get_data_mut().unwrap(); + + for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(height) { + for (dp, sp) in drow.iter_mut().zip(srow.chunks_exact(2)).take(width) { + *dp = u16::from(sp[0]) | (u16::from(sp[1]) << 8); + } + } + + buf + }, + 24 | 32 => { + let ncomp = vinfo.format.components as usize; + let sstride = (width * (depth / 8) + 3) & !3; + let offs = vec![0; ncomp]; + let mut strides = vec![0; ncomp]; + strides[0] = sstride; + NABufferType::VideoPacked(NAVideoBuffer::from_raw_parts(vinfo.clone(), src.clone(), offs, strides).into_ref()) + }, + _ => return Err(DecoderError::NotImplemented), + }; + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf); + frm.set_keyframe(true); + frm.set_frame_type(FrameType::I); + Ok(frm.into_ref()) + } else { + Err(DecoderError::Bug) + } + } + fn flush(&mut self) {} +} + + +impl NAOptionHandler for RawDecoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub fn get_decoder() -> Box { + Box::new(RawDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::generic_register_all_decoders; + use crate::generic_register_all_demuxers; + #[test] + fn test_rawvideo_ms_8() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + test_decoding("avi", "rawvideo-ms", "assets/Misc/8bpp.avi", Some(0), &dmx_reg, + &dec_reg, ExpectedTestResult::MD5([0xb6629439, 0x6ea482e9, 0x42c84d7c, 0x46c94431])); + } + #[test] + fn test_rawvideo_ms_16() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + test_decoding("avi", "rawvideo-ms", "assets/Misc/16bpp.avi", Some(0), &dmx_reg, + &dec_reg, ExpectedTestResult::MD5([0xe80e16a1, 0x2d50659e, 0x413d24af, 0xea3bee05])); + } + #[test] + fn test_rawvideo_ms_24() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + test_decoding("avi", "rawvideo-ms", "assets/Misc/keve.avi", Some(0), &dmx_reg, + &dec_reg, ExpectedTestResult::MD5([0x9514ac1f, 0x4512cc62, 0x069485ba, 0x084a1e63])); + } + #[test] + fn test_rawvideo_ms_32() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + test_decoding("avi", "rawvideo-ms", "assets/Misc/VRMLuncompressed.avi", Some(0), &dmx_reg, + &dec_reg, ExpectedTestResult::MD5([0xf4c9d468, 0x8f42c576, 0xc8eb522a, 0x75f654b1])); + } +} diff --git a/nihav-commonfmt/src/demuxers/avi.rs b/nihav-commonfmt/src/demuxers/avi.rs index 9adf54b..3e372e0 100644 --- a/nihav-commonfmt/src/demuxers/avi.rs +++ b/nihav-commonfmt/src/demuxers/avi.rs @@ -1,6 +1,7 @@ use nihav_core::demuxers::*; use nihav_registry::register; use nihav_core::demuxers::DemuxerError::*; +use std::str::FromStr; macro_rules! mktag { ($a:expr, $b:expr, $c:expr, $d:expr) => ({ @@ -423,6 +424,14 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize let format = if bitcount > 8 { RGB24_FORMAT } else { PAL8_FORMAT }; let mut vhdr = NAVideoInfo::new(width as usize, if flip { -height as usize } else { height as usize}, flip, format); vhdr.bits = (planes as u8) * (bitcount as u8); + let cname = if find_raw_fmt(&compression, planes, bitcount, &mut vhdr) { + "rawvideo-ms" + } else { + match register::find_codec_from_avi_fourcc(&compression) { + None => "unknown", + Some(name) => name, + } + }; let vci = NACodecTypeInfo::Video(vhdr); let edata = dmx.read_extradata(size - 40)?; if colors > 0 { @@ -438,10 +447,6 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize dmx.pal.push(pal); } } - let cname = match register::find_codec_from_avi_fourcc(&compression) { - None => "unknown", - Some(name) => name, - }; let vinfo = NACodecInfo::new(cname, vci, edata); let res = strmgr.add_stream(NAStream::new(StreamType::Video, u32::from(dmx.sstate.strm_no), vinfo, dmx.tb_num, dmx.tb_den, u64::from(dmx.strm_duration))); if res.is_none() { return Err(MemoryError); } @@ -449,6 +454,31 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize Ok(size) } +fn find_raw_fmt(compr: &[u8; 4], planes: u16, bitcount: u16, vhdr: &mut NAVideoInfo) -> bool { + match compr { + &[0, 0, 0, 0] | b"DIB " => { + if planes != 1 { + return false; + } + let fmt_name = match bitcount { + 8 => "pal8", + 16 => "bgr555", + 24 => "bgr24", + 32 => "bgra24", + _ => return false, + }; + if let Ok(fmt) = NAPixelFormaton::from_str(fmt_name) { + vhdr.format = fmt; + vhdr.flipped = true; + true + } else { + false + } + }, + _ => false, + } +} + #[allow(unused_variables)] fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) -> DemuxerResult { if size < 16 { return Err(InvalidData); } diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index d929e28..a89e2a7 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -187,6 +187,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[ desc!(video-ll; "rawvideo", "Raw video data"), + desc!(video-ll; "rawvideo-ms", "Raw video data"), desc!(video; "cinepak", "Cinepak"), -- 2.30.2