]> git.nihav.org Git - nihav.git/commitdiff
QT YUV2 and YUV4 encoders
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 4 Apr 2026 12:16:14 +0000 (14:16 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 4 Apr 2026 12:16:14 +0000 (14:16 +0200)
nihav-allstuff/src/lib.rs
nihav-qt/Cargo.toml
nihav-qt/src/codecs/mod.rs
nihav-qt/src/codecs/rawvidenc.rs [new file with mode: 0644]
nihav-qt/src/lib.rs

index 6f5ff694a523771ceee795cba2de147a2a517d40..371632b246fc5ad2f95751a13ec016ecd73bb23b 100644 (file)
@@ -86,6 +86,7 @@ pub fn nihav_register_all_encoders(re: &mut RegisteredEncoders) {
     duck_register_all_encoders(re);
     llaudio_register_all_encoders(re);
     ms_register_all_encoders(re);
+    qt_register_all_encoders(re);
     rad_register_all_encoders(re);
     realmedia_register_all_encoders(re);
 }
index 722357b17a620a37c4ad63296a25ffdde58925e8..c80a168e2895b55fdd92a8554a5163f35da386cc 100644 (file)
@@ -16,7 +16,7 @@ features = ["blockdsp", "fft", "qmf", "qt_pal"]
 nihav_commonfmt = { path = "../nihav-commonfmt", default-features=false, features = ["all_demuxers"] }
 
 [features]
-default = ["all_decoders", "all_demuxers"]
+default = ["all_decoders", "all_demuxers", "all_encoders"]
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 decoders = []
 
@@ -40,3 +40,11 @@ decoder_alac = ["decoders"]
 all_demuxers = ["demuxer_warhol"]
 demuxers = []
 demuxer_warhol = ["demuxers"]
+
+all_encoders = ["all_video_encoders", "all_audio_encoders"]
+encoders = []
+
+all_video_encoders = ["encoder_rawvid"]
+encoder_rawvid = ["encoders"]
+
+all_audio_encoders = []
index e771f212e0accd09cd5014c1bc09dbe288407c2f..b75a4c01602e967e78ede1281cb7ddc13b3ed25d 100644 (file)
@@ -99,9 +99,26 @@ const QT_CODECS: &[DecoderInfo] = &[
     DecoderInfo { name: "qdesign-music2", get_decoder: qdm2::get_decoder },
 ];
 
-/// Registers all available codecs provided by this crate.
+/// Registers all available decoders provided by this crate.
 pub fn qt_register_all_decoders(rd: &mut RegisteredDecoders) {
     for decoder in QT_CODECS.iter() {
         rd.add_decoder(*decoder);
     }
 }
+
+#[cfg(feature="encoder_rawvid")]
+mod rawvidenc;
+
+const QT_ENCODERS: &[EncoderInfo] = &[
+#[cfg(feature="encoder_rawvid")]
+    EncoderInfo { name: "qt-yuv2", get_encoder: rawvidenc::get_encoder_yuv2 },
+#[cfg(feature="encoder_rawvid")]
+    EncoderInfo { name: "qt-yuv4", get_encoder: rawvidenc::get_encoder_yuv4 },
+];
+
+/// Registers all available encoders provided by this crate.
+pub fn qt_register_all_encoders(re: &mut RegisteredEncoders) {
+    for encoder in QT_ENCODERS.iter() {
+        re.add_encoder(*encoder);
+    }
+}
diff --git a/nihav-qt/src/codecs/rawvidenc.rs b/nihav-qt/src/codecs/rawvidenc.rs
new file mode 100644 (file)
index 0000000..bae7a82
--- /dev/null
@@ -0,0 +1,161 @@
+use nihav_core::codecs::*;
+use std::str::FromStr;
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+enum RawType {
+    YUV2,
+    YUV4,
+}
+
+struct RawEncoder {
+    stream: Option<NAStreamRef>,
+    pkt:    Option<NAPacket>,
+    vinfo:  NAVideoInfo,
+    rtype:  RawType,
+}
+
+impl RawEncoder {
+    fn new(rtype: RawType) -> Self {
+        Self {
+            stream: None,
+            pkt:    None,
+            vinfo:  NAVideoInfo::new(0, 0, false, YUV420_FORMAT),
+            rtype,
+        }
+    }
+}
+
+impl NAEncoder for RawEncoder {
+    fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
+        match encinfo.format {
+            NACodecTypeInfo::None => {
+                let fmt = match self.rtype {
+                        RawType::YUV2 => NAPixelFormaton::from_str("yuv422p").unwrap(),
+                        RawType::YUV4 => YUV420_FORMAT,
+                    };
+                Ok(EncodeParameters {
+                    format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, fmt)),
+                    ..Default::default()
+                })
+            },
+            NACodecTypeInfo::Video(_) => {
+                let fmt = match self.rtype {
+                        RawType::YUV2 => NAPixelFormaton::from_str("yuv422p").unwrap(),
+                        RawType::YUV4 => YUV420_FORMAT,
+                    };
+                let mut info = *encinfo;
+                if let NACodecTypeInfo::Video(ref mut vinfo) = info.format {
+                    vinfo.format = fmt;
+                    vinfo.width = (vinfo.width + 1) & !1;
+                    if self.rtype == RawType::YUV4 {
+                        vinfo.height = (vinfo.height + 1) & !1;
+                    }
+                } else {
+                    return Err(EncoderError::FormatError);
+                }
+                Ok(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(vinfo) => {
+                self.vinfo = vinfo;
+                let (name, fmt) = match self.rtype {
+                        RawType::YUV2 => ("qt-yuv2", NAPixelFormaton::from_str("yuv422p").unwrap()),
+                        RawType::YUV4 => ("qt-yuv4", YUV420_FORMAT),
+                    };
+                if vinfo.format != fmt || (vinfo.width & 1) != 0 || (name == "qt-yuv4" && (vinfo.height & 1) != 0) {
+                    return Err(EncoderError::FormatError);
+                }
+                let info = NACodecInfo::new(name, 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;
+        if let Some(vinfo) = buf.get_video_info() {
+            if vinfo != self.vinfo {
+                println!("Input format differs from the initial one");
+                return Err(EncoderError::FormatError);
+            }
+        }
+        if let NABufferType::Video(ref vbuf) = buf {
+            let vinfo = vbuf.get_info();
+            let src = vbuf.get_data();
+            let yoff = vbuf.get_offset(0);
+            let uoff = vbuf.get_offset(1);
+            let voff = vbuf.get_offset(2);
+            let ystride = vbuf.get_stride(0);
+            let ustride = vbuf.get_stride(1);
+            let vstride = vbuf.get_stride(2);
+            match self.rtype {
+                RawType::YUV2 => {
+                    dbuf = Vec::with_capacity(vinfo.width * vinfo.height * 2);
+                    for (yline, (uline, vline)) in src[yoff..].chunks(ystride)
+                            .zip(src[uoff..].chunks(ustride).zip(src[voff..].chunks(vstride)))
+                            .take(vinfo.height) {
+                        for (y2, (&u, &v)) in yline.chunks_exact(2).zip(uline.iter().zip(vline.iter())) {
+                            dbuf.push(y2[0]);
+                            dbuf.push(u ^ 0x80);
+                            dbuf.push(y2[1]);
+                            dbuf.push(v ^ 0x80);
+                        }
+                    }
+                },
+                RawType::YUV4 => {
+                    dbuf = Vec::with_capacity(vinfo.width * vinfo.height * 3 / 2);
+                    for (ylines, (uline, vline)) in src[yoff..].chunks(ystride * 2)
+                            .zip(src[uoff..].chunks(ustride).zip(src[voff..].chunks(vstride)))
+                            .take(vinfo.height) {
+                        let (yline0, yline1) = ylines.split_at(ystride);
+                        for ((y0, y1), (&u, &v)) in yline0.chunks_exact(2).zip(yline1.chunks_exact(2))
+                                .zip(uline.iter().zip(vline.iter())) {
+                            dbuf.push(u ^ 0x80);
+                            dbuf.push(v ^ 0x80);
+                            dbuf.push(y0[0]);
+                            dbuf.push(y0[1]);
+                            dbuf.push(y1[0]);
+                            dbuf.push(y1[1]);
+                        }
+                    }
+                },
+            }
+        } else {
+            return Err(EncoderError::FormatError);
+        }
+        self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, true, dbuf));
+        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_yuv2() -> Box<dyn NAEncoder + Send> {
+    Box::new(RawEncoder::new(RawType::YUV2))
+}
+pub fn get_encoder_yuv4() -> Box<dyn NAEncoder + Send> {
+    Box::new(RawEncoder::new(RawType::YUV4))
+}
index 3869a58ed7efa6d4bb27f70374488346a73753a3..5396a3ecfd089431c3b0c6dc88188eaeb7a012ce 100644 (file)
@@ -8,6 +8,8 @@ extern crate nihav_codec_support;
 mod codecs;
 #[cfg(feature="decoders")]
 pub use crate::codecs::qt_register_all_decoders;
+#[cfg(feature="encoders")]
+pub use crate::codecs::qt_register_all_encoders;
 
 #[cfg(feature="demuxers")]
 mod demuxers;