1 use nihav_core::muxers::*;
4 bw: &'a mut ByteWriter<'a>,
11 impl<'a> GIFMuxer<'a> {
12 fn new(bw: &'a mut ByteWriter<'a>) -> Self {
21 fn write_pal(&mut self, pal: &[u8; 1024]) -> MuxerResult<()> {
23 for quad in pal.chunks_exact(4).rev() {
24 if quad[0] == 0 && quad[1] == 0 && quad[2] == 0 {
31 while (1 << pal_bits) < nclr {
34 self.bw.write_byte(0xF0 | (pal_bits - 1))?;
35 self.bw.write_byte(0)?; // background colour index
36 self.bw.write_byte(0)?; // aspect ratio
37 for quad in pal.chunks_exact(4).take(1 << pal_bits) {
38 self.bw.write_buf(&quad[..3])?;
44 impl<'a> MuxCore<'a> for GIFMuxer<'a> {
45 fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
46 if strmgr.get_num_streams() != 1 {
47 return Err(MuxerError::InvalidArgument);
49 let vstr = strmgr.get_stream(0).unwrap();
50 if vstr.get_media_type() != StreamType::Video {
51 return Err(MuxerError::UnsupportedFormat);
53 let info = vstr.get_info();
54 let vinfo = info.get_properties().get_video_info().unwrap();
55 if vinfo.width > 65535 || vinfo.height > 65535 || !vinfo.format.palette {
56 return Err(MuxerError::UnsupportedFormat);
61 self.bw.write_buf(b"GIF87a")?;
63 self.bw.write_buf(b"GIF89a")?;
65 self.bw.write_u16le(vinfo.width as u16)?;
66 self.bw.write_u16le(vinfo.height as u16)?;
70 fn mux_frame(&mut self, strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> {
71 if self.bw.tell() == 0 {
72 return Err(MuxerError::NotCreated);
74 if !self.pal_written {
75 let info = strmgr.get_stream(0).unwrap().get_info();
76 let mut tr_idx = None;
77 if let Some(ref edata) = info.get_extradata() {
79 tr_idx = Some(edata[0]);
80 } else if edata.len() >= 3 {
81 self.bw.write_buf(edata)?;
82 self.pal_written = true;
85 if !self.pal_written {
86 let mut pal_found = false;
87 for sdata in pkt.side_data.iter() {
88 if let NASideData::Palette(_, ref pal) = sdata {
89 self.write_pal(pal,)?;
95 return Err(MuxerError::InvalidArgument);
98 self.pal_written = true;
101 let vstr = strmgr.get_stream(0).unwrap();
103 let delay = NATimeInfo::ts_to_time(1, 100, vstr.tb_num, vstr.tb_den) as u16;
104 self.bw.write_byte(0x21)?; // graphic control
105 self.bw.write_byte(0xF9)?; // graphic control extension
106 self.bw.write_byte(4)?; // block size
107 self.bw.write_byte(if tr_idx.is_some() { 1 } else { 0 })?; // flags
108 self.bw.write_u16le(delay)?;
109 self.bw.write_byte(tr_idx.unwrap_or(0))?; // transparent colour index
110 self.bw.write_byte(0x00)?; // block terminator
112 self.bw.write_byte(0x21)?; // graphic control
113 self.bw.write_byte(0xFF)?; // application extension
114 let app_id = b"NETSCAPE2.0";
115 self.bw.write_byte(app_id.len() as u8)?;
116 self.bw.write_buf(app_id)?;
117 self.bw.write_byte(3)?; // application data block length
118 self.bw.write_byte(0x01)?;
119 self.bw.write_u16le(self.nloops)?;
120 self.bw.write_byte(0x00)?; // block terminator
122 } else if self.single { // just one frame is expected
123 return Err(MuxerError::InvalidArgument);
126 // buffer is supposed to have all the data starting from image descriptor
127 let src = pkt.get_buffer();
128 self.bw.write_buf(&src)?;
131 fn flush(&mut self) -> MuxerResult<()> {
134 fn end(&mut self) -> MuxerResult<()> {
135 self.bw.write_byte(0x3B)?; // GIF terminator
140 const MUXER_OPTS: &[NAOptionDefinition] = &[
142 name: "gif87", description: "Create GIF87 image",
143 opt_type: NAOptionDefinitionType::Bool },
145 name: "single", description: "Create single image",
146 opt_type: NAOptionDefinitionType::Bool },
148 name: "loops", description: "Number of times to loop the animation",
149 opt_type: NAOptionDefinitionType::Int(Some(0), Some(65535)) },
152 impl<'a> NAOptionHandler for GIFMuxer<'a> {
153 fn get_supported_options(&self) -> &[NAOptionDefinition] { MUXER_OPTS }
154 fn set_options(&mut self, options: &[NAOption]) {
155 for option in options.iter() {
156 for opt_def in MUXER_OPTS.iter() {
157 if opt_def.check(option).is_ok() {
160 if let NAValue::Bool(bval) = option.value {
165 if let NAValue::Bool(bval) = option.value {
170 if let NAValue::Int(ival) = option.value {
171 self.nloops = ival as u16;
180 fn query_option_value(&self, name: &str) -> Option<NAValue> {
182 "gif87" => Some(NAValue::Bool(self.gif87)),
183 "single" => Some(NAValue::Bool(self.single)),
184 "loops" => Some(NAValue::Int(i64::from(self.nloops))),
190 pub struct GIFMuxerCreator {}
192 impl MuxerCreator for GIFMuxerCreator {
193 fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> {
194 Box::new(GIFMuxer::new(bw))
196 fn get_name(&self) -> &'static str { "gif" }
197 fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleVideo("gif") }
202 use nihav_core::codecs::*;
203 use nihav_core::demuxers::*;
204 use nihav_core::muxers::*;
205 use nihav_codec_support::test::enc_video::*;
209 fn test_gif_muxer() {
210 let mut dmx_reg = RegisteredDemuxers::new();
211 generic_register_all_demuxers(&mut dmx_reg);
212 // sample: https://samples.mplayerhq.hu/image-samples/GIF/3D.gif
213 let dec_config = DecoderTestParams {
215 in_name: "assets/Misc/3D.gif",
217 stream_type: StreamType::None,
218 dmx_reg, dec_reg: RegisteredDecoders::new(),
220 let mut mux_reg = RegisteredMuxers::new();
221 generic_register_all_muxers(&mut mux_reg);
222 /*let enc_config = EncoderTestParams {
225 out_name: "muxed.gif",
226 mux_reg, enc_reg: RegisteredEncoders::new(),
228 test_remuxing(&dec_config, &enc_config);*/
229 test_remuxing_md5(&dec_config, "gif", &mux_reg,
230 [0x7192b724, 0x2bc4fd05, 0xaa65f268, 0x3929e8bf]);