]> git.nihav.org Git - nihav.git/commitdiff
add rawvideo-ms encoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 23 Mar 2025 17:19:41 +0000 (18:19 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 23 Mar 2025 17:19:41 +0000 (18:19 +0100)
nihav-commonfmt/src/codecs/mod.rs
nihav-commonfmt/src/codecs/rawvideomsenc.rs [new file with mode: 0644]

index 96ec0161ba9e14052490e4d160de2250ac3327d6..536bfa3e91370747d2e98a32f678a518fca4da21 100644 (file)
@@ -87,6 +87,8 @@ mod cinepakenc;
 mod gifenc;
 #[cfg(feature="encoder_rawvideo")]
 mod rawvideoenc;
+#[cfg(feature="encoder_rawvideo")]
+mod rawvideomsenc;
 #[cfg(feature="encoder_zmbv")]
 mod zmbvenc;
 
@@ -98,6 +100,8 @@ const ENCODERS: &[EncoderInfo] = &[
     EncoderInfo { name: "gif", get_encoder: gifenc::get_encoder },
 #[cfg(feature="encoder_rawvideo")]
     EncoderInfo { name: "rawvideo", get_encoder: rawvideoenc::get_encoder },
+#[cfg(feature="encoder_rawvideo")]
+    EncoderInfo { name: "rawvideo-ms", get_encoder: rawvideomsenc::get_encoder },
 #[cfg(feature="encoder_zmbv")]
     EncoderInfo { name: "zmbv", get_encoder: zmbvenc::get_encoder },
 
diff --git a/nihav-commonfmt/src/codecs/rawvideomsenc.rs b/nihav-commonfmt/src/codecs/rawvideomsenc.rs
new file mode 100644 (file)
index 0000000..3ef87fd
--- /dev/null
@@ -0,0 +1,200 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+
+const BGR555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
+                                        comp_info: [
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift:  5, comp_offs: 0, next_elem: 2 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift:  0, comp_offs: 0, next_elem: 2 }),
+                                            None, None],
+                                        elem_size: 2, be: false, alpha: false, palette: false };
+
+pub const BGR24_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
+                                        comp_info: [
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 3 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 3 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 3 }),
+                                            None, None],
+                                        elem_size: 3, be: false, alpha: false, palette: false };
+
+pub const BGR32_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 4,
+                                        comp_info: [
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }),
+                                            Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 3, next_elem: 4 }),
+                                            None],
+                                        elem_size: 4, be: false, alpha: true, palette: false };
+
+struct RawEncoder {
+    stream: Option<NAStreamRef>,
+    pkt:    Option<NAPacket>,
+    pal:    [u8; 768],
+    dpal:   Arc<[u8; 1024]>,
+}
+
+impl RawEncoder {
+    fn new() -> Self {
+        Self {
+            stream: None,
+            pkt:    None,
+            pal:    [0; 768],
+            dpal:   Arc::new([0; 1024]),
+        }
+    }
+}
+
+impl NAEncoder for RawEncoder {
+    fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
+        match encinfo.format {
+            NACodecTypeInfo::None => {
+                Ok(EncodeParameters {
+                    format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, YUV420_FORMAT)),
+                    ..Default::default()
+                })
+            },
+            NACodecTypeInfo::Video(_) => {
+                let mut new_info = *encinfo;
+                if let NACodecTypeInfo::Video(ref mut vinfo) = new_info.format {
+                    if vinfo.format.model.is_rgb() {
+                        let depth = if vinfo.format.is_paletted() { 8 } else { vinfo.format.get_total_depth() } as usize;
+                        vinfo.format = match depth {
+                                8 => PAL8_FORMAT,
+                                15 | 16 => BGR555_FORMAT,
+                                24 => BGR24_FORMAT,
+                                _ if vinfo.format.has_alpha() => BGR32_FORMAT,
+                                _ => BGR24_FORMAT,
+                            };
+                    } else {
+                        vinfo.format = BGR24_FORMAT;
+                    }
+                }
+                Ok(new_info)
+            },
+            NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
+        }
+    }
+    fn get_capabilities(&self) -> u64 { ENC_CAPS_CBR }
+    fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
+        match encinfo.format {
+            NACodecTypeInfo::None => Err(EncoderError::FormatError),
+            NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
+            NACodecTypeInfo::Video(_) => {
+                let info = NACodecInfo::new("rawvideo-ms", encinfo.format, None);
+                let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
+                stream.set_num(stream_id as usize);
+                let stream = stream.into_ref();
+                self.stream = Some(stream.clone());
+                Ok(stream)
+            }
+        }
+    }
+    fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
+        let buf = frm.get_buffer();
+        let mut dbuf;
+        let mut pal = None;
+        match buf {
+            NABufferType::Video(ref vbuf) | NABufferType::VideoPacked(ref vbuf) => {
+                let vinfo = vbuf.get_info();
+                let mut depth = if vinfo.format.is_paletted() { 8 } else { vinfo.format.get_total_depth() } as usize;
+                if depth == 15 {
+                    depth = 16;
+                }
+                if !vinfo.format.model.is_rgb() || !matches!(depth, 8 | 16 | 24 | 32) {
+                    return Err(EncoderError::NotImplemented);
+                }
+
+                let (width, height) = vbuf.get_dimensions(0);
+                let row_size = width * depth / 8;
+                let pad_len = if (row_size & 3) != 0 { 4 - (row_size & 3) } else { 0 };
+                let pad = &[0; 4][..pad_len];
+
+                let src = vbuf.get_data();
+                dbuf = Vec::with_capacity(height * (row_size + pad_len));
+                let sstride = vbuf.get_stride(0).max(row_size);
+                if vinfo.is_flipped() {
+                    for line in src.chunks_exact(sstride).take(height) {
+                        dbuf.extend_from_slice(&line[..row_size]);
+                        dbuf.extend_from_slice(pad);
+                    }
+                } else {
+                    for line in src.chunks_exact(sstride).take(height).rev() {
+                        dbuf.extend_from_slice(&line[..row_size]);
+                        dbuf.extend_from_slice(pad);
+                    }
+                }
+                if vinfo.format.is_paletted() {
+                    let pal_off = vbuf.get_offset(1);
+                    let src_pal = &src[pal_off..][..768];
+                    pal = if src_pal == self.pal {
+                            Some(NASideData::Palette(false, self.dpal.clone()))
+                        } else {
+                            let mut pal32 = [0; 1024];
+                            self.pal.copy_from_slice(src_pal);
+                            for (dclr, sclr) in pal32.chunks_exact_mut(4)
+                                    .zip(src_pal.chunks_exact(3)) {
+                                dclr[..3].copy_from_slice(sclr);
+                            }
+                            self.dpal = Arc::new(pal32);
+                            Some(NASideData::Palette(true, self.dpal.clone()))
+                        };
+                }
+            },
+            NABufferType::Video16(ref vbuf) => {
+                let vinfo = vbuf.get_info();
+                let (width, height) = vbuf.get_dimensions(0);
+                let row_size = width * 2;
+                let pad_len = if (row_size & 3) != 0 { 4 - (row_size & 3) } else { 0 };
+
+                let src = vbuf.get_data();
+                dbuf = vec![0; height * (row_size + pad_len)];
+                let sstride = vbuf.get_stride(0);
+                if vinfo.is_flipped() {
+                    for (dline, sline) in dbuf.chunks_exact_mut(row_size + pad_len)
+                            .zip(src.chunks_exact(sstride).take(height)) {
+                        for (dst, &pix) in dline.chunks_exact_mut(2).zip(sline[..width].iter()) {
+                            let _ = write_u16le(dst, pix);
+                        }
+                    }
+                } else {
+                    for (dline, sline) in dbuf.chunks_exact_mut(row_size + pad_len)
+                            .zip(src.chunks_exact(sstride).take(height).rev()) {
+                        for (dst, &pix) in dline.chunks_exact_mut(2).zip(sline[..width].iter()) {
+                            let _ = write_u16le(dst, pix);
+                        }
+                    }
+                }
+            },
+            NABufferType::Video32(ref _vbuf) => return Err(EncoderError::NotImplemented),
+            NABufferType::None => {
+                self.pkt = None;
+                return Ok(());
+            },
+            _ => return Err(EncoderError::FormatError),
+        };
+        let mut pkt = NAPacket::new(self.stream.clone().unwrap(), frm.ts, true, dbuf);
+        if let Some(pal_sd) = pal {
+            pkt.add_side_data(pal_sd);
+        }
+        self.pkt = Some(pkt);
+        Ok(())
+    }
+    fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
+        let mut npkt = None;
+        std::mem::swap(&mut self.pkt, &mut npkt);
+        Ok(npkt)
+    }
+    fn flush(&mut self) -> EncoderResult<()> {
+        Ok(())
+    }
+}
+
+impl NAOptionHandler for RawEncoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
+    Box::new(RawEncoder::new())
+}