forked from osmocom/wireshark
Add support for Exif decoding (initial framework).
Still lots of work to do here :) svn path=/trunk/; revision=11035
This commit is contained in:
parent
4e983558da
commit
0bcada8dc8
|
@ -3,7 +3,7 @@
|
|||
* Routines for JFIF image/jpeg media dissection
|
||||
* Copyright 2004, Olivier Biot.
|
||||
*
|
||||
* $Id: packet-image-jfif.c,v 1.4 2004/03/08 22:03:58 obiot Exp $
|
||||
* $Id: packet-image-jfif.c,v 1.5 2004/05/31 01:24:03 obiot Exp $
|
||||
*
|
||||
* Refer to the AUTHORS file or the AUTHORS section in the man page
|
||||
* for contacting the author(s) of this file.
|
||||
|
@ -17,6 +17,9 @@
|
|||
* The JFIF specifications are found at several locations, such as:
|
||||
* http://www.jpeg.org/public/jfif.pdf
|
||||
* http://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
||||
*
|
||||
* The Exif specifications are found at several locations, such as:
|
||||
* http://www.exif.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -239,6 +242,74 @@ static const value_string vals_extension_code[] = {
|
|||
{ 0x00, NULL }
|
||||
};
|
||||
|
||||
static const value_string vals_exif_tags[] = {
|
||||
/*
|
||||
* Tags related to image data structure:
|
||||
*/
|
||||
{ 0x0100, "ImageWidth" },
|
||||
{ 0x0101, "ImageLength" },
|
||||
{ 0x0102, "BitsPerSample" },
|
||||
{ 0x0103, "Compression" },
|
||||
{ 0x0106, "PhotometricInterpretation" },
|
||||
{ 0x0112, "Orientation" },
|
||||
{ 0x0115, "SamplesPerPixel" },
|
||||
{ 0x011C, "PlanarConfiguration" },
|
||||
{ 0x0212, "YCbCrSubSampling" },
|
||||
{ 0x0213, "YCbCrPositioning" },
|
||||
{ 0x011A, "XResolution" },
|
||||
{ 0x011B, "YResolution" },
|
||||
{ 0x0128, "ResolutionUnit" },
|
||||
/*
|
||||
* Tags relating to recording offset:
|
||||
*/
|
||||
{ 0x0111, "StripOffsets" },
|
||||
{ 0x0116, "RowsPerStrip" },
|
||||
{ 0x0117, "StripByteCounts" },
|
||||
{ 0x0201, "JPEGInterchangeFormat" },
|
||||
{ 0x0202, "JPEGInterchangeFormatLength" },
|
||||
/*
|
||||
* Tags relating to image data characteristics:
|
||||
*/
|
||||
{ 0x012D, "TransferFunction" },
|
||||
{ 0x013E, "WhitePoint" },
|
||||
{ 0x013F, "PrimaryChromaticities" },
|
||||
{ 0x0211, "YCbCrCoefficients" },
|
||||
{ 0x0214, "ReferenceBlackWhite" },
|
||||
/*
|
||||
* Other tags:
|
||||
*/
|
||||
{ 0x0132, "DateTime" },
|
||||
{ 0x010E, "ImageDescription" },
|
||||
{ 0x010F, "Make" },
|
||||
{ 0x0110, "Model" },
|
||||
{ 0x0131, "Software" },
|
||||
{ 0x013B, "Artist" },
|
||||
{ 0x8296, "Copyright" },
|
||||
/*
|
||||
* Exif-specific IFD:
|
||||
*/
|
||||
{ 0x8769, "Exif IFD Pointer"},
|
||||
{ 0x8825, "GPS IFD Pointer"},
|
||||
{ 0xA005, "Interoperability IFD Pointer"},
|
||||
|
||||
{ 0x0000, NULL }
|
||||
};
|
||||
|
||||
static const value_string vals_exif_types[] = {
|
||||
{ 0x0001, "BYTE" },
|
||||
{ 0x0002, "ASCII" },
|
||||
{ 0x0003, "SHORT" },
|
||||
{ 0x0004, "LONG" },
|
||||
{ 0x0005, "RATIONAL" },
|
||||
/* 0x0006 */
|
||||
{ 0x0007, "UNDEFINED" },
|
||||
/* 0x0008 */
|
||||
{ 0x0009, "SLONG" },
|
||||
{ 0x000A, "SRATIONAL" },
|
||||
|
||||
{ 0x0000, NULL }
|
||||
};
|
||||
|
||||
/* Initialize the protocol and registered fields */
|
||||
static int proto_jfif = -1;
|
||||
|
||||
|
@ -511,6 +582,170 @@ process_app0_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Process an APP1 block.
|
||||
*
|
||||
* XXX - This code only works on US-ASCII systems!!!
|
||||
*/
|
||||
static void
|
||||
process_app1_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
|
||||
guint16 marker, const char *marker_name)
|
||||
{
|
||||
proto_item *ti = NULL;
|
||||
proto_tree *subtree = NULL;
|
||||
proto_tree *subtree_details = NULL;
|
||||
char *str;
|
||||
gint str_size;
|
||||
guint32 offset, tiff_start;
|
||||
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
ti = proto_tree_add_item(tree, hf_marker_segment,
|
||||
tvb, 0, -1, FALSE);
|
||||
subtree = proto_item_add_subtree(ti, ett_marker_segment);
|
||||
|
||||
proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
|
||||
proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
|
||||
|
||||
proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
|
||||
|
||||
str = tvb_get_stringz(tvb, 4, &str_size);
|
||||
ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
|
||||
offset = tiff_start = 4 + str_size;
|
||||
if (strcmp(str, "Exif") == 0) {
|
||||
/*
|
||||
* Endianness
|
||||
*/
|
||||
gboolean is_little_endian;
|
||||
guint16 val_16;
|
||||
guint32 val_32;
|
||||
guint16 num_fields;
|
||||
|
||||
offset++; /* Skip a byte supposed to be 0x00 */
|
||||
|
||||
val_16 = tvb_get_ntohs(tvb, offset);
|
||||
if (val_16 == 0x4949) {
|
||||
is_little_endian = TRUE;
|
||||
proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: little endian");
|
||||
} else if (val_16 == 0x4D4D) {
|
||||
is_little_endian = FALSE;
|
||||
proto_tree_add_text(subtree, tvb, offset, 2, "Endianness: big endian");
|
||||
} else {
|
||||
/* Error: invalid endianness encoding */
|
||||
proto_tree_add_text(subtree, tvb, offset, 2,
|
||||
"Incorrect endianness encoding - skipping the remainder of this application marker");
|
||||
return;
|
||||
}
|
||||
offset += 2;
|
||||
/*
|
||||
* Fixed value 42 = 0x002a
|
||||
*/
|
||||
offset += 2;
|
||||
/*
|
||||
* Offset to IFD
|
||||
*/
|
||||
if (is_little_endian) {
|
||||
val_16 = tvb_get_letohs(tvb, offset);
|
||||
} else {
|
||||
val_16 = tvb_get_ntohs(tvb, offset);
|
||||
}
|
||||
proto_tree_add_text(subtree, tvb, offset, 4,
|
||||
"Start offset of IFD starting from the TIFF header start: %u bytes", val_16);
|
||||
offset += 4;
|
||||
/*
|
||||
* Skip the following portion
|
||||
*/
|
||||
proto_tree_add_text(subtree, tvb, offset, val_16 + tiff_start - offset,
|
||||
"Skipped data between end of TIFF header and start of IFD (%u bytes)",
|
||||
val_16 + tiff_start - offset);
|
||||
offset = val_16 + tiff_start + 1;
|
||||
/*
|
||||
* Process the "0th" IFD
|
||||
*/
|
||||
if (is_little_endian) {
|
||||
num_fields = tvb_get_letohs(tvb, offset);
|
||||
} else {
|
||||
num_fields = tvb_get_ntohs(tvb, offset);
|
||||
}
|
||||
proto_tree_add_text(subtree, tvb, offset, 2, "Number of fields in this IFD: %u", num_fields);
|
||||
offset += 2;
|
||||
while (num_fields-- > 0) {
|
||||
guint16 tag, type;
|
||||
guint32 count, off;
|
||||
|
||||
if (is_little_endian) {
|
||||
tag = tvb_get_letohs(tvb, offset);
|
||||
type = tvb_get_letohs(tvb, offset + 2);
|
||||
count = tvb_get_letohl(tvb, offset + 4);
|
||||
off = tvb_get_letohl(tvb, offset + 8);
|
||||
} else {
|
||||
tag = tvb_get_ntohs(tvb, offset);
|
||||
type = tvb_get_ntohs(tvb, offset + 2);
|
||||
count = tvb_get_ntohl(tvb, offset + 4);
|
||||
off = tvb_get_ntohl(tvb, offset + 8);
|
||||
}
|
||||
/* TODO - refine this */
|
||||
proto_tree_add_text(subtree, tvb, offset, 2,
|
||||
"Exif Tag: 0x%04X (%s), Type: %u (%s), Count: %u, "
|
||||
"Value offset from start of TIFF header: %u",
|
||||
tag, val_to_str(tag, vals_exif_tags, "Unknown Exif tag"),
|
||||
type, val_to_str(type, vals_exif_types, "Unknown Exif type"),
|
||||
count, off);
|
||||
offset += 12;
|
||||
}
|
||||
/*
|
||||
* Offset to the "1st" IFD
|
||||
*/
|
||||
if (is_little_endian) {
|
||||
val_32 = tvb_get_letohl(tvb, offset);
|
||||
} else {
|
||||
val_32 = tvb_get_ntohl(tvb, offset);
|
||||
}
|
||||
proto_tree_add_text(subtree, tvb, offset, 4,
|
||||
"Offset to next IFD from start of TIFF header: %u bytes", val_32);
|
||||
/* TODO - Continue parsing the "1th" IFD */
|
||||
offset += 4;
|
||||
proto_tree_add_text(subtree, tvb, offset, -1, "Remainder of APP1 marker skipped");
|
||||
} else {
|
||||
proto_item_append_text(ti, " (Unknown identifier)");
|
||||
}
|
||||
}
|
||||
|
||||
/* Process an APP2 block.
|
||||
*
|
||||
* XXX - This code only works on US-ASCII systems!!!
|
||||
*/
|
||||
static void
|
||||
process_app2_segment(proto_tree *tree, tvbuff_t *tvb, guint32 len,
|
||||
guint16 marker, const char *marker_name)
|
||||
{
|
||||
proto_item *ti = NULL;
|
||||
proto_tree *subtree = NULL;
|
||||
proto_tree *subtree_details = NULL;
|
||||
char *str;
|
||||
gint str_size;
|
||||
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
ti = proto_tree_add_item(tree, hf_marker_segment,
|
||||
tvb, 0, -1, FALSE);
|
||||
subtree = proto_item_add_subtree(ti, ett_marker_segment);
|
||||
|
||||
proto_item_append_text(ti, ": %s (0x%04X)", marker_name, marker);
|
||||
proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
|
||||
|
||||
proto_tree_add_item(subtree, hf_len, tvb, 2, 2, FALSE);
|
||||
|
||||
str = tvb_get_stringz(tvb, 4, &str_size);
|
||||
ti = proto_tree_add_item(subtree, hf_identifier, tvb, 4, str_size, FALSE);
|
||||
if (strcmp(str, "FPXR") == 0) {
|
||||
proto_tree_add_text(tree, tvb, 0, -1, "Exif FlashPix APP2 application marker");
|
||||
} else {
|
||||
proto_item_append_text(ti, " (Unknown identifier)");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
||||
{
|
||||
|
@ -542,24 +777,7 @@ dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
|||
if (tree)
|
||||
proto_tree_add_item(subtree, hf_marker, tvb, 0, 2, FALSE);
|
||||
|
||||
marker = tvb_get_ntohs(tvb, 2);
|
||||
if (marker != MARKER_APP0) {
|
||||
if (tree) {
|
||||
proto_tree_add_text(subtree, tvb, 2, 2, ErrorInvalidJFIF);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (! tree)
|
||||
return;
|
||||
|
||||
offset = 4;
|
||||
/* The APP0 segment has a length field */
|
||||
len = tvb_get_ntohs(tvb, offset);
|
||||
tmp_tvb = tvb_new_subset(tvb, offset - 2, 2 + len, 2 + len);
|
||||
process_app0_segment(subtree, tmp_tvb, len, marker,
|
||||
match_strval(marker, vals_marker));
|
||||
offset += len;
|
||||
offset = 2;
|
||||
|
||||
/*
|
||||
* Process the remaining markers and marker segments
|
||||
|
@ -577,6 +795,12 @@ dissect_jfif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
|
|||
case MARKER_APP0:
|
||||
process_app0_segment(subtree, tmp_tvb, len, marker, str);
|
||||
break;
|
||||
case MARKER_APP1:
|
||||
process_app1_segment(subtree, tmp_tvb, len, marker, str);
|
||||
break;
|
||||
case MARKER_APP2:
|
||||
process_app2_segment(subtree, tmp_tvb, len, marker, str);
|
||||
break;
|
||||
case MARKER_SOF0:
|
||||
case MARKER_SOF1:
|
||||
case MARKER_SOF2:
|
||||
|
|
Loading…
Reference in New Issue