simple FLAC encoder and muxer
[nihav.git] / nihav-llaudio / src / muxers / flac.rs
diff --git a/nihav-llaudio/src/muxers/flac.rs b/nihav-llaudio/src/muxers/flac.rs
new file mode 100644 (file)
index 0000000..5c649a5
--- /dev/null
@@ -0,0 +1,131 @@
+use nihav_core::muxers::*;
+
+struct FLACMuxer<'a> {
+    bw:             &'a mut ByteWriter<'a>,
+    maxpkt:         usize,
+    minpkt:         usize,
+    duration:       u64,
+    maxblk:         u16,
+    minblk:         u16,
+    bits:           u8,
+}
+
+impl<'a> FLACMuxer<'a> {
+    fn new(bw: &'a mut ByteWriter<'a>) -> Self {
+        Self {
+            bw,
+            maxpkt: std::usize::MAX, minpkt: 0,
+            maxblk: std::u16::MAX, minblk: 0,
+            duration: 0,
+            bits: 0,
+        }
+    }
+}
+
+impl<'a> MuxCore<'a> for FLACMuxer<'a> {
+    fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
+        if strmgr.get_num_streams() == 0 {
+            return Err(MuxerError::InvalidArgument);
+        }
+        let stream = strmgr.get_stream(0).unwrap();
+        if let NACodecTypeInfo::Audio(ref ainfo) = stream.get_info().get_properties() {
+            self.bw.write_buf(b"fLaC")?;
+            self.bw.write_byte(0x80)?; // last metadata block - streaminfo
+            self.bw.write_u24be(34)?; // streaminfo size
+            self.bw.write_u16be(2)?; // minimum block size
+            self.bw.write_u16be(ainfo.block_len as u16)?;
+            self.bw.write_u24be(0)?; // minimum frame size
+            self.bw.write_u24be(0)?; // maximum frame size
+
+            let bits = ainfo.format.bits - 1;
+            self.bits = bits;
+            self.bw.write_u24be(ainfo.sample_rate * 16 + u32::from(ainfo.channels - 1) * 2 + u32::from(bits >> 4))?;
+            self.bw.write_byte(bits << 4)?;
+            self.bw.write_u32be(0)?;//total samples low 32 bits
+            self.bw.write_u64be(0)?;self.bw.write_u64be(0)?; //MD5
+
+            Ok(())
+        } else {
+            Err(MuxerError::InvalidArgument)
+        }
+    }
+    fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> {
+        let pktlen  = pkt.get_buffer().len();
+        let slen    = pkt.ts.duration.unwrap_or(0);
+        let samples = if slen != 1 { slen } else { u64::from(pkt.ts.tb_den) };
+
+        self.maxpkt = self.maxpkt.max(pktlen);
+        self.minpkt = self.minpkt.min(pktlen);
+        self.maxblk = self.maxblk.max(samples as u16);
+        self.minblk = self.minblk.min(samples as u16);
+        self.duration += samples;
+
+        self.bw.write_buf(&pkt.get_buffer())?;
+        Ok(())
+    }
+    fn flush(&mut self) -> MuxerResult<()> {
+        Ok(())
+    }
+    fn end(&mut self) -> MuxerResult<()> {
+//todo: write MD5 somehow?
+        self.bw.seek(SeekFrom::Start(8))?;
+        self.bw.write_u16be(self.minblk)?;
+        self.bw.write_u16be(self.maxblk)?;
+        self.bw.write_u24be(self.minpkt as u32)?;
+        self.bw.write_u24be(self.maxpkt as u32)?;
+        self.bw.seek(SeekFrom::Current(3))?;
+        self.bw.write_byte((self.bits << 4) | (((self.duration >> 32) as u8) & 0xF))?;
+        self.bw.write_u32be(self.duration as u32)?;
+        Ok(())
+    }
+}
+
+impl<'a> NAOptionHandler for FLACMuxer<'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 FLACMuxerCreator {}
+
+impl MuxerCreator for FLACMuxerCreator {
+    fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> {
+        Box::new(FLACMuxer::new(bw))
+    }
+    fn get_name(&self) -> &'static str { "flac" }
+    fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleAudio("flac") }
+}
+
+#[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_flac_muxer() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        llaudio_register_all_demuxers(&mut dmx_reg);
+        let dec_config = DecoderTestParams {
+                demuxer:        "flac",
+                in_name:        "assets/LLaudio/luckynight.flac",
+                limit:          None,
+                stream_type:    StreamType::None,
+                dmx_reg, dec_reg: RegisteredDecoders::new(),
+            };
+        let mut mux_reg = RegisteredMuxers::new();
+        llaudio_register_all_muxers(&mut mux_reg);
+/*        let enc_config = EncoderTestParams {
+                muxer:      "flac",
+                enc_name:   "",
+                out_name:   "muxed.flac",
+                mux_reg, enc_reg: RegisteredEncoders::new(),
+            };
+        test_remuxing(&dec_config, &enc_config);*/
+        test_remuxing_md5(&dec_config, "flac", &mux_reg,
+                          [0x77afb7c0, 0x84d2bd87, 0x6e028092, 0x7db7c72e]);
+    }
+}
+