]>
Commit | Line | Data |
---|---|---|
92d9fb69 KS |
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 => { | |
6f263099 KS |
66 | Ok(EncodeParameters { |
67 | format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_S16_FORMAT, BLOCK_LEN)), | |
68 | ..Default::default() }) | |
92d9fb69 KS |
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 init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> { | |
85 | match encinfo.format { | |
86 | NACodecTypeInfo::None => Err(EncoderError::FormatError), | |
87 | NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), | |
88 | NACodecTypeInfo::Audio(ainfo) => { | |
89 | if ainfo.format != SND_S16P_FORMAT && ainfo.format != SND_S16_FORMAT { | |
90 | return Err(EncoderError::FormatError); | |
91 | } | |
92 | if ainfo.channels != 1 && ainfo.channels != 2 { | |
93 | return Err(EncoderError::FormatError); | |
94 | } | |
95 | self.channels = ainfo.channels as usize; | |
96 | ||
97 | let soniton = NASoniton::new(4, 0); | |
98 | let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, if self.channels == 1 { 2051 } else { 4101 }); | |
99 | let info = NACodecInfo::new("flv-adpcm", NACodecTypeInfo::Audio(out_ainfo), None); | |
100 | let mut stream = NAStream::new(StreamType::Audio, stream_id, info, 1, ainfo.sample_rate, 0); | |
101 | stream.set_num(stream_id as usize); | |
102 | let stream = stream.into_ref(); | |
103 | ||
104 | self.stream = Some(stream.clone()); | |
105 | self.samples = Vec::with_capacity(BLOCK_LEN * self.channels); | |
106 | self.srate = ainfo.sample_rate; | |
107 | self.flush = false; | |
108 | ||
109 | Ok(stream) | |
110 | }, | |
111 | } | |
112 | } | |
113 | fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { | |
114 | let buf = frm.get_buffer(); | |
115 | if let Some(ref abuf) = buf.get_abuf_i16() { | |
116 | let src = abuf.get_data(); | |
117 | let len = abuf.get_length(); | |
118 | let ch = abuf.get_chmap().num_channels(); | |
119 | if abuf.get_step() > 1 || ch == 1 { | |
120 | self.samples.extend_from_slice(&src[..len * ch]); | |
121 | } else { | |
122 | let astride = abuf.get_stride(); | |
123 | self.samples.reserve(len * ch); | |
124 | for i in 0..len { | |
125 | for ch in 0..self.channels { | |
126 | self.samples.push(src[ch * astride + i]); | |
127 | } | |
128 | } | |
129 | } | |
130 | Ok(()) | |
131 | } else { | |
132 | Err(EncoderError::InvalidParameters) | |
133 | } | |
134 | } | |
135 | fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> { | |
136 | if let Ok(pkt) = self.encode_packet() { | |
137 | Ok(Some(pkt)) | |
138 | } else { | |
139 | Ok(None) | |
140 | } | |
141 | } | |
142 | fn flush(&mut self) -> EncoderResult<()> { | |
143 | self.flush = true; | |
144 | Ok(()) | |
145 | } | |
146 | } | |
147 | ||
148 | impl NAOptionHandler for ADPCMEncoder { | |
149 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
150 | fn set_options(&mut self, _options: &[NAOption]) {} | |
151 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
152 | } | |
153 | ||
154 | pub fn get_encoder() -> Box<dyn NAEncoder + Send> { | |
155 | Box::new(ADPCMEncoder::new()) | |
156 | } | |
157 | ||
158 | #[cfg(test)] | |
159 | mod test { | |
160 | use nihav_core::codecs::*; | |
161 | use nihav_core::demuxers::*; | |
162 | use nihav_core::muxers::*; | |
163 | use nihav_codec_support::test::enc_video::*; | |
164 | use crate::*; | |
165 | ||
166 | #[test] | |
167 | fn test_flv_adpcm_encoder() { | |
168 | ||
169 | let mut dmx_reg = RegisteredDemuxers::new(); | |
170 | flash_register_all_demuxers(&mut dmx_reg); | |
171 | let mut dec_reg = RegisteredDecoders::new(); | |
172 | flash_register_all_decoders(&mut dec_reg); | |
173 | let mut mux_reg = RegisteredMuxers::new(); | |
174 | flash_register_all_muxers(&mut mux_reg); | |
175 | let mut enc_reg = RegisteredEncoders::new(); | |
176 | flash_register_all_encoders(&mut enc_reg); | |
177 | ||
886cde48 | 178 | // sample: https://samples.mplayerhq.hu/FLV/flash_flv_adpcm_testfiles/mono_11k.flv |
92d9fb69 KS |
179 | let dec_config = DecoderTestParams { |
180 | demuxer: "flv", | |
181 | in_name: "assets/Flash/mono_11k.flv", | |
182 | stream_type: StreamType::Audio, | |
183 | limit: Some(3700), | |
184 | dmx_reg, dec_reg, | |
185 | }; | |
186 | let enc_config = EncoderTestParams { | |
187 | muxer: "flv", | |
188 | enc_name: "flv-adpcm", | |
189 | out_name: "flv_adpcm.flv", | |
190 | mux_reg, enc_reg, | |
191 | }; | |
192 | let dst_ainfo = NAAudioInfo { | |
193 | sample_rate: 0, | |
194 | channels: 0, | |
195 | format: SND_S16_FORMAT, | |
196 | block_len: 128, | |
197 | }; | |
198 | let enc_params = EncodeParameters { | |
199 | format: NACodecTypeInfo::Audio(dst_ainfo), | |
200 | quality: 0, | |
201 | bitrate: 0, | |
202 | tb_num: 0, | |
203 | tb_den: 0, | |
204 | flags: 0, | |
205 | }; | |
206 | //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]); | |
207 | ||
208 | test_encoding_md5(&dec_config, &enc_config, enc_params, &[], | |
209 | &[0x6114b7de, 0xb84c226f, 0x0caab8e5, 0x2c7df63f]); | |
210 | } | |
211 | } |