metadata_t * getMetadata(const char *fileName) { metadata_t *mdata; if (metadataFromProgram) { if ((mdata = metadata_program(fileName, nFlag)) == NULL) return (NULL); if (!metadata_program_update(mdata, METADATA_ALL)) { metadata_free(&mdata); return (NULL); } } else { if ((mdata = metadata_file(fileName, nFlag)) == NULL) return (NULL); if (!metadata_file_update(mdata)) { metadata_free(&mdata); return (NULL); } } return (mdata); }
/** * a_geotag_get_exif_date_from_file: * @filename: The image file to process * @has_GPS_info: Returns whether the file has existing GPS information * * Returns: An allocated string with the date and time in EXIF_DATE_FORMAT, otherwise NULL if some kind of failure * * Here EXIF processing is used to get time information * */ gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_GPS_info ) { gchar* datetime = NULL; *has_GPS_info = FALSE; #ifdef HAVE_LIBGEXIV2 GExiv2Metadata *gemd = gexiv2_metadata_new (); if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) { gdouble lat, lon; *has_GPS_info = ( gexiv2_metadata_get_gps_longitude(gemd,&lon) && gexiv2_metadata_get_gps_latitude(gemd,&lat) ); // Prefer 'Photo' version over 'Image' if ( gexiv2_metadata_has_tag ( gemd, "Exif.Photo.DateTimeOriginal" ) ) datetime = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Photo.DateTimeOriginal" ) ); else datetime = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.DateTimeOriginal" ) ); } metadata_free ( gemd ); #else #ifdef HAVE_LIBEXIF ExifData *ed = exif_data_new_from_file ( filename ); // Detect EXIF load failure if ( !ed ) return datetime; gchar str[128]; ExifEntry *ee; ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL); if ( ee ) { exif_entry_get_value ( ee, str, 128 ); datetime = g_strdup ( str ); } // Check GPS Info ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID); // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0" if ( ee && ee->components == 4 ) *has_GPS_info = TRUE; // Check other basic GPS fields exist too // I have encountered some images which have just the EXIF_TAG_GPS_VERSION_ID but nothing else // So to confirm check more EXIF GPS TAGS: ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE); if ( !ee ) *has_GPS_info = FALSE; ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE); if ( !ee ) *has_GPS_info = FALSE; exif_data_free ( ed ); #endif #endif return datetime; }
static void handle_set_parameter_metadata(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { char *cp = req->content; int cl = req->contentlength; metadata *meta = metadata_init(); unsigned int off = 8; while (off < cl) { char tag[5]; strncpy(tag, cp+off, 4); tag[4] = '\0'; off += 4; uint32_t vl = ntohl(*(uint32_t *)(cp+off)); off += sizeof(uint32_t); char *val = malloc(vl+1); strncpy(val, cp+off, vl); val[vl] = '\0'; off += vl; debug(2, "Tag: %s Content: %s\n", tag, val); #if 0 if (!strncmp(tag, "asal ", 4) && meta->album == NULL) { debug(1, "META Album: %s\n", val); meta->album = strdup(val); setAlbumConfig(meta->album); } else if (!strncmp(tag, "asar ", 4) && meta->artist == NULL) { debug(1, "META Artist: %s\n", val); meta->artist = strdup(val); setCreatorConfig(meta->artist); } else if (!strncmp(tag, "ascm ", 4) && meta->comment == NULL) { debug(1, "META Comment: %s\n", val); meta->comment = strdup(val); } else if (!strncmp(tag, "asgn ", 4) && meta->genre == NULL) { debug(1, "META Genre: %s\n", val); meta->genre = strdup(val); } else if (!strncmp(tag, "minm ", 4) && meta->title == NULL) { debug(1, "META Title: %s\n", val); meta->title = strdup(val); setTitleConfig(meta->title); } #endif free(val); } // player_metadata(meta); metadata_free(meta); }
void metafile_key_unset (MetaFile *metafile, const char *key) { MetaData *data; data = metafile_key_lookup (metafile, key, FALSE); if (data) { metafile->data = g_list_remove (metafile->data, data); metadata_free (data); } }
/** * a_geotag_waypoint_positioned: * @filename: The image file to process * @coord: The location for positioning the Waypoint * @name: Returns a name for the Waypoint (can be NULL) * @waypoint: An existing waypoint to update (can be NULL to generate a new waypoint) * * Returns: An allocated waypoint if the input waypoint is NULL, * otherwise the passed in waypoint is updated * * Here EXIF processing is used to get non position related information (i.e. just the comment) * */ VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coord, gdouble alt, gchar **name, VikWaypoint *wp ) { *name = NULL; if ( wp == NULL ) { // Need to create waypoint wp = vik_waypoint_new(); wp->visible = TRUE; } wp->coord = coord; wp->altitude = alt; #ifdef HAVE_LIBGEXIV2 GExiv2Metadata *gemd = gexiv2_metadata_new (); if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) { wp->comment = geotag_get_exif_comment ( gemd ); if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPTitle" ) ) *name = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPTitle" ) ); } metadata_free ( gemd ); #else #ifdef HAVE_LIBEXIF ExifData *ed = exif_data_new_from_file ( filename ); // Set info from exif values if ( ed ) { wp->comment = geotag_get_exif_comment ( ed ); gchar str[128]; ExifEntry *ee; // Name ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE); if ( ee ) { exif_entry_get_value ( ee, str, 128 ); *name = g_strdup ( str ); } // Finished with EXIF exif_data_free ( ed ); } #endif #endif vik_waypoint_set_image ( wp, filename ); return wp; }
/** * a_geotag_get_position: * * @filename: The (JPG) file with EXIF information in it * * Returns: The position in LatLon format. * It will be 0,0 if some kind of failure occurs. */ struct LatLon a_geotag_get_position ( const gchar *filename ) { struct LatLon ll = { 0.0, 0.0 }; #ifdef HAVE_LIBGEXIV2 GExiv2Metadata *gemd = gexiv2_metadata_new (); if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) { gdouble lat; gdouble lon; gdouble alt; if ( gexiv2_metadata_get_gps_info ( gemd, &lon, &lat, &alt ) ) { ll.lat = lat; ll.lon = lon; } } metadata_free ( gemd ); #else #ifdef HAVE_LIBEXIF // open image with libexif ExifData *ed = exif_data_new_from_file ( filename ); // Detect EXIF load failure if ( !ed ) return ll; ExifEntry *ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID); // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0" if ( ! ( ee && ee->components == 4 ) ) goto MyReturn0; ll = get_latlon ( ed ); MyReturn0: // Finished with EXIF exif_data_free ( ed ); #endif #endif return ll; }
int streamFile(shout_t *shout, const char *fileName) { FILE *filepstream = NULL; int popenFlag = 0; char *songLenStr = NULL; int isStdin = 0; int ret, retval = 0; long songLen; metadata_t *mdata; struct timeval startTime; if ((filepstream = openResource(shout, fileName, &popenFlag, &mdata, &isStdin, &songLen)) == NULL) { if (++resource_errors > 100) { printf("%s: Too many errors -- giving up.\n", __progname); return (0); } /* Continue with next resource on failure: */ return (1); } resource_errors = 0; if (mdata != NULL) { char *tmp, *metaData; tmp = metadata_assemble_string(mdata); if ((metaData = UTF8toCHAR(tmp, ICONV_REPLACE)) == NULL) metaData = xstrdup("(unknown title)"); xfree(tmp); printf("%s: Streaming ``%s''", __progname, metaData); if (vFlag) printf(" (file: %s)\n", fileName); else printf("\n"); xfree(metaData); /* MP3 streams are special, so set the metadata explicitly: */ if (strcmp(pezConfig->format, MP3_FORMAT) == 0) setMetadata(shout, mdata, NULL); metadata_free(&mdata); } else if (isStdin) printf("%s: Streaming from standard input\n", __progname); if (songLen > 0) songLenStr = xstrdup(getTimeString(songLen)); ez_gettimeofday((void *)&startTime); do { ret = sendStream(shout, filepstream, fileName, isStdin, songLenStr, &startTime); if (quit) break; if (ret != STREAM_DONE) { if ((skipTrack && rereadPlaylist) || (skipTrack && queryMetadata)) { skipTrack = 0; ret = STREAM_CONT; } if (queryMetadata && rereadPlaylist) { queryMetadata = 0; ret = STREAM_CONT; } if (ret == STREAM_SKIP || skipTrack) { skipTrack = 0; if (!isStdin && vFlag) printf("%s: SIGUSR1 signal received, skipping current track\n", __progname); retval = 1; ret = STREAM_DONE; } if (ret == STREAM_UPDMDATA || queryMetadata) { queryMetadata = 0; if (mFlag) continue; if (metadataFromProgram) { char *mdataStr = NULL; metadata_t *prog_mdata; if (vFlag > 1) printf("%s: Querying '%s' for fresh metadata\n", __progname, pezConfig->metadataProgram); if ((prog_mdata = getMetadata(pezConfig->metadataProgram)) == NULL) { retval = 0; ret = STREAM_DONE; continue; } if (setMetadata(shout, prog_mdata, &mdataStr) != SHOUTERR_SUCCESS) { retval = 0; ret = STREAM_DONE; continue; } metadata_free(&prog_mdata); if (vFlag > 1) printf("%s: New metadata: ``%s''\n", __progname, mdataStr); xfree(mdataStr); } } if (ret == STREAM_SERVERR) { retval = 0; ret = STREAM_DONE; } } else retval = 1; } while (ret != STREAM_DONE); if (popenFlag) pclose(filepstream); else if (!isStdin) fclose(filepstream); if (songLenStr != NULL) xfree(songLenStr); return (retval); }
FILE * openResource(shout_t *shout, const char *fileName, int *popenFlag, metadata_t **mdata_p, int *isStdin, long *songLen) { FILE *filep = NULL; char extension[25]; char *p = NULL; char *pCommandString = NULL; metadata_t *mdata; if (mdata_p != NULL) *mdata_p = NULL; if (songLen != NULL) *songLen = 0; if (strcmp(fileName, "stdin") == 0) { if (metadataFromProgram) { if ((mdata = getMetadata(pezConfig->metadataProgram)) == NULL) return (NULL); if (setMetadata(shout, mdata, NULL) != SHOUTERR_SUCCESS) { metadata_free(&mdata); return (NULL); } if (mdata_p != NULL) *mdata_p = mdata; else metadata_free(&mdata); } if (isStdin != NULL) *isStdin = 1; #ifdef WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif filep = stdin; return (filep); } if (isStdin != NULL) *isStdin = 0; extension[0] = '\0'; p = strrchr(fileName, '.'); if (p != NULL) strlcpy(extension, p, sizeof(extension)); for (p = extension; *p != '\0'; p++) *p = tolower((int)*p); if (strlen(extension) == 0) { printf("%s: Error: Cannot determine file type of '%s'\n", __progname, fileName); return (filep); } if (metadataFromProgram) { if ((mdata = getMetadata(pezConfig->metadataProgram)) == NULL) return (NULL); } else { if ((mdata = getMetadata(fileName)) == NULL) return (NULL); } if (songLen != NULL) *songLen = metadata_get_length(mdata); *popenFlag = 0; if (pezConfig->reencode) { int stderr_fd = -1; pCommandString = buildCommandString(extension, fileName, mdata); if (mdata_p != NULL) *mdata_p = mdata; else metadata_free(&mdata); if (vFlag > 1) printf("%s: Running command `%s`\n", __progname, pCommandString); if (qFlag) { int fd; stderr_fd = dup(fileno(stderr)); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) { printf("%s: Cannot open %s for redirecting STDERR output: %s\n", __progname, _PATH_DEVNULL, strerror(errno)); exit(1); } dup2(fd, fileno(stderr)); if (fd > 2) close(fd); } fflush(NULL); errno = 0; if ((filep = popen(pCommandString, "r")) == NULL) { printf("%s: popen(): Error while executing '%s'", __progname, pCommandString); /* popen() does not set errno reliably ... */ if (errno) printf(": %s\n", strerror(errno)); else printf("\n"); } else { *popenFlag = 1; #ifdef WIN32 _setmode(_fileno(filep), _O_BINARY ); #endif } xfree(pCommandString); if (qFlag) dup2(stderr_fd, fileno(stderr)); if (stderr_fd > 2) close(stderr_fd); return (filep); } if (mdata_p != NULL) *mdata_p = mdata; else metadata_free(&mdata); if ((filep = fopen(fileName, "rb")) == NULL) printf("%s: %s: %s\n", __progname, fileName, strerror(errno)); return (filep); }
/** * a_geotag_write_exif_gps: * @filename: The image file to save information in * @coord: The location * @alt: The elevation * @direction: The image direction (if NAN then these direction fields are not written) * @direction_ref: The image direction value type * * Returns: A value indicating success: 0, or some other value for failure * */ gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt, gdouble direction, VikWaypointImageDirectionRef direction_ref, gboolean no_change_mtime ) { gint result = 0; // OK so far... // Save mtime for later use struct stat stat_save; if ( no_change_mtime ) if ( stat ( filename, &stat_save ) != 0 ) g_warning ( "%s couldn't read: %s", __FUNCTION__, filename ); #ifdef HAVE_LIBGEXIV2 GExiv2Metadata *gemd = gexiv2_metadata_new (); if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) { struct LatLon ll; vik_coord_to_latlon ( &coord, &ll ); if ( ! gexiv2_metadata_set_gps_info ( gemd, ll.lon, ll.lat, alt ) ) { result = 1; // Failed } else { if ( !isnan(direction) ) { gint nom = (gint)round(direction * 10.0); gboolean set_d = gexiv2_metadata_set_exif_tag_rational ( gemd, EXIF_GPS_IMGDIR, nom, 10 ); if ( !set_d ) result = 1; // Failed gboolean set_r = gexiv2_metadata_set_tag_string ( gemd, EXIF_GPS_IMGDIR_REF, direction_ref == WP_IMAGE_DIRECTION_REF_TRUE ? "T" : "M" ); if ( !set_r ) result = 1; // Failed } // Still OK to save - no fails yet if ( result == 0 ) { GError *error = NULL; if ( ! gexiv2_metadata_save_file ( gemd, filename, &error ) ) { result = 2; g_warning ( "Write EXIF failure:%s" , error->message ); g_error_free ( error ); } } } } metadata_free ( gemd ); #else #ifdef HAVE_LIBEXIF /* Appears libexif doesn't actually support writing EXIF data directly to files Thus embed command line exif writing method within Viking (for example this is done by Enlightment - http://www.enlightenment.org/ ) This appears to be JPEG only, but is probably 99% of our use case Alternatively consider using libexiv2 and C++... */ // Actual EXIF settings here... JPEGData *jdata; /* Parse the JPEG file. */ jdata = jpeg_data_new (); jpeg_data_load_file (jdata, filename); // Get current values ExifData *ed = exif_data_new_from_file ( filename ); if ( !ed ) ed = exif_data_new (); // Update ExifData with our new settings ExifEntry *ee; // // I don't understand it, but when saving the 'ed' nothing gets set after putting in the GPS ID tag - so it must come last // (unless of course there is some bug in the setting of the ID, that prevents subsequent tags) // ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE, EXIF_IFD_GPS); convert_to_entry ( NULL, alt, ee, exif_data_get_byte_order(ed) ); // byte 0 meaning "sea level" or 1 if the value is negative. ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_IFD_GPS); convert_to_entry ( alt < 0.0 ? "1" : "0", 0.0, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_IFD_GPS); // see http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html convert_to_entry ( "MANUAL", 0.0, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_MAP_DATUM, EXIF_IFD_GPS); convert_to_entry ( "WGS-84", 0.0, ee, exif_data_get_byte_order(ed) ); struct LatLon ll; vik_coord_to_latlon ( &coord, &ll ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS); // N or S convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE, EXIF_IFD_GPS); convert_to_entry ( NULL, ll.lat, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS); // E or W convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE, EXIF_IFD_GPS); convert_to_entry ( NULL, ll.lon, ee, exif_data_get_byte_order(ed) ); ee = my_exif_create_value (ed, EXIF_TAG_GPS_VERSION_ID, EXIF_IFD_GPS); //convert_to_entry ( "2 0 0 0", 0.0, ee, exif_data_get_byte_order(ed) ); convert_to_entry ( "2 2 0 0", 0.0, ee, exif_data_get_byte_order(ed) ); jpeg_data_set_exif_data (jdata, ed); if ( jdata ) { /* Save the modified image. */ result = jpeg_data_save_file (jdata, filename); // Convert result from 1 for success, 0 for failure into our scheme result = !result; jpeg_data_unref (jdata); } else { // Epic fail - file probably not a JPEG result = 2; } exif_data_free ( ed ); #endif #endif if ( no_change_mtime ) { // Restore mtime, using the saved value struct stat stat_tmp; struct utimbuf utb; (void)stat ( filename, &stat_tmp ); utb.actime = stat_tmp.st_atime; utb.modtime = stat_save.st_mtime; // Not security critical, thus potential Time of Check Time of Use race condition is not bad // coverity[toctou] if ( g_utime ( filename, &utb ) != 0 ) g_warning ( "%s couldn't set time on: %s", __FUNCTION__, filename ); } return result; }
/** * a_geotag_create_waypoint_from_file: * @filename: The image file to process * @vcmode: The current location mode to use in the positioning of Waypoint * @name: Returns a name for the Waypoint (can be NULL) * * Returns: An allocated Waypoint or NULL if Waypoint could not be generated (e.g. no EXIF info) * */ VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoordMode vcmode, gchar **name ) { // Default return values (for failures) *name = NULL; VikWaypoint *wp = NULL; #ifdef HAVE_LIBGEXIV2 GExiv2Metadata *gemd = gexiv2_metadata_new (); if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) { gdouble lat; gdouble lon; gdouble alt; if ( gexiv2_metadata_get_gps_info ( gemd, &lon, &lat, &alt ) ) { struct LatLon ll; ll.lat = lat; ll.lon = lon; // // Now create Waypoint with acquired information // wp = vik_waypoint_new(); wp->visible = TRUE; // Set info from exif values // Location vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll ); // Altitude wp->altitude = alt; if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPTitle" ) ) *name = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPTitle" ) ); wp->comment = geotag_get_exif_comment ( gemd ); // Direction VikWaypointImageDirectionRef ref = WP_IMAGE_DIRECTION_REF_TRUE; if ( gexiv2_metadata_has_tag ( gemd, EXIF_GPS_IMGDIR_REF ) ) { gchar* ref_str = gexiv2_metadata_get_tag_interpreted_string(gemd, EXIF_GPS_IMGDIR_REF); if ( ref_str && g_ascii_strncasecmp ("M", ref_str, 1) == 0 ) ref = WP_IMAGE_DIRECTION_REF_MAGNETIC; g_free ( ref_str ); } if ( gexiv2_metadata_has_tag ( gemd, EXIF_GPS_IMGDIR ) ) { gint nom; gint den; gdouble direction = NAN; if ( gexiv2_metadata_get_exif_tag_rational (gemd, EXIF_GPS_IMGDIR, &nom, &den) ) if ( den != 0 ) direction = (gdouble)nom/(gdouble)den; if ( !isnan(direction) ) vik_waypoint_set_image_direction_info ( wp, direction, ref ); } vik_waypoint_set_image ( wp, filename ); } } metadata_free ( gemd ); #else #ifdef HAVE_LIBEXIF // TODO use log? //ExifLog *log = NULL; // open image with libexif ExifData *ed = exif_data_new_from_file ( filename ); // Detect EXIF load failure if ( !ed ) // return with no Waypoint return wp; struct LatLon ll; gchar str[128]; ExifEntry *ee; ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID); // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0" if ( ! ( ee && ee->components == 4 ) ) goto MyReturn; // Could test for these versions explicitly but may have byte order issues... //if ( ! ( ee->data[0] == 2 && ee->data[2] == 0 && ee->data[3] == 0 ) ) // goto MyReturn; ll = get_latlon ( ed ); // Hopefully won't have valid images at 0,0! if ( ll.lat == 0.0 && ll.lon == 0.0 ) goto MyReturn; // // Not worried if none of the other fields exist, as can default the values to something // gdouble alt = VIK_DEFAULT_ALTITUDE; ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE); if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_RATIONAL ) { alt = Rational2Double ( ee->data, 0, exif_data_get_byte_order(ed) ); ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE_REF); if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_BYTE && ee->data[0] == 1 ) alt = -alt; } // Name ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE); if ( ee ) { exif_entry_get_value ( ee, str, 128 ); *name = g_strdup ( str ); } // // Now create Waypoint with acquired information // wp = vik_waypoint_new(); wp->visible = TRUE; // Set info from exif values // Location vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll ); // Altitude wp->altitude = alt; wp->comment = geotag_get_exif_comment ( ed ); vik_waypoint_set_image ( wp, filename ); MyReturn: // Finished with EXIF exif_data_free ( ed ); #endif #endif return wp; }