编译环境:Ubuntu16.04 64位
交叉编译工具:arm-hisiv500-linux-gcc

最近需要在hi3519抓拍到的JPEG图片增加GPS等EXIF信息,在网上也找了一些资料,介绍EXIF格式的很多,写到读写EXIF信息的很少,可以使用开源库libexif(C语言)或者exiv2(C++)实现。 libexif代码量相对较少(70+,exiv2有300+),也方便移植。果断下载libexif源码(版本0.6.21_release),结果发现里面没有linux平台的编译脚本,算了,自己写。

1. sample代码

在contrib/examples目录下,有份写exif的sample代码write-exif.c,后面可以根据这份代码添加我们需要的EXIF信息(GPS等),代码如下:


/*
 * write-exif.c
 *
 * Placed into the public domain by Daniel Fandrich
 *
 * Create a new EXIF data block and write it into a JPEG image file.
 *
 * The JPEG image data used in this example is fixed and is guaranteed not
 * to contain an EXIF tag block already, so it is easy to precompute where
 * in the file the EXIF data should be. In real life, a library like
 * libjpeg (included with the exif command-line tool source code) would
 * be used to write to an existing JPEG file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>

/* this file will be unilaterally overwritten */
#define FILE_NAME "write-exif.jpg"

/* raw JPEG image data */
static const unsigned char image_jpg[] = {
  0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
  0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
  0x00, 0x14, 0x0e, 0x0f, 0x12, 0x0f, 0x0d, 0x14, 0x12, 0x10, 0x12, 0x17,
  0x15, 0x14, 0x18, 0x1e, 0x32, 0x21, 0x1e, 0x1c, 0x1c, 0x1e, 0x3d, 0x2c,
  0x2e, 0x24, 0x32, 0x49, 0x40, 0x4c, 0x4b, 0x47, 0x40, 0x46, 0x45, 0x50,
  0x5a, 0x73, 0x62, 0x50, 0x55, 0x6d, 0x56, 0x45, 0x46, 0x64, 0x88, 0x65,
  0x6d, 0x77, 0x7b, 0x81, 0x82, 0x81, 0x4e, 0x60, 0x8d, 0x97, 0x8c, 0x7d,
  0x96, 0x73, 0x7e, 0x81, 0x7c, 0xff, 0xc0, 0x00, 0x0b, 0x08, 0x00, 0x40,
  0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xff, 0xc4, 0x00, 0x1b, 0x00, 0x00,
  0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x04, 0x03, 0x07, 0x02, 0x01, 0xff,
  0xc4, 0x00, 0x2f, 0x10, 0x00, 0x01, 0x03, 0x03, 0x02, 0x05, 0x03, 0x03,
  0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x11,
  0x00, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x81, 0x61, 0x71,
  0x91, 0x13, 0x32, 0xa1, 0x14, 0x22, 0xc1, 0x15, 0x23, 0x52, 0xd1, 0xf0,
  0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xb3, 0xa2,
  0x8a, 0x28, 0xa2, 0x8a, 0x28, 0xa2, 0x97, 0x64, 0x72, 0xd6, 0x58, 0xd4,
  0x8f, 0xd5, 0x3d, 0xca, 0xa5, 0x7d, 0xa8, 0x4e, 0xaa, 0x3e, 0xb0, 0x3a,
  0x7a, 0x9d, 0x2b, 0x2d, 0x87, 0x13, 0xe3, 0xaf, 0x9f, 0x0c, 0xb6, 0xb5,
  0xb6, 0xe2, 0xb4, 0x48, 0x71, 0x30, 0x14, 0x7b, 0x02, 0x09, 0x14, 0xee,
  0x94, 0x64, 0x38, 0x87, 0x1f, 0x8d, 0x5f, 0xd3, 0x7d, 0xe2, 0xa7, 0x46,
  0xe8, 0x6c, 0x49, 0x1e, 0xfd, 0x07, 0xb1, 0x33, 0x5f, 0xb8, 0xdc, 0xf5,
  0x96, 0x51, 0x7c, 0x96, 0xee, 0x14, 0xba, 0x04, 0xf2, 0x2c, 0x42, 0xa3,
  0xd3, 0x70, 0x7c, 0x1a, 0x6d, 0x45, 0x2c, 0xcd, 0xe5, 0x11, 0x89, 0xb1,
  0x5b, 0xc4, 0x02, 0xe1, 0xd1, 0xb4, 0xcf, 0xdc, 0xa3, 0xfc, 0x0d, 0xcd,
  0x41, 0xde, 0xe3, 0xb2, 0x2e, 0xda, 0x1c, 0xbd, 0xe2, 0x4a, 0x90, 0xe9,
  0x04, 0x95, 0x2b, 0xf7, 0x41, 0xd0, 0x18, 0xe8, 0x36, 0x03, 0xc6, 0x91,
  0x4a, 0x81, 0x20, 0x82, 0x24, 0x10, 0x66, 0x45, 0x5a, 0xdd, 0xf1, 0x32,
  0xff, 0x00, 0xa2, 0x5a, 0x8b, 0x62, 0x57, 0x7d, 0x70, 0x80, 0x93, 0x1a,
  0x94, 0x10, 0x79, 0x4a, 0xa3, 0xb9, 0x20, 0xc0, 0xf3, 0xd3, 0x55, 0x59,
  0x4e, 0x1c, 0x7a, 0xc7, 0x12, 0x9b, 0xc7, 0x94, 0xa7, 0x2e, 0x14, 0xa9,
  0x74, 0x0d, 0x42, 0x01, 0x04, 0xc9, 0x3b, 0x93, 0x30, 0x09, 0xdb, 0x5f,
  0x35, 0x9b, 0x85, 0xad, 0xde, 0x7f, 0x39, 0x6e, 0xa6, 0x82, 0xa1, 0xa5,
  0x73, 0x2d, 0x40, 0x68, 0x13, 0x06, 0x67, 0xdf, 0x6f, 0x35, 0xe9, 0xb4,
  0x52, 0x4c, 0xbe, 0x0c, 0x65, 0x72, 0x16, 0xcf, 0x3a, 0xff, 0x00, 0xf6,
  0x5a, 0xd1, 0x4d, 0x11, 0xa1, 0x13, 0x26, 0x0f, 0x73, 0x00, 0x1f, 0x41,
  0x5b, 0x72, 0xd6, 0xa2, 0xeb, 0x15, 0x73, 0x6e, 0x13, 0xf7, 0x36, 0x42,
  0x40, 0xee, 0x04, 0x8f, 0xc8, 0x15, 0xe4, 0xf5, 0x59, 0xc1, 0x16, 0x0d,
  0x3d, 0x70, 0xed, 0xdb, 0x84, 0x29, 0x6c, 0x40, 0x42, 0x3b, 0x13, 0x3f,
  0xb8, 0xfc, 0x10, 0x3c, 0xd5, 0xc1, 0x00, 0x88, 0x22, 0x41, 0xef, 0x5f,
  0x0d, 0xb4, 0x86, 0x81, 0x0d, 0xa1, 0x28, 0x07, 0x52, 0x12, 0x00, 0x06,
  0xba, 0xd1, 0x45, 0x71, 0xb8, 0xb8, 0x6e, 0xd9, 0x85, 0xbc, 0xf2, 0xb9,
  0x5b, 0x40, 0x25, 0x47, 0xb0, 0xaf, 0x24, 0x79, 0x48, 0x53, 0xce, 0x29,
  0xb4, 0x94, 0xa0, 0xa8, 0x94, 0xa7, 0xb0, 0x27, 0x41, 0xf1, 0x54, 0x9c,
  0x09, 0x71, 0xc9, 0x92, 0x7d, 0x82, 0x74, 0x75, 0xa9, 0x1e, 0xa5, 0x27,
  0xfd, 0x13, 0x55, 0xd9, 0x3c, 0x83, 0x38, 0xbb, 0x45, 0x5c, 0xbf, 0x3c,
  0xa1, 0x40, 0x40, 0x02, 0x54, 0x4f, 0x41, 0x3e, 0x4f, 0x83, 0x5a, 0x2d,
  0xae, 0x1b, 0xba, 0xb7, 0x6d, 0xf6, 0x89, 0x28, 0x71, 0x21, 0x49, 0x24,
  0x41, 0x83, 0xe9, 0x5d, 0xe9, 0x3f, 0x12, 0x64, 0x97, 0x8b, 0xc6, 0x17,
  0x59, 0x03, 0xea, 0xa9, 0x61, 0x08, 0x24, 0x48, 0x04, 0xc9, 0x9f, 0x80,
  0x6a, 0x4b, 0x17, 0xc4, 0xd7, 0xac, 0xdf, 0x36, 0xab, 0xbb, 0x85, 0x39,
  0x6e, 0xb5, 0x00, 0xe0, 0x50, 0x06, 0x01, 0x3a, 0x91, 0x02, 0x44, 0x6f,
  0x02, 0xb7, 0x65, 0xb2, 0x0e, 0x71, 0x25, 0xf3, 0x58, 0xdc, 0x72, 0x8f,
  0xe9, 0xc1, 0xe6, 0x52, 0xe0, 0xc2, 0xa3, 0x72, 0x46, 0xe0, 0x0e, 0x83,
  0xa9, 0xf1, 0x4b, 0x38, 0x9f, 0x16, 0xde, 0x2e, 0xfd, 0xa4, 0x32, 0x92,
  0x1a, 0x5b, 0x40, 0x83, 0xdc, 0x8d, 0x0f, 0x9d, 0x01, 0x3e, 0xf5, 0x83,
  0x17, 0x7a, 0xac, 0x76, 0x41, 0x9b, 0xa4, 0xa7, 0x9b, 0xe9, 0xab, 0x51,
  0x31, 0x20, 0x82, 0x08, 0xf8, 0x34, 0xf9, 0xdb, 0x97, 0xb8, 0xb7, 0x2c,
  0xcd, 0xba, 0x12, 0xa6, 0xec, 0xda, 0x3c, 0xca, 0x13, 0x24, 0x0e, 0xa4,
  0x91, 0xa4, 0x9d, 0x87, 0x69, 0xf7, 0xab, 0x74, 0x21, 0x2d, 0xa1, 0x28,
  0x4a, 0x42, 0x52, 0x90, 0x02, 0x40, 0xd8, 0x01, 0xb0, 0xae, 0x95, 0x87,
  0x29, 0x8f, 0x6f, 0x27, 0x64, 0xe5, 0xb3, 0xc4, 0x80, 0xa8, 0x29, 0x50,
  0xdd, 0x24, 0x6c, 0x47, 0xfd, 0xd4, 0xd4, 0x92, 0x78, 0x1e, 0xef, 0xeb,
  0x42, 0xae, 0x98, 0x0d, 0x7f, 0x90, 0x0a, 0x27, 0xe2, 0x00, 0xfc, 0xd5,
  0x4e, 0x27, 0x11, 0x6d, 0x89, 0x60, 0xb7, 0x6e, 0x92, 0x54, 0xa8, 0xe6,
  0x71, 0x5b, 0xa8, 0xff, 0x00, 0x03, 0xd2, 0xbe, 0x73, 0x38, 0x86, 0x72,
  0xf6, 0xc1, 0xb7, 0x4f, 0x2a, 0xd1, 0x25, 0x0e, 0x01, 0x25, 0x27, 0xae,
  0x9d, 0x41, 0xed, 0xed, 0x52, 0xe8, 0xe0, 0x7b, 0xcf, 0xab, 0x0e, 0x5d,
  0x30, 0x96, 0xe7, 0xee, 0x4f, 0x31, 0x57, 0xc1, 0x00, 0x7e, 0x6a, 0xaf,
  0x15, 0x8b, 0x63, 0x17, 0x6e, 0x1a, 0x61, 0x24, 0x92, 0x65, 0x6b, 0x3b,
  0xa8, 0xf7, 0x3e, 0x9d, 0x87, 0x4a, 0x63, 0x45, 0x14, 0x51, 0x45, 0x14,
  0x51, 0x45, 0x7f, 0xff, 0xd9
};

/* length of data in image_jpg */
static const unsigned int image_jpg_len = sizeof(image_jpg);

/* dimensions of image */
static const unsigned int image_jpg_x = 64;
static const unsigned int image_jpg_y = 64;

/* start of JPEG image data section */
static const unsigned int image_data_offset = 20;
#define image_data_len (image_jpg_len - image_data_offset)

/* raw EXIF header data */
static const unsigned char exif_header[] = {
  0xff, 0xd8, 0xff, 0xe1
};
/* length of data in exif_header */
static const unsigned int exif_header_len = sizeof(exif_header);

/* byte order to use in the EXIF block */
#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL

/* comment to write into the EXIF block */
#define FILE_COMMENT "libexif demonstration image"

/* special header required for EXIF_TAG_USER_COMMENT */
#define ASCII_COMMENT "ASCII\0\0\0"


/* Get an existing tag, or create one if it doesn't exist */
static ExifEntry *init_tag(ExifData *exif, ExifIfd ifd, ExifTag tag)
{
 ExifEntry *entry;
 /* Return an existing tag if one exists */
 if (!((entry = exif_content_get_entry (exif->ifd[ifd], tag)))) {
     /* Allocate a new entry */
     entry = exif_entry_new ();
     assert(entry != NULL); /* catch an out of memory condition */
     entry->tag = tag; /* tag must be set before calling
     exif_content_add_entry */

     /* Attach the ExifEntry to an IFD */
     exif_content_add_entry (exif->ifd[ifd], entry);

     /* Allocate memory for the entry and fill with default data */
     exif_entry_initialize (entry, tag);

     /* Ownership of the ExifEntry has now been passed to the IFD.
      * One must be very careful in accessing a structure after
      * unref'ing it; in this case, we know "entry" won't be freed
      * because the reference count was bumped when it was added to
      * the IFD.
      */
     exif_entry_unref(entry);
 }
 return entry;
}

/* Create a brand-new tag with a data field of the given length, in the
 * given IFD. This is needed when exif_entry_initialize() isn't able to create
 * this type of tag itself, or the default data length it creates isn't the
 * correct length.
 */
static ExifEntry *create_tag(ExifData *exif, ExifIfd ifd, ExifTag tag, size_t len)
{
 void *buf;
 ExifEntry *entry;

 /* Create a memory allocator to manage this ExifEntry */
 ExifMem *mem = exif_mem_new_default();
 assert(mem != NULL); /* catch an out of memory condition */

 /* Create a new ExifEntry using our allocator */
 entry = exif_entry_new_mem (mem);
 assert(entry != NULL);

 /* Allocate memory to use for holding the tag data */
 buf = exif_mem_alloc(mem, len);
 assert(buf != NULL);

 /* Fill in the entry */
 entry->data = buf;
 entry->size = len;
 entry->tag = tag;
 entry->components = len;
 entry->format = EXIF_FORMAT_UNDEFINED;

 /* Attach the ExifEntry to an IFD */
 exif_content_add_entry (exif->ifd[ifd], entry);

 /* The ExifMem and ExifEntry are now owned elsewhere */
 exif_mem_unref(mem);
 exif_entry_unref(entry);

 return entry;
}

int main(int argc, char **argv)
{
 int rc = 1;
 FILE *f;
 unsigned char *exif_data;
 unsigned int exif_data_len;
 ExifEntry *entry;
 ExifData *exif = exif_data_new();
 if (!exif) {
  fprintf(stderr, "Out of memory\n");
  return 2;
 }

 /* Set the image options */
 exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
 exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
 exif_data_set_byte_order(exif, FILE_BYTE_ORDER);

 /* Create the mandatory EXIF fields with default data */
 exif_data_fix(exif);

 /* All these tags are created with default values by exif_data_fix() */
 /* Change the data to the correct values for this image. */
 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
 exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_x);

 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
 exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_y);

 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
 exif_set_short(entry->data, FILE_BYTE_ORDER, 1);

 /* Create a EXIF_TAG_USER_COMMENT tag. This one must be handled
  * differently because that tag isn't automatically created and
  * allocated by exif_data_fix(), nor can it be created using
  * exif_entry_initialize() so it must be explicitly allocated here.
  */
 entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT, 
   sizeof(ASCII_COMMENT) + sizeof(FILE_COMMENT) - 2);
 /* Write the special header needed for a comment tag */
 memcpy(entry->data, ASCII_COMMENT, sizeof(ASCII_COMMENT)-1);
 /* Write the actual comment text, without the trailing NUL character */
 memcpy(entry->data+8, FILE_COMMENT, sizeof(FILE_COMMENT)-1);
 /* create_tag() happens to set the format and components correctly for
  * EXIF_TAG_USER_COMMENT, so there is nothing more to do. */

 /* Create a EXIF_TAG_SUBJECT_AREA tag */
 entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_AREA,
      4 * exif_format_get_size(EXIF_FORMAT_SHORT));
 entry->format = EXIF_FORMAT_SHORT;
 entry->components = 4;
 exif_set_short(entry->data, FILE_BYTE_ORDER, image_jpg_x / 2);
 exif_set_short(entry->data+2, FILE_BYTE_ORDER, image_jpg_y / 2);
 exif_set_short(entry->data+4, FILE_BYTE_ORDER, image_jpg_x);
 exif_set_short(entry->data+6, FILE_BYTE_ORDER, image_jpg_y);

 /* Get a pointer to the EXIF data block we just created */
 exif_data_save_data(exif, &exif_data, &exif_data_len);
 assert(exif_data != NULL);

 f = fopen(FILE_NAME, "wb");
 if (!f) {
  fprintf(stderr, "Error creating file %s\n", FILE_NAME);
  exif_data_unref(exif);
  return rc;
 }
 /* Write EXIF header */
 if (fwrite(exif_header, exif_header_len, 1, f) != 1) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  goto errout;
 }
 /* Write EXIF block length in big-endian order */
 if (fputc((exif_data_len+2) >> 8, f) < 0) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  goto errout;
 }
 if (fputc((exif_data_len+2) & 0xff, f) < 0) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  goto errout;
 }
 /* Write EXIF data block */
 if (fwrite(exif_data, exif_data_len, 1, f) != 1) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  goto errout;
 }
 /* Write JPEG image data, skipping the non-EXIF header */
 if (fwrite(image_jpg+image_data_offset, image_data_len, 1, f) != 1) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  goto errout;
 }
 printf("Wrote file %s\n", FILE_NAME);
 rc = 0;

errout:
 if (fclose(f)) {
  fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
  rc = 1;
 }
 /* The allocator we're using for ExifData is the standard one, so use
  * it directly to free this pointer.
  */
 free(exif_data);
 exif_data_unref(exif);

 return rc;
}

2. 建立新的exif目录结构

从write-exif.c中可以看到,sample只需要#include <libexif/exif-data.h>即可。这样我就基于exif-data.h和exif-data.c开始,找到所需要的所有的源码,过程略。最终另外建立的目录结果如下:

jerry@ubuntu:~/work/exif/exif_my$ tree
.
├── exif-byte-order.c
├── exif-content.c
├── exif-data.c
├── exif-entry.c
├── exif-format.c
├── exif-ifd.c
├── exif-loader.c
├── exif-log.c
├── exif-mem.c
├── exif-mnote-data.c
├── exif-mnote-data-canon.c
├── exif-mnote-data-fuji.c
├── exif-mnote-data-olympus.c
├── exif-mnote-data-pentax.c
├── exif-tag.c
├── exif-utils.c
├── include
│   ├── config.h
│   └── libexif
│       ├── canon
│       │   ├── exif-mnote-data-canon.h
│       │   ├── mnote-canon-entry.h
│       │   └── mnote-canon-tag.h
│       ├── exif-byte-order.h
│       ├── exif-content.h
│       ├── exif-data.h
│       ├── exif-data-type.h
│       ├── exif-entry.h
│       ├── exif-format.h
│       ├── exif.h
│       ├── exif-ifd.h
│       ├── exif-loader.h
│       ├── exif-log.h
│       ├── exif-mem.h
│       ├── exif-mnote-data.h
│       ├── exif-mnote-data-priv.h
│       ├── exif-system.h
│       ├── exif-tag.h
│       ├── exif-utils.h
│       ├── fuji
│       │   ├── exif-mnote-data-fuji.h
│       │   ├── mnote-fuji-entry.h
│       │   └── mnote-fuji-tag.h
│       ├── i18n.h
│       ├── olympus
│       │   ├── exif-mnote-data-olympus.h
│       │   ├── mnote-olympus-entry.h
│       │   └── mnote-olympus-tag.h
│       ├── pentax
│       │   ├── exif-mnote-data-pentax.h
│       │   ├── mnote-pentax-entry.h
│       │   └── mnote-pentax-tag.h
│       └── _stdint.h
├── lib
├── Makefile
├── Makefile.param
├── mnote-canon-entry.c
├── mnote-canon-tag.c
├── mnote-fuji-entry.c
├── mnote-fuji-tag.c
├── mnote-olympus-entry.c
├── mnote-olympus-tag.c
├── mnote-pentax-entry.c
├── mnote-pentax-tag.c
└── sample
    ├── Makefile
    ├── sample.c
    └── write-exif.jpg

8 directories, 60 files

3. 新增以及修改的文件

新增文件Makefile、Makefile.param、sample/Makefile,sample/sample.c是write-exif.c修改文件名得到,sample/write-exif.jpg是指定sample程序后得到的jpeg图片,lib目录用来放置生成的libexif.a。

3.1 config.h

其实没什么用,只是为了尽量减少修改点。

#ifndef __EXIF_CONFIG_H__
#define __EXIF_CONFIG_H__

#endif

3.2 i18n.h

#ifndef __I18N_H__
#define __I18N_H__

#include "config.h"

#define _(String) (String)
#define N_(String) (String)

#endif /* __I18N_H__ */

3.3 _stdint.h

#ifndef __STDINT_H
#define __STDINT_H
#include <sys/types.h>

typedef  unsigned char   uint8_t;
typedef  unsigned short  uint16_t;
typedef  unsigned long   uint32_t;

#if 0
typedef    signed char   int8_t;
typedef    signed short  int16_t;
typedef    signed long   int32_t;
#endif
#endif

3.4 Makefile

PWD := $(shell pwd)
PARAM_FILE:=$(PWD)/Makefile.param
include $(PARAM_FILE)

CFLAGS = -I$(PWD)/include/
TARGET = $(PWD)/lib/libexif.a
OBJS := $(patsubst %.c,%.o,$(wildcard *.c))

all : $(TARGET)

$(TARGET) : $(OBJS)
 $(AR) -rc $@ $(OBJS)

.PHONY : clean
clean :
 rm -f $(OBJS) $(TARGET)


3.5 Makefile.param

这里指定交叉编译工具,我用的是arm-hisiv500-linux-,需要做对应的修改。


export CROSS := arm-hisiv500-linux-
export CC := $(CROSS)gcc
export AR := $(CROSS)ar

3.6 sample/Makefile

PWD := $(shell pwd)
PARAM_FILE := $(PWD)/../Makefile.param
include $(PARAM_FILE)

TARGET = $(PWD)/sample
CFLAGS = -L$(PWD)/../lib -I$(PWD)/../include -lexif -lm

all :
 $(CC) sample.c -o $(TARGET) $(CFLAGS)

clean :
 rm -f $(TARGET)

3.7 其他文件

以上操作完成后,使用命令make执行Makefile,会出现一系列头文件找不到的问题和几个类型不匹配的警告,可以直接在代码中修改包含头文件的路径,也可以在Makefile中添加头文件的查找路径,直到编译顺利完成,生成静态库lib/libexif.a。
注意:libexif.a使用了log10和pow函数,依赖math库,可以看到在编译sample.c的时候添加了-lm选项。

4. libexif的使用

4.1 移植

移植不需要所有的头文件,如下:

jerry@ubuntu:~/work/exif/exif_explant$ tree
.
├── include
│   └── libexif
│       ├── exif-byte-order.h
│       ├── exif-content.h
│       ├── exif-data.h
│       ├── exif-data-type.h
│       ├── exif-entry.h
│       ├── exif-format.h
│       ├── exif-ifd.h
│       ├── exif-log.h
│       ├── exif-mem.h
│       ├── exif-mnote-data.h
│       ├── exif-tag.h
│       ├── exif-utils.h
│       └── _stdint.h
└── lib
    └── libexif.a

3 directories, 14 files

4.2 使用(添加GPS信息)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>

static const unsigned char g_Make[16] = "AF";
static const unsigned char g_Model[16] = "JerryJiu";
static const unsigned char g_Version[16] = "181203Release";

/* start of JPEG image data section */
static const unsigned int image_data_offset = 20;

/* raw EXIF header data */
static const unsigned char exif_header[] = {
 0xff, 0xd8, 0xff, 0xe1
};

/* GPS Version */
static const unsigned char g_GPSVersion[] = {
 0x02, 0x02, 0x00, 0x00
};

/* length of data in exif_header */
static const unsigned int exif_header_len = sizeof(exif_header);

/* byte order to use in the EXIF block */
#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL

/* Get an existing tag, or create one if it doesn't exist */
static ExifEntry *init_tag(ExifData *exif, ExifIfd ifd, ExifTag tag)
{
 ExifEntry *entry;
 /* Return an existing tag if one exists */
 if (!((entry = exif_content_get_entry (exif->ifd[ifd], tag)))) {
     /* Allocate a new entry */
     entry = exif_entry_new ();
     assert(entry != NULL); /* catch an out of memory condition */
     entry->tag = tag; /* tag must be set before calling
     exif_content_add_entry */

     /* Attach the ExifEntry to an IFD */
     exif_content_add_entry (exif->ifd[ifd], entry);

     /* Allocate memory for the entry and fill with default data */
     exif_entry_initialize (entry, tag);

     /* Ownership of the ExifEntry has now been passed to the IFD.
      * One must be very careful in accessing a structure after
      * unref'ing it; in this case, we know "entry" won't be freed
      * because the reference count was bumped when it was added to
      * the IFD.
      */
     exif_entry_unref(entry);
 }
 return entry;
}

/* Create a brand-new tag with a data field of the given length, in the
 * given IFD. This is needed when exif_entry_initialize() isn't able to create
 * this type of tag itself, or the default data length it creates isn't the
 * correct length.
 */
static ExifEntry *create_tag(ExifData *exif, ExifIfd ifd, ExifTag tag, size_t len)
{
 void *buf;
 ExifEntry *entry;

 /* Create a memory allocator to manage this ExifEntry */
 ExifMem *mem = exif_mem_new_default();
 assert(mem != NULL); /* catch an out of memory condition */

 /* Create a new ExifEntry using our allocator */
 entry = exif_entry_new_mem (mem);
 assert(entry != NULL);

 /* Allocate memory to use for holding the tag data */
 buf = exif_mem_alloc(mem, len);
 assert(buf != NULL);

 /* Fill in the entry */
 entry->data = buf;
 entry->size = len;
 entry->tag = tag;
 entry->components = len;
 entry->format = EXIF_FORMAT_UNDEFINED;

 /* Attach the ExifEntry to an IFD */
 exif_content_add_entry (exif->ifd[ifd], entry);

 /* The ExifMem and ExifEntry are now owned elsewhere */
 exif_mem_unref(mem);
 exif_entry_unref(entry);

 return entry;
}

static int AF_SaveJpeg(char *pFilePath, unsigned char *pFrame, unsigned int nFrameLen, unsigned int nWidth, unsigned int nHeight)
{
 int nRet = 0;
 FILE *pFile = NULL;

 unsigned char *exif_data = NULL;
 unsigned int exif_data_len = 0; 
 ExifEntry *entry = NULL;
 ExifData *exif = exif_data_new();
 if (!exif) {
  nRet = -1;
  goto ERR_EXIT;
 }

 /* Set the image options */
 exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
 exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
 exif_data_set_byte_order(exif, FILE_BYTE_ORDER);

 /* Create the mandatory EXIF fields with default data */
 exif_data_fix(exif);

 /* All these tags are created with default values by exif_data_fix() */
 /* Change the data to the correct values for this image. */
 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
 exif_set_long(entry->data, FILE_BYTE_ORDER, nWidth);

 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
 exif_set_long(entry->data, FILE_BYTE_ORDER, nHeight);

 entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
 exif_set_short(entry->data, FILE_BYTE_ORDER, 1);

 entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_VERSION_ID, 4 * exif_format_get_size(EXIF_FORMAT_BYTE));
 entry->format = EXIF_FORMAT_BYTE;
 entry->components = 4;
 int i = 0;
 for (i = 0; i < 4; i++) {
  exif_set_sshort(entry->data + i, FILE_BYTE_ORDER, g_GPSVersion[i]);
 }

 // 上海北纬 30度40分~31度53分 东经 120度51分~122度12分
 entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = 2;
 memcpy(entry->data, "N", 2);// N北纬/S南纬

 entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, 3 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
 entry->format = EXIF_FORMAT_RATIONAL;
    entry->components = 3;
    ExifRational fir, mid, las;
    fir.numerator = 31;
    fir.denominator = 1;
    mid.numerator = 00;
    mid.denominator = 1;
    las.numerator = 00;
    las.denominator = 1; 
    exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
    exif_set_rational(entry->data + 8, FILE_BYTE_ORDER, mid);
    exif_set_rational(entry->data + 16 ,FILE_BYTE_ORDER, las);

    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
    entry->format = EXIF_FORMAT_ASCII;
    entry->components = 2;
    memcpy(entry->data, "E", 2);// E东经/W西经

    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, 3 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
    entry->format = EXIF_FORMAT_RATIONAL;
    entry->components = 3;
    fir.numerator = 121;
    fir.denominator = 1;
    mid.numerator = 0;
    mid.denominator = 1;
    las.numerator = 0;
    las.denominator = 1; 
    exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
    exif_set_rational(entry->data + 8, FILE_BYTE_ORDER, mid);
    exif_set_rational(entry->data + 16, FILE_BYTE_ORDER, las);
    
    // 绝对-海平面
    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, 1 * exif_format_get_size(EXIF_FORMAT_BYTE));
    entry->format = EXIF_FORMAT_BYTE;
    entry->components = 1;
    exif_set_short(entry->data, FILE_BYTE_ORDER, 0);// 0-海面上 1-海面下

    // 高度
    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE, 1 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
    entry->format = EXIF_FORMAT_RATIONAL;
    entry->components = 1;
    fir.numerator = 100;
    fir.denominator = 11;
    exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
 
    //GPS速度单位K KM/H
    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_SPEED_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
    entry->format = EXIF_FORMAT_ASCII;
    entry->components = 2;
    memcpy(entry->data, "K", 2);

    //GPS速度值
    entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_SPEED, 1 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
    entry->format = EXIF_FORMAT_RATIONAL;
    entry->components = 1;
    fir.numerator = 50;
    fir.denominator = 1;
    exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);

 // 拍摄时间 
 // EXIF_TAG_SUB_SEC_TIME EXIF_TAG_SUB_SEC_TIME_ORIGINAL EXIF_TAG_SUB_SEC_TIME_DIGITIZED 毫秒时间不写入
 entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = 20;
 time_t t = time(NULL);
 struct tm stTime;
 localtime_r(&t, &stTime); 
 char pDataTime[20] = { 0 };
 snprintf(pDataTime, sizeof(pDataTime), "%04d-%02d-%02d %02d:%02d:%02d", stTime.tm_year + 1900, stTime.tm_mon + 1, stTime.tm_mday, stTime.tm_hour, stTime.tm_min, stTime.tm_sec);
 memcpy(entry->data, pDataTime, sizeof(pDataTime));

 // 数字化时间
 entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = 20;
 memcpy(entry->data, pDataTime, sizeof(pDataTime));

 //制造商
 entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_MAKE, sizeof(g_Make) * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = sizeof(g_Make);
 memcpy(entry->data, g_Make, sizeof(g_Make));

 // 型号
 entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_MODEL, sizeof(g_Model) * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = sizeof(g_Model);
 memcpy(entry->data, g_Model, sizeof(g_Model));

 // 修改时间
 entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_DATE_TIME, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = 20;
 memcpy(entry->data, pDataTime, sizeof(pDataTime));

 // 软件
 entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_SOFTWARE, sizeof(g_Version) * exif_format_get_size(EXIF_FORMAT_ASCII));
 entry->format = EXIF_FORMAT_ASCII;
 entry->components = sizeof(g_Version);
 memcpy(entry->data, g_Version, sizeof(g_Version));
 
 exif_data_save_data(exif, &exif_data, &exif_data_len);
 assert(exif_data != NULL);

 pFile = fopen(pFilePath, "wb+");
 if (!pFile) {
  nRet = -1;
  goto ERR_EXIT;
 }

 /* Write EXIF header */
 if (fwrite(exif_header, exif_header_len, 1, pFile) != 1) {
  nRet = -1;
  goto ERR_EXIT;
 }
 /* Write EXIF block length in big-endian order */
 if (fputc((exif_data_len+2) >> 8, pFile) < 0) {
  nRet = -1;
  goto ERR_EXIT;
 }
 if (fputc((exif_data_len+2) & 0xff, pFile) < 0) {
  nRet = -1;
  goto ERR_EXIT;
 }
 /* Write EXIF data block */
 if (fwrite(exif_data, exif_data_len, 1, pFile) != 1) {
  nRet = -1;
  goto ERR_EXIT;
 }
 /* Write JPEG image data, skipping the non-EXIF header */
 if (fwrite(pFrame + image_data_offset, nFrameLen - image_data_offset, 1, pFile) != 1) {
  nRet = -1;
  goto ERR_EXIT;
 }

ERR_EXIT:
 if (pFile) {
  fclose(pFile);
 }
 if (exif_data) {
  free(exif_data);
 }
 if (exif) {
  exif_data_unref(exif);
 }

 return nRet;
}

修改后的libexif源码包点击 使用libexif为JPEG图片添加EXIF属性 下载。

转载请注明出处,如有错漏之处,敬请指正。

Logo

更多推荐