nihav_core = { path = "../nihav-core" }
nihav_commonfmt = { path = "../nihav-commonfmt" }
nihav_duck = { path = "../nihav-duck" }
+nihav_flash = { path = "../nihav-flash" }
nihav_game = { path = "../nihav-game" }
nihav_indeo = { path = "../nihav-indeo" }
nihav_itu = { path = "../nihav-itu" }
use nihav_duck::duck_register_all_demuxers;
use nihav_duck::duck_register_all_encoders;
+use nihav_flash::flash_register_all_decoders;
+use nihav_flash::flash_register_all_demuxers;
+use nihav_flash::flash_register_all_encoders;
+use nihav_flash::flash_register_all_muxers;
+
use nihav_game::game_register_all_decoders;
use nihav_game::game_register_all_demuxers;
pub fn nihav_register_all_decoders(rd: &mut RegisteredDecoders) {
generic_register_all_decoders(rd);
duck_register_all_decoders(rd);
+ flash_register_all_decoders(rd);
game_register_all_decoders(rd);
indeo_register_all_decoders(rd);
itu_register_all_decoders(rd);
pub fn nihav_register_all_demuxers(rd: &mut RegisteredDemuxers) {
duck_register_all_demuxers(rd);
generic_register_all_demuxers(rd);
+ flash_register_all_demuxers(rd);
game_register_all_demuxers(rd);
llaudio_register_all_demuxers(rd);
rad_register_all_demuxers(rd);
/// Registers all known encoders.
pub fn nihav_register_all_encoders(re: &mut RegisteredEncoders) {
+ flash_register_all_encoders(re);
generic_register_all_encoders(re);
duck_register_all_encoders(re);
llaudio_register_all_encoders(re);
/// Registers all known demuxers.
pub fn nihav_register_all_muxers(rm: &mut RegisteredMuxers) {
+ flash_register_all_muxers(rm);
generic_register_all_muxers(rm);
llaudio_register_all_muxers(rm);
}
DecoderInfo { name: "vp5", get_decoder: vp5::get_decoder },
#[cfg(feature="decoder_vp6")]
DecoderInfo { name: "vp6", get_decoder: vp6::get_decoder_vp6 },
+#[cfg(feature="decoder_vp6")]
+ DecoderInfo { name: "vp6f", get_decoder: vp6::get_decoder_vp6f },
#[cfg(feature="decoder_vp6")]
DecoderInfo { name: "vp6a", get_decoder: vp6::get_decoder_vp6_alpha },
#[cfg(feature="decoder_vp7")]
const DUCK_ENCODERS: &[EncoderInfo] = &[
#[cfg(feature="encoder_vp6")]
EncoderInfo { name: "vp6", get_encoder: vp6enc::get_encoder },
+#[cfg(feature="encoder_vp6")]
+ EncoderInfo { name: "vp6f", get_encoder: vp6enc::get_encoder_flv },
];
/// Registers all available encoders provided by this crate.
let hdr = br.parse_header(&mut bc)?;
validate!((hdr.offset as usize) < aoffset); //XXX: take alpha 3 byte offset into account?
- if hdr.mb_w != 0 {
+ if hdr.mb_w != 0 && (usize::from(hdr.mb_w) != self.mb_w || usize::from(hdr.mb_h) != self.mb_h) {
self.set_dimensions((hdr.mb_w as usize) * 16, (hdr.mb_h as usize) * 16);
}
let fmt = if !self.has_alpha {
info: NACodecInfoRef,
br: VP6BR,
has_alpha: bool,
+ flipped: bool,
}
impl VP6Decoder {
- fn new(has_alpha: bool) -> Self {
+ fn new(flipped: bool, has_alpha: bool) -> Self {
Self {
- dec: VP56Decoder::new(6, has_alpha, true),
+ dec: VP56Decoder::new(6, has_alpha, flipped),
info: NACodecInfoRef::default(),
br: VP6BR::new(),
has_alpha,
+ flipped,
}
}
}
} else {
VP_YUVA420_FORMAT
};
- let myvinfo = NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, fmt);
+ let mut width = vinfo.get_width();
+ let mut height = vinfo.get_height();
+ if let (false, Some(edta)) = (self.flipped, info.get_extradata()) {
+ if (width & 0xF) == 0 && (height & 0xF) == 0 {
+ width -= usize::from(edta[0] >> 4);
+ height -= usize::from(edta[0] & 0xF);
+ }
+ }
+ let myvinfo = NAVideoInfo::new(width, height, self.flipped, fmt);
let myinfo = NACodecTypeInfo::Video(myvinfo);
self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
self.dec.init(supp, myvinfo)?;
}
pub fn get_decoder_vp6() -> Box<dyn NADecoder + Send> {
- Box::new(VP6Decoder::new(false))
+ Box::new(VP6Decoder::new(true, false))
+}
+
+pub fn get_decoder_vp6f() -> Box<dyn NADecoder + Send> {
+ Box::new(VP6Decoder::new(false, false))
}
pub fn get_decoder_vp6_alpha() -> Box<dyn NADecoder + Send> {
- Box::new(VP6Decoder::new(true))
+ Box::new(VP6Decoder::new(false, true))
}
#[cfg(test)]
[0xd3801a96, 0x1b5384ff, 0x422b228c, 0x9c4582c4],
[0x8ddb0dfe, 0x302eb1ed, 0x10e64e22, 0x5a5a62b9],
[0x79338328, 0x06113781, 0x8b116d18, 0xde56707e],
- [0x671fa1b3, 0x0b3e41e9, 0xeb3a494c, 0xcdd24b1b]]));
+ [0xdb58433b, 0x1de4ce67, 0x15fcbcee, 0x1df9de61]]));
}
}
fenc: FrameEncoder,
ratectl: RateControl,
+ flipped: bool,
huffman: bool,
version: u8,
}
impl VP6Encoder {
- fn new() -> Self {
+ fn new(flipped: bool) -> Self {
let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, VP_YUVA420_FORMAT), 4).unwrap();
let mc_buf = vt.get_vbuf().unwrap();
Self {
ratectl: RateControl::new(),
mc_buf,
+ flipped,
huffman: false,
version: VERSION_VP60,
match encinfo.format {
NACodecTypeInfo::None => {
let mut ofmt = EncodeParameters::default();
- ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT));
+ ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, self.flipped, YUV420_FORMAT));
Ok(ofmt)
},
NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
NACodecTypeInfo::Video(vinfo) => {
- let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, YUV420_FORMAT);
+ let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, self.flipped, YUV420_FORMAT);
let mut ofmt = *encinfo;
ofmt.format = NACodecTypeInfo::Video(outinfo);
Ok(ofmt)
return Err(EncoderError::FormatError);
}
- let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, vinfo.format);
- let info = NACodecInfo::new("vp6", NACodecTypeInfo::Video(out_info), None);
+ let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, self.flipped, vinfo.format);
+ let info = NACodecInfo::new(if self.flipped { "vp6" } else { "vp6f" }, NACodecTypeInfo::Video(out_info), 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();
}
pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
- Box::new(VP6Encoder::new())
+ Box::new(VP6Encoder::new(true))
+}
+
+pub fn get_encoder_flv() -> Box<dyn NAEncoder + Send> {
+ Box::new(VP6Encoder::new(false))
}
#[cfg(test)]
--- /dev/null
+[package]
+name = "nihav_flash"
+version = "0.1.0"
+authors = ["Kostya Shishkov <kostya.shishkov@gmail.com>"]
+edition = "2018"
+
+[dependencies.nihav_core]
+path = "../nihav-core"
+
+[dependencies.nihav_registry]
+path = "../nihav-registry"
+
+[dependencies.nihav_codec_support]
+path = "../nihav-codec-support"
+features = ["h263", "mdct", "dsp_window"]
+
+[features]
+default = ["all_decoders", "all_demuxers", "all_encoders", "all_muxers"]
+decoders = []
+demuxers = []
+encoders = []
+muxers = []
+all_demuxers = ["demuxer_flv"]
+demuxer_flv = ["demuxers"]
+
+all_decoders = ["all_video_decoders", "all_audio_decoders"]
+
+all_video_decoders = ["decoder_flv263", "decoder_flashsv"]
+decoder_flv263 = ["decoders"]
+decoder_flashsv = ["decoders"]
+
+all_audio_decoders = ["decoder_flv_adpcm", "decoder_asao"]
+decoder_flv_adpcm = ["decoders"]
+decoder_asao = ["decoders"]
+
+all_encoders = ["all_audio_encoders"]
+all_audio_encoders = ["encoder_flv_adpcm"]
+encoder_flv_adpcm = ["encoders"]
+
+all_muxers = ["muxer_flv"]
+muxer_flv = ["muxers"]
\ No newline at end of file
--- /dev/null
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use nihav_codec_support::codecs::imaadpcm::*;
+use std::str::FromStr;
+
+const STEPS: [&[i8]; 4] = [
+ &[ -1, 2],
+ &[ -1, -1, 2, 4],
+ &[ -1, -1, -1, -1, 2, 4, 6, 8],
+ &[ -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 ]
+];
+
+#[derive(Clone,Copy)]
+struct State {
+ predictor: i32,
+ step: usize,
+ smask: u8,
+ steps: &'static [i8],
+}
+
+impl State {
+ fn new() -> Self {
+ Self {
+ predictor: 0,
+ step: 0,
+ smask: 0,
+ steps: STEPS[2],
+ }
+ }
+ fn expand_sample(&mut self, nibble: u8) -> i16 {
+ let istep = (self.step as isize) + isize::from(self.steps[(nibble & !self.smask) as usize]);
+ let sign = (nibble & self.smask) != 0;
+ let diff = (i32::from(2 * (nibble & !self.smask) + 1) * IMA_STEP_TABLE[self.step]) >> 3;
+ let sample = if !sign { self.predictor + diff } else { self.predictor - diff };
+ self.predictor = sample.max(i32::from(std::i16::MIN)).min(i32::from(std::i16::MAX));
+ self.step = istep.max(0).min(IMA_MAX_STEP as isize) as usize;
+ self.predictor as i16
+ }
+}
+
+const BLOCK_LEN: usize = 4096;
+
+struct ADPCMDecoder {
+ ainfo: NAAudioInfo,
+ chmap: NAChannelMap,
+ ch_state: [State; 2],
+}
+
+impl ADPCMDecoder {
+ fn new() -> Self {
+ Self {
+ ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, BLOCK_LEN),
+ chmap: NAChannelMap::new(),
+ ch_state: [State::new(), State::new()],
+ }
+ }
+}
+
+impl NADecoder for ADPCMDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+ let channels = ainfo.get_channels() as usize;
+ validate!(channels == 2 || channels == 1);
+ self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, 0);
+ self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap();
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let info = pkt.get_stream().get_info();
+ if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+ let src = pkt.get_buffer();
+ let channels = self.chmap.num_channels();
+ let mut br = BitReader::new(&src, BitReaderMode::BE);
+ let step_size = br.read(2)? as usize + 2;
+
+ let pkt_size = (16 + 6 + (BLOCK_LEN - 1) * step_size) * channels;
+ let num_pkts = ((br.left() as usize) / pkt_size).max(1);
+ let nsamples = num_pkts * BLOCK_LEN;
+ let mut abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?;
+ let mut adata = abuf.get_abuf_i16().unwrap();
+ let mut off = [adata.get_offset(0), adata.get_offset(1)];
+ let dst = adata.get_data_mut().unwrap();
+
+ let mut tot_samples = 0;
+ for _pkt in 0..num_pkts {
+ for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() {
+ state.predictor = br.read_s(16)?;
+ state.step = br.read(6)? as usize;
+ state.steps = STEPS[step_size - 2];
+ state.smask = 1 << (step_size - 1);
+ dst[off[ch]] = state.predictor as i16;
+ off[ch] += 1;
+ }
+ tot_samples += 1;
+ let cur_samples = ((br.left() as usize) / step_size / channels).min(BLOCK_LEN - 1);
+ for _ in 0..cur_samples {
+ for (ch, state) in self.ch_state[..channels].iter_mut().enumerate() {
+ let idx = br.read(step_size as u8)? as u8;
+ dst[off[ch]] = state.expand_sample(idx);
+ off[ch] += 1;
+ }
+ }
+ tot_samples += cur_samples;
+ }
+ abuf.truncate_audio(tot_samples);
+
+ let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf);
+ frm.set_duration(Some(tot_samples as u64));
+ frm.set_keyframe(true);
+ Ok(frm.into_ref())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn flush(&mut self) {
+ }
+}
+
+impl NAOptionHandler for ADPCMDecoder {
+ 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_decoder() -> Box<dyn NADecoder + Send> {
+ Box::new(ADPCMDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::flash_register_all_decoders;
+ use crate::flash_register_all_demuxers;
+ #[test]
+ fn test_flv_adpcm_mono() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decoding("flv", "flv-adpcm", "assets/Flash/mono_11k.flv", Some(3000), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0x4cf30e71, 0x4360c85b, 0x21c52863, 0x1782160e]));
+ }
+ #[test]
+ fn test_flv_adpcm_stereo() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decoding("flv", "flv-adpcm", "assets/Flash/stereo_44k.flv", Some(3000), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0xae108d38, 0xb36236f8, 0x2bc18d31, 0xac600424]));
+ }
+}
--- /dev/null
+use nihav_core::codecs::*;
+use nihav_core::io::bitwriter::*;
+use nihav_codec_support::codecs::imaadpcm::*;
+
+#[derive(Default)]
+struct ADPCMEncoder {
+ stream: Option<NAStreamRef>,
+ samples: Vec<i16>,
+ flush: bool,
+ state: [IMAState; 2],
+ channels: usize,
+ srate: u32,
+ pos: u64,
+}
+
+const BLOCK_LEN: usize = 4096;
+
+impl ADPCMEncoder {
+ fn new() -> Self { Self::default() }
+ fn encode_packet(&mut self) -> EncoderResult<NAPacket> {
+ if self.samples.is_empty() {
+ return Err(EncoderError::TryAgain);
+ }
+ let cur_len = (self.samples.len() / self.channels).min(BLOCK_LEN);
+ if cur_len < BLOCK_LEN && !self.flush {
+ return Err(EncoderError::TryAgain);
+ }
+
+ let bsize = (2 + (16 + 6 + (cur_len - 1) * 4) * self.channels + 7) / 8;
+
+ let mut bw = BitWriter::new(Vec::with_capacity(bsize), BitWriterMode::BE);
+
+ bw.write(2, 2);
+
+ for ch in 0..self.channels {
+ self.state[ch].predictor = i32::from(self.samples[ch]);
+ self.state[ch].step = 30;
+
+ bw.write_s(i32::from(self.samples[ch]), 16);
+ bw.write(self.state[ch].step as u32, 6);
+ }
+
+ let mut siter = self.samples[self.channels..].iter();
+ for _ in 1..cur_len {
+ for state in self.state[..self.channels].iter_mut() {
+ let samp = *siter.next().unwrap();
+ let nib = state.compress_sample(samp);
+ state.expand_sample(nib);
+ bw.write(u32::from(nib), 4);
+ }
+ }
+
+ let data = bw.end();
+
+ self.samples.drain(..cur_len * self.channels);
+ let ts = NATimeInfo::new(Some(self.pos), None, Some(cur_len as u64), 1, self.srate);
+ self.pos += cur_len as u64;
+ Ok(NAPacket::new(self.stream.clone().unwrap(), ts, true, data))
+ }
+}
+
+impl NAEncoder for ADPCMEncoder {
+ fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
+ match encinfo.format {
+ NACodecTypeInfo::None => {
+ let mut ofmt = EncodeParameters::default();
+ ofmt.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_S16_FORMAT, BLOCK_LEN));
+ Ok(ofmt)
+ },
+ NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
+ NACodecTypeInfo::Audio(ainfo) => {
+ let mut outinfo = ainfo;
+ outinfo.channels = outinfo.channels.max(1).min(2);
+ if outinfo.format != SND_S16P_FORMAT && outinfo.format != SND_S16_FORMAT {
+ outinfo.format = SND_S16_FORMAT;
+ }
+ outinfo.block_len = BLOCK_LEN;
+ let mut ofmt = *encinfo;
+ ofmt.format = NACodecTypeInfo::Audio(outinfo);
+ Ok(ofmt)
+ }
+ }
+ }
+ fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
+ match encinfo.format {
+ NACodecTypeInfo::None => Err(EncoderError::FormatError),
+ NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
+ NACodecTypeInfo::Audio(ainfo) => {
+ if ainfo.format != SND_S16P_FORMAT && ainfo.format != SND_S16_FORMAT {
+ return Err(EncoderError::FormatError);
+ }
+ if ainfo.channels != 1 && ainfo.channels != 2 {
+ return Err(EncoderError::FormatError);
+ }
+ self.channels = ainfo.channels as usize;
+
+ let soniton = NASoniton::new(4, 0);
+ let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, if self.channels == 1 { 2051 } else { 4101 });
+ let info = NACodecInfo::new("flv-adpcm", NACodecTypeInfo::Audio(out_ainfo), None);
+ let mut stream = NAStream::new(StreamType::Audio, stream_id, info, 1, ainfo.sample_rate, 0);
+ stream.set_num(stream_id as usize);
+ let stream = stream.into_ref();
+
+ self.stream = Some(stream.clone());
+ self.samples = Vec::with_capacity(BLOCK_LEN * self.channels);
+ self.srate = ainfo.sample_rate;
+ self.flush = false;
+
+ Ok(stream)
+ },
+ }
+ }
+ fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
+ let buf = frm.get_buffer();
+ if let Some(ref abuf) = buf.get_abuf_i16() {
+ let src = abuf.get_data();
+ let len = abuf.get_length();
+ let ch = abuf.get_chmap().num_channels();
+ if abuf.get_step() > 1 || ch == 1 {
+ self.samples.extend_from_slice(&src[..len * ch]);
+ } else {
+ let astride = abuf.get_stride();
+ self.samples.reserve(len * ch);
+ for i in 0..len {
+ for ch in 0..self.channels {
+ self.samples.push(src[ch * astride + i]);
+ }
+ }
+ }
+ Ok(())
+ } else {
+ Err(EncoderError::InvalidParameters)
+ }
+ }
+ fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
+ if let Ok(pkt) = self.encode_packet() {
+ Ok(Some(pkt))
+ } else {
+ Ok(None)
+ }
+ }
+ fn flush(&mut self) -> EncoderResult<()> {
+ self.flush = true;
+ Ok(())
+ }
+}
+
+impl NAOptionHandler for ADPCMEncoder {
+ 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(ADPCMEncoder::new())
+}
+
+#[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_flv_adpcm_encoder() {
+
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+ let mut mux_reg = RegisteredMuxers::new();
+ flash_register_all_muxers(&mut mux_reg);
+ let mut enc_reg = RegisteredEncoders::new();
+ flash_register_all_encoders(&mut enc_reg);
+
+ let dec_config = DecoderTestParams {
+ demuxer: "flv",
+ in_name: "assets/Flash/mono_11k.flv",
+ stream_type: StreamType::Audio,
+ limit: Some(3700),
+ dmx_reg, dec_reg,
+ };
+ let enc_config = EncoderTestParams {
+ muxer: "flv",
+ enc_name: "flv-adpcm",
+ out_name: "flv_adpcm.flv",
+ mux_reg, enc_reg,
+ };
+ let dst_ainfo = NAAudioInfo {
+ sample_rate: 0,
+ channels: 0,
+ format: SND_S16_FORMAT,
+ block_len: 128,
+ };
+ let enc_params = EncodeParameters {
+ format: NACodecTypeInfo::Audio(dst_ainfo),
+ quality: 0,
+ bitrate: 0,
+ tb_num: 0,
+ tb_den: 0,
+ flags: 0,
+ };
+ //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
+
+ test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
+ &[0x6114b7de, 0xb84c226f, 0x0caab8e5, 0x2c7df63f]);
+ }
+}
--- /dev/null
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use nihav_codec_support::dsp::mdct::*;
+use nihav_codec_support::dsp::window::*;
+
+struct Random { state: u32 }
+
+impl Random {
+ fn new(seed: u32) -> Self { Self { state: seed } }
+ fn next(&mut self) -> u32 {
+ self.state ^= self.state << 13;
+ self.state ^= self.state >> 17;
+ self.state ^= self.state << 5;
+ self.state
+ }
+}
+
+const NUM_BANDS: usize = 23;
+const BLOCK_LEN: usize = 256;
+const PACKED_BLK_LEN: usize = 64;
+const CODED_LEN: usize = 124;
+
+struct ASAODecoder {
+ ainfo: NAAudioInfo,
+ chmap: NAChannelMap,
+ window: [f32; 128],
+ imdct: IMDCT,
+ rng: Random,
+
+ scales: [f32; 128],
+ iscales: [i32; 128],
+ bits: [i8; 128],
+ coeffs: [f32; 128],
+ prev: [f32; 128],
+ tmp: [f32; 128],
+}
+
+const HEADER_BITS: u32 = 116;
+const SUBPART_BITS: i32 = (((PACKED_BLK_LEN as u32) * 8 - HEADER_BITS) / 2) as i32;
+const MAX_CBITS: i32 = 6;
+const BASE_OFF: i32 = 4228;
+const BASE_SHIFT: i8 = 19;
+
+trait SignedShift {
+ type Output;
+ fn sshift(self, shift: i8) -> Self::Output;
+}
+
+impl SignedShift for i32 {
+ type Output = Self;
+ fn sshift(self, shift: i8) -> Self::Output {
+ if shift >= 0 {
+ self << shift
+ } else {
+ self >> -shift
+ }
+ }
+}
+
+fn norm(val: &mut i32) -> i8 {
+ if *val == 0 {
+ 31
+ } else {
+ let shift = val.abs().leading_zeros() - 1;
+ *val <<= shift;
+ shift as i8
+ }
+}
+
+fn sum_bits(src: &[i32; 128], shift: i8, off: i32) -> i32 {
+ let mut sum = 0;
+ for &el in src[..CODED_LEN].iter() {
+ let val = (((el - off) >> (shift - 1)) + 1) >> 1;
+ sum += val.max(0).min(MAX_CBITS);
+ }
+ sum
+}
+
+fn bitalloc(bits: &mut [i8; 128], scales: &[i32; 128]) {
+ let mut max = scales[..CODED_LEN].iter().fold(scales[0], |v, &x| v.max(x));
+ let mut shift = norm(&mut max) - 16;
+
+ let mut tmp = [0; 128];
+ let mut sum = 0;
+ for i in 0..CODED_LEN {
+ tmp[i] = (scales[i].sshift(shift) * 3) >> 2;
+ sum += tmp[i];
+ }
+
+ shift += 11;
+ let ref_shift = shift;
+
+ sum -= SUBPART_BITS << shift;
+ shift += norm(&mut sum);
+ shift = ref_shift - (BASE_SHIFT + shift - 31);
+ let mut offset = ((BASE_OFF * (sum >> 16)) >> 15).sshift(shift);
+
+ let mut bitsum = sum_bits(&tmp, ref_shift, offset);
+
+ if bitsum != SUBPART_BITS {
+ let mut off2 = bitsum - SUBPART_BITS;
+ shift = 0;
+ while off2.abs() <= 0x3FFF {
+ off2 <<= 1;
+ shift += 1;
+ }
+
+ shift = ref_shift - (BASE_SHIFT + shift - 15);
+ off2 = ((BASE_OFF * off2) >> 15).sshift(shift);
+
+ let mut last_off = 0;
+ let mut last_bitsum = 0;
+ let mut iter = 1;
+ while iter < 20 {
+ last_off = offset;
+ offset += off2;
+ last_bitsum = bitsum;
+ bitsum = sum_bits(&tmp, ref_shift, offset);
+ if (bitsum - SUBPART_BITS) * (last_bitsum - SUBPART_BITS) <= 0 {
+ break;
+ }
+ iter += 1;
+ }
+
+ let (mut big_off, mut small_off, mut big_sum, mut small_sum) = if bitsum > SUBPART_BITS {
+ (offset, last_off, bitsum, last_bitsum)
+ } else {
+ (last_off, offset, last_bitsum, bitsum)
+ };
+
+ while bitsum != SUBPART_BITS && iter < 20 {
+ let off = (big_off + small_off) >> 1;
+ bitsum = sum_bits(&tmp, ref_shift, off);
+ if bitsum > SUBPART_BITS {
+ big_off = off;
+ big_sum = bitsum;
+ } else {
+ small_off = off;
+ small_sum = bitsum;
+ }
+ iter += 1;
+ }
+
+ if (big_sum - SUBPART_BITS).abs() >= (small_sum - SUBPART_BITS).abs() {
+ offset = small_off;
+ bitsum = small_sum;
+ } else {
+ offset = big_off;
+ bitsum = big_sum;
+ }
+ }
+
+ for (bits, &val) in bits.iter_mut().zip(tmp.iter()).take(CODED_LEN) {
+ *bits = ((((val - offset) >> (ref_shift - 1)) + 1) >> 1).max(0).min(MAX_CBITS) as i8;
+ }
+ if bitsum > SUBPART_BITS {
+ let mut sum = 0;
+ let mut i = 0;
+ while sum < SUBPART_BITS {
+ sum += i32::from(bits[i]);
+ i += 1;
+ }
+ bits[i - 1] -= (sum - SUBPART_BITS) as i8;
+ while i < CODED_LEN {
+ bits[i] = 0;
+ i += 1;
+ }
+ }
+}
+
+fn overlap_add(dst: &mut [f32], src: &[f32; 128], prev: &[f32; 128], window: &[f32; 128]) {
+ for i in 0..64 {
+ let p = prev[64 + i];
+ let s = src [63 - i];
+ let w0 = window[i];
+ let w1 = window[127 - i];
+ dst[i] = p * w1 - s * w0;
+ dst[127 - i] = p * w0 + s * w1;
+ }
+}
+
+impl ASAODecoder {
+ fn new() -> Self {
+ let mut window = [0.0; 128];
+ generate_window(WindowType::Sine, 1.0/1024.0, 128, true, &mut window);
+
+ Self {
+ ainfo: NAAudioInfo::new(0, 1, SND_F32P_FORMAT, BLOCK_LEN),
+ chmap: NAChannelMap::new(),
+ window,
+ imdct: IMDCT::new(256, true),
+ rng: Random::new(42),
+
+ scales: [0.0; 128],
+ iscales: [0; 128],
+ bits: [0; 128],
+ coeffs: [0.0; 128],
+ prev: [0.0; 128],
+ tmp: [0.0; 128],
+ }
+ }
+ fn decode_block(&mut self, br: &mut BitReader, dst: &mut [f32]) -> DecoderResult<()> {
+ let mut scale = i32::from(SCALE[br.read(6)? as usize]);
+ let mut sc_iter = self.scales.iter_mut();
+ let mut isc_iter = self.iscales.iter_mut();
+ for (band, &band_size) in BAND_SIZES.iter().enumerate() {
+ if band > 0 {
+ scale += i32::from(SCALE_DIFF[br.read(5)? as usize]);
+ }
+ let scf = -(2.0f32.powf((scale as f32) / 2048.0));
+ for _ in 0..band_size {
+ *sc_iter.next().unwrap() = scf;
+ *isc_iter.next().unwrap() = scale;
+ }
+ }
+
+ bitalloc(&mut self.bits, &self.iscales);
+
+ let mut coeffs = &mut self.coeffs;
+ let mut prev = &mut self.prev;
+ for (i, out) in dst.chunks_exact_mut(BLOCK_LEN / 2).take(2).enumerate() {
+ br.seek(HEADER_BITS + (SUBPART_BITS as u32) * (i as u32))?;
+ for j in 0..CODED_LEN {
+ if self.bits[j] <= 0 {
+ self.tmp[j] = std::f32::consts::FRAC_1_SQRT_2;
+ if (self.rng.next() & 1) != 0 {
+ self.tmp[j] = -self.tmp[j];
+ }
+ } else {
+ let val = br.read(self.bits[j] as u8)? as usize;
+ self.tmp[j] = QUANT_VALS[self.bits[j] as usize][val];
+ }
+ self.tmp[j] *= self.scales[j];
+ }
+ for j in CODED_LEN..128 {
+ self.tmp[j] = 0.0;
+ }
+ self.imdct.imdct_half(&self.tmp, coeffs);
+ overlap_add(out, coeffs, prev, &self.window);
+ std::mem::swap(&mut coeffs, &mut prev);
+ }
+
+ Ok(())
+ }
+}
+
+
+impl NADecoder for ASAODecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+ self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), 1, SND_F32P_FORMAT, BLOCK_LEN);
+ self.chmap = NAChannelMap::new();
+ self.chmap.add_channel(NAChannelType::C);
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let info = pkt.get_stream().get_info();
+ if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+ let src = pkt.get_buffer();
+
+ validate!((src.len() % PACKED_BLK_LEN) == 0);
+ let npkts = src.len() / PACKED_BLK_LEN;
+ let nsamples = npkts * BLOCK_LEN;
+ let abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?;
+ let mut adata = abuf.get_abuf_f32().unwrap();
+ let dst = adata.get_data_mut().unwrap();
+
+ for (src, dst) in src.chunks_exact(PACKED_BLK_LEN).zip(dst.chunks_mut(BLOCK_LEN)) {
+ let mut br = BitReader::new(&src, BitReaderMode::LE);
+ self.decode_block(&mut br, dst)?;
+ }
+
+ let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf);
+ frm.set_duration(Some(nsamples as u64));
+ frm.set_keyframe(true);
+ Ok(frm.into_ref())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn flush(&mut self) {
+ }
+}
+
+impl NAOptionHandler for ASAODecoder {
+ 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_decoder() -> Box<dyn NADecoder + Send> {
+ Box::new(ASAODecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::flash_register_all_decoders;
+ use crate::flash_register_all_demuxers;
+ #[test]
+ fn test_asao() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decode_audio("flv", "assets/Flash/nellymoser-in-flv.flv", Some(3000), None/*Some("asao")*/, &dmx_reg, &dec_reg);
+ }
+}
+
+#[allow(clippy::excessive_precision)]
+const QUANT_VALS: [&[f32]; 7] = [
+ &[ 0.0000000000 ],
+ &[ -0.8472560048, 0.7224709988 ],
+ &[ -1.5247479677, -0.4531480074, 0.3753609955, 1.4717899561 ],
+ &[ -1.9822579622, -1.1929379702, -0.5829370022, -0.0693780035,
+ 0.3909569979, 0.9069200158, 1.4862740040, 2.2215409279 ],
+ &[ -2.3887870312, -1.8067539930, -1.4105420113, -1.0773609877,
+ -0.7995010018, -0.5558109879, -0.3334020078, -0.1324490011,
+ 0.0568020009, 0.2548770010, 0.4773550034, 0.7386850119,
+ 1.0443060398, 1.3954459429, 1.8098750114, 2.3918759823 ],
+ &[ -2.3893830776, -1.9884680510, -1.7514040470, -1.5643119812,
+ -1.3922129869, -1.2164649963, -1.0469499826, -0.8905100226,
+ -0.7645580173, -0.6454579830, -0.5259280205, -0.4059549868,
+ -0.3029719889, -0.2096900046, -0.1239869967, -0.0479229987,
+ 0.0257730000, 0.1001340002, 0.1737180054, 0.2585540116,
+ 0.3522900045, 0.4569880068, 0.5767750144, 0.7003160119,
+ 0.8425520062, 1.0093879700, 1.1821349859, 1.3534560204,
+ 1.5320819616, 1.7332619429, 1.9722349644, 2.3978140354 ],
+ &[ -2.5756309032, -2.0573320389, -1.8984919786, -1.7727810144,
+ -1.6662600040, -1.5742180347, -1.4993319511, -1.4316639900,
+ -1.3652280569, -1.3000990152, -1.2280930281, -1.1588579416,
+ -1.0921250582, -1.0135740042, -0.9202849865, -0.8287050128,
+ -0.7374889851, -0.6447759867, -0.5590940118, -0.4857139885,
+ -0.4110319912, -0.3459700048, -0.2851159871, -0.2341620028,
+ -0.1870580018, -0.1442500055, -0.1107169986, -0.0739680007,
+ -0.0365610011, -0.0073290002, 0.0203610007, 0.0479039997,
+ 0.0751969963, 0.0980999991, 0.1220389977, 0.1458999962,
+ 0.1694349945, 0.1970459968, 0.2252430022, 0.2556869984,
+ 0.2870100141, 0.3197099864, 0.3525829911, 0.3889069855,
+ 0.4334920049, 0.4769459963, 0.5204820037, 0.5644530058,
+ 0.6122040153, 0.6685929894, 0.7341650128, 0.8032159805,
+ 0.8784040213, 0.9566209912, 1.0397069454, 1.1293770075,
+ 1.2211159468, 1.3080279827, 1.4024800062, 1.5056819916,
+ 1.6227730513, 1.7724959850, 1.9430880547, 2.2903931141 ]
+];
+
+const SCALE_DIFF: [i16; 32] = [
+ -11725, -9420, -7910, -6801, -5948, -5233, -4599, -4039,
+ -3507, -3030, -2596, -2170, -1774, -1383, -1016, -660,
+ -329, -1, 337, 696, 1085, 1512, 1962, 2433,
+ 2968, 3569, 4314, 5279, 6622, 8154, 10076, 12975
+];
+
+const BAND_SIZES: [usize; NUM_BANDS] = [
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 9, 10, 12, 14, 15
+];
+
+const SCALE: [u16; 64] = [
+ 3134, 5342, 6870, 7792, 8569, 9185, 9744, 10191,
+ 10631, 11061, 11434, 11770, 12116, 12513, 12925, 13300,
+ 13674, 14027, 14352, 14716, 15117, 15477, 15824, 16157,
+ 16513, 16804, 17090, 17401, 17679, 17948, 18238, 18520,
+ 18764, 19078, 19381, 19640, 19921, 20205, 20500, 20813,
+ 21162, 21465, 21794, 22137, 22453, 22756, 23067, 23350,
+ 23636, 23926, 24227, 24521, 24819, 25107, 25414, 25730,
+ 26120, 26497, 26895, 27344, 27877, 28463, 29426, 31355
+];
--- /dev/null
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use nihav_core::compr::deflate::*;
+
+#[derive(Default)]
+struct FSVShuffler {
+ lastframe: Option<NAVideoBufferRef<u8>>,
+ keyframe: Option<NAVideoBufferRef<u8>>,
+}
+
+#[allow(dead_code)]
+impl FSVShuffler {
+ fn new() -> Self { Self::default() }
+ fn clear(&mut self) {
+ self.keyframe = None;
+ self.lastframe = None;
+ }
+ fn add_frame(&mut self, buf: NAVideoBufferRef<u8>) {
+ self.lastframe = Some(buf);
+ }
+ fn add_keyframe(&mut self, buf: NAVideoBufferRef<u8>) {
+ self.keyframe = Some(buf);
+ }
+ fn clone_ref(&mut self) -> Option<NAVideoBufferRef<u8>> {
+ if let Some(ref mut frm) = self.lastframe {
+ let newfrm = frm.copy_buffer();
+ *frm = newfrm.clone().into_ref();
+ Some(newfrm.into_ref())
+ } else {
+ None
+ }
+ }
+ fn has_last_frame(&self) -> bool { self.lastframe.is_some() }
+ fn get_key_frame(&mut self) -> Option<NAVideoBufferRef<u8>> {
+ match self.keyframe {
+ Some(ref frm) => Some(frm.clone()),
+ None => None,
+ }
+ }
+ fn get_last_frame(&mut self) -> Option<NAVideoBufferRef<u8>> {
+ match self.lastframe {
+ Some(ref frm) => Some(frm.clone()),
+ None => None,
+ }
+ }
+}
+
+
+struct FSVDecoder {
+ info: NACodecInfoRef,
+ shuf: FSVShuffler,
+ w: usize,
+ h: usize,
+ block_w: usize,
+ block_h: usize,
+ ver1: bool,
+ has_pal: bool,
+ has_ifrm: bool,
+ tile: Vec<u8>,
+ cbuf: [u8; 65536],
+ pal: [u8; 128 * 3],
+ inflate: Inflate,
+ kdata: Vec<u8>,
+ bpos: Vec<usize>,
+ bsize: Vec<usize>,
+}
+
+impl FSVDecoder {
+ fn new(ver1: bool) -> Self {
+ Self {
+ info: NACodecInfo::new_dummy(),
+ shuf: FSVShuffler::new(),
+ w: 0,
+ h: 0,
+ block_w: 0,
+ block_h: 0,
+ ver1,
+ has_pal: false,
+ has_ifrm: false,
+ tile: Vec::new(),
+ cbuf: [0; 65536],
+ pal: DEFAULT_PAL,
+ inflate: Inflate::new(),
+ kdata: Vec::new(),
+ bpos: Vec::new(),
+ bsize: Vec::new(),
+ }
+ }
+ fn decode_v1(&mut self, br: &mut ByteReader, data: &mut [u8], stride: usize) -> DecoderResult<bool> {
+ let mut is_intra = true;
+ for (yy, row) in data.chunks_mut(stride * self.block_h).enumerate() {
+ let cur_h = (self.h - yy * self.block_h).min(self.block_h);
+ for x in (0..self.w).step_by(self.block_w) {
+ let cur_w = (self.w - x).min(self.block_w);
+
+ let data_size = br.read_u16be()? as usize;
+ if data_size > 0 {
+ br.read_buf(&mut self.cbuf[..data_size])?;
+ self.inflate = Inflate::new();
+ if self.inflate.decompress_block(&self.cbuf[..data_size], &mut self.tile[..cur_w * cur_h * 3]).is_err() {
+ return Err(DecoderError::InvalidData);
+ }
+ for (dst, src) in row[x * 3..].chunks_mut(stride).zip(self.tile.chunks(cur_w * 3)) {
+ dst[..cur_w * 3].copy_from_slice(src);
+ }
+ } else {
+ is_intra = false;
+ }
+ }
+ }
+ Ok(is_intra)
+ }
+ fn decode_v2(&mut self, br: &mut ByteReader, data: &mut [u8], stride: usize, keyframe: bool) -> DecoderResult<bool> {
+ let mut is_intra = !self.has_ifrm;
+ let bstride = (self.w + self.block_w - 1) / self.block_w;
+ for y in (0..self.h).step_by(self.block_h) {
+ let cur_h = (self.h - y).min(self.block_h);
+ for x in (0..self.w).step_by(self.block_w) {
+ let cur_w = (self.w - x).min(self.block_w);
+
+ let mut data_size = br.read_u16be()? as usize;
+ validate!(!keyframe || data_size > 0);
+ if data_size == 0 {
+ is_intra = false;
+ continue;
+ }
+ let blk_start = br.tell();
+ let flags = br.read_byte()?;
+ let depth = (flags >> 3) & 3;
+ validate!(depth == 0 || depth == 2);
+ let has_diff = (flags & 4) != 0;
+ let cpriming = (flags & 2) != 0;
+ let ppriming = (flags & 1) != 0;
+ let (start, height) = if has_diff {
+ let start = br.read_byte()? as usize;
+ let height = br.read_byte()? as usize;
+ validate!(start + height <= cur_h);
+ (start, height)
+ } else {
+ (0, cur_h)
+ };
+ if has_diff {
+ let ret = self.shuf.get_key_frame();
+ if ret.is_none() {
+ return Err(DecoderError::MissingReference);
+ }
+ let src = ret.unwrap();
+ let src = src.get_data();
+ for (dst, src) in data[x * 3 + y * stride..].chunks_mut(stride).take(cur_h).zip(src[x * 3 + y * stride..].chunks(stride)) {
+ dst[..cur_w * 3].copy_from_slice(&src[..cur_w * 3]);
+ }
+ }
+ if height != cur_h {
+ is_intra = false;
+ }
+ let ppos = if cpriming {
+ let xpos = br.read_byte()? as usize;
+ let ypos = br.read_byte()? as usize;
+ xpos + ypos * bstride
+ } else {
+ x / self.block_w + y / self.block_h * bstride
+ };
+ data_size -= (br.tell() - blk_start) as usize;
+ if keyframe {
+ self.bpos.push(br.tell() as usize);
+ self.bsize.push(data_size);
+ }
+ if data_size > 0 {
+ br.read_buf(&mut self.cbuf[..data_size])?;
+ self.inflate = Inflate::new();
+ if cpriming || ppriming {
+ if self.bpos.is_empty() {
+ return Err(DecoderError::MissingReference);
+ }
+ let ret = self.inflate.decompress_block(&self.kdata[self.bpos[ppos]..][..self.bsize[ppos]], &mut self.tile);
+ if ret.is_err() {
+ return Err(DecoderError::InvalidData);
+ }
+ let ssize = ret.unwrap();
+ self.inflate = Inflate::new();
+ self.inflate.set_dict(&self.tile[..ssize]);
+ }
+ let ret = self.inflate.decompress_block(&self.cbuf[..data_size], &mut self.tile[..cur_w * height * 3]);
+ if ret.is_err() {
+ return Err(DecoderError::InvalidData);
+ }
+ let src_len = ret.unwrap();
+
+ let dst = &mut data[x * 3 + y * stride..];
+ match depth {
+ 0 => {
+ validate!(src_len == cur_w * cur_h * 3);
+ for (dst, src) in dst.chunks_mut(stride).skip(start).take(height).zip(self.tile.chunks(cur_w * 3)) {
+ dst[..cur_w * 3].copy_from_slice(src);
+ }
+ },
+ 2 => {
+ let mut mr = MemoryReader::new_read(&self.tile[..src_len]);
+ let mut br = ByteReader::new(&mut mr);
+ for line in dst.chunks_mut(stride).skip(start).take(height) {
+ for rgb in line.chunks_mut(3).take(cur_w) {
+ let b = br.read_byte()?;
+ if (b & 0x80) == 0 {
+ rgb.copy_from_slice(&self.pal[(b as usize) * 3..][..3]);
+ } else {
+ let c = br.read_byte()?;
+ let clr = (u16::from(b & 0x7F) << 8) | u16::from(c);
+ let r = (clr >> 10) as u8;
+ let g = ((clr >> 5) & 0x1F) as u8;
+ let b = (clr & 0x1F) as u8;
+ rgb[0] = (r << 3) | (r >> 2);
+ rgb[1] = (g << 3) | (g >> 2);
+ rgb[2] = (b << 3) | (b >> 2);
+ }
+ }
+ }
+ },
+ _ => unreachable!(),
+ };
+ } else {
+ is_intra = false;
+ }
+ }
+ }
+ if self.has_ifrm {
+unimplemented!();
+ }
+ Ok(is_intra)
+ }
+}
+
+impl NADecoder for FSVDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+ let w = vinfo.get_width();
+ let h = vinfo.get_height();
+ let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, true, RGB24_FORMAT));
+ self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let src = pkt.get_buffer();
+
+ validate!(src.len() > 4);
+ let mut mr = MemoryReader::new_read(&src);
+ let mut br = ByteReader::new(&mut mr);
+
+ let hdr0 = br.read_u16be()? as usize;
+ let hdr1 = br.read_u16be()? as usize;
+ let w = hdr0 & 0xFFF;
+ let h = hdr1 & 0xFFF;
+ let blk_w = (hdr0 >> 12) * 16 + 16;
+ let blk_h = (hdr1 >> 12) * 16 + 16;
+ validate!(w != 0 && h != 0 && blk_w != 0 && blk_h != 0);
+
+ if !self.ver1 {
+ let flags = br.read_byte()?;
+ self.has_pal = (flags & 1) != 0;
+ self.has_ifrm = (flags & 2) != 0;
+ if self.has_pal {
+ let pal_sz = br.read_u16be()? as usize;
+ br.read_buf(&mut self.cbuf[..pal_sz])?;
+ self.inflate = Inflate::new();
+ if self.inflate.decompress_block(&self.cbuf[..pal_sz], &mut self.pal).is_err() {
+ return Err(DecoderError::InvalidData);
+ }
+ }
+ if pkt.keyframe {
+ self.kdata.clear();
+ self.kdata.extend_from_slice(&src);
+ self.bpos.clear();
+ self.bsize.clear();
+ }
+ }
+ if self.w != w || self.h != h || self.block_w != blk_w || self.block_h != blk_h {
+ self.flush();
+ self.tile.resize(blk_w * blk_h * 3, 0);
+ self.w = w;
+ self.h = h;
+ self.block_w = blk_w;
+ self.block_h = blk_h;
+ }
+
+ let mut buf = if let Some(buffer) = self.shuf.clone_ref() {
+ buffer
+ } else {
+ let vinfo = self.info.get_properties().get_video_info().unwrap();
+ let bufinfo = alloc_video_buffer(vinfo, 0)?;
+ bufinfo.get_vbuf().unwrap()
+ };
+ let stride = buf.get_stride(0);
+ let data = buf.get_data_mut().unwrap();
+ let is_intra = if self.ver1 {
+ self.decode_v1(&mut br, data, stride)?
+ } else {
+ self.decode_v2(&mut br, data, stride, pkt.keyframe)?
+ };
+
+ if !is_intra && !self.shuf.has_last_frame() {
+ return Err(DecoderError::MissingReference);
+ }
+
+ if pkt.is_keyframe() {
+ self.shuf.add_keyframe(buf.clone());
+ }
+ self.shuf.add_frame(buf.clone());
+
+ let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::VideoPacked(buf));
+ frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P });
+ Ok(frm.into_ref())
+ }
+ fn flush(&mut self) {
+ self.shuf.clear();
+ }
+}
+
+impl NAOptionHandler for FSVDecoder {
+ 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_decoder() -> Box<dyn NADecoder + Send> {
+ Box::new(FSVDecoder::new(true))
+}
+
+pub fn get_decoder_v2() -> Box<dyn NADecoder + Send> {
+ Box::new(FSVDecoder::new(false))
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::flash_register_all_decoders;
+ use crate::flash_register_all_demuxers;
+ #[test]
+ fn test_flashsv1() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decoding("flv", "flashsv", "assets/Flash/screen.flv",
+ Some(3000), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xb45b899e, 0x417b17d5, 0x7bfe898b, 0x026b289f],
+ [0xc04d4d1c, 0xbb1f4b4f, 0xe9f3d85e, 0xa40aff68],
+ [0x172e5bbe, 0xe44caba3, 0x6cb2a263, 0xcb79a89a]]));
+ }
+ #[test]
+ fn test_flashsv2() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decoding("flv", "flashsv2", "assets/Flash/screen2.flv",
+ Some(4700), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x55522afa, 0x9c7dd794, 0xdd67aa2e, 0x8b8c525e],
+ [0x9809efc2, 0xec5385aa, 0xb5eb9320, 0x4a47188e],
+ [0x40c77877, 0x58183722, 0x5700eb17, 0x27a00e33],
+ [0x802c2c6a, 0x3e08dd62, 0xa6c94df3, 0xc6318a6f],
+ [0x2aa70255, 0x652f0ca4, 0xe79817f9, 0x4f67e7ba],
+ [0x5cf34d91, 0xdfc54992, 0x4368180d, 0xfbe747d4],
+ [0x266d8bc4, 0x2b492ef4, 0xb42401a0, 0x23e530ec],
+ [0xa0e46b1c, 0x47d0620e, 0x0cbcb15b, 0x243e7f13]]));
+ }
+}
+
+const DEFAULT_PAL: [u8; 128 * 3] = [
+ 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x66, 0x66, 0x66, 0x99, 0x99, 0x99,
+ 0xCC, 0xCC, 0xCC, 0xFF, 0xFF, 0xFF, 0x33, 0x00, 0x00, 0x66, 0x00, 0x00,
+ 0x99, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x33, 0x00,
+ 0x00, 0x66, 0x00, 0x00, 0x99, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xFF, 0x00,
+ 0x00, 0x00, 0x33, 0x00, 0x00, 0x66, 0x00, 0x00, 0x99, 0x00, 0x00, 0xCC,
+ 0x00, 0x00, 0xFF, 0x33, 0x33, 0x00, 0x66, 0x66, 0x00, 0x99, 0x99, 0x00,
+ 0xCC, 0xCC, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x33, 0x33, 0x00, 0x66, 0x66,
+ 0x00, 0x99, 0x99, 0x00, 0xCC, 0xCC, 0x00, 0xFF, 0xFF, 0x33, 0x00, 0x33,
+ 0x66, 0x00, 0x66, 0x99, 0x00, 0x99, 0xCC, 0x00, 0xCC, 0xFF, 0x00, 0xFF,
+ 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0x66, 0xFF, 0xFF, 0x99, 0xFF, 0xFF, 0xCC,
+ 0xFF, 0x33, 0xFF, 0xFF, 0x66, 0xFF, 0xFF, 0x99, 0xFF, 0xFF, 0xCC, 0xFF,
+ 0x33, 0xFF, 0xFF, 0x66, 0xFF, 0xFF, 0x99, 0xFF, 0xFF, 0xCC, 0xFF, 0xFF,
+ 0xCC, 0xCC, 0x33, 0xCC, 0xCC, 0x66, 0xCC, 0xCC, 0x99, 0xCC, 0xCC, 0xFF,
+ 0xCC, 0x33, 0xCC, 0xCC, 0x66, 0xCC, 0xCC, 0x99, 0xCC, 0xCC, 0xFF, 0xCC,
+ 0x33, 0xCC, 0xCC, 0x66, 0xCC, 0xCC, 0x99, 0xCC, 0xCC, 0xFF, 0xCC, 0xCC,
+ 0x99, 0x99, 0x33, 0x99, 0x99, 0x66, 0x99, 0x99, 0xCC, 0x99, 0x99, 0xFF,
+ 0x99, 0x33, 0x99, 0x99, 0x66, 0x99, 0x99, 0xCC, 0x99, 0x99, 0xFF, 0x99,
+ 0x33, 0x99, 0x99, 0x66, 0x99, 0x99, 0xCC, 0x99, 0x99, 0xFF, 0x99, 0x99,
+ 0x66, 0x66, 0x33, 0x66, 0x66, 0x99, 0x66, 0x66, 0xCC, 0x66, 0x66, 0xFF,
+ 0x66, 0x33, 0x66, 0x66, 0x99, 0x66, 0x66, 0xCC, 0x66, 0x66, 0xFF, 0x66,
+ 0x33, 0x66, 0x66, 0x99, 0x66, 0x66, 0xCC, 0x66, 0x66, 0xFF, 0x66, 0x66,
+ 0x33, 0x33, 0x66, 0x33, 0x33, 0x99, 0x33, 0x33, 0xCC, 0x33, 0x33, 0xFF,
+ 0x33, 0x66, 0x33, 0x33, 0x99, 0x33, 0x33, 0xCC, 0x33, 0x33, 0xFF, 0x33,
+ 0x66, 0x33, 0x33, 0x99, 0x33, 0x33, 0xCC, 0x33, 0x33, 0xFF, 0x33, 0x33,
+ 0x00, 0x33, 0x66, 0x33, 0x66, 0x00, 0x66, 0x00, 0x33, 0x00, 0x66, 0x33,
+ 0x33, 0x00, 0x66, 0x66, 0x33, 0x00, 0x33, 0x66, 0x99, 0x66, 0x99, 0x33,
+ 0x99, 0x33, 0x66, 0x33, 0x99, 0x66, 0x66, 0x33, 0x99, 0x99, 0x66, 0x33,
+ 0x66, 0x99, 0xCC, 0x99, 0xCC, 0x66, 0xCC, 0x66, 0x99, 0x66, 0xCC, 0x99,
+ 0x99, 0x66, 0xCC, 0xCC, 0x99, 0x66, 0x99, 0xCC, 0xFF, 0xCC, 0xFF, 0x99,
+ 0xFF, 0x99, 0xCC, 0x99, 0xFF, 0xCC, 0xCC, 0x99, 0xFF, 0xFF, 0xCC, 0x99,
+ 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x44, 0x44, 0x44, 0x55, 0x55, 0x55,
+ 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE
+];
--- /dev/null
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::formats;
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_codec_support::codecs::{MV, ZIGZAG};
+use nihav_codec_support::codecs::h263::*;
+use nihav_codec_support::codecs::h263::code::*;
+use nihav_codec_support::codecs::h263::decoder::*;
+use nihav_codec_support::codecs::h263::data::*;
+
+#[allow(dead_code)]
+struct Tables {
+ intra_mcbpc_cb: Codebook<u8>,
+ inter_mcbpc_cb: Codebook<u8>,
+ cbpy_cb: Codebook<u8>,
+ rl_cb: Codebook<H263RLSym>,
+ aic_rl_cb: Codebook<H263RLSym>,
+ mv_cb: Codebook<u8>,
+}
+
+struct SparkDecoder {
+ info: NACodecInfoRef,
+ dec: H263BaseDecoder,
+ tables: Tables,
+ w: usize,
+ h: usize,
+ bdsp: H263BlockDSP,
+}
+
+struct SparkBR<'a> {
+ br: BitReader<'a>,
+ tables: &'a Tables,
+ ver1: bool,
+ mb_w: usize,
+ mb_h: usize,
+}
+
+fn decode_mv_component(br: &mut BitReader, mv_cb: &Codebook<u8>) -> DecoderResult<i16> {
+ let code = i16::from(br.read_cb(mv_cb)?);
+ if code == 0 { return Ok(0) }
+ if !br.read_bool()? {
+ Ok(code)
+ } else {
+ Ok(-code)
+ }
+}
+
+fn decode_mv(br: &mut BitReader, mv_cb: &Codebook<u8>) -> DecoderResult<MV> {
+ let xval = decode_mv_component(br, mv_cb)?;
+ let yval = decode_mv_component(br, mv_cb)?;
+ Ok(MV::new(xval, yval))
+}
+
+impl<'a> SparkBR<'a> {
+ fn new(src: &'a [u8], tables: &'a Tables, w: usize, h: usize) -> Self {
+ Self {
+ br: BitReader::new(src, BitReaderMode::BE),
+ tables,
+ ver1: false,
+ mb_w: (w + 15) >> 4,
+ mb_h: (h + 15) >> 4,
+ }
+ }
+
+ fn decode_block(&mut self, quant: u8, intra: bool, coded: bool, blk: &mut [i16; 64]) -> DecoderResult<()> {
+ let br = &mut self.br;
+ let mut idx = 0;
+ if intra {
+ let mut dc = br.read(8)?;
+ if dc == 255 { dc = 128; }
+ blk[0] = (dc as i16) << 3;
+ idx = 1;
+ }
+ if !coded { return Ok(()); }
+
+ let rl_cb = &self.tables.rl_cb; // could be aic too
+ let q_add = if quant == 0 { 0i16 } else { i16::from((quant - 1) | 1) };
+ let q = i16::from(quant * 2);
+ while idx < 64 {
+ let code = br.read_cb(rl_cb)?;
+ let run;
+ let mut level;
+ let last;
+ if !code.is_escape() {
+ run = code.get_run();
+ level = code.get_level();
+ last = code.is_last();
+ if br.read_bool()? { level = -level; }
+ if level > 0 {
+ level = (level * q) + q_add;
+ } else {
+ level = (level * q) - q_add;
+ }
+ } else if !self.ver1 {
+ last = br.read_bool()?;
+ run = br.read(6)? as u8;
+ level = br.read_s(8)? as i16;
+ if level == -128 {
+ let low = br.read(5)? as i16;
+ let top = br.read_s(6)? as i16;
+ level = (top << 5) | low;
+ }
+ if level > 0 {
+ level = (level * q) + q_add;
+ } else {
+ level = (level * q) - q_add;
+ }
+ if level < -2048 { level = -2048; }
+ if level > 2047 { level = 2047; }
+ } else {
+ let fmt_bit = br.read_bool()?;
+ last = br.read_bool()?;
+ run = br.read(6)? as u8;
+ level = br.read_s(if !fmt_bit { 7 } else { 11 })? as i16;
+ validate!(level != 0);
+ if !fmt_bit {
+ validate!(level != 64);
+ } else {
+ validate!(level != 1024);
+ }
+ if level > 0 {
+ level = (level * q) + q_add;
+ } else {
+ level = (level * q) - q_add;
+ }
+ if level < -2048 { level = -2048; }
+ if level > 2047 { level = 2047; }
+ }
+ idx += run;
+ validate!(idx < 64);
+ let oidx = ZIGZAG[idx as usize];
+ blk[oidx] = level;
+ idx += 1;
+ if last { break; }
+ }
+ Ok(())
+ }
+}
+
+
+impl<'a> BlockDecoder for SparkBR<'a> {
+ fn decode_pichdr(&mut self) -> DecoderResult<PicInfo> {
+ let br = &mut self.br;
+ let syncw = br.read(17)?;
+ validate!(syncw == 1);
+ let version = br.read(5)?;
+ validate!(version == 0 || version == 1);
+ self.ver1 = version == 1;
+ let tr = (br.read(8)? << 8) as u16;
+ let sfmt = br.read(3)?;
+ validate!(sfmt != 0b111);
+ let (w, h) = match sfmt {
+ 0 => {
+ let w = br.read(8)? as usize;
+ let h = br.read(8)? as usize;
+ validate!(w != 0 && h != 0);
+ (w, h)
+ },
+ 1 => {
+ let w = br.read(16)? as usize;
+ let h = br.read(16)? as usize;
+ validate!(w != 0 && h != 0);
+ (w, h)
+ },
+ 2 => (352, 288),
+ 3 => (176, 144),
+ 4 => (128, 96),
+ 5 => (320, 240),
+ 6 => (160, 120),
+ _ => unreachable!(),
+ };
+ let pic_type = br.read(2)?;
+ let ftype = match pic_type {
+ 0 => Type::I,
+ 1 => Type::P,
+ 2 => Type::Special,
+ _ => return Err(DecoderError::InvalidData),
+ };
+ let deblock = br.read_bool()?;
+ let quant = br.read(5)?;
+ while br.read_bool()? { // skip PEI
+ br.read(8)?;
+ }
+ self.mb_w = (w + 15) >> 4;
+ self.mb_h = (h + 15) >> 4;
+
+ let plusinfo = if deblock { Some(PlusInfo::new(false, deblock, false, false)) } else { None };
+ let picinfo = PicInfo::new(w, h, ftype, MVMode::Long, true, false, quant as u8, tr, None, plusinfo);
+ Ok(picinfo)
+ }
+ fn decode_slice_header(&mut self, info: &PicInfo) -> DecoderResult<SliceInfo> {
+ Ok(SliceInfo::new(0, 0, self.mb_w * self.mb_h, info.quant))
+ }
+ fn decode_block_header(&mut self, info: &PicInfo, slice: &SliceInfo, _sstate: &SliceState) -> DecoderResult<BlockInfo> {
+ let br = &mut self.br;
+ let mut q = slice.get_quant();
+ match info.get_mode() {
+ Type::I => {
+ let mut cbpc = br.read_cb(&self.tables.intra_mcbpc_cb)?;
+ while cbpc == 8 { cbpc = br.read_cb(&self.tables.intra_mcbpc_cb)?; }
+ let cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+ let cbp = (cbpy << 2) | (cbpc & 3);
+ let dquant = (cbpc & 4) != 0;
+ if dquant {
+ let idx = br.read(2)? as usize;
+ q = (i16::from(q) + i16::from(H263_DQUANT_TAB[idx])) as u8;
+ }
+ Ok(BlockInfo::new(Type::I, cbp, q))
+ },
+ Type::P | Type::Special => {
+ if br.read_bool()? {
+ return Ok(BlockInfo::new(Type::Skip, 0, info.get_quant()));
+ }
+ let mut cbpc = br.read_cb(&self.tables.inter_mcbpc_cb)?;
+ while cbpc == 20 { cbpc = br.read_cb(&self.tables.inter_mcbpc_cb)?; }
+ let is_intra = (cbpc & 0x04) != 0;
+ let dquant = (cbpc & 0x08) != 0;
+ let is_4x4 = (cbpc & 0x10) != 0;
+ if is_intra {
+ let cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+ let cbp = (cbpy << 2) | (cbpc & 3);
+ if dquant {
+ let idx = br.read(2)? as usize;
+ q = ((q as i16) + (H263_DQUANT_TAB[idx] as i16)) as u8;
+ validate!(q < 32);
+ }
+ let binfo = BlockInfo::new(Type::I, cbp, q);
+ return Ok(binfo);
+ }
+
+ let mut cbpy = br.read_cb(&self.tables.cbpy_cb)?;
+// if /* !aiv && */(cbpc & 3) != 3 {
+ cbpy ^= 0xF;
+// }
+ let cbp = (cbpy << 2) | (cbpc & 3);
+ if dquant {
+ let idx = br.read(2)? as usize;
+ q = ((q as i16) + (H263_DQUANT_TAB[idx] as i16)) as u8;
+ validate!(q < 32);
+ }
+ let mut binfo = BlockInfo::new(Type::P, cbp, q);
+ if !is_4x4 {
+ let mvec: [MV; 1] = [decode_mv(br, &self.tables.mv_cb)?];
+ binfo.set_mv(&mvec);
+ } else {
+ let mvec: [MV; 4] = [
+ decode_mv(br, &self.tables.mv_cb)?,
+ decode_mv(br, &self.tables.mv_cb)?,
+ decode_mv(br, &self.tables.mv_cb)?,
+ decode_mv(br, &self.tables.mv_cb)?
+ ];
+ binfo.set_mv(&mvec);
+ }
+ Ok(binfo)
+ },
+ _ => { Err(DecoderError::InvalidData) },
+ }
+ }
+ fn decode_block_intra(&mut self, _info: &BlockInfo, _sstate: &SliceState, quant: u8, _no: usize, coded: bool, blk: &mut [i16; 64]) -> DecoderResult<()> {
+ self.decode_block(quant, true, coded, blk)
+ }
+ fn decode_block_inter(&mut self, _info: &BlockInfo, _sstate: &SliceState, quant: u8, _no: usize, coded: bool, blk: &mut [i16; 64]) -> DecoderResult<()> {
+ self.decode_block(quant, false, coded, blk)
+ }
+ fn is_slice_end(&mut self) -> bool { self.br.peek(16) == 0 }
+}
+
+impl SparkDecoder {
+ fn new() -> Self {
+ let mut coderead = H263ShortCodeReader::new(H263_INTRA_MCBPC);
+ let intra_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let mut coderead = H263ShortCodeReader::new(H263_INTER_MCBPC);
+ let inter_mcbpc_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let mut coderead = H263ShortCodeReader::new(H263_CBPY);
+ let cbpy_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let mut coderead = H263RLCodeReader::new(H263_RL_CODES);
+ let rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let mut coderead = H263RLCodeReader::new(H263_RL_CODES_AIC);
+ let aic_rl_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let mut coderead = H263ShortCodeReader::new(H263_MV);
+ let mv_cb = Codebook::new(&mut coderead, CodebookMode::MSB).unwrap();
+ let tables = Tables {
+ intra_mcbpc_cb,
+ inter_mcbpc_cb,
+ cbpy_cb,
+ rl_cb,
+ aic_rl_cb,
+ mv_cb,
+ };
+
+ Self {
+ info: NACodecInfo::new_dummy(),
+ dec: H263BaseDecoder::new(true),
+ tables,
+ bdsp: H263BlockDSP::new(),
+ w: 0,
+ h: 0,
+ }
+ }
+}
+
+impl NADecoder for SparkDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+ let w = vinfo.get_width();
+ let h = vinfo.get_height();
+ let fmt = formats::YUV420_FORMAT;
+ let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, fmt));
+ self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+ self.w = w;
+ self.h = h;
+
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let src = pkt.get_buffer();
+
+ let mut ibr = SparkBR::new(&src, &self.tables, self.w, self.h);
+ let bufinfo = self.dec.parse_frame(&mut ibr, &self.bdsp)?;
+
+ let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+ frm.set_keyframe(self.dec.is_intra());
+ frm.set_frame_type(if self.dec.is_intra() { FrameType::I } else { FrameType::P });
+ Ok(frm.into_ref())
+ }
+ fn flush(&mut self) {
+ self.dec.flush();
+ }
+}
+
+impl NAOptionHandler for SparkDecoder {
+ 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_decoder() -> Box<dyn NADecoder + Send> {
+ Box::new(SparkDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::flash_register_all_decoders;
+ use crate::flash_register_all_demuxers;
+ #[test]
+ fn test_flv263() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ flash_register_all_decoders(&mut dec_reg);
+
+ test_decoding("flv", "flv263", "assets/Flash/input.flv",
+ Some(1000), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+ [0x9110718e, 0x794e22ad, 0x3324e552, 0xf58a0449],
+ [0x2f7cfcd1, 0x1d6bb63b, 0x37dcd87d, 0xb0247d9c],
+ [0x317e6355, 0xc632f2d5, 0x1d6ae472, 0x45cc1ba6],
+ [0x7d883ffc, 0xaa8e7c68, 0x8dec683b, 0x0e0dcdea],
+ [0x79d4cece, 0x98749753, 0xfedb0fb1, 0x5398f6a0],
+ [0x153f1558, 0xd98a700c, 0xdb166ebe, 0xc347fd61],
+ [0x0b31c6a9, 0xcb126876, 0xd7dd8626, 0x4a6fea9f]]));
+ }
+}
--- /dev/null
+use nihav_core::codecs::*;
+
+macro_rules! validate {
+ ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } };
+}
+
+#[cfg(feature="decoder_flv263")]
+mod flv263;
+#[cfg(feature="decoder_flashsv")]
+mod flashsv;
+
+#[cfg(feature="decoder_flv_adpcm")]
+mod adpcm;
+#[cfg(feature="decoder_asao")]
+mod asao;
+
+const DECODERS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_flv263")]
+ DecoderInfo { name: "flv263", get_decoder: flv263::get_decoder },
+#[cfg(feature="decoder_flashsv")]
+ DecoderInfo { name: "flashsv", get_decoder: flashsv::get_decoder },
+#[cfg(feature="decoder_flashsv")]
+ DecoderInfo { name: "flashsv2", get_decoder: flashsv::get_decoder_v2 },
+
+#[cfg(feature="decoder_flv_adpcm")]
+ DecoderInfo { name: "flv-adpcm", get_decoder: adpcm::get_decoder },
+#[cfg(feature="decoder_asao")]
+ DecoderInfo { name: "asao", get_decoder: asao::get_decoder },
+];
+
+/// Registers all available codecs provided by this crate.
+pub fn flash_register_all_decoders(rd: &mut RegisteredDecoders) {
+ for decoder in DECODERS.iter() {
+ rd.add_decoder(*decoder);
+ }
+}
+
+#[cfg(feature="encoder_flv_adpcm")]
+mod adpcmenc;
+
+const ENCODERS: &[EncoderInfo] = &[
+#[cfg(feature="encoder_flv_adpcm")]
+ EncoderInfo { name: "flv-adpcm", get_encoder: adpcmenc::get_encoder },
+];
+
+/// Registers all available encoders provided by this crate.
+pub fn flash_register_all_encoders(re: &mut RegisteredEncoders) {
+ for encoder in ENCODERS.iter() {
+ re.add_encoder(*encoder);
+ }
+}
--- /dev/null
+use nihav_core::demuxers::*;
+use nihav_core::io::bitreader::*;
+
+const AVC_ID: u8 = 7;
+
+struct FLVDemuxer<'a> {
+ src: &'a mut ByteReader<'a>,
+ vpkts: Vec<NAPacket>,
+ vtag: Option<u8>,
+ apkts: Vec<NAPacket>,
+ atag: Option<u8>,
+ vstream: usize,
+ astream: usize,
+ duration: u64,
+ width: usize,
+ height: usize,
+}
+
+fn get_vcodec_name(tag: u8) -> DemuxerResult<&'static str> {
+ match tag {
+ 2 => Ok("flv263"),
+ 3 => Ok("flashsv"),
+ 4 => Ok("vp6f"),
+ 5 => Ok("vp6a"),
+ 6 => Ok("flashsv2"),
+ 7 => Ok("h264"),
+ _ => Err(DemuxerError::InvalidData),
+ }
+}
+
+impl<'a> DemuxCore<'a> for FLVDemuxer<'a> {
+ fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+ let mut tag = [0; 3];
+ self.src.read_buf(&mut tag)?;
+ validate!(&tag == b"FLV");
+ let ver = self.src.read_byte()?;
+ validate!(ver == 0 || ver == 1);
+ let hdr = self.src.read_byte()?;
+ validate!((hdr & 0xF2) == 0);
+ let has_audio = (hdr & 4) != 0;
+ let has_video = (hdr & 1) != 0;
+ validate!(has_video || has_audio);
+ let hdr_size = self.src.read_u32be()?;
+ validate!(hdr_size >= 9);
+
+ let first_prev_tag = self.src.peek_u32be()?;
+ validate!(first_prev_tag == 0);
+
+ while (self.vtag.is_some() != has_video) || (self.atag.is_some() != has_audio) {
+ self.parse_tag(strmgr)?;
+ if self.apkts.len() > 100 || self.vpkts.len() > 100 {
+ return Err(DemuxerError::InvalidData);
+ }
+ }
+
+ seek_index.mode = SeekIndexMode::Automatic;
+
+ Ok(())
+ }
+
+ fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+ loop {
+ if !self.vpkts.is_empty() && self.vpkts.len() >= self.apkts.len() {
+ return Ok(self.vpkts.remove(0));
+ }
+ if !self.apkts.is_empty() {
+ return Ok(self.apkts.remove(0));
+ }
+ self.parse_tag(strmgr)?;
+ }
+ }
+ fn seek(&mut self, time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+ let dst_ms = match time {
+ NATimePoint::PTS(pts) => pts,
+ NATimePoint::Milliseconds(ms) => ms,
+ NATimePoint::None => return Err(DemuxerError::SeekError),
+ };
+ self.apkts.clear();
+ self.vpkts.clear();
+ let mut prev = None;
+ loop {
+ let ppos = self.src.read_u32be()?;
+ let ret = self.src.read_byte();
+ if let Err(ByteIOError::EOF) = ret {
+ self.src.seek(SeekFrom::Current(-8 - i64::from(ppos)))?;
+ continue;
+ }
+ let data_size = self.src.read_u24be()?;
+ let time = self.src.read_u24be()?;
+ let ext_time = self.src.read_byte()?;
+ let _stream_id = self.src.read_u24be()?;
+ let ts = (u64::from(ext_time) << 32) | u64::from(time);
+ if dst_ms == ts {
+ self.src.seek(SeekFrom::Current(-15))?;
+ return Ok(());
+ }
+ if let Some(p_ts) = prev {
+ if dst_ms > p_ts && dst_ms < ts {
+ self.src.seek(SeekFrom::Current(-19 - i64::from(ppos)))?;
+ return Ok(());
+ }
+ }
+ prev = Some(ts);
+ if dst_ms < ts {
+ self.src.seek(SeekFrom::Current(-19 - i64::from(ppos)))?;
+ } else {
+ self.src.seek(SeekFrom::Current(i64::from(data_size)))?;
+ }
+ }
+ }
+ fn get_duration(&self) -> u64 { self.duration }
+}
+
+impl<'a> NAOptionHandler for FLVDemuxer<'a> {
+ fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+ fn set_options(&mut self, _options: &[NAOption]) { }
+ fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+impl<'a> FLVDemuxer<'a> {
+ fn new(io: &'a mut ByteReader<'a>) -> Self {
+ Self {
+ src: io,
+ vpkts: Vec::with_capacity(2),
+ apkts: Vec::with_capacity(2),
+ vtag: None,
+ atag: None,
+ vstream: 0,
+ astream: 0,
+ duration: 0,
+ width: 0,
+ height: 0,
+ }
+ }
+ fn parse_tag(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
+ let _prev_tag_size = self.src.read_u32be()?;
+ let ret = self.src.read_byte();
+ if let Err(ByteIOError::EOF) = ret {
+ return Err(DemuxerError::EOF);
+ }
+
+ let tag = ret?;
+ let mut data_size = self.src.read_u24be()? as usize;
+ let time = self.src.read_u24be()?;
+ let ext_time = self.src.read_byte()?;
+ let stream_id = self.src.read_u24be()?;
+ validate!(stream_id == 0);
+ if data_size == 0 {
+ return Ok(());
+ }
+ let pkt_start = self.src.tell();
+ match tag {
+ 8 => {
+ let hdr = self.src.read_byte()?;
+ if let Some(tag) = self.atag {
+ validate!(tag == (hdr >> 4));
+ } else if data_size > 0 {
+ let cname = match hdr >> 4 {
+ 0 | 3 => "pcm",
+ 1 => "flv-adpcm",
+ 2 | 14 => "mp3",
+ 4..=6 => "asao",
+ 7 => "alaw",
+ 8 => "ulaw",
+ 10 => "aac",
+ 11 => "speex",
+ _ => return Err(DemuxerError::InvalidData),
+ };
+ let mut srate = match (hdr >> 2) & 0x3 {
+ 0 => 5500,
+ 1 => 11025,
+ 2 => 22050,
+ _ => 44100,
+ };
+ let bits = if (hdr & 2) == 0 { 8 } else { 16 };
+ let mut channels = if (hdr & 1) == 0 { 1 } else { 2 };
+ let mut aac_edata = false;
+ match hdr >> 4 {
+ 4 => { srate = 16000; channels = 1; },
+ 5 => { srate = 8000; channels = 1; },
+ 10 => { aac_edata = self.src.read_byte()? == 0; },
+ 14 => srate = 8000,
+ _ => {},
+ };
+ let edata = if aac_edata {
+ let pkt_hdr_size = (self.src.tell() - pkt_start) as usize;
+ validate!(data_size >= pkt_hdr_size);
+ let mut data = vec![0; data_size - pkt_hdr_size];
+ self.src.read_buf(&mut data)?;
+ Some(data)
+ } else {
+ None
+ };
+ let soniton = if bits == 16 { SND_S16P_FORMAT } else { SND_U8_FORMAT };
+ let ahdr = NAAudioInfo::new(srate, channels, soniton, 0);
+ let ci = NACodecTypeInfo::Audio(ahdr);
+ let ainfo = NACodecInfo::new(cname, ci, edata);
+ if let Some(id) = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 1000, 0)) {
+ self.astream = id;
+ } else {
+ return Err(DemuxerError::MemoryError);
+ }
+ self.atag = Some(hdr >> 4);
+
+ if aac_edata {
+ return Ok(());
+ }
+ }
+ if (hdr >> 4) == 10 {
+ let pkt_type = self.src.read_byte()?;
+ validate!(pkt_type == 1);
+ }
+ let pkt_hdr_size = (self.src.tell() - pkt_start) as usize;
+ validate!(data_size >= pkt_hdr_size);
+ data_size -= pkt_hdr_size;
+ if data_size > 0 {
+ let stream = strmgr.get_stream(self.astream).unwrap();
+ let (tb_num, tb_den) = stream.get_timebase();
+ let pts = (u64::from(ext_time) << 24) | u64::from(time);
+ let ts = NATimeInfo::new(Some(pts), None, None, tb_num, tb_den);
+ self.apkts.push(self.src.read_packet(stream, ts, true, data_size)?);
+ }
+ },
+ 9 => {
+ let hdr = self.src.read_byte()?;
+ let ftype = match hdr >> 4 {
+ 1 => FrameType::I,
+ 2 => FrameType::P,
+ 3 => FrameType::P, // droppable
+ 4 => FrameType::Other, // generated key frame
+ 5 => FrameType::Other, // video info/command frame
+ _ => return Err(DemuxerError::InvalidData),
+ };
+ let codec_tag = hdr & 0xF;
+ if let Some(id) = self.vtag {
+ validate!(id == codec_tag);
+ } else {
+ let cname = get_vcodec_name(codec_tag)?;
+ let is_avc = codec_tag == AVC_ID;
+ if is_avc {
+ let pkt_type = self.src.read_byte()?;
+ validate!(pkt_type == 0);
+ self.src.read_u24be()?;
+ }
+ let mut edata = None;
+ let (width, height) = match codec_tag {
+ 2 => {
+ let mut buf = [0; 9];
+ self.src.peek_buf(&mut buf)?;
+ let mut br = BitReader::new(&buf, BitReaderMode::BE);
+ br.skip(30).unwrap_or(());
+ let sfmt = br.read(3).unwrap_or(7);
+ match sfmt {
+ 0 => {
+ let w = br.read(8).unwrap_or(0) as usize;
+ let h = br.read(8).unwrap_or(0) as usize;
+ (w, h)
+ },
+ 1 => {
+ let w = br.read(16).unwrap_or(0) as usize;
+ let h = br.read(16).unwrap_or(0) as usize;
+ (w, h)
+ },
+ 2 => (352, 288),
+ 3 => (176, 144),
+ 4 => (128, 96),
+ 5 => (320, 240),
+ 6 => (160, 120),
+ _ => (0, 0),
+ }
+ },
+ 3 | 6 => {
+ let mut buf = [0; 4];
+ self.src.peek_buf(&mut buf)?;
+ let w = (read_u16be(&buf[0..])? & 0xFFF) as usize;
+ let h = (read_u16be(&buf[2..])? & 0xFFF) as usize;
+ (w, h)
+ },
+ 4 => {
+ let mut buf = [0; 7];
+ self.src.peek_buf(&mut buf)?;
+ let off = if (buf[1] & 1) != 0 || (buf[2] & 6) == 0 { 5 } else { 3 };
+ validate!(buf[off] != 0 && buf[off + 1] != 0);
+ let w = usize::from(buf[off + 1]) * 16 - usize::from(buf[0] >> 4);
+ let h = usize::from(buf[off]) * 16 - usize::from(buf[0] & 0xF);
+
+ edata = Some(vec![buf[0]]);
+
+ (w, h)
+ },
+ 5 => {
+ let mut buf = [0; 10];
+ self.src.peek_buf(&mut buf)?;
+ let off = if (buf[4] & 1) != 0 || (buf[5] & 6) == 0 { 8 } else { 6 };
+ validate!(buf[off] != 0 && buf[off + 1] != 0);
+ let w = usize::from(buf[off + 1]) * 16 - usize::from(buf[0] >> 4);
+ let h = usize::from(buf[off]) * 16 - usize::from(buf[0] & 0xF);
+
+ edata = Some(vec![buf[0]]);
+
+ (w, h)
+ },
+ 7 => {
+ let pkt_hdr_size = (self.src.tell() - pkt_start) as usize;
+ validate!(data_size >= pkt_hdr_size);
+ data_size -= pkt_hdr_size;
+ let mut data = vec![0; data_size + 4];
+ data[..4].copy_from_slice(b"avcC");
+ self.src.read_buf(&mut data[4..])?;
+ edata = Some(data);
+ (self.width, self.height)
+ },
+ _ => unreachable!(),
+ };
+
+ let vhdr = NAVideoInfo::new(width, height, false, YUV420_FORMAT);
+ let vci = NACodecTypeInfo::Video(vhdr);
+ let vinfo = NACodecInfo::new(cname, vci, edata);
+ if let Some(id) = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 1000, 0)) {
+ self.vstream = id;
+ } else {
+ return Err(DemuxerError::MemoryError);
+ }
+ self.vtag = Some(codec_tag);
+ if is_avc {
+ return Ok(());
+ }
+ }
+ let mut cts = 0;
+ match codec_tag {
+ 4 | 5 => {
+ self.src.read_skip(1)?;
+ },
+ 7 => {
+ let pkt_type = self.src.read_byte()?;
+ if pkt_type == 1 {
+ cts = ((self.src.read_u24be()? << 8) as i32) >> 8;
+ } else if pkt_type == 2 {
+ let pkt_hdr_size = (self.src.tell() - pkt_start) as usize;
+ validate!(data_size >= pkt_hdr_size);
+ data_size -= pkt_hdr_size;
+ self.src.read_skip(data_size)?;
+ return Ok(());
+ }
+ },
+ _ => {},
+ };
+
+ let pkt_hdr_size = (self.src.tell() - pkt_start) as usize;
+ validate!(data_size >= pkt_hdr_size);
+ data_size -= pkt_hdr_size;
+
+ if data_size > 0 {
+ let stream = strmgr.get_stream(self.vstream).unwrap();
+ let (tb_num, tb_den) = stream.get_timebase();
+ let pts = (u64::from(ext_time) << 24) | u64::from(time);
+ let dts = ((pts as i64) + i64::from(cts)).max(0) as u64;
+ let ts = NATimeInfo::new(Some(pts), Some(dts), None, tb_num, tb_den);
+ self.vpkts.push(self.src.read_packet(stream, ts, ftype == FrameType::I, data_size)?);
+ }
+ },
+ 18 => {
+ let end = self.src.tell() + (data_size as u64);
+ let ntype = self.src.read_byte()?;
+ validate!(ntype == 2);
+ let nlen = self.src.read_u16be()? as usize;
+ validate!(nlen > 0);
+ let mut name = vec![0; nlen];
+ self.src.read_buf(&mut name)?;
+ if &name == b"onMetaData" {
+ let otype = self.src.read_byte()?;
+ validate!(otype == 8);
+ let _size = self.src.read_u32be()?;
+ while self.src.tell() < end {
+ let nlen = self.src.read_u16be()? as usize;
+ if nlen == 0 {
+ let emarker = self.src.peek_byte()?;
+ if emarker == 9 {
+ self.src.read_skip(1)?;
+ break;
+ }
+ }
+ let mut name = vec![0; nlen];
+ self.src.read_buf(&mut name)?;
+ let vtype = self.src.read_byte()?;
+ match vtype {
+ 0 => {
+ let val = self.src.read_f64be()?;
+ match name.as_slice() {
+ b"duration" => self.duration = (val * 1000.0) as u64,
+ b"width" => self.width = val as usize,
+ b"height" => self.height = val as usize,
+ b"videocodecid" => {
+ let codec_tag = val as u8;
+ if self.vtag.is_none() && codec_tag != AVC_ID && self.width != 0 && self.height != 0 {
+ let cname = get_vcodec_name(codec_tag)?;
+ let edata = if cname.starts_with("vp6") {
+ let ebyte = ((16 - (self.width & 0xF)) & 0xF) * 16 + ((16 - (self.height & 0xF)) & 0xF);
+ Some(vec![ebyte as u8])
+ } else { None };
+ let vhdr = NAVideoInfo::new(self.width, self.height, false, YUV420_FORMAT);
+ let vci = NACodecTypeInfo::Video(vhdr);
+ let vinfo = NACodecInfo::new(cname, vci, edata);
+ if let Some(id) = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 1000, 0)) {
+ self.vstream = id;
+ } else {
+ return Err(DemuxerError::MemoryError);
+ }
+ self.vtag = Some(codec_tag);
+ }
+ },
+ _ => {},
+ };
+ },
+ 1 => {
+ let _val = self.src.read_byte()?;
+ },
+ 2 => {
+ let len = self.src.read_u16be()? as usize;
+ let mut val = vec![0; len];
+ self.src.read_buf(&mut val)?;
+ },
+ 3 => {
+ break;//unimplemented!();
+ },
+ 5 => {},
+ 6 => {},
+ 7 => {
+ self.src.read_u16be()?;
+ },
+ 8 => {
+ unimplemented!();
+ },
+ 10 => {
+ unimplemented!();
+ },
+ 11 => {
+ self.src.read_f64be()?;
+ self.src.read_u16be()?;
+ },
+ 12 => {
+ let len = self.src.read_u16be()? as usize;
+ let mut val = vec![0; len];
+ self.src.read_buf(&mut val)?;
+ },
+ _ => break,
+ };
+ }
+ }
+ validate!(self.src.tell() <= end);
+ let to_skip = (end - self.src.tell()) as usize;
+ self.src.read_skip(to_skip)?;
+ },
+ _ => {
+ self.src.read_skip(data_size)?;
+ },
+ };
+ Ok(())
+ }
+}
+
+pub struct FLVDemuxerCreator { }
+
+impl DemuxerCreator for FLVDemuxerCreator {
+ fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+ Box::new(FLVDemuxer::new(br))
+ }
+ fn get_name(&self) -> &'static str { "flv" }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_flv_demux() {
+ let mut file = File::open("assets/Flash/input.flv").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = FLVDemuxer::new(&mut br);
+ let mut sm = StreamManager::new();
+ let mut si = SeekIndex::new();
+ dmx.open(&mut sm, &mut si).unwrap();
+
+ loop {
+ let pktres = dmx.get_frame(&mut sm);
+ if let Err(e) = pktres {
+ if e == DemuxerError::EOF { break; }
+ panic!("error");
+ }
+ let pkt = pktres.unwrap();
+ println!("Got {}", pkt);
+ }
+ }
+ #[test]
+ fn test_flv_demux_back() {
+ let mut file = File::open("assets/Flash/input.flv").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = FLVDemuxer::new(&mut br);
+ let mut sm = StreamManager::new();
+ let mut si = SeekIndex::new();
+ dmx.open(&mut sm, &mut si).unwrap();
+ dmx.src.seek(SeekFrom::End(-4)).unwrap();
+ dmx.seek(NATimePoint::Milliseconds(7500), &si).unwrap();
+
+ loop {
+ let pktres = dmx.get_frame(&mut sm);
+ if let Err(e) = pktres {
+ if e == DemuxerError::EOF { break; }
+ panic!("error");
+ }
+ let pkt = pktres.unwrap();
+ println!("Got {}", pkt);
+ }
+ }
+}
--- /dev/null
+use nihav_core::demuxers::*;
+
+
+#[allow(unused_macros)]
+macro_rules! validate {
+ ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } };
+}
+
+#[cfg(feature="demuxer_flv")]
+mod flv;
+
+const DEMUXERS: &[&dyn DemuxerCreator] = &[
+#[cfg(feature="demuxer_flv")]
+ &flv::FLVDemuxerCreator {},
+];
+
+/// Registers all available demuxers provided by this crate.
+pub fn flash_register_all_demuxers(rd: &mut RegisteredDemuxers) {
+ for demuxer in DEMUXERS.iter() {
+ rd.add_demuxer(*demuxer);
+ }
+}
--- /dev/null
+//! Crate for providing support for various Flash-related formats.
+extern crate nihav_core;
+extern crate nihav_codec_support;
+
+mod codecs;
+
+#[cfg(feature="decoders")]
+pub use crate::codecs::flash_register_all_decoders;
+
+#[cfg(feature="demuxers")]
+mod demuxers;
+#[cfg(feature="demuxers")]
+pub use crate::demuxers::flash_register_all_demuxers;
+
+#[cfg(feature="encoders")]
+pub use crate::codecs::flash_register_all_encoders;
+
+#[cfg(feature="muxers")]
+mod muxers;
+#[cfg(feature="muxers")]
+pub use crate::muxers::flash_register_all_muxers;
--- /dev/null
+use nihav_core::muxers::*;
+
+const FLV_VCODECS: &[(&str, u8)] = &[
+ ("flv263", 2),
+ ("flashsv", 3),
+ ("vp6f", 4),
+ ("vp6a", 5),
+ ("flashsv2", 6),
+ ("h264", AVC_ID)
+];
+
+const NO_CODEC: u8 = 0;
+const AVC_ID: u8 = 7;
+const AAC_ID: u8 = 10;
+
+fn find_audio_tag(cname: &'static str, rate: u32, channels: u8) -> MuxerResult<u8> {
+ if channels != 1 && channels != 2 {
+ return Err(MuxerError::InvalidArgument);
+ }
+ let tag = match cname {
+ "flv-adpcm" => 1,
+ "pcm" => 3,
+ "mp3" => if rate != 8000 { 2 } else { return Ok(14); },
+ "asao" => {
+ if channels != 1 {
+ return Err(MuxerError::InvalidArgument);
+ }
+ match rate {
+ 16000 => return Ok(4),
+ 8000 => return Ok(5),
+ _ => 6,
+ }
+ },
+ "alaw" => 7,
+ "ulaw" => 8,
+ "aac" => AAC_ID,
+ "speex" => 11,
+ _ => return Err(MuxerError::InvalidArgument),
+ };
+ match rate {
+ 5500 | 11025 | 22050 | 44100 => {},
+ _ => return Err(MuxerError::InvalidArgument),
+ };
+ Ok(tag)
+}
+
+trait FLVPropertyWriter {
+ fn write_property_num(&mut self, name: &str, val: f64) -> MuxerResult<()>;
+ fn write_property_bool(&mut self, name: &str, val: bool) -> MuxerResult<()>;
+}
+
+impl<'a> FLVPropertyWriter for ByteWriter<'a> {
+ fn write_property_num(&mut self, name: &str, val: f64) -> MuxerResult<()> {
+ self.write_u16be(name.len() as u16)?;
+ self.write_buf(name.as_bytes())?;
+ self.write_byte(0)?;
+ self.write_f64be(val)?;
+ Ok(())
+ }
+ fn write_property_bool(&mut self, name: &str, val: bool) -> MuxerResult<()> {
+ self.write_u16be(name.len() as u16)?;
+ self.write_buf(name.as_bytes())?;
+ self.write_byte(1)?;
+ self.write_byte(val as u8)?;
+ Ok(())
+ }
+}
+
+macro_rules! write_packet {
+ ($self: expr, $pkt_type: expr, $ts: expr, $code: block) => {
+ let start = $self.bw.tell();
+ $self.bw.write_byte($pkt_type)?;
+ $self.bw.write_u24be(0)?;
+ $self.bw.write_u24be($ts)?;
+ $self.bw.write_byte(($ts >> 24) as u8)?;
+ $self.bw.write_u24be(0)?;
+
+ $code
+
+ let end = $self.bw.tell();
+ let size = end - start - 11;
+ $self.bw.seek(SeekFrom::Start(start + 1))?;
+ $self.bw.write_u24be(size as u32)?;
+ $self.bw.seek(SeekFrom::Start(end))?;
+ $self.bw.write_u32be((size + 11) as u32)?;
+ }
+}
+
+struct FLVMuxer<'a> {
+ bw: &'a mut ByteWriter<'a>,
+ atag: u8,
+ ahdr: u8,
+ vtag: u8,
+ vp6b: u8,
+ time: u32,
+ dpos: u64,
+}
+
+impl<'a> FLVMuxer<'a> {
+ fn new(bw: &'a mut ByteWriter<'a>) -> Self {
+ Self {
+ bw,
+ atag: NO_CODEC,
+ ahdr: 0,
+ vtag: NO_CODEC,
+ vp6b: 0,
+ time: 0,
+ dpos: 0,
+ }
+ }
+ fn write_metadata(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
+ write_packet!(self, 18, 0, {
+ self.bw.write_buf(b"\x02\x00\x0AonMetaData\x08\x00\x00\x00\x00")?;
+ for stream in strmgr.iter() {
+ match stream.get_info().get_properties() {
+ NACodecTypeInfo::Video(ref vinfo) => {
+ self.bw.write_property_num("width", vinfo.width as f64)?;
+ self.bw.write_property_num("height", vinfo.height as f64)?;
+ self.bw.write_property_num("videocodecid", self.vtag as f64)?;
+ },
+ NACodecTypeInfo::Audio(ref ainfo) => {
+ self.bw.write_property_num("audiosamplerate", ainfo.sample_rate as f64)?;
+ self.bw.write_property_bool("stereo", ainfo.channels == 2)?;
+ self.bw.write_property_num("audiocodecid", self.atag as f64)?;
+ },
+ _ => {},
+ };
+ }
+ self.bw.write_property_num("duration", 0.0)?;
+ self.dpos = self.bw.tell() - 8;
+ self.bw.write_u16be(0)?;
+ self.bw.write_byte(9)?;
+ });
+
+ Ok(())
+ }
+}
+
+impl<'a> MuxCore<'a> for FLVMuxer<'a> {
+ fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
+ if strmgr.get_num_streams() == 0 || strmgr.get_num_streams() > 2 {
+ return Err(MuxerError::InvalidArgument);
+ }
+
+ let mut astream = None;
+ let mut vstream = None;
+ for stream in strmgr.iter() {
+ let cname = stream.get_info().get_name();
+ match stream.get_media_type() {
+ StreamType::Video => {
+ vstream = Some(stream.clone());
+ if self.vtag != NO_CODEC {
+ return Err(MuxerError::InvalidArgument);
+ }
+ for &(name, tag) in FLV_VCODECS.iter() {
+ if name == cname {
+ self.vtag = tag;
+ break;
+ }
+ }
+ if self.vtag == NO_CODEC {
+ return Err(MuxerError::UnsupportedFormat);
+ }
+ },
+ StreamType::Audio => {
+ astream = Some(stream.clone());
+ if self.atag != NO_CODEC {
+ return Err(MuxerError::InvalidArgument);
+ }
+ if let Some(ainfo) = stream.get_info().get_properties().get_audio_info() {
+ self.atag = find_audio_tag(cname, ainfo.sample_rate, ainfo.channels)?;
+ self.ahdr = (self.atag << 4) |
+ (match ainfo.sample_rate {
+ 5500 => 0,
+ 11025 => 1,
+ 22050 => 2,
+ _ => 3,
+ } << 2) |
+ if ainfo.format.bits == 8 { 0 } else { 2 } |
+ if ainfo.channels == 1 { 0 } else { 1 };
+ } else {
+ return Err(MuxerError::InvalidArgument);
+ }
+ },
+ _ => return Err(MuxerError::UnsupportedFormat),
+ };
+ }
+
+ self.bw.write_buf(b"FLV\x01")?;
+ let flags = 0x8 | if self.atag != NO_CODEC { 4 } else { 0 } | if self.vtag != NO_CODEC { 1 } else { 0 };
+ self.bw.write_byte(flags)?;
+ self.bw.write_u32be(9)?;
+ self.bw.write_u32be(0)?;
+
+ self.write_metadata(strmgr)?;
+
+ if let (true, Some(ref stream)) = (self.vtag == 4 || self.vtag == 5, &vstream) {
+ if let Some(edata) = stream.get_info().get_extradata() {
+ if !edata.is_empty() {
+ self.vp6b = edata[0];
+ }
+ }
+ }
+
+ if let (true, Some(stream)) = (self.vtag == AVC_ID, vstream) {
+ if let Some(edata) = stream.get_info().get_extradata() {
+ validate!(edata.len() > 4);
+ write_packet!(self, 9, 0, {
+ self.bw.write_byte(0x57)?;
+ self.bw.write_byte(0x00)?;
+ self.bw.write_u24be(0)?;
+ self.bw.write_buf(&edata[4..])?;
+ });
+ }
+ }
+ if let (true, Some(stream)) = (self.atag == AAC_ID, astream) {
+ if let Some(edata) = stream.get_info().get_extradata() {
+ write_packet!(self, 8, 0, {
+ self.bw.write_byte(self.ahdr)?;
+ self.bw.write_byte(0x00)?;
+ self.bw.write_buf(&edata)?;
+ });
+ }
+ }
+
+ Ok(())
+ }
+ fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> {
+ let stream = pkt.get_stream();
+ let pts = pkt.get_pts().unwrap_or(0);
+ let ms = NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32;
+ self.time = self.time.max(ms);
+ match stream.get_media_type() {
+ StreamType::Video => {
+ write_packet!(self, 9, ms, {
+ let hdr = self.vtag | if pkt.keyframe { 0x10 } else { 0x20 };
+ self.bw.write_byte(hdr)?;
+ match self.vtag {
+ 4 | 5 => {
+ self.bw.write_byte(self.vp6b)?;
+ },
+ AVC_ID => {
+ self.bw.write_byte(1)?;
+ let cms = NATimeInfo::ts_to_time(pkt.get_pts().unwrap_or(pts), 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32;
+ let cts = cms.wrapping_sub(ms) << 8 >> 8;
+ self.bw.write_u24be(cts)?;
+ },
+ _ => {},
+ };
+ self.bw.write_buf(&pkt.get_buffer())?;
+ });
+ },
+ StreamType::Audio => {
+ write_packet!(self, 8, ms, {
+ self.bw.write_byte(self.ahdr)?;
+ if self.atag == AAC_ID {
+ self.bw.write_byte(1)?;
+ }
+ self.bw.write_buf(&pkt.get_buffer())?;
+ });
+ },
+ _ => return Err(MuxerError::InvalidData),
+ };
+ Ok(())
+ }
+ fn flush(&mut self) -> MuxerResult<()> {
+ Ok(())
+ }
+ fn end(&mut self) -> MuxerResult<()> {
+ self.bw.seek(SeekFrom::Start(self.dpos))?;
+ self.bw.write_f64be((self.time as f64) / 1000.0)?;
+ Ok(())
+ }
+}
+
+impl<'a> NAOptionHandler for FLVMuxer<'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 FLVMuxerCreator {}
+
+impl MuxerCreator for FLVMuxerCreator {
+ fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> {
+ Box::new(FLVMuxer::new(bw))
+ }
+ fn get_name(&self) -> &'static str { "flv" }
+ fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleVideoAndAudio("any", "any") }
+}
+
+#[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_flv_muxer() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ flash_register_all_demuxers(&mut dmx_reg);
+ let dec_config = DecoderTestParams {
+ demuxer: "flv",
+ in_name: "assets/Flash/input.flv",
+ limit: None,
+ stream_type: StreamType::None,
+ dmx_reg, dec_reg: RegisteredDecoders::new(),
+ };
+ let mut mux_reg = RegisteredMuxers::new();
+ flash_register_all_muxers(&mut mux_reg);
+ /*let enc_config = EncoderTestParams {
+ muxer: "flv",
+ enc_name: "",
+ out_name: "muxed.flv",
+ mux_reg, enc_reg: RegisteredEncoders::new(),
+ };
+ test_remuxing(&dec_config, &enc_config);*/
+ test_remuxing_md5(&dec_config, "flv", &mux_reg,
+ [0xc777b605, 0x5777919d, 0x47996fe8, 0xf5e8d64f]);
+ }
+}
--- /dev/null
+use nihav_core::muxers::*;
+
+#[allow(unused_macros)]
+macro_rules! validate {
+ ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(MuxerError::InvalidData); } };
+}
+
+#[cfg(feature="muxer_flv")]
+mod flv;
+
+const MUXERS: &[&dyn MuxerCreator] = &[
+#[cfg(feature="muxer_flv")]
+ &flv::FLVMuxerCreator {},
+];
+
+pub fn flash_register_all_muxers(rm: &mut RegisteredMuxers) {
+ for muxer in MUXERS.iter() {
+ rm.add_muxer(*muxer);
+ }
+}
extensions: ".y4m",
conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"YUV4MPEG2 ") }],
},
+ DetectConditions {
+ demux_name: "flv",
+ extensions: ".flv",
+ conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"FLV") },
+ CheckItem{offs: 3, cond: &CC::Le(Arg::Byte(1)) }],
+ },
DetectConditions {
demux_name: "ivf",
extensions: ".ivf",
desc!(video; "vp4", "VP4"),
desc!(video; "vp5", "VP5"),
desc!(video; "vp6", "VP6"),
- desc!(video; "vp6a", "VP6"),
+ desc!(video; "vp6f", "VP6 (in Flash)"),
+ desc!(video; "vp6a", "VP6 with alpha"),
desc!(video; "vp7", "VP7"),
desc!(video; "vp8", "VP8"),
desc!(video; "vp9", "VP9"),
desc!(audio; "on2avc-500", "On2 AVC"),
desc!(audio; "on2avc-501", "On2 AVC"),
+ desc!(video; "flv263", "Sorenson H.263"),
+ desc!(video-llp; "flashsv", "Flash Screen Video"),
+ desc!(video-llp; "flashsv2", "Flash Screen Video 2"),
+ desc!(audio; "asao", "N*llym*s*r ASAO"),
+ desc!(audio; "flv-adpcm", "Flash ADPCM"),
+
+ desc!(audio; "mp3", "MPEG Audio Layer III"),
+ desc!(audio; "speex", "Speex"),
+
desc!(video; "gdv-video", "Gremlin Digital Video - video"),
desc!(audio; "gdv-audio", "Gremlin Digital Video - audio"),
desc!(video; "bmv-video", "BMV video"),