]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::muxers::*; | |
2 | ||
3 | struct EAMuxer<'a> { | |
4 | bw: &'a mut ByteWriter<'a>, | |
5 | has_alpha: bool, | |
6 | nframes: u32, | |
7 | max_size: [u32; 2], | |
8 | } | |
9 | ||
10 | impl<'a> EAMuxer<'a> { | |
11 | fn new(bw: &'a mut ByteWriter<'a>) -> Self { | |
12 | Self { | |
13 | bw, | |
14 | has_alpha: false, | |
15 | nframes: 0, | |
16 | max_size: [0; 2], | |
17 | } | |
18 | } | |
19 | } | |
20 | ||
21 | impl<'a> MuxCore<'a> for EAMuxer<'a> { | |
22 | #[allow(clippy::unreadable_literal)] | |
23 | #[allow(clippy::cast_lossless)] | |
24 | fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> { | |
25 | if strmgr.get_num_streams() == 0 { | |
26 | return Err(MuxerError::InvalidArgument); | |
27 | } | |
28 | ||
29 | let mut nvideo = 0; | |
30 | for stream in strmgr.iter() { | |
31 | if stream.get_media_type() == StreamType::Video { | |
32 | if stream.get_info().get_name() != "vp6" { | |
33 | return Err(MuxerError::UnsupportedFormat); | |
34 | } | |
35 | nvideo += 1; | |
36 | } else { | |
37 | return Err(MuxerError::UnsupportedFormat); | |
38 | } | |
39 | } | |
40 | if nvideo == 0 || nvideo > 2 { | |
41 | return Err(MuxerError::UnsupportedFormat); | |
42 | } | |
43 | self.has_alpha = nvideo == 2; | |
44 | ||
45 | if self.has_alpha { | |
46 | self.bw.write_buf(b"AVP6\x08\x00\x00\x00")?; | |
47 | } | |
48 | for (str_no, stream) in strmgr.iter().enumerate() { | |
49 | if let NACodecTypeInfo::Video(ref vinfo) = stream.get_info().get_properties() { | |
50 | let tag = if str_no == 0 { b"MVhd" } else { b"AVhd" }; | |
51 | ||
52 | self.bw.write_buf(tag)?; | |
53 | self.bw.write_u32le(0x20)?; | |
54 | self.bw.write_buf(b"vp60")?; | |
55 | self.bw.write_u16le(vinfo.width as u16)?; | |
56 | self.bw.write_u16le(vinfo.height as u16)?; | |
57 | self.bw.write_u32le(0)?; | |
58 | self.bw.write_u32le(0)?; | |
59 | self.bw.write_u32le(stream.tb_den)?; | |
60 | self.bw.write_u32le(stream.tb_num)?; | |
61 | } else { | |
62 | unimplemented!(); | |
63 | } | |
64 | } | |
65 | ||
66 | Ok(()) | |
67 | } | |
68 | #[allow(clippy::collapsible_else_if)] | |
69 | fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { | |
70 | let stream = pkt.get_stream(); | |
71 | let str_num = stream.get_num(); | |
72 | if str_num > 2 { | |
73 | return Err(MuxerError::UnsupportedFormat); | |
74 | } | |
75 | ||
76 | let chunk_len = pkt.get_buffer().len() as u32; | |
77 | ||
78 | let tag = if pkt.is_keyframe() { | |
79 | if str_num == 0 { b"MV0K" } else { b"AV0K" } | |
80 | } else { | |
81 | if str_num == 0 { b"MV0F" } else { b"AV0F" } | |
82 | }; | |
83 | self.max_size[str_num] = self.max_size[str_num].max(pkt.get_buffer().len() as u32); | |
84 | self.bw.write_buf(tag)?; | |
85 | self.bw.write_u32le(chunk_len + 8)?; | |
86 | self.bw.write_buf(&pkt.get_buffer())?; | |
87 | ||
88 | if str_num == 0 { | |
89 | self.nframes += 1; | |
90 | } | |
91 | ||
92 | Ok(()) | |
93 | } | |
94 | fn flush(&mut self) -> MuxerResult<()> { | |
95 | Ok(()) | |
96 | } | |
97 | fn end(&mut self) -> MuxerResult<()> { | |
98 | if !self.has_alpha { | |
99 | self.bw.seek(SeekFrom::Start(0x10))?; | |
100 | self.bw.write_u32le(self.nframes)?; | |
101 | self.bw.write_u32le(self.max_size[0])?; | |
102 | } else { | |
103 | self.bw.seek(SeekFrom::Start(0x18))?; | |
104 | self.bw.write_u32le(self.nframes)?; | |
105 | self.bw.write_u32le(self.max_size[0])?; | |
106 | self.bw.seek(SeekFrom::Start(0x38))?; | |
107 | self.bw.write_u32le(self.nframes)?; | |
108 | self.bw.write_u32le(self.max_size[1])?; | |
109 | } | |
110 | Ok(()) | |
111 | } | |
112 | } | |
113 | ||
114 | impl<'a> NAOptionHandler for EAMuxer<'a> { | |
115 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
116 | fn set_options(&mut self, _options: &[NAOption]) { } | |
117 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
118 | } | |
119 | ||
120 | pub struct EAMuxerCreator {} | |
121 | ||
122 | impl MuxerCreator for EAMuxerCreator { | |
123 | fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> { | |
124 | Box::new(EAMuxer::new(bw)) | |
125 | } | |
126 | fn get_name(&self) -> &'static str { "ea" } | |
127 | fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::OnlyVideo } | |
128 | } | |
129 | ||
130 | #[cfg(test)] | |
131 | mod test { | |
132 | use nihav_core::codecs::*; | |
133 | use nihav_core::demuxers::*; | |
134 | use nihav_core::muxers::*; | |
135 | use nihav_codec_support::test::enc_video::*; | |
136 | use crate::*; | |
137 | ||
138 | #[test] | |
139 | fn test_ea_muxer() { | |
140 | let mut dmx_reg = RegisteredDemuxers::new(); | |
141 | nihav_commonfmt::generic_register_all_demuxers(&mut dmx_reg); | |
142 | let dec_config = DecoderTestParams { | |
143 | demuxer: "avi", | |
144 | in_name: "assets/Duck/vp6_crash.avi", | |
145 | limit: None, | |
146 | stream_type: StreamType::None, | |
147 | dmx_reg, dec_reg: RegisteredDecoders::new(), | |
148 | }; | |
149 | let mut mux_reg = RegisteredMuxers::new(); | |
150 | game_register_all_muxers(&mut mux_reg); | |
151 | /*let enc_config = EncoderTestParams { | |
152 | muxer: "ea", | |
153 | enc_name: "", | |
154 | out_name: "muxed.ea", | |
155 | mux_reg, enc_reg: RegisteredEncoders::new(), | |
156 | }; | |
157 | test_remuxing(&dec_config, &enc_config);*/ | |
158 | test_remuxing_md5(&dec_config, "ea", &mux_reg, | |
159 | [0xc8c6484d, 0x863de1ae, 0x97a38a31, 0x59e2a7ef]); | |
160 | } | |
161 | } |