3 * Routines for JFIF image/jpeg media dissection
4 * Copyright 2004, Olivier Biot.
8 * Refer to the AUTHORS file or the AUTHORS section in the man page
9 * for contacting the author(s) of this file.
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
15 * JFIF media decoding functionality provided by Olivier Biot.
17 * The JFIF specifications are found at several locations, such as:
18 * http://www.jpeg.org/public/jfif.pdf
19 * http://www.w3.org/Graphics/JPEG/itu-t81.pdf
21 * The Exif specifications are found at several locations, such as:
22 * http://www.exif.org/
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software
36 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39 /* Edit this file with 4-space tabulation */
51 #include <epan/packet.h>
53 /* General-purpose debug logger.
54 * Requires double parentheses because of variable arguments of printf().
56 * Enable debug logging for JFIF by defining AM_CFLAGS
57 * so that it contains "-DDEBUG_image_jfif" or "-DDEBUG_image"
59 #if (defined(DEBUG_image_jfif) || defined(DEBUG_image))
61 g_print("%s:%u: ", __FILE__, __LINE__); \
67 #define IMG_JFIF "image-jfif"
69 /************************** Variable declarations **************************/
71 #define MARKER_TEM 0xFF01
73 /* 0xFF02 -- 0xFFBF are reserved */
75 #define MARKER_SOF0 0xFFC0
76 #define MARKER_SOF1 0xFFC1
77 #define MARKER_SOF2 0xFFC2
78 #define MARKER_SOF3 0xFFC3
80 #define MARKER_DHT 0xFFC4
82 #define MARKER_SOF5 0xFFC5
83 #define MARKER_SOF6 0xFFC6
84 #define MARKER_SOF7 0xFFC7
85 #define MARKER_SOF8 0xFFC8
86 #define MARKER_SOF9 0xFFC9
87 #define MARKER_SOF10 0xFFCA
88 #define MARKER_SOF11 0xFFCB
90 #define MARKER_DAC 0xFFCC
92 #define MARKER_SOF13 0xFFCD
93 #define MARKER_SOF14 0xFFCE
94 #define MARKER_SOF15 0xFFCF
96 #define MARKER_RST0 0xFFD0
97 #define MARKER_RST1 0xFFD1
98 #define MARKER_RST2 0xFFD2
99 #define MARKER_RST3 0xFFD3
100 #define MARKER_RST4 0xFFD4
101 #define MARKER_RST5 0xFFD5
102 #define MARKER_RST6 0xFFD6
103 #define MARKER_RST7 0xFFD7
105 #define MARKER_FFDB 0xFFDB
106 #define MARKER_SOI 0xFFD8
107 #define MARKER_EOI 0xFFD9
108 #define MARKER_SOS 0xFFDA
109 #define MARKER_DQT 0xFFDB
110 #define MARKER_DNL 0xFFDC
111 #define MARKER_DRI 0xFFDD
112 #define MARKER_DHP 0xFFDE
113 #define MARKER_EXP 0xFFDF
115 #define MARKER_APP0 0xFFE0
116 #define MARKER_APP1 0xFFE1
117 #define MARKER_APP2 0xFFE2
118 #define MARKER_APP3 0xFFE3
119 #define MARKER_APP4 0xFFE4
120 #define MARKER_APP5 0xFFE5
121 #define MARKER_APP6 0xFFE6
122 #define MARKER_APP7 0xFFE7
123 #define MARKER_APP8 0xFFE8
124 #define MARKER_APP9 0xFFE9
125 #define MARKER_APP10 0xFFEA
126 #define MARKER_APP11 0xFFEB
127 #define MARKER_APP12 0xFFEC
128 #define MARKER_APP13 0xFFED
129 #define MARKER_APP14 0xFFEE
130 #define MARKER_APP15 0xFFEF
132 #define MARKER_JPG0 0xFFF0
133 #define MARKER_JPG1 0xFFF1
134 #define MARKER_JPG2 0xFFF2
135 #define MARKER_JPG3 0xFFF3
136 #define MARKER_JPG4 0xFFF4
137 #define MARKER_JPG5 0xFFF5
138 #define MARKER_JPG6 0xFFF6
139 #define MARKER_JPG7 0xFFF7
140 #define MARKER_JPG8 0xFFF8
141 #define MARKER_JPG9 0xFFF9
142 #define MARKER_JPG10 0xFFFA
143 #define MARKER_JPG11 0xFFFB
144 #define MARKER_JPG12 0xFFFC
145 #define MARKER_JPG13 0xFFFD
147 #define MARKER_COM 0xFFFE
149 #define marker_has_length(marker) ( ! ( \
150 ((marker) == MARKER_TEM) \
151 || ((marker) == MARKER_SOI) \
152 || ((marker) == MARKER_EOI) \
153 || ( ((marker) >= MARKER_RST0) && ((marker) <= MARKER_RST7) ) \
157 static const value_string vals_marker[] = {
158 { MARKER_TEM, "Reserved - For temporary private use in arithmetic coding" },
159 { MARKER_SOF0, "Start of Frame (non-differential, Huffman coding) - Baseline DCT" },
160 { MARKER_SOF1, "Start of Frame (non-differential, Huffman coding) - Extended sequential DCT" },
161 { MARKER_SOF2, "Start of Frame (non-differential, Huffman coding) - Progressive DCT" },
162 { MARKER_SOF3, "Start of Frame (non-differential, Huffman coding) - Lossless (sequential)" },
163 { MARKER_DHT, "Define Huffman table(s)" },
164 { MARKER_SOF5, "Start of Frame (differential, Huffman coding) - Differential sequential DCT" },
165 { MARKER_SOF6, "Start of Frame (differential, Huffman coding) - Differential progressive DCT" },
166 { MARKER_SOF7, "Start of Frame (differential, Huffman coding) - Differential lossless (sequential)" },
167 { MARKER_SOF8, "Start of Frame (non-differential, arithmetic coding) - Reserved for JPEG extensions" },
168 { MARKER_SOF9, "Start of Frame (non-differential, arithmetic coding) - Extended sequential DCT" },
169 { MARKER_SOF10, "Start of Frame (non-differential, arithmetic coding) - Progressive DCT" },
170 { MARKER_SOF11, "Start of Frame (non-differential, arithmetic coding) - Lossless (sequential)" },
171 { MARKER_DAC, "Define arithmetic coding conditioning(s)" },
172 { MARKER_SOF13, "Start of Frame (differential, arithmetic coding) - Differential sequential DCT" },
173 { MARKER_SOF14, "Start of Frame (differential, arithmetic coding) - Differential progressive DCT" },
174 { MARKER_SOF15, "Start of Frame (differential, arithmetic coding) - Differential lossless (sequential)" },
175 { MARKER_RST0, "Restart interval termination - Restart with modulo 8 count 0" },
176 { MARKER_RST1, "Restart interval termination - Restart with modulo 8 count 1" },
177 { MARKER_RST2, "Restart interval termination - Restart with modulo 8 count 2" },
178 { MARKER_RST3, "Restart interval termination - Restart with modulo 8 count 3" },
179 { MARKER_RST4, "Restart interval termination - Restart with modulo 8 count 4" },
180 { MARKER_RST5, "Restart interval termination - Restart with modulo 8 count 5" },
181 { MARKER_RST6, "Restart interval termination - Restart with modulo 8 count 6" },
182 { MARKER_RST7, "Restart interval termination - Restart with modulo 8 count 7" },
183 { MARKER_SOI, "Start of Image" },
184 { MARKER_EOI, "End of Image" },
185 { MARKER_SOS, "Start of Scan" },
186 { MARKER_DQT, "Define quantization table(s)" },
187 { MARKER_DNL, "Define number of lines" },
188 { MARKER_DRI, "Define restart interval" },
189 { MARKER_DHP, "Define hierarchical progression" },
190 { MARKER_EXP, "Expand reference component(s)" },
191 { MARKER_APP0, "Reserved for application segments - 0" },
192 { MARKER_APP1, "Reserved for application segments - 1" },
193 { MARKER_APP2, "Reserved for application segments - 2" },
194 { MARKER_APP3, "Reserved for application segments - 3" },
195 { MARKER_APP4, "Reserved for application segments - 4" },
196 { MARKER_APP5, "Reserved for application segments - 5" },
197 { MARKER_APP6, "Reserved for application segments - 6" },
198 { MARKER_APP7, "Reserved for application segments - 7" },
199 { MARKER_APP8, "Reserved for application segments - 8" },
200 { MARKER_APP9, "Reserved for application segments - 9" },
201 { MARKER_APP10, "Reserved for application segments - 10" },
202 { MARKER_APP11, "Reserved for application segments - 11" },
203 { MARKER_APP12, "Reserved for application segments - 12" },
204 { MARKER_APP13, "Reserved for application segments - 13" },
205 { MARKER_APP14, "Reserved for application segments - 14" },
206 { MARKER_APP15, "Reserved for application segments - 15" },
207 { MARKER_JPG0, "Reserved for JPEG extensions - 0" },
208 { MARKER_JPG1, "Reserved for JPEG extensions - 1" },
209 { MARKER_JPG2, "Reserved for JPEG extensions - 2" },
210 { MARKER_JPG3, "Reserved for JPEG extensions - 3" },
211 { MARKER_JPG4, "Reserved for JPEG extensions - 4" },
212 { MARKER_JPG5, "Reserved for JPEG extensions - 5" },
213 { MARKER_JPG6, "Reserved for JPEG extensions - 6" },
214 { MARKER_JPG7, "Reserved for JPEG extensions - 7" },
215 { MARKER_JPG8, "Reserved for JPEG extensions - 8" },
216 { MARKER_JPG9, "Reserved for JPEG extensions - 9" },
217 { MARKER_JPG10, "Reserved for JPEG extensions - 10" },
218 { MARKER_JPG11, "Reserved for JPEG extensions - 11" },
219 { MARKER_JPG12, "Reserved for JPEG extensions - 12" },
220 { MARKER_JPG13, "Reserved for JPEG extensions - 13" },
221 { MARKER_COM, "Comment" },
225 static const value_string vals_units[] = {
226 { 0, "No units; Xdensity and Ydensity specify the pixel aspect ratio" },
227 { 1, "Dots per inch" },
228 { 2, "Dots per centimeter" },
232 static const value_string vals_extension_code[] = {
233 { 0x10, "Thumbnail encoded using JPEG" },
234 { 0x11, "Thumbnail encoded using 1 byte (8 bits) per pixel" },
235 { 0x13, "Thumbnail encoded using 3 bytes (24 bits) per pixel" },
239 static const value_string vals_exif_tags[] = {
241 * Tags related to image data structure:
243 { 0x0100, "ImageWidth" },
244 { 0x0101, "ImageLength" },
245 { 0x0102, "BitsPerSample" },
246 { 0x0103, "Compression" },
247 { 0x0106, "PhotometricInterpretation" },
248 { 0x0112, "Orientation" },
249 { 0x0115, "SamplesPerPixel" },
250 { 0x011C, "PlanarConfiguration" },
251 { 0x0212, "YCbCrSubSampling" },
252 { 0x0213, "YCbCrPositioning" },
253 { 0x011A, "XResolution" },
254 { 0x011B, "YResolution" },
255 { 0x0128, "ResolutionUnit" },
257 * Tags relating to recording offset:
259 { 0x0111, "StripOffsets" },
260 { 0x0116, "RowsPerStrip" },
261 { 0x0117, "StripByteCounts" },
262 { 0x0201, "JPEGInterchangeFormat" },
263 { 0x0202, "JPEGInterchangeFormatLength" },
265 * Tags relating to image data characteristics:
267 { 0x012D, "TransferFunction" },
268 { 0x013E, "WhitePoint" },
269 { 0x013F, "PrimaryChromaticities" },
270 { 0x0211, "YCbCrCoefficients" },
271 { 0x0214, "ReferenceBlackWhite" },
275 { 0x0132, "DateTime" },
276 { 0x010E, "ImageDescription" },
279 { 0x0131, "Software" },
280 { 0x013B, "Artist" },
281 { 0x8296, "Copyright" },
285 { 0x8769, "Exif IFD Pointer"},
286 { 0x8825, "GPS IFD Pointer"},
287 { 0xA005, "Interoperability IFD Pointer"},
292 static const value_string vals_exif_types[] = {
297 { 0x0005, "RATIONAL" },
299 { 0x0007, "UNDEFINED" },
302 { 0x000A, "SRATIONAL" },
307 /* Initialize the protocol and registered fields */
308 static int proto_jfif = -1;
311 static gint hf_marker = -1;
313 static gint hf_marker_segment = -1;
314 static gint hf_len = -1;
316 static gint hf_identifier = -1;
317 /* MARKER_APP0 - JFIF */
318 static gint hf_version = -1;
319 static gint hf_version_major = -1;
320 static gint hf_version_minor = -1;
321 static gint hf_units = -1;
322 static gint hf_xdensity = -1;
323 static gint hf_ydensity = -1;
324 static gint hf_xthumbnail = -1;
325 static gint hf_ythumbnail = -1;
326 static gint hf_rgb = -1;
327 /* MARKER_APP0 - JFXX */
328 static gint hf_extension_code = -1;
330 static gint hf_sof_header = -1;
331 static gint hf_sof_precision = -1;
332 static gint hf_sof_lines = -1;
333 static gint hf_sof_samples_per_line = -1;
334 static gint hf_sof_nf = -1;
335 static gint hf_sof_c_i = -1;
336 static gint hf_sof_h_i = -1;
337 static gint hf_sof_v_i = -1;
338 static gint hf_sof_tq_i = -1;
341 static gint hf_sos_header = -1;
342 static gint hf_sos_ns = -1;
343 static gint hf_sos_cs_j = -1;
344 static gint hf_sos_td_j = -1;
345 static gint hf_sos_ta_j = -1;
346 static gint hf_sos_ss = -1;
347 static gint hf_sos_se = -1;
348 static gint hf_sos_ah = -1;
349 static gint hf_sos_al = -1;
351 /* Initialize the subtree pointers */
352 static gint ett_jfif = -1;
353 static gint ett_marker_segment = -1;
354 static gint ett_details = -1;
357 /**************** GIF related declarations and definitions ****************/
360 /************************** Function prototypes **************************/
364 dissect_jfif(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
367 proto_register_jfif(void);
371 /****************** JFIF protocol dissection functions ******************/
373 #define ErrorInvalidJFIF "This is not a valid JFIF (JPEG) object"
377 * Process a marker segment (with length).
380 process_marker_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
381 guint16 marker, const char *marker_name)
383 proto_item *ti = NULL;
384 proto_tree *subtree = NULL;
389 ti = proto_tree_add_item(tree, hf_marker_segment,
391 subtree = proto_item_add_subtree(ti, ett_marker_segment);
393 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
394 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
396 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
398 proto_tree_add_text(subtree, tvb, 4, -1,
399 "Remaining segment data (%u bytes)", len - 2);
403 * Process a Start of Frame header (with length).
406 process_sof_header(proto_tree *tree, tvbuff_t *tvb, guint32 len _U_,
407 guint16 marker, const char *marker_name)
409 proto_item *ti = NULL;
410 proto_tree *subtree = NULL;
415 ti = proto_tree_add_item(tree, hf_sof_header,
417 subtree = proto_item_add_subtree(ti, ett_marker_segment);
419 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
420 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
422 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
424 proto_tree_add_item(subtree, hf_sof_precision, tvb, 4, 1, FALSE);
426 proto_tree_add_item(subtree, hf_sof_lines, tvb, 5, 2, FALSE);
428 proto_tree_add_item(subtree, hf_sof_samples_per_line, tvb, 7, 2, FALSE);
430 proto_tree_add_item(subtree, hf_sof_nf, tvb, 9, 1, FALSE);
432 guint8 count = tvb_get_guint8(tvb, 9);
435 proto_tree_add_item(subtree, hf_sof_c_i, tvb, offset++, 1, FALSE);
436 proto_tree_add_item(subtree, hf_sof_h_i, tvb, offset, 1, FALSE);
437 proto_tree_add_item(subtree, hf_sof_v_i, tvb, offset++, 1, FALSE);
438 proto_tree_add_item(subtree, hf_sof_tq_i, tvb, offset++, 1, FALSE);
445 * Process a Start of Segment header (with length).
448 process_sos_header(proto_tree *tree, tvbuff_t *tvb, guint32 len _U_,
449 guint16 marker, const char *marker_name)
451 proto_item *ti = NULL;
452 proto_tree *subtree = NULL;
458 ti = proto_tree_add_item(tree, hf_sos_header,
460 subtree = proto_item_add_subtree(ti, ett_marker_segment);
462 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
463 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
465 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
467 proto_tree_add_item(subtree, hf_sos_ns, tvb, 4, 1, FALSE);
469 guint8 count = tvb_get_guint8(tvb, 4);
472 proto_tree_add_item(subtree, hf_sos_cs_j, tvb, offset++, 1, FALSE);
473 proto_tree_add_item(subtree, hf_sos_td_j, tvb, offset, 1, FALSE);
474 proto_tree_add_item(subtree, hf_sos_ta_j, tvb, offset++, 1, FALSE);
479 proto_tree_add_item(subtree, hf_sos_ss, tvb, offset++, 1, FALSE);
480 proto_tree_add_item(subtree, hf_sos_se, tvb, offset++, 1, FALSE);
482 proto_tree_add_item(subtree, hf_sos_ah, tvb, offset, 1, FALSE);
483 proto_tree_add_item(subtree, hf_sos_al, tvb, offset++, 1, FALSE);
486 /* Process an APP0 block.
488 * XXX - This code only works on US-ASCII systems!!!
491 process_app0_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
492 guint16 marker, const char *marker_name)
494 proto_item *ti = NULL;
495 proto_tree *subtree = NULL;
496 proto_tree *subtree_details = NULL;
504 ti = proto_tree_add_item(tree, hf_marker_segment,
506 subtree = proto_item_add_subtree(ti, ett_marker_segment);
508 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
509 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
511 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
513 str = tvb_get_ephemeral_stringz(tvb, 4, &str_size);
514 ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
515 if (strcmp(str, "JFIF") == 0) {
517 ti = proto_tree_add_none_format(subtree, hf_version,
518 tvb, 9, 2, "Version: %u.%u",
519 tvb_get_guint8(tvb, 9),
520 tvb_get_guint8(tvb, 10));
521 subtree_details = proto_item_add_subtree(ti, ett_details);
522 proto_tree_add_item(subtree_details, hf_version_major,
524 proto_tree_add_item(subtree_details, hf_version_minor,
527 proto_tree_add_item(subtree, hf_units,
531 proto_tree_add_item(subtree, hf_xdensity,
533 proto_tree_add_item(subtree, hf_ydensity,
537 proto_tree_add_item(subtree, hf_xthumbnail,
539 proto_tree_add_item(subtree, hf_ythumbnail,
542 guint16 x = tvb_get_guint8(tvb, 16);
543 guint16 y = tvb_get_guint8(tvb, 17);
545 proto_tree_add_item(subtree, hf_rgb,
546 tvb, 18, 3 * (x * y), FALSE);
547 offset = 18 + (3 * (x * y));
552 } else if (strcmp(str, "JFXX") == 0) {
553 proto_tree_add_item(subtree, hf_extension_code,
556 guint8 code = tvb_get_guint8(tvb, 9);
558 case 0x10: /* Thumbnail coded using JPEG */
560 case 0x11: /* thumbnail stored using 1 byte per pixel */
562 case 0x13: /* thumbnail stored using 3 bytes per pixel */
568 } else { /* Unknown */
569 proto_item_append_text(ti, " (unknown identifier)");
570 offset = 4 + str_size;
572 proto_tree_add_text(subtree, tvb, offset, -1,
573 "Remaining segment data (%u bytes)", len - 2 - str_size);
578 /* Process an APP1 block.
580 * XXX - This code only works on US-ASCII systems!!!
583 process_app1_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
584 guint16 marker, const char *marker_name)
586 proto_item *ti = NULL;
587 proto_tree *subtree = NULL;
596 ti = proto_tree_add_item(tree, hf_marker_segment,
598 subtree = proto_item_add_subtree(ti, ett_marker_segment);
600 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
601 proto_tree_add_item(subtree, hf_marker, tvb, offset, 2, FALSE);
604 proto_tree_add_item(subtree, hf_len, tvb, offset, 2, FALSE);
607 str = tvb_get_ephemeral_stringz(tvb, offset, &str_size);
608 ti = proto_tree_add_item(subtree, hf_identifier, tvb, offset, str_size, FALSE);
610 if (strcmp(str, "Exif") == 0) {
614 gboolean is_little_endian;
619 offset++; /* Skip a byte supposed to be 0x00 */
622 val_16 = tvb_get_ntohs(tvb, offset);
623 if (val_16 == 0x4949) {
624 is_little_endian = TRUE;
625 proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: little endian");
626 } else if (val_16 == 0x4D4D) {
627 is_little_endian = FALSE;
628 proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: big endian");
630 /* Error: invalid endianness encoding */
631 proto_tree_add_text(subtree, tvb, offset, 2,
632 "Incorrect endianness encoding - skipping the remainder of this application marker");
637 * Fixed value 42 = 0x002a
643 if (is_little_endian) {
644 val_32 = tvb_get_letohl(tvb, offset);
646 val_32 = tvb_get_ntohl(tvb, offset);
649 * Check for a bogus val_32 value.
650 * XXX - bogus value message should also deal with a
651 * value that's too large and causes an overflow.
652 * Or should it just check against the segment length,
655 if (val_32 + tiff_start < (guint32)offset + 4) {
656 proto_tree_add_text(subtree, tvb, offset, 4,
657 "Start offset of IFD starting from the TIFF header start: %u bytes (bogus, should be >= %u",
658 val_32, offset + 4 - tiff_start);
661 proto_tree_add_text(subtree, tvb, offset, 4,
662 "Start offset of IFD starting from the TIFF header start: %u bytes",
666 * Skip the following portion
668 if (val_32 + tiff_start > (guint32)offset) {
669 proto_tree_add_text(subtree, tvb, offset, val_32 + tiff_start - offset,
670 "Skipped data between end of TIFF header and start of IFD (%u bytes)",
671 val_32 + tiff_start - offset);
674 offset = val_32 + tiff_start;
678 if (is_little_endian) {
679 num_fields = tvb_get_letohs(tvb, offset);
681 num_fields = tvb_get_ntohs(tvb, offset);
683 proto_tree_add_text(subtree, tvb, offset, 2, "Number of fields in this IFD: %u", num_fields);
685 while (num_fields-- > 0) {
689 if (is_little_endian) {
690 tag = tvb_get_letohs(tvb, offset);
691 type = tvb_get_letohs(tvb, offset + 2);
692 count = tvb_get_letohl(tvb, offset + 4);
693 off = tvb_get_letohl(tvb, offset + 8);
695 tag = tvb_get_ntohs(tvb, offset);
696 type = tvb_get_ntohs(tvb, offset + 2);
697 count = tvb_get_ntohl(tvb, offset + 4);
698 off = tvb_get_ntohl(tvb, offset + 8);
700 /* TODO - refine this */
701 proto_tree_add_text(subtree, tvb, offset, 2,
702 "Exif Tag: 0x%04X (%s), Type: %u (%s), Count: %u, "
703 "Value offset from start of TIFF header: %u",
704 tag, val_to_str(tag, vals_exif_tags, "Unknown Exif tag"),
705 type, val_to_str(type, vals_exif_types, "Unknown Exif type"),
710 * Offset to the next IFD
712 if (is_little_endian) {
713 val_32 = tvb_get_letohl(tvb, offset);
715 val_32 = tvb_get_ntohl(tvb, offset);
718 val_32 + tiff_start < (guint32)offset + 4) {
719 proto_tree_add_text(subtree, tvb, offset, 4,
720 "Offset to next IFD from start of TIFF header: %u bytes (bogus, should be >= %u)",
721 val_32, offset + 4 - tiff_start);
724 proto_tree_add_text(subtree, tvb, offset, 4,
725 "Offset to next IFD from start of TIFF header: %u bytes",
732 proto_tree_add_text(subtree, tvb, offset, -1,
733 "Remaining segment data (%u bytes)", len - 2 - str_size);
734 proto_item_append_text(ti, " (Unknown identifier)");
738 /* Process an APP2 block.
740 * XXX - This code only works on US-ASCII systems!!!
743 process_app2_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
744 guint16 marker, const char *marker_name)
746 proto_item *ti = NULL;
747 proto_tree *subtree = NULL;
754 ti = proto_tree_add_item(tree, hf_marker_segment,
756 subtree = proto_item_add_subtree(ti, ett_marker_segment);
758 proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
759 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
761 proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
763 str = tvb_get_ephemeral_stringz(tvb, 4, &str_size);
764 ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
765 if (strcmp(str, "FPXR") == 0) {
766 proto_tree_add_text(tree, tvb, 0, -1, "Exif FlashPix APP2 application marker");
768 proto_tree_add_text(subtree, tvb, 4 + str_size, -1,
769 "Remaining segment data (%u bytes)", len - 2 - str_size);
770 proto_item_append_text(ti, " (Unknown identifier)");
775 dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
777 proto_tree *subtree = NULL;
779 guint tvb_len = tvb_reported_length(tvb);
785 /* Add summary to INFO column if it is enabled */
786 if (check_col(pinfo->cinfo, COL_INFO))
787 col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "(JPEG JFIF image)");
790 ti = proto_tree_add_item(tree, proto_jfif,
792 subtree = proto_item_add_subtree(ti, ett_jfif);
795 marker = tvb_get_ntohs(tvb, 0);
796 if (marker != MARKER_SOI) {
798 proto_tree_add_text(subtree, tvb, 0, 2, ErrorInvalidJFIF);
803 proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
808 * Process the remaining markers and marker segments
810 while (offset < tvb_len) {
812 marker = tvb_get_ntohs(tvb, offset);
813 str = match_strval(marker, vals_marker);
814 if (str) { /* Known marker */
815 if (marker_has_length(marker)) { /* Marker segment */
816 /* Length of marker segment = 2 + len */
817 len = tvb_get_ntohs(tvb, offset + 2);
818 tmp_tvb = tvb_new_subset(tvb, offset, 2 + len, 2 + len);
821 process_app0_segment(subtree, tmp_tvb, len, marker, str);
824 process_app1_segment(subtree, tmp_tvb, len, marker, str);
827 process_app2_segment(subtree, tmp_tvb, len, marker, str);
843 process_sof_header(subtree, tmp_tvb, len, marker, str);
846 process_sos_header(subtree, tmp_tvb, len, marker, str);
848 * TODO - dissect a scan; the scan data is encoded.
850 proto_tree_add_text(subtree, tvb, offset + 2 + len, -1,
851 "JFIF dissection stops here (dissection of a scan is not yet implemented)");
855 process_marker_segment(subtree, tmp_tvb, len, marker, str);
859 } else { /* Marker but no segment */
861 proto_tree_add_item(subtree, hf_marker,
862 tvb, offset, 2, FALSE);
865 } else { /* Reserved! */
866 ti = proto_tree_add_item(subtree, hf_marker,
867 tvb, offset, 2, FALSE);
868 proto_item_append_text(ti, " (Reserved)");
877 /****************** Register the protocol with Wireshark ******************/
880 /* This format is required because a script is used to build the C function
881 * that calls the protocol registration. */
884 proto_register_jfif(void)
887 * Setup list of header fields.
889 static hf_register_info hf[] = {
894 FT_UINT8, BASE_HEX, VALS(vals_marker), 0x00,
900 { &hf_marker_segment,
902 IMG_JFIF "marker_segment",
903 FT_NONE, BASE_NONE, NULL, 0x00,
911 FT_UINT16, BASE_DEC, 0, 0x00,
912 "Length of segment (including length field)",
919 IMG_JFIF ".identifier",
920 FT_STRINGZ, BASE_NONE, NULL, 0x00,
921 "Identifier of the segment",
925 /* MARKER_APP0 - JFIF */
929 FT_NONE, BASE_NONE, NULL, 0x00,
936 IMG_JFIF ".version.major",
937 FT_UINT8, BASE_DEC, NULL, 0x00,
938 "JFIF Major Version",
944 IMG_JFIF ".version.minor",
945 FT_UINT8, BASE_DEC, NULL, 0x00,
946 "JFIF Minor Version",
953 FT_UINT8, BASE_DEC, VALS(vals_units), 0x00,
954 "Units used in this segment",
960 IMG_JFIF ".Xdensity",
961 FT_UINT16, BASE_DEC, NULL, 0x00,
962 "Horizontal pixel density",
968 IMG_JFIF ".Ydensity",
969 FT_UINT16, BASE_DEC, NULL, 0x00,
970 "Vertical pixel density",
976 IMG_JFIF ".Xthumbnail",
977 FT_UINT16, BASE_DEC, NULL, 0x00,
978 "Thumbnail horizontal pixel count",
984 IMG_JFIF ".Ythumbnail",
985 FT_UINT16, BASE_DEC, NULL, 0x00,
986 "Thumbnail vertical pixel count",
991 { "RGB values of thumbnail pixels",
993 FT_BYTES, BASE_NONE, NULL, 0x00,
994 "RGB values of the thumbnail pixels (24 bit per pixel, Xthumbnail x Ythumbnail pixels)",
998 /* MARKER_APP0 - JFXX */
999 { &hf_extension_code,
1001 IMG_JFIF ".extension.code",
1002 FT_UINT8, BASE_HEX, VALS(vals_extension_code), 0x00,
1003 "JFXX extension code for thumbnail encoding",
1007 /* Header: Start of Frame (MARKER_SOF) */
1009 { "Start of Frame header",
1011 FT_NONE, BASE_NONE, NULL, 0x00,
1016 { &hf_sof_precision,
1017 { "Sample Precision (bits)",
1018 IMG_JFIF ".sof.precision",
1019 FT_UINT8, BASE_DEC, NULL, 0x00,
1020 "Specifies the precision in bits for the samples of the components in the frame.",
1026 IMG_JFIF ".sof.lines",
1027 FT_UINT16, BASE_DEC, NULL, 0x00,
1028 "Specifies the maximum number of lines in the source image.",
1032 { &hf_sof_samples_per_line,
1033 { "Samples per line",
1034 IMG_JFIF ".sof.samples_per_line",
1035 FT_UINT16, BASE_DEC, NULL, 0x00,
1036 "Specifies the maximum number of samples per line in the source image.",
1041 { "Number of image components in frame",
1043 FT_UINT8, BASE_DEC, NULL, 0x00,
1044 "Specifies the number of source image components in the frame.",
1049 { "Component identifier",
1050 IMG_JFIF ".sof.c_i",
1051 FT_UINT8, BASE_DEC, NULL, 0x00,
1052 "Assigns a unique label to the ith component in the sequence of frame component specification parameters.",
1057 { "Horizontal sampling factor",
1058 IMG_JFIF ".sof.h_i",
1059 FT_UINT8, BASE_DEC, NULL, 0xF0,
1060 "Specifies the relationship between the component horizontal dimension and maximum image dimension X.",
1065 { "Vertical sampling factor",
1066 IMG_JFIF ".sof.v_i",
1067 FT_UINT8, BASE_DEC, NULL, 0x0F,
1068 "Specifies the relationship between the component vertical dimension and maximum image dimension Y.",
1073 { "Quantization table destination selector",
1074 IMG_JFIF ".sof.tq_i",
1075 FT_UINT8, BASE_DEC, NULL, 0x00,
1076 "Specifies one of four possible quantization table destinations from which the quantization table to use for dequantization of DCT coefficients of component Ci is retrieved.",
1081 /* Header: Start of Segment (MARKER_SOS) */
1083 { "Start of Segment header",
1084 IMG_JFIF ".header.sos",
1085 FT_NONE, BASE_NONE, NULL, 0x00,
1091 { "Number of image components in scan",
1093 FT_UINT8, BASE_DEC, NULL, 0x00,
1094 "Specifies the number of source image components in the scan.",
1099 { "Scan component selector",
1100 IMG_JFIF ".sos.component_selector",
1101 FT_UINT8, BASE_DEC, NULL, 0x00,
1102 "Selects which of the Nf image components specified in the frame parameters shall be the jth component in the scan.",
1107 { "DC entropy coding table destination selector",
1108 IMG_JFIF ".sos.dc_entropy_selector",
1109 FT_UINT8, BASE_DEC, NULL, 0xF0,
1110 "Specifies one of four possible DC entropy coding table destinations from which the entropy table needed for decoding of the DC coefficients of component Csj is retrieved.",
1115 { "AC entropy coding table destination selector",
1116 IMG_JFIF ".sos.ac_entropy_selector",
1117 FT_UINT8, BASE_DEC, NULL, 0x0F,
1118 "Specifies one of four possible AC entropy coding table destinations from which the entropy table needed for decoding of the AC coefficients of component Csj is retrieved.",
1123 { "Start of spectral or predictor selection",
1125 FT_UINT8, BASE_DEC, NULL, 0x00,
1126 "In the DCT modes of operation, this parameter specifies the first DCT coefficient in each block in zig-zag order which shall be coded in the scan. This parameter shall be set to zero for the sequential DCT processes. In the lossless mode of operations this parameter is used to select the predictor.",
1131 { "End of spectral selection",
1133 FT_UINT8, BASE_DEC, NULL, 0x00,
1134 "Specifies the last DCT coefficient in each block in zig-zag order which shall be coded in the scan. This parameter shall be set to 63 for the sequential DCT processes. In the lossless mode of operations this parameter has no meaning. It shall be set to zero.",
1139 { "Successive approximation bit position high",
1141 FT_UINT8, BASE_DEC, NULL, 0xF0,
1142 "This parameter specifies the point transform used in the preceding scan (i.e. successive approximation bit position low in the preceding scan) for the band of coefficients specified by Ss and Se. This parameter shall be set to zero for the first scan of each band of coefficients. In the lossless mode of operations this parameter has no meaning. It shall be set to zero.",
1147 { "Successive approximation bit position low or point transform",
1149 FT_UINT8, BASE_DEC, NULL, 0x0F,
1150 "In the DCT modes of operation this parameter specifies the point transform, i.e. bit position low, used before coding the band of coefficients specified by Ss and Se. This parameter shall be set to zero for the sequential DCT processes. In the lossless mode of operations, this parameter specifies the point transform, Pt.",
1156 /* Setup protocol subtree array */
1157 static gint *ett[] = {
1159 &ett_marker_segment,
1163 /* Register the protocol name and description */
1164 proto_jfif = proto_register_protocol(
1165 "JPEG File Interchange Format",
1166 "JFIF (JPEG) image",
1170 /* Required function calls to register the header fields
1171 * and subtrees used */
1172 proto_register_field_array(proto_jfif, hf, array_length(hf));
1173 proto_register_subtree_array(ett, array_length(ett));
1175 register_dissector("image-jfif", dissect_jfif, proto_jfif);
1180 proto_reg_handoff_jfif(void)
1182 dissector_handle_t jfif_handle;
1184 jfif_handle = find_dissector("image-jfif");
1186 /* Register the JPEG media type */
1187 dissector_add_string("media_type", "image/jfif", jfif_handle);
1188 dissector_add_string("media_type", "image/jpg", jfif_handle);
1189 dissector_add_string("media_type", "image/jpeg", jfif_handle);
1191 dissector_add("wtap_encap", WTAP_ENCAP_JPEG_JFIF, jfif_handle);