]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::codecs::*; | |
2 | use nihav_core::io::bitwriter::*; | |
3 | use nihav_codec_support::codecs::imaadpcm::*; | |
4 | ||
5 | #[derive(Default)] | |
6 | struct ADPCMEncoder { | |
7 | stream: Option<NAStreamRef>, | |
8 | samples: Vec<i16>, | |
9 | flush: bool, | |
10 | state: [IMAState; 2], | |
11 | channels: usize, | |
12 | srate: u32, | |
13 | pos: u64, | |
14 | } | |
15 | ||
16 | const BLOCK_LEN: usize = 4096; | |
17 | ||
18 | impl ADPCMEncoder { | |
19 | fn new() -> Self { Self::default() } | |
20 | fn encode_packet(&mut self) -> EncoderResult<NAPacket> { | |
21 | if self.samples.is_empty() { | |
22 | return Err(EncoderError::TryAgain); | |
23 | } | |
24 | let cur_len = (self.samples.len() / self.channels).min(BLOCK_LEN); | |
25 | if cur_len < BLOCK_LEN && !self.flush { | |
26 | return Err(EncoderError::TryAgain); | |
27 | } | |
28 | ||
29 | let bsize = (2 + (16 + 6 + (cur_len - 1) * 4) * self.channels + 7) / 8; | |
30 | ||
31 | let mut bw = BitWriter::new(Vec::with_capacity(bsize), BitWriterMode::BE); | |
32 | ||
33 | bw.write(2, 2); | |
34 | ||
35 | for ch in 0..self.channels { | |
36 | self.state[ch].predictor = i32::from(self.samples[ch]); | |
37 | self.state[ch].step = 30; | |
38 | ||
39 | bw.write_s(i32::from(self.samples[ch]), 16); | |
40 | bw.write(self.state[ch].step as u32, 6); | |
41 | } | |
42 | ||
43 | let mut siter = self.samples[self.channels..].iter(); | |
44 | for _ in 1..cur_len { | |
45 | for state in self.state[..self.channels].iter_mut() { | |
46 | let samp = *siter.next().unwrap(); | |
47 | let nib = state.compress_sample(samp); | |
48 | state.expand_sample(nib); | |
49 | bw.write(u32::from(nib), 4); | |
50 | } | |
51 | } | |
52 | ||
53 | let data = bw.end(); | |
54 | ||
55 | self.samples.drain(..cur_len * self.channels); | |
56 | let ts = NATimeInfo::new(Some(self.pos), None, Some(cur_len as u64), 1, self.srate); | |
57 | self.pos += cur_len as u64; | |
58 | Ok(NAPacket::new(self.stream.clone().unwrap(), ts, true, data)) | |
59 | } | |
60 | } | |
61 | ||
62 | impl NAEncoder for ADPCMEncoder { | |
63 | fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> { | |
64 | match encinfo.format { | |
65 | NACodecTypeInfo::None => { | |
66 | Ok(EncodeParameters { | |
67 | format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_S16_FORMAT, BLOCK_LEN)), | |
68 | ..Default::default() }) | |
69 | }, | |
70 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
71 | NACodecTypeInfo::Audio(ainfo) => { | |
72 | let mut outinfo = ainfo; | |
73 | outinfo.channels = outinfo.channels.max(1).min(2); | |
74 | if outinfo.format != SND_S16P_FORMAT && outinfo.format != SND_S16_FORMAT { | |
75 | outinfo.format = SND_S16_FORMAT; | |
76 | } | |
77 | outinfo.block_len = BLOCK_LEN; | |
78 | let mut ofmt = *encinfo; | |
79 | ofmt.format = NACodecTypeInfo::Audio(outinfo); | |
80 | Ok(ofmt) | |
81 | } | |
82 | } | |
83 | } | |
84 | fn get_capabilities(&self) -> u64 { ENC_CAPS_CBR } | |
85 | fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { | |
86 | match encinfo.format { | |
87 | NACodecTypeInfo::None => Err(EncoderError::FormatError), | |
88 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
89 | NACodecTypeInfo::Audio(ainfo) => { | |
90 | if ainfo.format != SND_S16P_FORMAT && ainfo.format != SND_S16_FORMAT { | |
91 | return Err(EncoderError::FormatError); | |
92 | } | |
93 | if ainfo.channels != 1 && ainfo.channels != 2 { | |
94 | return Err(EncoderError::FormatError); | |
95 | } | |
96 | self.channels = ainfo.channels as usize; | |
97 | ||
98 | let soniton = NASoniton::new(4, 0); | |
99 | let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, if self.channels == 1 { 2051 } else { 4101 }); | |
100 | let info = NACodecInfo::new("flv-adpcm", NACodecTypeInfo::Audio(out_ainfo), None); | |
101 | let mut stream = NAStream::new(StreamType::Audio, stream_id, info, 1, ainfo.sample_rate, 0); | |
102 | stream.set_num(stream_id as usize); | |
103 | let stream = stream.into_ref(); | |
104 | ||
105 | self.stream = Some(stream.clone()); | |
106 | self.samples = Vec::with_capacity(BLOCK_LEN * self.channels); | |
107 | self.srate = ainfo.sample_rate; | |
108 | self.flush = false; | |
109 | ||
110 | Ok(stream) | |
111 | }, | |
112 | } | |
113 | } | |
114 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { | |
115 | let buf = frm.get_buffer(); | |
116 | if let Some(ref abuf) = buf.get_abuf_i16() { | |
117 | let src = abuf.get_data(); | |
118 | let len = abuf.get_length(); | |
119 | let ch = abuf.get_chmap().num_channels(); | |
120 | if abuf.get_step() > 1 || ch == 1 { | |
121 | self.samples.extend_from_slice(&src[..len * ch]); | |
122 | } else { | |
123 | let astride = abuf.get_stride(); | |
124 | self.samples.reserve(len * ch); | |
125 | for i in 0..len { | |
126 | for ch in 0..self.channels { | |
127 | self.samples.push(src[ch * astride + i]); | |
128 | } | |
129 | } | |
130 | } | |
131 | Ok(()) | |
132 | } else { | |
133 | Err(EncoderError::InvalidParameters) | |
134 | } | |
135 | } | |
136 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { | |
137 | if let Ok(pkt) = self.encode_packet() { | |
138 | Ok(Some(pkt)) | |
139 | } else { | |
140 | Ok(None) | |
141 | } | |
142 | } | |
143 | fn flush(&mut self) -> EncoderResult<()> { | |
144 | self.flush = true; | |
145 | Ok(()) | |
146 | } | |
147 | } | |
148 | ||
149 | impl NAOptionHandler for ADPCMEncoder { | |
150 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
151 | fn set_options(&mut self, _options: &[NAOption]) {} | |
152 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
153 | } | |
154 | ||
155 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { | |
156 | Box::new(ADPCMEncoder::new()) | |
157 | } | |
158 | ||
159 | #[cfg(test)] | |
160 | mod test { | |
161 | use nihav_core::codecs::*; | |
162 | use nihav_core::demuxers::*; | |
163 | use nihav_core::muxers::*; | |
164 | use nihav_codec_support::test::enc_video::*; | |
165 | use crate::*; | |
166 | ||
167 | #[test] | |
168 | fn test_flv_adpcm_encoder() { | |
169 | ||
170 | let mut dmx_reg = RegisteredDemuxers::new(); | |
171 | flash_register_all_demuxers(&mut dmx_reg); | |
172 | let mut dec_reg = RegisteredDecoders::new(); | |
173 | flash_register_all_decoders(&mut dec_reg); | |
174 | let mut mux_reg = RegisteredMuxers::new(); | |
175 | flash_register_all_muxers(&mut mux_reg); | |
176 | let mut enc_reg = RegisteredEncoders::new(); | |
177 | flash_register_all_encoders(&mut enc_reg); | |
178 | ||
179 | // sample: https://samples.mplayerhq.hu/FLV/flash_flv_adpcm_testfiles/mono_11k.flv | |
180 | let dec_config = DecoderTestParams { | |
181 | demuxer: "flv", | |
182 | in_name: "assets/Flash/mono_11k.flv", | |
183 | stream_type: StreamType::Audio, | |
184 | limit: Some(3700), | |
185 | dmx_reg, dec_reg, | |
186 | }; | |
187 | let enc_config = EncoderTestParams { | |
188 | muxer: "flv", | |
189 | enc_name: "flv-adpcm", | |
190 | out_name: "flv_adpcm.flv", | |
191 | mux_reg, enc_reg, | |
192 | }; | |
193 | let dst_ainfo = NAAudioInfo { | |
194 | sample_rate: 0, | |
195 | channels: 0, | |
196 | format: SND_S16_FORMAT, | |
197 | block_len: 128, | |
198 | }; | |
199 | let enc_params = EncodeParameters { | |
200 | format: NACodecTypeInfo::Audio(dst_ainfo), | |
201 | quality: 0, | |
202 | bitrate: 0, | |
203 | tb_num: 0, | |
204 | tb_den: 0, | |
205 | flags: 0, | |
206 | }; | |
207 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]); | |
208 | ||
209 | test_encoding_md5(&dec_config, &enc_config, enc_params, &[], | |
210 | &[0x6114b7de, 0xb84c226f, 0x0caab8e5, 0x2c7df63f]); | |
211 | } | |
212 | } |