add YUV4MPEG muxer
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 3 Jun 2023 14:56:56 +0000 (16:56 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 4 Jun 2023 13:36:47 +0000 (15:36 +0200)
nihav-commonfmt/Cargo.toml
nihav-commonfmt/src/muxers/mod.rs
nihav-commonfmt/src/muxers/y4m.rs [new file with mode: 0644]

index 12c33fdb5ee59343b534add9f5d9e9a68665fc4c..6e7d02094ed16f7eec60c40e5c9bb54947d14ab0 100644 (file)
@@ -28,9 +28,10 @@ demuxer_avi = ["demuxers"]
 demuxer_mov = ["demuxers"]
 demuxer_wav = ["demuxers"]
 demuxer_y4m = ["demuxers"]
-all_muxers = ["muxer_avi", "muxer_wav"]
+all_muxers = ["muxer_avi", "muxer_wav", "muxer_y4m"]
 muxer_avi = ["muxers"]
 muxer_wav = ["muxers"]
+muxer_y4m = ["muxers"]
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 
index 6df929cd5077e28594470bee133c348b7d56d602..809283f6c2705ad6c37429fb3386e272353e41c6 100644 (file)
@@ -4,12 +4,16 @@ use nihav_core::muxers::*;
 mod avi;
 #[cfg(feature="muxer_wav")]
 mod wav;
+#[cfg(feature="muxer_y4m")]
+mod y4m;
 
 const MUXERS: &[&dyn MuxerCreator] = &[
 #[cfg(feature="muxer_avi")]
     &avi::AVIMuxerCreator {},
 #[cfg(feature="muxer_wav")]
     &wav::WAVMuxerCreator {},
+#[cfg(feature="muxer_y4m")]
+    &y4m::Y4MMuxerCreator {},
 ];
 
 pub fn generic_register_all_muxers(rm: &mut RegisteredMuxers) {
diff --git a/nihav-commonfmt/src/muxers/y4m.rs b/nihav-commonfmt/src/muxers/y4m.rs
new file mode 100644 (file)
index 0000000..600f78e
--- /dev/null
@@ -0,0 +1,120 @@
+use nihav_core::muxers::*;
+
+struct Y4MMuxer<'a> {
+    bw:             &'a mut ByteWriter<'a>,
+}
+
+impl<'a> Y4MMuxer<'a> {
+    fn new(bw: &'a mut ByteWriter<'a>) -> Self {
+        Self {
+            bw,
+        }
+    }
+}
+
+fn get_format_name(fmt: NAPixelFormaton) -> MuxerResult<&'static str> {
+    if fmt.model.is_yuv() && fmt.get_max_depth() == 8 {
+        match fmt.components {
+            1 => Ok("mono"),
+            3 => {
+                let uinfo = fmt.comp_info[1].unwrap();
+                let vinfo = fmt.comp_info[2].unwrap();
+                if uinfo.h_ss != vinfo.h_ss || uinfo.v_ss != vinfo.v_ss {
+                    return Err(MuxerError::UnsupportedFormat);
+                }
+                match (uinfo.h_ss, vinfo.v_ss) {
+                    (0, 0) => Ok("444"),
+                    (1, 0) => Ok("422"),
+                    (1, 1) => Ok("420"),
+                    (2, 0) => Ok("411"),
+                    _ => Err(MuxerError::UnsupportedFormat),
+                }
+            },
+            _ => Err(MuxerError::UnsupportedFormat),
+        }
+    } else {
+        Err(MuxerError::UnsupportedFormat)
+    }
+}
+
+impl<'a> MuxCore<'a> for Y4MMuxer<'a> {
+    fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
+        if strmgr.get_num_streams() != 1 {
+            return Err(MuxerError::InvalidArgument);
+        }
+        let vstr = strmgr.get_stream(0).unwrap();
+        if vstr.get_media_type() != StreamType::Video {
+            return Err(MuxerError::UnsupportedFormat);
+        }
+        let info = vstr.get_info();
+        let vinfo = info.get_properties().get_video_info().unwrap();
+        self.bw.write_buf(format!("YUV4MPEG2 W{} H{} F{}:{} C{}\n", vinfo.width, vinfo.height, vstr.tb_den, vstr.tb_num, get_format_name(vinfo.format)?).as_bytes())?;
+
+        Ok(())
+    }
+    fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> {
+        if self.bw.tell() == 0 {
+            return Err(MuxerError::NotCreated);
+        }
+        let src = pkt.get_buffer();
+        self.bw.write_buf(b"FRAME\n")?;
+        self.bw.write_buf(&src)?;
+        Ok(())
+    }
+    fn flush(&mut self) -> MuxerResult<()> {
+        Ok(())
+    }
+    fn end(&mut self) -> MuxerResult<()> {
+        Ok(())
+    }
+}
+
+impl<'a> NAOptionHandler for Y4MMuxer<'a> {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub struct Y4MMuxerCreator {}
+
+impl MuxerCreator for Y4MMuxerCreator {
+    fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> {
+        Box::new(Y4MMuxer::new(bw))
+    }
+    fn get_name(&self) -> &'static str { "yuv4mpeg" }
+    fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleVideo("rawvideo") }
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::*;
+    use nihav_core::demuxers::*;
+    use nihav_core::muxers::*;
+    use nihav_codec_support::test::enc_video::*;
+    use crate::*;
+
+    #[test]
+    fn test_y4m_muxer() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        // sample: self-created with avconv
+        let dec_config = DecoderTestParams {
+                demuxer:        "yuv4mpeg",
+                in_name:        "assets/Misc/test.y4m",
+                limit:          None,
+                stream_type:    StreamType::None,
+                dmx_reg, dec_reg: RegisteredDecoders::new(),
+            };
+        let mut mux_reg = RegisteredMuxers::new();
+        generic_register_all_muxers(&mut mux_reg);
+        /*let enc_config = EncoderTestParams {
+                muxer:      "yuv4mpeg",
+                enc_name:   "",
+                out_name:   "muxed.y4m",
+                mux_reg, enc_reg: RegisteredEncoders::new(),
+            };
+        test_remuxing(&dec_config, &enc_config);*/
+        test_remuxing_md5(&dec_config, "yuv4mpeg", &mux_reg,
+                          [0x7586c1c5, 0x388209b8, 0xe08af8f8, 0x4b6b96c7]);
+    }
+}