static void compare_shutter_speed (ExifEntry * entry, ExifTagCheckData * testdata) { gdouble gst_num, exif_num; ExifSRational rational; GValue exif_value = { 0 }; const GValue *gst_value = NULL; gst_value = gst_tag_list_get_value_index (testdata->taglist, GST_TAG_CAPTURING_SHUTTER_SPEED, 0); if (gst_value == NULL) { GST_WARNING ("Failed to get shutter-speed from taglist"); return; } rational = exif_get_srational (entry->data, exif_data_get_byte_order (entry->parent->parent)); g_value_init (&exif_value, GST_TYPE_FRACTION); gst_value_set_fraction (&exif_value, rational.numerator, rational.denominator); gst_util_fraction_to_double (gst_value_get_fraction_numerator (&exif_value), gst_value_get_fraction_denominator (&exif_value), &exif_num); g_value_unset (&exif_value); gst_util_fraction_to_double (gst_value_get_fraction_numerator (gst_value), gst_value_get_fraction_denominator (gst_value), &gst_num); exif_num = pow (2, -exif_num); GST_LOG ("Shutter speed in gst=%lf and in exif=%lf", gst_num, exif_num); fail_unless (ABS (gst_num - exif_num) < 0.001); testdata->result = TRUE; }
/** * gst_video_time_code_to_date_time: * @tc: #GstVideoTimeCode to convert * * The @tc.config->latest_daily_jam is required to be non-NULL. * * Returns: the #GDateTime representation of @tc. * * Since: 1.10 */ GDateTime * gst_video_time_code_to_date_time (const GstVideoTimeCode * tc) { GDateTime *ret; GDateTime *ret2; gdouble add_us; g_return_val_if_fail (gst_video_time_code_is_valid (tc), NULL); g_return_val_if_fail (tc->config.latest_daily_jam != NULL, NULL); ret = g_date_time_ref (tc->config.latest_daily_jam); if (ret == NULL) { gchar *tc_str = gst_video_time_code_to_string (tc); GST_WARNING ("Asked to convert time code %s to GDateTime, but its latest daily jam is NULL", tc_str); g_free (tc_str); return NULL; } if (tc->config.fps_n == 0 && tc->config.fps_d == 1) { gchar *tc_str = gst_video_time_code_to_string (tc); GST_WARNING ("Asked to convert time code %s to GDateTime, but its framerate is unknown", tc_str); g_free (tc_str); return NULL; } gst_util_fraction_to_double (tc->frames * tc->config.fps_d, tc->config.fps_n, &add_us); if ((tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_INTERLACED) && tc->field_count == 1) { gdouble sub_us; gst_util_fraction_to_double (tc->config.fps_d, 2 * tc->config.fps_n, &sub_us); add_us -= sub_us; } ret2 = g_date_time_add_seconds (ret, add_us + tc->seconds); g_date_time_unref (ret); ret = g_date_time_add_minutes (ret2, tc->minutes); g_date_time_unref (ret2); ret2 = g_date_time_add_hours (ret, tc->hours); g_date_time_unref (ret); return ret2; }
static void compare_aperture_value (ExifEntry * entry, ExifTagCheckData * testdata) { gdouble gst_value, exif_value; ExifSRational rational; GValue value = { 0 }; if (!gst_tag_list_get_double_index (testdata->taglist, GST_TAG_CAPTURING_FOCAL_RATIO, 0, &gst_value)) { GST_WARNING ("Failed to get focal ratio from taglist"); return; } rational = exif_get_srational (entry->data, exif_data_get_byte_order (entry->parent->parent)); g_value_init (&value, GST_TYPE_FRACTION); gst_value_set_fraction (&value, rational.numerator, rational.denominator); gst_util_fraction_to_double (gst_value_get_fraction_numerator (&value), gst_value_get_fraction_denominator (&value), &exif_value); g_value_unset (&value); exif_value = pow (2, exif_value / 2); GST_LOG ("Aperture value in gst=%lf and in exif=%lf", gst_value, exif_value); fail_unless (ABS (gst_value - exif_value) < 0.001); testdata->result = TRUE; }
static void parse_exif_rational_tag (GstExifReader * exif_reader, const gchar * gst_tag, guint32 count, guint32 offset, gdouble multiplier) { GstByteReader data_reader; guint32 real_offset; guint32 frac_n = 0; guint32 frac_d = 1; gdouble value; if (count > 1) { GST_WARNING ("Rationals with multiple entries are not supported"); } if (offset < exif_reader->base_offset) { GST_WARNING ("Offset is smaller (%u) than base offset (%u)", offset, exif_reader->base_offset); return; } real_offset = offset - exif_reader->base_offset; if (real_offset >= GST_BUFFER_SIZE (exif_reader->buffer)) { GST_WARNING ("Invalid offset %u for buffer of size %u, not adding tag %s", real_offset, GST_BUFFER_SIZE (exif_reader->buffer), gst_tag); return; } gst_byte_reader_init_from_buffer (&data_reader, exif_reader->buffer); if (!gst_byte_reader_set_pos (&data_reader, real_offset)) goto reader_fail; if (exif_reader->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint32_le (&data_reader, &frac_n) || !gst_byte_reader_get_uint32_le (&data_reader, &frac_d)) goto reader_fail; } else { if (!gst_byte_reader_get_uint32_be (&data_reader, &frac_n) || !gst_byte_reader_get_uint32_be (&data_reader, &frac_d)) goto reader_fail; } GST_DEBUG ("Read fraction for tag %s: %u/%u", gst_tag, frac_n, frac_d); gst_util_fraction_to_double (frac_n, frac_d, &value); value *= multiplier; GST_DEBUG ("Adding %s tag: %lf", gst_tag, value); gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, gst_tag, value, NULL); return; reader_fail: GST_WARNING ("Failed to read from byte reader. (Buffer too short?)"); }
static void init_params (GstMfxFilter * filter) { gdouble frame_rate; filter->params.vpp.In = filter->frame_info; /* Aligned frame dimensions may differ between input and output surfaces * so we sanitize the input frame dimensions, since output frame dimensions * could have certain alignment requirements used in HEVC HW encoding */ if (filter->shared_request[1]) { filter->params.vpp.In.Width = GST_ROUND_UP_16 (filter->frame_info.CropW); filter->params.vpp.In.Height = (MFX_PICSTRUCT_PROGRESSIVE == filter->frame_info.PicStruct) ? GST_ROUND_UP_16 (filter->frame_info.CropH) : GST_ROUND_UP_32 (filter->frame_info.CropH); } filter->params.vpp.Out = filter->frame_info; if (filter->fourcc) filter->params.vpp.Out.FourCC = filter->fourcc; if (filter->width) { filter->params.vpp.Out.CropW = filter->width; filter->params.vpp.Out.Width = GST_ROUND_UP_16 (filter->width); } if (filter->height) { filter->params.vpp.Out.CropH = filter->height; filter->params.vpp.Out.Height = (MFX_PICSTRUCT_PROGRESSIVE == filter->frame_info.PicStruct) ? GST_ROUND_UP_16 (filter->height) : GST_ROUND_UP_32 (filter->height); } if (filter->filter_op & GST_MFX_FILTER_DEINTERLACING) { /* Setup special double frame rate deinterlace mode */ gst_util_fraction_to_double (filter->params.vpp.In.FrameRateExtN, filter->params.vpp.In.FrameRateExtD, &frame_rate); if ((filter->frame_info.PicStruct == MFX_PICSTRUCT_FIELD_TFF || filter->frame_info.PicStruct == MFX_PICSTRUCT_FIELD_BFF) && (int)(frame_rate + 0.5) == 60) filter->params.vpp.In.FrameRateExtN /= 2; filter->params.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; } if (filter->filter_op & GST_MFX_FILTER_FRAMERATE_CONVERSION && (filter->fps_n && filter->fps_d)) { filter->params.vpp.Out.FrameRateExtN = filter->fps_n; filter->params.vpp.Out.FrameRateExtD = filter->fps_d; } configure_filters (filter); }
/** * gst_video_time_code_frames_since_daily_jam: * @tc: a #GstVideoTimeCode * * Returns: how many frames have passed since the daily jam of @tc . * * Since: 1.10 */ guint64 gst_video_time_code_frames_since_daily_jam (const GstVideoTimeCode * tc) { guint ff_nom; gdouble ff; g_return_val_if_fail (gst_video_time_code_is_valid (tc), -1); g_assert (tc->hours <= 24); g_assert (tc->minutes < 60); g_assert (tc->seconds < 60); g_assert (tc->frames <= tc->config.fps_n / tc->config.fps_d); gst_util_fraction_to_double (tc->config.fps_n, tc->config.fps_d, &ff); if (tc->config.fps_d == 1001) { ff_nom = tc->config.fps_n / 1000; } else { ff_nom = ff; } if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) { /* these need to be truncated to integer: side effect, code looks cleaner * */ guint ff_minutes = 60 * ff; guint ff_hours = 3600 * ff; /* for 30000/1001 we drop the first 2 frames per minute, for 60000/1001 we * drop the first 4 : so we use this number */ guint dropframe_multiplier; if (tc->config.fps_n == 30000) { dropframe_multiplier = 2; } else if (tc->config.fps_n == 60000) { dropframe_multiplier = 4; } else { GST_ERROR ("Unsupported drop frame rate %u/%u", tc->config.fps_n, tc->config.fps_d); return -1; } return tc->frames + (ff_nom * tc->seconds) + (ff_minutes * tc->minutes) + dropframe_multiplier * ((gint) (tc->minutes / 10)) + (ff_hours * tc->hours); } else { return tc->frames + (ff_nom * (tc->seconds + (60 * (tc->minutes + (60 * tc->hours))))); } }
/* Return the possible output caps based on inputs and downstream prefs */ static GstCaps * _update_caps (GstVideoAggregator * vagg, GstCaps * caps) { GstGLStereoMix *mix = GST_GL_STEREO_MIX (vagg); GList *l; gint best_width = -1, best_height = -1; gdouble best_fps = -1, cur_fps; gint best_fps_n = 0, best_fps_d = 1; GstVideoInfo *mix_info; GstCaps *blend_caps, *tmp_caps; GstCaps *out_caps; GST_OBJECT_LOCK (vagg); for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { GstVideoAggregatorPad *pad = l->data; GstVideoInfo tmp = pad->info; gint this_width, this_height; gint fps_n, fps_d; if (!pad->info.finfo) continue; /* This can happen if we release a pad and another pad hasn't been negotiated_caps yet */ if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN) continue; /* Convert to per-view width/height for unpacked forms */ gst_video_multiview_video_info_change_mode (&tmp, GST_VIDEO_MULTIVIEW_MODE_SEPARATED, GST_VIDEO_MULTIVIEW_FLAGS_NONE); this_width = GST_VIDEO_INFO_WIDTH (&tmp); this_height = GST_VIDEO_INFO_HEIGHT (&tmp); fps_n = GST_VIDEO_INFO_FPS_N (&tmp); fps_d = GST_VIDEO_INFO_FPS_D (&tmp); GST_INFO_OBJECT (vagg, "Input pad %" GST_PTR_FORMAT " w %u h %u", pad, this_width, this_height); if (this_width == 0 || this_height == 0) continue; if (best_width < this_width) best_width = this_width; if (best_height < this_height) best_height = this_height; if (fps_d == 0) cur_fps = 0.0; else gst_util_fraction_to_double (fps_n, fps_d, &cur_fps); if (best_fps < cur_fps) { best_fps = cur_fps; best_fps_n = fps_n; best_fps_d = fps_d; } /* FIXME: Preserve PAR for at least one input when different sized inputs */ } GST_OBJECT_UNLOCK (vagg); mix_info = &mix->mix_info; gst_video_info_set_format (mix_info, GST_VIDEO_FORMAT_RGBA, best_width, best_height); GST_VIDEO_INFO_FPS_N (mix_info) = best_fps_n; GST_VIDEO_INFO_FPS_D (mix_info) = best_fps_d; GST_VIDEO_INFO_MULTIVIEW_MODE (mix_info) = GST_VIDEO_MULTIVIEW_MODE_SEPARATED; GST_VIDEO_INFO_VIEWS (mix_info) = 2; /* FIXME: If input is marked as flipped or flopped, preserve those flags */ GST_VIDEO_INFO_MULTIVIEW_FLAGS (mix_info) = GST_VIDEO_MULTIVIEW_FLAGS_NONE; /* Choose our output format based on downstream preferences */ blend_caps = gst_video_info_to_caps (mix_info); gst_caps_set_features (blend_caps, 0, gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY)); tmp_caps = get_converted_caps (GST_GL_STEREO_MIX (vagg), blend_caps); gst_caps_unref (blend_caps); out_caps = gst_caps_intersect (caps, tmp_caps); gst_caps_unref (tmp_caps); GST_DEBUG_OBJECT (vagg, "Possible output caps %" GST_PTR_FORMAT, out_caps); return out_caps; }
static gint deserialize_geo_coordinate (GstExifReader * exif_reader, GstByteReader * reader, const GstExifTagMatch * exiftag, GstExifTagData * tagdata) { GstByteReader fractions_reader; gint multiplier; GstExifTagData next_tagdata; gint ret = 0; /* for the conversion */ guint32 degrees_n = 0; guint32 degrees_d = 1; guint32 minutes_n = 0; guint32 minutes_d = 1; guint32 seconds_n = 0; guint32 seconds_d = 1; gdouble degrees; gdouble minutes; gdouble seconds; GST_LOG ("Starting to parse %s tag in exif 0x%x", exiftag->gst_tag, exiftag->exif_tag); if (exiftag->complementary_tag != tagdata->tag) { /* First should come the 'Ref' tags */ GST_WARNING ("Tag %d is not the 'Ref' tag for latitude nor longitude", tagdata->tag); return ret; } if (tagdata->offset_as_data[0] == 'N' || tagdata->offset_as_data[0] == 'E') { multiplier = 1; } else if (tagdata->offset_as_data[0] == 'S' || tagdata->offset_as_data[0] == 'W') { multiplier = -1; } else { GST_WARNING ("Invalid LatitudeRef or LongitudeRef %c", tagdata->offset_as_data[0]); return ret; } /* now read the following tag that must be the latitude or longitude */ if (exif_reader->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_peek_uint16_le (reader, &next_tagdata.tag)) goto reader_fail; } else { if (!gst_byte_reader_peek_uint16_be (reader, &next_tagdata.tag)) goto reader_fail; } if (exiftag->exif_tag != next_tagdata.tag) { GST_WARNING ("This is not a geo cordinate tag"); return ret; } /* read the remaining tag entry data */ if (!parse_exif_tag_header (reader, exif_reader->byte_order, &next_tagdata)) { ret = -1; goto reader_fail; } ret = 1; /* some checking */ if (next_tagdata.tag_type != EXIF_TYPE_RATIONAL) { GST_WARNING ("Invalid type %d for geo coordinate (latitude/longitude)", next_tagdata.tag_type); return ret; } if (next_tagdata.count != 3) { GST_WARNING ("Geo coordinate should use 3 fractions, we have %u", next_tagdata.count); return ret; } /* now parse the fractions */ gst_byte_reader_init_from_buffer (&fractions_reader, exif_reader->buffer); if (!gst_byte_reader_set_pos (&fractions_reader, next_tagdata.offset - exif_reader->base_offset)) goto reader_fail; if (exif_reader->byte_order == G_LITTLE_ENDIAN) { if (!gst_byte_reader_get_uint32_le (&fractions_reader, °rees_n) || !gst_byte_reader_get_uint32_le (&fractions_reader, °rees_d) || !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_n) || !gst_byte_reader_get_uint32_le (&fractions_reader, &minutes_d) || !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_n) || !gst_byte_reader_get_uint32_le (&fractions_reader, &seconds_d)) goto reader_fail; } else { if (!gst_byte_reader_get_uint32_be (&fractions_reader, °rees_n) || !gst_byte_reader_get_uint32_be (&fractions_reader, °rees_d) || !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_n) || !gst_byte_reader_get_uint32_be (&fractions_reader, &minutes_d) || !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_n) || !gst_byte_reader_get_uint32_be (&fractions_reader, &seconds_d)) goto reader_fail; } GST_DEBUG ("Read degrees fraction for tag %s: %u/%u %u/%u %u/%u", exiftag->gst_tag, degrees_n, degrees_d, minutes_n, minutes_d, seconds_n, seconds_d); gst_util_fraction_to_double (degrees_n, degrees_d, °rees); gst_util_fraction_to_double (minutes_n, minutes_d, &minutes); gst_util_fraction_to_double (seconds_n, seconds_d, &seconds); minutes += seconds / 60; degrees += minutes / 60; degrees *= multiplier; GST_DEBUG ("Adding %s tag: %lf", exiftag->gst_tag, degrees); gst_tag_list_add (exif_reader->taglist, GST_TAG_MERGE_REPLACE, exiftag->gst_tag, degrees, NULL); return ret; reader_fail: GST_WARNING ("Failed to read fields from buffer (too short?)"); return ret; }
/** * gst_video_time_code_add_frames: * @tc: a #GstVideoTimeCode * @frames: How many frames to add or subtract * * Adds or subtracts @frames amount of frames to @tc . * * Since: 1.10 */ void gst_video_time_code_add_frames (GstVideoTimeCode * tc, gint64 frames) { guint64 framecount; guint64 h_notmod24; guint64 h_new, min_new, sec_new, frames_new; gdouble ff; guint ff_nom; /* This allows for better readability than putting G_GUINT64_CONSTANT(60) * into a long calculation line */ const guint64 sixty = 60; /* formulas found in SMPTE ST 2059-1:2015 section 9.4.3 * and adapted for 60/1.001 as well as 30/1.001 */ g_return_if_fail (gst_video_time_code_is_valid (tc)); g_assert (tc->hours <= 24); g_assert (tc->minutes < 60); g_assert (tc->seconds < 60); g_assert (tc->frames <= tc->config.fps_n / tc->config.fps_d); gst_util_fraction_to_double (tc->config.fps_n, tc->config.fps_d, &ff); if (tc->config.fps_d == 1001) { ff_nom = tc->config.fps_n / 1000; } else { ff_nom = ff; if (tc->config.fps_d != 1) GST_WARNING ("Unsupported frame rate %u/%u, results may be wrong", tc->config.fps_n, tc->config.fps_d); } if (tc->config.flags & GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME) { /* these need to be truncated to integer: side effect, code looks cleaner * */ guint ff_minutes = 60 * ff; guint ff_hours = 3600 * ff; /* a bunch of intermediate variables, to avoid monster code with possible * integer overflows */ guint64 min_new_tmp1, min_new_tmp2, min_new_tmp3, min_new_denom; /* for 30000/1001 we drop the first 2 frames per minute, for 60000/1001 we * drop the first 4 : so we use this number */ guint dropframe_multiplier; if (tc->config.fps_n == 30000) dropframe_multiplier = 2; else if (tc->config.fps_n == 60000) dropframe_multiplier = 4; else { GST_ERROR ("Unsupported drop frame rate %u/%u", tc->config.fps_n, tc->config.fps_d); return; } framecount = frames + tc->frames + (ff_nom * tc->seconds) + (ff_minutes * tc->minutes) + dropframe_multiplier * ((gint) (tc->minutes / 10)) + (ff_hours * tc->hours); h_notmod24 = gst_util_uint64_scale_int (framecount, 1, ff_hours); min_new_denom = sixty * ff_nom; min_new_tmp1 = (framecount - (h_notmod24 * ff_hours)) / min_new_denom; min_new_tmp2 = framecount + dropframe_multiplier * min_new_tmp1; min_new_tmp1 = (framecount - (h_notmod24 * ff_hours)) / (sixty * 10 * ff_nom); min_new_tmp3 = dropframe_multiplier * min_new_tmp1 + (h_notmod24 * ff_hours); min_new = gst_util_uint64_scale_int (min_new_tmp2 - min_new_tmp3, 1, min_new_denom); sec_new = (guint64) ((framecount - (ff_minutes * min_new) - dropframe_multiplier * ((gint) (min_new / 10)) - (ff_hours * h_notmod24)) / ff_nom); frames_new = framecount - (ff_nom * sec_new) - (ff_minutes * min_new) - (dropframe_multiplier * ((gint) (min_new / 10))) - (ff_hours * h_notmod24); } else { framecount = frames + tc->frames + (ff_nom * (tc->seconds + (sixty * (tc->minutes + (sixty * tc->hours))))); h_notmod24 = gst_util_uint64_scale_int (framecount, 1, ff_nom * sixty * sixty); min_new = gst_util_uint64_scale_int ((framecount - (ff_nom * sixty * sixty * h_notmod24)), 1, (ff_nom * sixty)); sec_new = gst_util_uint64_scale_int ((framecount - (ff_nom * sixty * (min_new + (sixty * h_notmod24)))), 1, ff_nom); frames_new = framecount - (ff_nom * (sec_new + sixty * (min_new + (sixty * h_notmod24)))); if (frames_new > ff_nom) frames_new = 0; } h_new = h_notmod24 % 24; g_assert (min_new < 60); g_assert (sec_new < 60); g_assert (frames_new < ff_nom); tc->hours = h_new; tc->minutes = min_new; tc->seconds = sec_new; tc->frames = frames_new; }
static void deserialize_exif_gps_direction (GstTagList * taglist, const gchar * gst_tag, const gchar * xmp_tag, const gchar * str, GSList ** pending_tags, const gchar * direction_tag, const gchar * directionref_tag) { const gchar *dir_str = NULL; const gchar *dirref_str = NULL; gint frac_n; gint frac_d; gdouble value; GSList *entry; PendingXmpTag *ptag = NULL; /* find the other missing part */ if (strcmp (xmp_tag, direction_tag) == 0) { dir_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, directionref_tag) == 0) { dirref_str = ptag->str; break; } } } else if (strcmp (xmp_tag, directionref_tag) == 0) { dirref_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, direction_tag) == 0) { dir_str = ptag->str; break; } } } else { GST_WARNING ("Unexpected xmp tag %s", xmp_tag); return; } if (!dir_str) { GST_WARNING ("Missing %s tag", dir_str); return; } if (!dirref_str) { GST_WARNING ("Missing %s tag", dirref_str); return; } if (sscanf (dir_str, "%d/%d", &frac_n, &frac_d) != 2) { GST_WARNING ("Failed to parse fraction: %s", dir_str); return; } gst_util_fraction_to_double (frac_n, frac_d, &value); if (dirref_str[0] == 'T') { /* nop */ } else if (dirref_str[0] == 'M') { GST_WARNING ("Magnetic direction tags aren't supported yet"); return; } else { GST_WARNING ("Unexpected %s value: %s", directionref_tag, dirref_str); return; } /* add to the taglist */ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, gst_tag, value, NULL); /* clean up entry */ g_free (ptag->str); g_slice_free (PendingXmpTag, ptag); *pending_tags = g_slist_delete_link (*pending_tags, entry); }
static void deserialize_exif_gps_speed (GstTagList * taglist, const gchar * gst_tag, const gchar * xmp_tag, const gchar * str, GSList ** pending_tags) { const gchar *speed_str = NULL; const gchar *speedref_str = NULL; gint frac_n; gint frac_d; gdouble value; GSList *entry; PendingXmpTag *ptag = NULL; /* find the other missing part */ if (strcmp (xmp_tag, "exif:GPSSpeed") == 0) { speed_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, "exif:GPSSpeedRef") == 0) { speedref_str = ptag->str; break; } } } else if (strcmp (xmp_tag, "exif:GPSSpeedRef") == 0) { speedref_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, "exif:GPSSpeed") == 0) { speed_str = ptag->str; break; } } } else { GST_WARNING ("Unexpected xmp tag %s", xmp_tag); return; } if (!speed_str) { GST_WARNING ("Missing exif:GPSSpeed tag"); return; } if (!speedref_str) { GST_WARNING ("Missing exif:GPSSpeedRef tag"); return; } if (sscanf (speed_str, "%d/%d", &frac_n, &frac_d) != 2) { GST_WARNING ("Failed to parse fraction: %s", speed_str); return; } gst_util_fraction_to_double (frac_n, frac_d, &value); if (speedref_str[0] == 'K') { value *= KILOMETERS_PER_HOUR_TO_METERS_PER_SECOND; } else if (speedref_str[0] == 'M') { value *= MILES_PER_HOUR_TO_METERS_PER_SECOND; } else if (speedref_str[0] == 'N') { value *= KNOTS_TO_METERS_PER_SECOND; } else { GST_WARNING ("Unexpected exif:SpeedRef value: %s", speedref_str); return; } /* add to the taglist */ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, value, NULL); /* clean up entry */ g_free (ptag->str); g_slice_free (PendingXmpTag, ptag); *pending_tags = g_slist_delete_link (*pending_tags, entry); }
static void deserialize_exif_altitude (GstTagList * taglist, const gchar * gst_tag, const gchar * xmp_tag, const gchar * str, GSList ** pending_tags) { const gchar *altitude_str = NULL; const gchar *altituderef_str = NULL; gint frac_n; gint frac_d; gdouble value; GSList *entry; PendingXmpTag *ptag = NULL; /* find the other missing part */ if (strcmp (xmp_tag, "exif:GPSAltitude") == 0) { altitude_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, "exif:GPSAltitudeRef") == 0) { altituderef_str = ptag->str; break; } } } else if (strcmp (xmp_tag, "exif:GPSAltitudeRef") == 0) { altituderef_str = str; for (entry = *pending_tags; entry; entry = g_slist_next (entry)) { ptag = (PendingXmpTag *) entry->data; if (strcmp (ptag->xmp_tag->tag_name, "exif:GPSAltitude") == 0) { altitude_str = ptag->str; break; } } } else { GST_WARNING ("Unexpected xmp tag %s", xmp_tag); return; } if (!altitude_str) { GST_WARNING ("Missing exif:GPSAltitude tag"); return; } if (!altituderef_str) { GST_WARNING ("Missing exif:GPSAltitudeRef tag"); return; } if (sscanf (altitude_str, "%d/%d", &frac_n, &frac_d) != 2) { GST_WARNING ("Failed to parse fraction: %s", altitude_str); return; } gst_util_fraction_to_double (frac_n, frac_d, &value); if (altituderef_str[0] == '0') { /* nop */ } else if (altituderef_str[0] == '1') { value *= -1; } else { GST_WARNING ("Unexpected exif:AltitudeRef value: %s", altituderef_str); return; } /* add to the taglist */ gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_GEO_LOCATION_ELEVATION, value, NULL); /* clean up entry */ g_free (ptag->str); g_slice_free (PendingXmpTag, ptag); *pending_tags = g_slist_delete_link (*pending_tags, entry); }