Skip to content

Commit 1599d62

Browse files
authored
Merge pull request #77 from kaksmet/mjpeg
Add support for decoding MJPEG frames
2 parents f7ddf39 + 4b1585a commit 1599d62

File tree

7 files changed

+76
-2
lines changed

7 files changed

+76
-2
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: rust
22
rust:
3-
- 1.12.0
3+
- 1.13.0
44
- stable
55
- beta
66
- nightly

src/decoder.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use byteorder::ReadBytesExt;
22
use error::{Error, Result, UnsupportedFeature};
3-
use huffman::{HuffmanDecoder, HuffmanTable};
3+
use huffman::{fill_default_mjpeg_tables, HuffmanDecoder, HuffmanTable};
44
use marker::Marker;
55
use parser::{AdobeColorTransform, AppData, CodingProcess, Component, Dimensions, EntropyCoding, FrameInfo,
66
parse_app, parse_com, parse_dht, parse_dqt, parse_dri, parse_sof, parse_sos, ScanInfo};
@@ -60,6 +60,7 @@ pub struct Decoder<R> {
6060
restart_interval: u16,
6161
color_transform: Option<AdobeColorTransform>,
6262
is_jfif: bool,
63+
is_mjpeg: bool,
6364

6465
// Used for progressive JPEGs.
6566
coefficients: Vec<Vec<i16>>,
@@ -79,6 +80,7 @@ impl<R: Read> Decoder<R> {
7980
restart_interval: 0,
8081
color_transform: None,
8182
is_jfif: false,
83+
is_mjpeg: false,
8284
coefficients: Vec::new(),
8385
coefficients_finished: [0; MAX_COMPONENTS],
8486
}
@@ -287,6 +289,7 @@ impl<R: Read> Decoder<R> {
287289

288290
self.is_jfif = true;
289291
},
292+
AppData::Avi1 => self.is_mjpeg = true,
290293
}
291294
}
292295
},
@@ -368,6 +371,10 @@ impl<R: Read> Decoder<R> {
368371
return Err(Error::Format("use of unset quantization table".to_owned()));
369372
}
370373

374+
if self.is_mjpeg {
375+
fill_default_mjpeg_tables(scan, &mut self.dc_huffman_tables, &mut self.ac_huffman_tables);
376+
}
377+
371378
// Verify that all required huffman tables has been set.
372379
if scan.spectral_selection.start == 0 &&
373380
scan.dc_table_indices.iter().any(|&i| self.dc_huffman_tables[i].is_none()) {

src/huffman.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use byteorder::ReadBytesExt;
22
use error::{Error, Result};
33
use marker::Marker;
4+
use parser::ScanInfo;
45
use std::io::Read;
56
use std::iter::repeat;
67

@@ -279,3 +280,64 @@ fn derive_huffman_codes(bits: &[u8; 16]) -> Result<(Vec<u16>, Vec<u8>)> {
279280

280281
Ok((huffcode, huffsize))
281282
}
283+
284+
// https://www.loc.gov/preservation/digital/formats/fdd/fdd000063.shtml
285+
// "Avery Lee, writing in the rec.video.desktop newsgroup in 2001, commented that "MJPEG, or at
286+
// least the MJPEG in AVIs having the MJPG fourcc, is restricted JPEG with a fixed -- and
287+
// *omitted* -- Huffman table. The JPEG must be YCbCr colorspace, it must be 4:2:2, and it must
288+
// use basic Huffman encoding, not arithmetic or progressive.... You can indeed extract the
289+
// MJPEG frames and decode them with a regular JPEG decoder, but you have to prepend the DHT
290+
// segment to them, or else the decoder won't have any idea how to decompress the data.
291+
// The exact table necessary is given in the OpenDML spec.""
292+
pub fn fill_default_mjpeg_tables(scan: &ScanInfo,
293+
dc_huffman_tables: &mut[Option<HuffmanTable>],
294+
ac_huffman_tables: &mut[Option<HuffmanTable>]) {
295+
// Section K.3.3
296+
297+
if dc_huffman_tables[0].is_none() && scan.dc_table_indices.iter().any(|&i| i == 0) {
298+
// Table K.3
299+
dc_huffman_tables[0] = Some(HuffmanTable::new(
300+
&[0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
301+
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
302+
}
303+
if dc_huffman_tables[1].is_none() && scan.dc_table_indices.iter().any(|&i| i == 1) {
304+
// Table K.4
305+
dc_huffman_tables[1] = Some(HuffmanTable::new(
306+
&[0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00],
307+
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B], HuffmanTableClass::DC).unwrap());
308+
}
309+
if ac_huffman_tables[0].is_none() && scan.ac_table_indices.iter().any(|&i| i == 0) {
310+
// Table K.5
311+
ac_huffman_tables[0] = Some(HuffmanTable::new(
312+
&[0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D],
313+
&[0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
314+
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
315+
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
316+
0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
317+
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
318+
0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
319+
0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
320+
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
321+
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
322+
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
323+
0xF9, 0xFA
324+
], HuffmanTableClass::AC).unwrap());
325+
}
326+
if ac_huffman_tables[1].is_none() && scan.ac_table_indices.iter().any(|&i| i == 1) {
327+
// Table K.6
328+
ac_huffman_tables[1] = Some(HuffmanTable::new(
329+
&[0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77],
330+
&[0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
331+
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
332+
0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
333+
0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
334+
0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
335+
0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
336+
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
337+
0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
338+
0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
339+
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
340+
0xF9, 0xFA
341+
], HuffmanTableClass::AC).unwrap());
342+
}
343+
}

src/parser.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub struct Component {
6666
pub enum AppData {
6767
Adobe(AdobeColorTransform),
6868
Jfif,
69+
Avi1,
6970
}
7071

7172
// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe
@@ -479,6 +480,9 @@ pub fn parse_app<R: Read>(reader: &mut R, marker: Marker) -> Result<Option<AppDa
479480
// http://www.w3.org/Graphics/JPEG/jfif3.pdf
480481
if &buffer[0 .. 5] == &[b'J', b'F', b'I', b'F', b'\0'] {
481482
result = Some(AppData::Jfif);
483+
// https://sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#AVI1
484+
} else if &buffer[0 .. 5] == &[b'A', b'V', b'I', b'1', b'\0'] {
485+
result = Some(AppData::Avi1);
482486
}
483487
}
484488
},

tests/reftest/images/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ File | Source
66
------------------| ------
77
16bit-qtables.jpg | Created using <code>convert mozilla/jpg-size-1x1.png tga:- &#124; cjpeg -quality 10 -outfile 16bit-qtables.jpg</code>
88
extraneous-data.jpg | `mozilla/jpg-size-16x16.jpg` with 6 random bytes inserted before the EOI marker
9+
mjpeg.jpg | https://bugzilla.mozilla.org/show_bug.cgi?id=963907
910
restarts.jpg | `mozilla/jpg-size-33x33.jpg` with added restart markers.
1011
rgb.jpg | Created from `ycck.jpg` using <code>convert ycck.jpg tga:- &#124; cjpeg -rgb -outfile rgb.jpg</code>
1112
ycck.jpg | https://en.wikipedia.org/wiki/File:Channel_digital_image_CMYK_color.jpg

tests/reftest/images/mjpeg.jpg

119 KB
Loading

tests/reftest/images/mjpeg.png

1010 KB
Loading

0 commit comments

Comments
 (0)