double parseEXIFRational(const unsigned char *buf, bool intel) { double numerator = 0; double denominator = 1; numerator = (double)parse32(buf, intel); denominator = (double)parse32(buf + 4, intel); if (denominator < 1e-20) return 0; return numerator / denominator; }
IFEntry parseIFEntry(const unsigned char *buf, const unsigned offs, const bool alignIntel, const unsigned base, const unsigned len) { IFEntry result; // 每个条目的组成 // 2 bytes: 标记数字(数据域) // 2 bytes: 数据格式 // 4 bytes: 组件数 // 4 bytes: 数据值或数据值的偏移量 result.tag = parse16(buf + offs, alignIntel); result.format = parse16(buf + offs + 2, alignIntel); result.length = parse32(buf + offs + 4, alignIntel); result.data = parse32(buf + offs + 8, alignIntel); // 按照指定格式进行解析 switch (result.format) { case 1: result.val_byte = (unsigned char)*(buf + offs + 8); break; case 2: result.val_string = parseEXIFString(buf, result.length, result.data, base, len); break; case 3: result.val_16 = parse16((const unsigned char *)buf + offs + 8, alignIntel); break; case 4: result.val_32 = result.data; break; case 5: if (base + result.data + 8 <= len) result.val_rational = parseEXIFRational(buf + base + result.data, alignIntel); break; case 7: case 9: case 10: break; default: result.tag = 0xFF; } return result; }
bool readIdent( const char * file ) { FILE * fh = fopen( file, "r" ); unsigned char e_ident[EI_NIDENT]; if( EI_NIDENT != fread( e_ident, 1, EI_NIDENT, fh )) { fclose(fh); fprintf( stderr, "Failed to read magic number of file %s\n", file ); return 1; } if( e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { fprintf( stderr, "Invalid ELF Magic number bytes\n" ); return 2; } if( e_ident[EI_VERSION] != EV_CURRENT ) { fprintf( stderr, "Invalid ELF version number %d\n", e_ident[EI_VERSION] ); return 3; } bool is64 = false; bool is32 = false; switch(e_ident[EI_CLASS]) { case ELFCLASSNONE: printf( "class=NONE\n" ); break; case ELFCLASS32: printf( "class=32\n" ); is32=true; break; case ELFCLASS64: printf( "class=64\n" ); is64=true; break; default: printf( "class=%d\n", e_ident[EI_CLASS] ); } bool isBig = false; bool isLittle = false; switch( e_ident[EI_DATA]) { case ELFDATANONE: printf( "data=NONE\n" ); break; case ELFDATA2LSB: printf( "data=2's complement, little endian\n" ); isLittle = true; break; case ELFDATA2MSB: printf( "data=2's complement, big endian \n" ); isBig = true; break; default: printf( "class=%d\n", e_ident[EI_DATA] ); } switch(e_ident[EI_OSABI]) { case ELFOSABI_SYSV: printf( "ABI=SYSV\n" ); break; case ELFOSABI_HPUX: printf( "ABI=HPUX\n" ); break; case ELFOSABI_NETBSD: printf( "ABI=NETBSD\n" ); break; case ELFOSABI_GNU: printf( "ABI=GNU\n" ); break; case ELFOSABI_SOLARIS: printf( "ABI=SOLARIS\n" ); break; case ELFOSABI_AIX: printf( "ABI=AIX\n" ); break; case ELFOSABI_IRIX: printf( "ABI=IRIX\n" ); break; case ELFOSABI_FREEBSD: printf( "ABI=FREEBSD\n" ); break; case ELFOSABI_TRU64: printf( "ABI=TRU64\n" ); break; case ELFOSABI_MODESTO: printf( "ABI=MODESTO\n" ); break; case ELFOSABI_OPENBSD: printf( "ABI=OPENBSD\n" ); break; case ELFOSABI_ARM_AEABI: printf( "ABI=ARM_AEABI\n" ); break; case ELFOSABI_ARM: printf( "ABI=ARM\n" ); break; case ELFOSABI_STANDALONE: printf( "ABI=STANDALONE\n" ); break; default: printf( "ABI=%d\n", e_ident[EI_OSABI] ); } if(!is32 && !is64 ) { fprintf( stderr, "ELF architecture must be either 32 or 64\n" ); return 4; } if(!isBig && !isLittle ) { fprintf( stderr, "ELF architecture must be either big or little endian\n" ); return 5; } bool rc; if(is64) rc = parse64(fh); else rc = parse32(fh); fclose(fh); return rc; }
int ParseEXIF(unsigned char *buf, unsigned len, EXIFInfo &result) { bool alignIntel = true; // byte alignment unsigned offs = 0; // current offset into buffer if(len == 0) return PARSE_EXIF_ERROR_NO_EXIF; // Prepare return structure memset(&result, 0, sizeof(result)); // Scan for EXIF header and do a sanity check for(offs = 0; offs < len-1; offs++) if(buf[offs] == 0xFF && buf[offs+1] == 0xE1) break; if(offs == len-1) return PARSE_EXIF_ERROR_NO_EXIF; offs += 4; if(buf[offs] != 0x45 || buf[offs+1] != 0x78 || buf[offs+2] != 0x69) return PARSE_EXIF_ERROR_NO_EXIF; // Get byte alignment (Motorola or Intel) offs += 6; if(buf[offs] == 0x49 && buf[offs+1] == 0x49) alignIntel = true; else { if(buf[offs] == 0x4d && buf[offs+1] == 0x4d) alignIntel = false; else return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN; } result.byteAlign = alignIntel; // Get offset into first IFD offs += 4; unsigned x = parse32(buf+offs, alignIntel); if(offs + x >= len) { return PARSE_EXIF_ERROR_CORRUPT; } // Jump to the first IFD, scan tags there. offs += x-4; int nentries = parse16(buf+offs, alignIntel); offs += 2; unsigned ifdOffset = offs-10; unsigned exifSubIFD = 0; unsigned gpsIFD = 0; for(int j = 0; j < nentries; j++) { unsigned short tag = parse16(buf+offs, alignIntel); unsigned ncomp = parse32(buf+offs+4, alignIntel); unsigned coffs = parse32(buf+offs+8, alignIntel); switch(tag) { case 0x8769: // EXIF subIFD offset exifSubIFD = ifdOffset + coffs; break; case 0x10F: case 0x110: case 0x10E: break; case 0x132: // EXIF/TIFF date/time of image copyEXIFString(&result.dateTimeModified, ncomp, ifdOffset, coffs, buf); break; case 0x8825: gpsIFD = ifdOffset + coffs; break; } offs += 12; } if(!exifSubIFD) return 0; // At the EXIF SubIFD, read the rest of the EXIF tags offs = exifSubIFD; nentries = parse16(buf+offs, alignIntel); offs += 2; for(int j = 0; j < nentries; j++) { unsigned short tag = parse16(buf+offs, alignIntel); unsigned ncomp = parse32(buf+offs+4, alignIntel); unsigned coffs = parse32(buf+offs+8, alignIntel); switch(tag) { case 0x9003: // original image date/time string copyEXIFString(&result.dateTimeOriginal, ncomp, ifdOffset, coffs, buf); break; case 0x920a: case 0x829D: case 0x829A: break; } offs += 12; } return 0; }
//对EXIF段进行解析 'buf'必须开始于EXIF TIFF,为字节"Exif\0\0" ;'len'为buffer的长度 int EXIFInfo::parseFromEXIFSegment(const unsigned char *buf, unsigned len) { bool alignIntel = true; // 字节对齐方式 (定义在 EXIF 头信息中) unsigned offs = 0; // 缓冲区中当前偏移量 if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF; if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF; offs += 6; // 现在解析 TIFF 头. 前两个字节为"II"或者"MM"表示Intel或者 Motorola字节对齐方式 // 安全性检查,接下来的两个字节必须是0x2a. 最后4个字节是第一个IFD的偏移量 // 将会被添加到全局偏移计数器中 // 这块的最小大小8个字节: // 2 bytes: 'II' or 'MM' // 2 bytes: 0x002a // 4 bytes: 第一个IDF的偏移量 // ----------------------------- // 8 bytes if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT; unsigned tiff_header_start = offs; if (buf[offs] == 'I' && buf[offs + 1] == 'I') alignIntel = true; else { if (buf[offs] == 'M' && buf[offs + 1] == 'M') alignIntel = false; else return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN; } this->m_byte_align = alignIntel; offs += 2; if (0x2a != parse16(buf + offs, alignIntel)) return PARSE_EXIF_ERROR_CORRUPT; offs += 2; unsigned first_ifd_offset = parse32(buf + offs, alignIntel); offs += first_ifd_offset - 4; if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT; // 先解析第一个图像文件目录 (IFD0,主图像). // 一个IFD包含多个12字节的目录条目 // IFD段的前2个字节包含目录条数 // 最后4个字节包含一个偏移量对下一个IFD // 这表明IFD段必须包含6 + 12 * num个字节数 if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT; int num_entries = parse16(buf + offs, alignIntel); if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; offs += 2; unsigned exif_sub_ifd_offset = len; unsigned gps_sub_ifd_offset = len; while (--num_entries >= 0) { IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len); offs += 12; switch (result.tag) { case 0x102: // 取样数 if (result.format == 3) this->m_bits_per_sample = result.val_16; break; case 0x10E: // 图像描述 if (result.format == 2) this->m_image_description = result.val_string; break; case 0x10F: // 相机制造商 if (result.format == 2) this->m_make = result.val_string; break; case 0x110: // 相机型号 if (result.format == 2) this->m_model = result.val_string; break; case 0x112: // 图片方向 if (result.format == 3) this->m_orientation = result.val_16; break; case 0x131: // 使用软件 if (result.format == 2) this->m_software = result.val_string; break; case 0x132: // EXIF/TIFF 文件修改的日期与时间 if (result.format == 2) this->m_datetime = result.val_string; break; case 0x8298: // 文件的版权信息 if (result.format == 2) this->m_copyright = result.val_string; break; case 0x8825: // GPS IFS 偏移量 gps_sub_ifd_offset = tiff_header_start + result.data; break; case 0x8769: // EXIF SubIFD 偏移量 exif_sub_ifd_offset = tiff_header_start + result.data; break; } } // 如果存在,则跳转到EXIF SubIFD,解析其所有信息。 // 这边要注意的是EXIF SubIFD可能不存在 // EXIF SubIFD包含了很多有用的信息 if (exif_sub_ifd_offset + 4 <= len) { offs = exif_sub_ifd_offset; int num_entries = parse16(buf + offs, alignIntel); if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; offs += 2; while (--num_entries >= 0) { IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len); switch (result.tag) { case 0x829a: // 曝光时间,单位秒 if (result.format == 5) this->m_exposure_time = result.val_rational; break; case 0x829d: // 相机光圈数 if (result.format == 5) this->m_f_number = result.val_rational; break; case 0x8827: // ISO 感光度 if (result.format == 3) this->m_iso_speed_ratings = result.val_16; break; case 0x9003: //原始文件的日期和时间 if (result.format == 2) this->m_datetime_original = result.val_string; break; case 0x9004: // 数字化的日期和时间 if (result.format == 2) this->m_datetime_digitized = result.val_string; break; case 0x9201: // 快门速度(曝光时间的倒数) if (result.format == 5) this->m_shutter_speed_value = result.val_rational; break; case 0x9204: // 曝光补偿值 if (result.format == 5) this->m_exposure_bias_value = result.val_rational; break; case 0x9206: // 对焦点的距离 if (result.format == 5) this->m_subject_distance = result.val_rational; break; case 0x9209: // 是否使用闪光 if (result.format == 3) this->m_flash = result.data ? 1 : 0; break; case 0x920a: // 焦距 if (result.format == 5) this->m_focal_length = result.val_rational; break; case 0x9207: // 测光模式 if (result.format == 3) this->m_metering_mode = result.val_16; break; case 0x9291: // 图像被拍摄的低于秒级的时间 if (result.format == 2) this->m_sub_sec_time_original = result.val_string; break; case 0xa002: // EXIF 图像宽度 if (result.format == 4) this->m_image_width = result.val_32; else if (result.format == 3) this->m_image_width = result.val_16; else this->m_image_width = result.data; break; case 0xa003: // EXIF 图像高度 if (result.format == 4) this->m_image_height = result.val_32; else if (result.format == 3) this->m_image_height = result.val_16; else this->m_image_height = result.data; break; case 0xa405: // 35mm等效焦距 if (result.format == 3) this->m_focal_length_in_35mm = result.val_16; break; } offs += 12; } } // 如果GPS存在则跳转到 GPS SubIFD 进行解析 if (gps_sub_ifd_offset + 4 <= len) { offs = gps_sub_ifd_offset; int num_entries = parse16(buf + offs, alignIntel); if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; offs += 2; while (--num_entries >= 0) { unsigned short tag = parse16(buf + offs, alignIntel); unsigned short format = parse16(buf + offs + 2, alignIntel); unsigned length = parse32(buf + offs + 4, alignIntel); unsigned data = parse32(buf + offs + 8, alignIntel); switch (tag) { case 1: // GPS 南 或者 北 this->GeoLocation.LatComponents.direction = *(buf + offs + 8); if ('S' == this->GeoLocation.LatComponents.direction) this->GeoLocation.latitude = -this->GeoLocation.latitude; break; case 2: // GPS 纬度信息 if (format == 5 && length == 3) { this->GeoLocation.LatComponents.degrees = parseEXIFRational(buf + data + tiff_header_start, alignIntel); this->GeoLocation.LatComponents.minutes = parseEXIFRational(buf + data + tiff_header_start + 8, alignIntel); this->GeoLocation.LatComponents.seconds = parseEXIFRational(buf + data + tiff_header_start + 16, alignIntel); this->GeoLocation.latitude = this->GeoLocation.LatComponents.degrees + this->GeoLocation.LatComponents.minutes / 60 + this->GeoLocation.LatComponents.seconds / 3600; if ('S' == this->GeoLocation.LatComponents.direction) this->GeoLocation.latitude = -this->GeoLocation.latitude; } break; case 3: // GPS 东或西 this->GeoLocation.LonComponents.direction = *(buf + offs + 8); if ('W' == this->GeoLocation.LonComponents.direction) this->GeoLocation.longitude = -this->GeoLocation.longitude; break; case 4: // GPS 经度信息 if (format == 5 && length == 3) { this->GeoLocation.LonComponents.degrees = parseEXIFRational(buf + data + tiff_header_start, alignIntel); this->GeoLocation.LonComponents.minutes = parseEXIFRational(buf + data + tiff_header_start + 8, alignIntel); this->GeoLocation.LonComponents.seconds = parseEXIFRational(buf + data + tiff_header_start + 16, alignIntel); this->GeoLocation.longitude = this->GeoLocation.LonComponents.degrees + this->GeoLocation.LonComponents.minutes / 60 + this->GeoLocation.LonComponents.seconds / 3600; if ('W' == this->GeoLocation.LonComponents.direction) this->GeoLocation.longitude = -this->GeoLocation.longitude; } break; case 5: // 相对于海平面的高度 this->GeoLocation.altitude_ref = *(buf + offs + 8); if (1 == this->GeoLocation.altitude_ref) this->GeoLocation.altitude = -this->GeoLocation.altitude; break; case 6: // 与海平面的相对位置 if (format == 5) { this->GeoLocation.altitude = parseEXIFRational(buf + data + tiff_header_start, alignIntel); if (1 == this->GeoLocation.altitude_ref) this->GeoLocation.altitude = -this->GeoLocation.altitude; } break; } offs += 12; } } return PARSE_EXIF_SUCCESS; }