]> git.nihav.org Git - nihav.git/blame_incremental - nihav-flash/src/muxers/flv.rs
VP7 encoder
[nihav.git] / nihav-flash / src / muxers / flv.rs
... / ...
CommitLineData
1use nihav_core::muxers::*;
2
3const FLV_VCODECS: &[(&str, u8)] = &[
4 ("flv263", 2),
5 ("flashsv", 3),
6 ("vp6f", 4),
7 ("vp6a", 5),
8 ("flashsv2", 6),
9 ("h264", AVC_ID)
10];
11
12const NO_CODEC: u8 = 0;
13const AVC_ID: u8 = 7;
14const AAC_ID: u8 = 10;
15
16fn find_audio_tag(cname: &'static str, rate: u32, channels: u8) -> MuxerResult<u8> {
17 if channels != 1 && channels != 2 {
18 return Err(MuxerError::InvalidArgument);
19 }
20 let tag = match cname {
21 "flv-adpcm" => 1,
22 "pcm" => 3,
23 "mp3" => if rate != 8000 { 2 } else { return Ok(14); },
24 "asao" => {
25 if channels != 1 {
26 return Err(MuxerError::InvalidArgument);
27 }
28 match rate {
29 16000 => return Ok(4),
30 8000 => return Ok(5),
31 _ => 6,
32 }
33 },
34 "alaw" => 7,
35 "ulaw" => 8,
36 "aac" => AAC_ID,
37 "speex" => 11,
38 _ => return Err(MuxerError::InvalidArgument),
39 };
40 match rate {
41 5500 | 11025 | 22050 | 44100 => {},
42 _ => return Err(MuxerError::InvalidArgument),
43 };
44 Ok(tag)
45}
46
47trait FLVPropertyWriter {
48 fn write_property_num(&mut self, name: &str, val: f64) -> MuxerResult<()>;
49 fn write_property_bool(&mut self, name: &str, val: bool) -> MuxerResult<()>;
50}
51
52impl<'a> FLVPropertyWriter for ByteWriter<'a> {
53 fn write_property_num(&mut self, name: &str, val: f64) -> MuxerResult<()> {
54 self.write_u16be(name.len() as u16)?;
55 self.write_buf(name.as_bytes())?;
56 self.write_byte(0)?;
57 self.write_f64be(val)?;
58 Ok(())
59 }
60 fn write_property_bool(&mut self, name: &str, val: bool) -> MuxerResult<()> {
61 self.write_u16be(name.len() as u16)?;
62 self.write_buf(name.as_bytes())?;
63 self.write_byte(1)?;
64 self.write_byte(val as u8)?;
65 Ok(())
66 }
67}
68
69macro_rules! write_packet {
70 ($self: expr, $pkt_type: expr, $ts: expr, $code: block) => {
71 let start = $self.bw.tell();
72 $self.bw.write_byte($pkt_type)?;
73 $self.bw.write_u24be(0)?;
74 $self.bw.write_u24be($ts)?;
75 $self.bw.write_byte(($ts >> 24) as u8)?;
76 $self.bw.write_u24be(0)?;
77
78 $code
79
80 let end = $self.bw.tell();
81 let size = end - start - 11;
82 $self.bw.seek(SeekFrom::Start(start + 1))?;
83 $self.bw.write_u24be(size as u32)?;
84 $self.bw.seek(SeekFrom::Start(end))?;
85 $self.bw.write_u32be((size + 11) as u32)?;
86 }
87}
88
89struct FLVMuxer<'a> {
90 bw: &'a mut ByteWriter<'a>,
91 atag: u8,
92 ahdr: u8,
93 vtag: u8,
94 vp6b: u8,
95 time: u32,
96 dpos: u64,
97}
98
99impl<'a> FLVMuxer<'a> {
100 fn new(bw: &'a mut ByteWriter<'a>) -> Self {
101 Self {
102 bw,
103 atag: NO_CODEC,
104 ahdr: 0,
105 vtag: NO_CODEC,
106 vp6b: 0,
107 time: 0,
108 dpos: 0,
109 }
110 }
111 fn write_metadata(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
112 write_packet!(self, 18, 0, {
113 self.bw.write_buf(b"\x02\x00\x0AonMetaData\x08\x00\x00\x00\x00")?;
114 for stream in strmgr.iter() {
115 match stream.get_info().get_properties() {
116 NACodecTypeInfo::Video(ref vinfo) => {
117 self.bw.write_property_num("width", vinfo.width as f64)?;
118 self.bw.write_property_num("height", vinfo.height as f64)?;
119 self.bw.write_property_num("videocodecid", self.vtag as f64)?;
120 },
121 NACodecTypeInfo::Audio(ref ainfo) => {
122 self.bw.write_property_num("audiosamplerate", ainfo.sample_rate as f64)?;
123 self.bw.write_property_bool("stereo", ainfo.channels == 2)?;
124 self.bw.write_property_num("audiocodecid", self.atag as f64)?;
125 },
126 _ => {},
127 };
128 }
129 self.bw.write_property_num("duration", 0.0)?;
130 self.dpos = self.bw.tell() - 8;
131 self.bw.write_u16be(0)?;
132 self.bw.write_byte(9)?;
133 });
134
135 Ok(())
136 }
137}
138
139impl<'a> MuxCore<'a> for FLVMuxer<'a> {
140 fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
141 if strmgr.get_num_streams() == 0 || strmgr.get_num_streams() > 2 {
142 return Err(MuxerError::InvalidArgument);
143 }
144
145 let mut astream = None;
146 let mut vstream = None;
147 for stream in strmgr.iter() {
148 let cname = stream.get_info().get_name();
149 match stream.get_media_type() {
150 StreamType::Video => {
151 vstream = Some(stream.clone());
152 if self.vtag != NO_CODEC {
153 return Err(MuxerError::InvalidArgument);
154 }
155 for &(name, tag) in FLV_VCODECS.iter() {
156 if name == cname {
157 self.vtag = tag;
158 break;
159 }
160 }
161 if self.vtag == NO_CODEC {
162 return Err(MuxerError::UnsupportedFormat);
163 }
164 },
165 StreamType::Audio => {
166 astream = Some(stream.clone());
167 if self.atag != NO_CODEC {
168 return Err(MuxerError::InvalidArgument);
169 }
170 if let Some(ainfo) = stream.get_info().get_properties().get_audio_info() {
171 self.atag = find_audio_tag(cname, ainfo.sample_rate, ainfo.channels)?;
172 self.ahdr = (self.atag << 4) |
173 (match ainfo.sample_rate {
174 5500 => 0,
175 11025 => 1,
176 22050 => 2,
177 _ => 3,
178 } << 2) |
179 if ainfo.format.bits == 8 { 0 } else { 2 } |
180 if ainfo.channels == 1 { 0 } else { 1 };
181 } else {
182 return Err(MuxerError::InvalidArgument);
183 }
184 },
185 _ => return Err(MuxerError::UnsupportedFormat),
186 };
187 }
188
189 self.bw.write_buf(b"FLV\x01")?;
190 let flags = 0x8 | if self.atag != NO_CODEC { 4 } else { 0 } | if self.vtag != NO_CODEC { 1 } else { 0 };
191 self.bw.write_byte(flags)?;
192 self.bw.write_u32be(9)?;
193 self.bw.write_u32be(0)?;
194
195 self.write_metadata(strmgr)?;
196
197 if let (true, Some(ref stream)) = (self.vtag == 4 || self.vtag == 5, &vstream) {
198 if let Some(edata) = stream.get_info().get_extradata() {
199 if !edata.is_empty() {
200 self.vp6b = edata[0];
201 }
202 }
203 }
204
205 if let (true, Some(stream)) = (self.vtag == AVC_ID, vstream) {
206 if let Some(edata) = stream.get_info().get_extradata() {
207 validate!(edata.len() > 4);
208 write_packet!(self, 9, 0, {
209 self.bw.write_byte(0x57)?;
210 self.bw.write_byte(0x00)?;
211 self.bw.write_u24be(0)?;
212 self.bw.write_buf(&edata[4..])?;
213 });
214 }
215 }
216 if let (true, Some(stream)) = (self.atag == AAC_ID, astream) {
217 if let Some(edata) = stream.get_info().get_extradata() {
218 write_packet!(self, 8, 0, {
219 self.bw.write_byte(self.ahdr)?;
220 self.bw.write_byte(0x00)?;
221 self.bw.write_buf(&edata)?;
222 });
223 }
224 }
225
226 Ok(())
227 }
228 fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> {
229 let stream = pkt.get_stream();
230 let pts = pkt.get_pts().unwrap_or(0);
231 let ms = NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32;
232 self.time = self.time.max(ms);
233 match stream.get_media_type() {
234 StreamType::Video => {
235 write_packet!(self, 9, ms, {
236 let hdr = self.vtag | if pkt.keyframe { 0x10 } else { 0x20 };
237 self.bw.write_byte(hdr)?;
238 match self.vtag {
239 4 | 5 => {
240 self.bw.write_byte(self.vp6b)?;
241 },
242 AVC_ID => {
243 self.bw.write_byte(1)?;
244 let cms = NATimeInfo::ts_to_time(pkt.get_pts().unwrap_or(pts), 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32;
245 let cts = cms.wrapping_sub(ms) << 8 >> 8;
246 self.bw.write_u24be(cts)?;
247 },
248 _ => {},
249 };
250 self.bw.write_buf(&pkt.get_buffer())?;
251 });
252 },
253 StreamType::Audio => {
254 write_packet!(self, 8, ms, {
255 self.bw.write_byte(self.ahdr)?;
256 if self.atag == AAC_ID {
257 self.bw.write_byte(1)?;
258 }
259 self.bw.write_buf(&pkt.get_buffer())?;
260 });
261 },
262 _ => return Err(MuxerError::InvalidData),
263 };
264 Ok(())
265 }
266 fn flush(&mut self) -> MuxerResult<()> {
267 Ok(())
268 }
269 fn end(&mut self) -> MuxerResult<()> {
270 self.bw.seek(SeekFrom::Start(self.dpos))?;
271 self.bw.write_f64be((self.time as f64) / 1000.0)?;
272 Ok(())
273 }
274}
275
276impl<'a> NAOptionHandler for FLVMuxer<'a> {
277 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
278 fn set_options(&mut self, _options: &[NAOption]) { }
279 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
280}
281
282pub struct FLVMuxerCreator {}
283
284impl MuxerCreator for FLVMuxerCreator {
285 fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> {
286 Box::new(FLVMuxer::new(bw))
287 }
288 fn get_name(&self) -> &'static str { "flv" }
289 fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleVideoAndAudio("any", "any") }
290}
291
292#[cfg(test)]
293mod test {
294 use nihav_core::codecs::*;
295 use nihav_core::demuxers::*;
296 use nihav_core::muxers::*;
297 use nihav_codec_support::test::enc_video::*;
298 use crate::*;
299
300 #[test]
301 fn test_flv_muxer() {
302 let mut dmx_reg = RegisteredDemuxers::new();
303 flash_register_all_demuxers(&mut dmx_reg);
304 // sample: https://samples.mplayerhq.hu/A-codecs/Nelly_Moser/input.flv
305 let dec_config = DecoderTestParams {
306 demuxer: "flv",
307 in_name: "assets/Flash/input.flv",
308 limit: None,
309 stream_type: StreamType::None,
310 dmx_reg, dec_reg: RegisteredDecoders::new(),
311 };
312 let mut mux_reg = RegisteredMuxers::new();
313 flash_register_all_muxers(&mut mux_reg);
314 /*let enc_config = EncoderTestParams {
315 muxer: "flv",
316 enc_name: "",
317 out_name: "muxed.flv",
318 mux_reg, enc_reg: RegisteredEncoders::new(),
319 };
320 test_remuxing(&dec_config, &enc_config);*/
321 test_remuxing_md5(&dec_config, "flv", &mux_reg,
322 [0xc777b605, 0x5777919d, 0x47996fe8, 0xf5e8d64f]);
323 }
324}