exiftool_vendored 12.81.0 → 12.83.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +39 -1
  3. data/bin/MANIFEST +4 -18
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +3 -2
  7. data/bin/build_geolocation +867 -0
  8. data/bin/exiftool +37 -9
  9. data/bin/fmt_files/gpx.fmt +2 -1
  10. data/bin/fmt_files/gpx_wpt.fmt +2 -1
  11. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +47 -31
  12. data/bin/lib/Image/ExifTool/CanonVRD.pm +6 -5
  13. data/bin/lib/Image/ExifTool/DJI.pm +29 -0
  14. data/bin/lib/Image/ExifTool/Exif.pm +19 -2
  15. data/bin/lib/Image/ExifTool/FujiFilm.pm +9 -2
  16. data/bin/lib/Image/ExifTool/GM.pm +552 -0
  17. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  18. data/bin/lib/Image/ExifTool/Geolocation.pm +90 -54
  19. data/bin/lib/Image/ExifTool/Nikon.pm +9 -7
  20. data/bin/lib/Image/ExifTool/QuickTime.pm +31 -23
  21. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +66 -30
  22. data/bin/lib/Image/ExifTool/README +2 -0
  23. data/bin/lib/Image/ExifTool/Sony.pm +15 -6
  24. data/bin/lib/Image/ExifTool/TagLookup.pm +37 -6
  25. data/bin/lib/Image/ExifTool/TagNames.pod +407 -239
  26. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +5 -2
  27. data/bin/lib/Image/ExifTool/Writer.pl +165 -133
  28. data/bin/lib/Image/ExifTool/XMP.pm +3 -2
  29. data/bin/lib/Image/ExifTool/XMP2.pl +3 -0
  30. data/bin/lib/Image/ExifTool.pm +38 -9
  31. data/bin/lib/Image/ExifTool.pod +31 -18
  32. data/bin/perl-Image-ExifTool.spec +1 -1
  33. data/lib/exiftool_vendored/version.rb +1 -1
  34. metadata +4 -20
  35. data/bin/lib/Image/ExifTool/GeoLang/cs.pm +0 -978
  36. data/bin/lib/Image/ExifTool/GeoLang/de.pm +0 -1975
  37. data/bin/lib/Image/ExifTool/GeoLang/en_ca.pm +0 -44
  38. data/bin/lib/Image/ExifTool/GeoLang/en_gb.pm +0 -124
  39. data/bin/lib/Image/ExifTool/GeoLang/es.pm +0 -2921
  40. data/bin/lib/Image/ExifTool/GeoLang/fi.pm +0 -1116
  41. data/bin/lib/Image/ExifTool/GeoLang/fr.pm +0 -3171
  42. data/bin/lib/Image/ExifTool/GeoLang/it.pm +0 -2750
  43. data/bin/lib/Image/ExifTool/GeoLang/ja.pm +0 -10256
  44. data/bin/lib/Image/ExifTool/GeoLang/ko.pm +0 -4499
  45. data/bin/lib/Image/ExifTool/GeoLang/nl.pm +0 -1270
  46. data/bin/lib/Image/ExifTool/GeoLang/pl.pm +0 -3019
  47. data/bin/lib/Image/ExifTool/GeoLang/ru.pm +0 -18220
  48. data/bin/lib/Image/ExifTool/GeoLang/sk.pm +0 -441
  49. data/bin/lib/Image/ExifTool/GeoLang/sv.pm +0 -714
  50. data/bin/lib/Image/ExifTool/GeoLang/tr.pm +0 -452
  51. data/bin/lib/Image/ExifTool/GeoLang/zh_cn.pm +0 -2225
  52. data/bin/lib/Image/ExifTool/GeoLang/zh_tw.pm +0 -72
@@ -9,14 +9,15 @@
9
9
  #
10
10
  # References: https://download.geonames.org/export/
11
11
  #
12
- # Notes: Set $Image::ExifTool::Geolocation::geoDir to override
13
- # default directory for the database file Geolocation.dat
14
- # and language directory GeoLang.
12
+ # Notes: Set $Image::ExifTool::Geolocation::geoDir to override the
13
+ # default directory containing the database file Geolocation.dat
14
+ # and the GeoLang directory with the alternate language files.
15
+ # If set, this directory is
15
16
  #
16
- # Set $Image::ExifTool::Geolocation::altDir to use a database
17
- # of alternate city names. The file is called AltNames.dat
18
- # with entries in the same order as Geolocation.dat. Each
19
- # entry is a newline-separated list of alternate names
17
+ # AltNames.dat may be loaded from a different directory by
18
+ # specifying $Image::ExifTool::Geolocation::altDir. This
19
+ # database and has entries in the same order as Geolocation.dat,
20
+ # and each entry is a newline-separated list of alternate names
20
21
  # terminated by a null byte.
21
22
  #
22
23
  # Databases are based on data from geonames.org with a
@@ -34,9 +35,10 @@
34
35
  # 5 int8u - index of country in country list
35
36
  # 6 int8u - 0xf0 = population E exponent (in format "N.Fe+0E"), 0x0f = population N digit
36
37
  # 7 int16u - 0xf000 = population F digit, 0x0fff = index in region list (admin1)
37
- # 9 int16u - 0x7fff = index in subregion (admin2), 0x8000 = high bit of time zone
38
+ # 9 int16u - v1.02: 0x7fff = index in subregion (admin2), 0x8000 = high bit of time zone
39
+ # 9 int16u - v1.03: index in subregion (admin2)
38
40
  # 11 int8u - low byte of time zone index
39
- # 12 int8u - 0x0f - feature code index (see below)
41
+ # 12 int8u - 0x0f = feature code index (see below), v1.03: 0x80 = high bit of time zone
40
42
  # 13 string - UTF8 City name, terminated by newline
41
43
  # "\0\0\0\0\x01"
42
44
  # Country entries:
@@ -51,11 +53,14 @@
51
53
  # "\0\0\0\0\x04"
52
54
  # Time zone entries:
53
55
  # 1. Time zone name, terminated by newline
56
+ # "\0\0\0\0\x05" (feature codes added in v1.03)
57
+ # Feature codes:
58
+ # 1. Feature code, terminated by newline
54
59
  # "\0\0\0\0\0"
55
60
  #
56
- # Feature Codes: (see http://www.geonames.org/export/codes.html#P for descriptions)
61
+ # Feature Codes (v1.02): (see http://www.geonames.org/export/codes.html#P for descriptions)
57
62
  #
58
- # 0. Other 3. PPLA2 6. PPLA5 9. PPLF 12. PPLR
63
+ # 0. Other 3. PPLA2 6. PPLA5 9. PPLF 12. PPLR 15. PPLX
59
64
  # 1. PPL 4. PPLA3 7. PPLC 10. PPLG 13. PPLS
60
65
  # 2. PPLA 5. PPLA4 8. PPLCH 11. PPLL 14. STLMT
61
66
  #------------------------------------------------------------------------------
@@ -65,9 +70,7 @@ package Image::ExifTool::Geolocation;
65
70
  use strict;
66
71
  use vars qw($VERSION $geoDir $altDir $dbInfo);
67
72
 
68
- $VERSION = '1.03';
69
-
70
- my $databaseVersion = '1.02';
73
+ $VERSION = '1.06'; # (this is the module version number, not the database version)
71
74
 
72
75
  my $debug; # set to output processing time for testing
73
76
 
@@ -79,16 +82,15 @@ sub Geolocate($;$$$$$);
79
82
 
80
83
  my (@cityList, @countryList, @regionList, @subregionList, @timezoneList);
81
84
  my (%countryNum, %regionNum, %subregionNum, %timezoneNum); # reverse lookups
82
- my (@sortOrder, @altNames, %langLookup, $nCity);
85
+ my (@sortOrder, @altNames, %langLookup, $nCity, %featureCodes);
83
86
  my ($lastArgs, %lastFound, @lastByPop, @lastByLat); # cached city matches
87
+ my $dbVer = '1.03';
84
88
  my $sortedBy = 'Latitude';
85
89
  my $pi = 3.1415926536;
86
90
  my $earthRadius = 6371; # earth radius in km
87
-
88
- my @featureCodes = qw(Other PPL PPLA PPLA2 PPLA3 PPLA4 PPLA5
89
- PPLC PPLCH PPLF PPLG PPLL PPLR PPLS STLMT ?);
90
- my $i = 0;
91
- my %featureCodes = map { lc($_) => $i++ } @featureCodes;
91
+ # hard-coded feature codes for v1.02 database
92
+ my @featureCodes = qw(Other PPL PPLA PPLA2 PPLA3 PPLA4 PPLA5 PPLC
93
+ PPLCH PPLF PPLG PPLL PPLR PPLS STLMT PPLX);
92
94
 
93
95
  # get path name for database file from lib/Image/ExifTool/Geolocation.dat by default,
94
96
  # or according to $Image::ExifTool::Geolocation::directory if specified
@@ -107,12 +109,10 @@ unless (defined $geoDir and not $geoDir) {
107
109
  }
108
110
  }
109
111
 
110
- # set directory for language files
111
- my $geoLang;
112
- if ($geoDir and -d "$geoDir/GeoLang") {
113
- $geoLang = "$geoDir/GeoLang";
114
- } elsif ($geoDir or not defined $geoDir) {
115
- $geoLang = "$defaultDir/GeoLang";
112
+ # set directory for language files and alternate names
113
+ $geoDir = $defaultDir unless defined $geoDir;
114
+ if (not defined $altDir and $geoDir and -e "$geoDir/AltNames.dat") {
115
+ $altDir = $geoDir;
116
116
  }
117
117
 
118
118
  # add user-defined entries to the database
@@ -136,16 +136,16 @@ sub ReadDatabase($)
136
136
  close(DATFILE);
137
137
  return 0;
138
138
  }
139
- if ($1 != $databaseVersion) {
140
- my $which = $1 < $databaseVersion ? 'database' : 'ExifTool';
139
+ ($dbVer, $nCity) = ($1, $2);
140
+ if ($dbVer !~ /^1\.0[23]$/) {
141
+ my $which = $dbVer < 1.03 ? 'database' : 'ExifTool';
141
142
  warn("Incompatible Geolocation database (update your $which)\n");
142
143
  close(DATFILE);
143
144
  return 0;
144
145
  }
145
- $nCity = $2;
146
146
  my $comment = <DATFILE>;
147
- defined $comment and $comment =~ /(\d+)/ or close(DATFILE), return 0;
148
- $dbInfo = "$datfile v$databaseVersion: $nCity cities with population > $1";
147
+ defined $comment and $comment =~ / (\d+) / or close(DATFILE), return 0;
148
+ $dbInfo = "$datfile v$dbVer: $nCity cities with population > $1";
149
149
  my $isUserDefined = @Image::ExifTool::UserDefined::Geolocation;
150
150
 
151
151
  undef @altNames; # reset altNames
@@ -193,7 +193,20 @@ sub ReadDatabase($)
193
193
  push @timezoneList, $line;
194
194
  $timezoneNum{lc $line} = $#timezoneList if $isUserDefined;
195
195
  }
196
+ # read feature codes if available
197
+ if ($line eq "\0\0\0\0\x05\n") {
198
+ undef @featureCodes;
199
+ for (;;) {
200
+ $line = <DATFILE>;
201
+ last if length($line) == 6 and $line =~ /\0\0\0\0/;
202
+ chomp $line;
203
+ push @featureCodes, $line;
204
+ }
205
+ }
196
206
  close DATFILE;
207
+ # initialize featureCodes lookup
208
+ $i = 0;
209
+ %featureCodes = map { lc($_) => $i++ } @featureCodes;
197
210
  return 1;
198
211
  }
199
212
 
@@ -268,11 +281,12 @@ sub SortDatabase($)
268
281
  # Add cities to the Geolocation database
269
282
  # Inputs: 0-8) city,region,subregion,country_code,country,timezone,feature_code,population,lat,lon,altNames
270
283
  # eg. AddEntry('Sinemorets','Burgas','Obshtina Tsarevo','BG','Bulgaria','Europe/Sofia','',400,42.06115,27.97833)
284
+ # Returns: true on success, otherwise issues warning
271
285
  sub AddEntry(@)
272
286
  {
273
287
  my ($city, $region, $subregion, $cc, $country, $timezone, $fc, $pop, $lat, $lon, $altNames) = @_;
274
- @_ < 10 and warn("Too few arguments in $city definition (check for updated format)\n"), return;
275
- length($cc) != 2 and warn("Country code '${cc}' is not 2 characters\n"), return;
288
+ @_ < 10 and warn("Too few arguments in $city definition (check for updated format)\n"), return 0;
289
+ length($cc) != 2 and warn("Country code '${cc}' is not 2 characters\n"), return 0;
276
290
  $fc = $featureCodes{lc $fc} || 0;
277
291
  chomp $lon; # (just in case it was read from file)
278
292
  # create reverse lookups for country/region/subregion/timezone if not done already
@@ -286,6 +300,7 @@ sub AddEntry(@)
286
300
  }
287
301
  my $cn = $countryNum{lc $cc};
288
302
  unless (defined $cn) {
303
+ $#countryList >= 0xff and warn("AddEntry: Too many countries\n"), return 0;
289
304
  push @countryList, "$cc$country";
290
305
  $cn = $countryNum{lc $cc} = $#countryList;
291
306
  } elsif ($country) {
@@ -293,16 +308,20 @@ sub AddEntry(@)
293
308
  }
294
309
  my $tn = $timezoneNum{lc $timezone};
295
310
  unless (defined $tn) {
311
+ $#timezoneList >= 0x1ff and warn("AddEntry: Too many time zones\n"), return 0;
296
312
  push @timezoneList, $timezone;
297
313
  $tn = $timezoneNum{lc $timezone} = $#timezoneList;
298
314
  }
299
315
  my $rn = $regionNum{lc $region};
300
316
  unless (defined $rn) {
317
+ $#regionList >= 0xfff and warn("AddEntry: Too many regions\n"), return 0;
301
318
  push @regionList, $region;
302
319
  $rn = $regionNum{lc $region} = $#regionList;
303
320
  }
304
321
  my $sn = $subregionNum{lc $subregion};
305
322
  unless (defined $sn) {
323
+ my $max = $dbVer eq '1.02' ? 0x0fff : 0xffff;
324
+ $#subregionList >= $max and warn("AddEntry: Too many subregions\n"), return 0;
306
325
  push @subregionList, $subregion;
307
326
  $sn = $subregionNum{lc $subregion} = $#subregionList;
308
327
  }
@@ -310,7 +329,14 @@ sub AddEntry(@)
310
329
  # pack CC index, population and region index into a 32-bit integer
311
330
  my $code = ($cn << 24) | (substr($pop,-1,1)<<20) | (substr($pop,0,1)<<16) | (substr($pop,2,1)<<12) | $rn;
312
331
  # store high bit of timezone index
313
- $tn > 255 and $sn |= 0x8000, $tn -= 256;
332
+ if ($tn > 255) {
333
+ if ($dbVer eq '1.02') {
334
+ $sn |= 0x8000;
335
+ } else {
336
+ $fc |= 0x80;
337
+ }
338
+ $tn -= 256;
339
+ }
314
340
  $lat = int(($lat + 90) / 180 * 0x100000 + 0.5) & 0xfffff;
315
341
  $lon = int(($lon + 180) / 360 * 0x100000 + 0.5) & 0xfffff;
316
342
  my $hdr = pack('nCnNnCC', $lat>>4, (($lat&0x0f)<<4)|($lon&0x0f), $lon>>4, $code, $sn, $tn, $fc);
@@ -328,6 +354,7 @@ sub AddEntry(@)
328
354
  }
329
355
  $sortedBy = '';
330
356
  undef $lastArgs; # (faster than ClearLastArgs)
357
+ return 1;
331
358
  }
332
359
 
333
360
  #------------------------------------------------------------------------------
@@ -341,24 +368,28 @@ sub GetEntry($;$$)
341
368
  my ($entryNum, $lang, $sort) = @_;
342
369
  return() if $entryNum > $#cityList;
343
370
  $entryNum = $sortOrder[$entryNum] if $sort and @sortOrder > $entryNum;
344
- my ($lt,$f,$ln,$code,$sb,$tn,$fc) = unpack('nCnNnCC', $cityList[$entryNum]);
371
+ my ($lt,$f,$ln,$code,$sn,$tn,$fc) = unpack('nCnNnCC', $cityList[$entryNum]);
345
372
  my $city = substr($cityList[$entryNum],13);
346
373
  my $ctry = $countryList[$code >> 24];
347
374
  my $rgn = $regionList[$code & 0x0fff];
348
- my $sub = $subregionList[$sb & 0x7fff];
375
+ if ($dbVer eq '1.02') {
376
+ $sn & 0x8000 and $tn += 256, $sn &= 0x7fff;
377
+ } else {
378
+ $fc & 0x80 and $tn += 256;
379
+ }
380
+ my $sub = $subregionList[$sn];
349
381
  # convert population digits back into exponent format
350
382
  my $pop = (($code>>16 & 0x0f) . '.' . ($code>>12 & 0x0f) . 'e+' . ($code>>20 & 0x0f)) + 0;
351
- $tn += 256 if $sb & 0x8000;
352
383
  $lt = sprintf('%.4f', (($lt<<4)|($f >> 4)) * 180 / 0x100000 - 90);
353
384
  $ln = sprintf('%.4f', (($ln<<4)|($f & 0x0f))* 360 / 0x100000 - 180);
354
- $fc = $featureCodes[$fc & 0x0f];
385
+ $fc = $featureCodes[$fc & 0x1f];
355
386
  my $cc = substr($ctry, 0, 2);
356
387
  my $country = substr($ctry, 2);
357
388
  if ($lang) {
358
389
  my $xlat = $langLookup{$lang};
359
390
  # load language lookups if not done already
360
391
  if (not defined $xlat) {
361
- if (eval "require '$geoLang/$lang.pm'") {
392
+ if (eval "require '$geoDir/GeoLang/$lang.pm'") {
362
393
  my $trans = "Image::ExifTool::GeoLang::${lang}::Translate";
363
394
  no strict 'refs';
364
395
  $xlat = \%$trans if %$trans;
@@ -511,9 +542,10 @@ Entry: for (; $i<@cityList; ++$i) {
511
542
  if ($regex{8}) { $cty =~ $_ or next Entry foreach @{$regex{8}} }
512
543
  if ($regex{18}) { $cty !~ $_ or next Entry foreach @{$regex{18}} }
513
544
  # test other arguments
514
- my ($cd,$sb) = unpack('x5Nn', $cityList[$i]);
545
+ my ($cd,$sn) = unpack('x5Nn', $cityList[$i]);
515
546
  my $ct = $countryList[$cd >> 24];
516
- my @geo = (substr($ct,0,2), substr($ct,2), $regionList[$cd & 0x0fff], $subregionList[$sb & 0x7fff]);
547
+ $sn &= 0x7fff if $dbVer eq '1.02';
548
+ my @geo = (substr($ct,0,2), substr($ct,2), $regionList[$cd & 0x0fff], $subregionList[$sn]);
517
549
  if (@exact) {
518
550
  # make quick lookup for all names at this location
519
551
  my %geoLkup;
@@ -672,10 +704,10 @@ True on success.
672
704
  =head2 ReadAltNames
673
705
 
674
706
  Load the alternate names database. Before calling this method the $altDir
675
- package variable must be set to a directory containing the AltNames.dat
676
- database that matches the current Geolocation.dat. This method is called
677
- automatically by L</Geolocate> if $altDir is set and the GeolocAltNames
678
- option is used and an input city name is provided.
707
+ package variable may be set, otherwise AltNames.dat is loaded from the same
708
+ directory as Geolocation.dat. This method is called automatically by
709
+ L</Geolocate> if the GeolocAltNames option is used and an input city name is
710
+ provided.
679
711
 
680
712
  Image::ExifTool::Geolocation::ReadAltNames();
681
713
 
@@ -687,8 +719,8 @@ option is used and an input city name is provided.
687
719
 
688
720
  =item Return Value:
689
721
 
690
- True on success. Resets the value of $altDir to prevent further attempts at
691
- re-loading the same database.
722
+ True on success. May be called repeatedly, but AltNames.dat is loaded only
723
+ on the first call.
692
724
 
693
725
  =back
694
726
 
@@ -746,6 +778,10 @@ database, then the database entry is updated with the new country name.
746
778
 
747
779
  10) Optional comma-separated list of alternate names for the city
748
780
 
781
+ =item Return Value:
782
+
783
+ 1 on success, otherwise sends a warning message to stderr
784
+
749
785
  =back
750
786
 
751
787
  =head2 GetEntry
@@ -810,8 +846,7 @@ Comma-separated string of alternate names for this city.
810
846
 
811
847
  =item Notes:
812
848
 
813
- Must set the $altDir package variable and call L</ReadAltNames> before
814
- calling this routine.
849
+ L</ReadAltNames> must be called before calling this routine.
815
850
 
816
851
  =back
817
852
 
@@ -873,11 +908,12 @@ contain the Geolocation.dat file, and optionally a GeoLang directory for the
873
908
  language translations. The $geoDir variable may be set to an empty string
874
909
  to disable loading of a database.
875
910
 
876
- A database of alternate city names may be loaded by setting the package
877
- $altDir variable. This directory should contain the AltNames.dat database
878
- that matches the version of Geolocation.dat being used. When searching for
879
- a city by name, the alternate-names database is checked to provide
880
- additional possibilities for matches.
911
+ When searching for a city by name, AltNames.dat is checked to provide
912
+ additional possibilities for matches if the GeolocAltNames option is set.
913
+ The package $altDir variable may be set to specify a different directory for
914
+ AltNames.dat, otherwise the Geolocation.dat directory is assumed. The
915
+ entries in AltNames.dat must match those in the currently loaded version of
916
+ Geolocation.dat.
881
917
 
882
918
  =head1 ADDING USER-DEFINED DATABASE ENTRIES
883
919
 
@@ -65,7 +65,7 @@ use Image::ExifTool::Exif;
65
65
  use Image::ExifTool::GPS;
66
66
  use Image::ExifTool::XMP;
67
67
 
68
- $VERSION = '4.31';
68
+ $VERSION = '4.33';
69
69
 
70
70
  sub LensIDConv($$$);
71
71
  sub ProcessNikonAVI($$$);
@@ -678,6 +678,7 @@ sub GetAFPointGrid($$;$);
678
678
  'FD 00 50 50 18 18 DF 00' => 'Voigtlander APO-Lanthar 50mm F2 Aspherical', #35
679
679
  'FD 00 44 44 18 18 DF 00' => 'Voigtlander APO-Lanthar 35mm F2', #30
680
680
  'FD 00 59 59 18 18 DF 00' => 'Voigtlander Macro APO-Lanthar 65mm F2', #30
681
+ 'FD 00 48 48 07 07 DF 00' => 'Voigtlander Nokton 40mm F1.2 Aspherical', #30
681
682
  #
682
683
  '00 40 2D 2D 2C 2C 00 00' => 'Carl Zeiss Distagon T* 3.5/18 ZF.2',
683
684
  '00 48 27 27 24 24 00 00' => 'Carl Zeiss Distagon T* 2.8/15 ZF.2', #MykytaKozlov
@@ -5498,20 +5499,21 @@ my %nikonFocalConversions = (
5498
5499
  28 => 'Nikkor Z 100-400mm f/4.5-5.6 VR S', #28
5499
5500
  29 => 'Nikkor Z 28mm f/2.8', #IB
5500
5501
  30 => 'Nikkor Z 400mm f/2.8 TC VR S', #28
5501
- 31 => 'Nikkor Z 24-120 f/4', #28
5502
+ 31 => 'Nikkor Z 24-120mm f/4 S', #github#250
5502
5503
  32 => 'Nikkor Z 800mm f/6.3 VR S', #28
5503
5504
  35 => 'Nikkor Z 28-75mm f/2.8', #IB
5504
5505
  36 => 'Nikkor Z 400mm f/4.5 VR S', #IB
5505
5506
  37 => 'Nikkor Z 600mm f/4 TC VR S', #28
5506
5507
  38 => 'Nikkor Z 85mm f/1.2 S', #28
5507
5508
  39 => 'Nikkor Z 17-28mm f/2.8', #IB
5508
- 40 => 'NIKKOR Z 26mm f/2.8', #28
5509
- 41 => 'NIKKOR Z DX 12-28mm f/3.5-5.6 PZ VR', #28
5509
+ 40 => 'Nikkor Z 26mm f/2.8', #28
5510
+ 41 => 'Nikkor Z DX 12-28mm f/3.5-5.6 PZ VR', #28
5510
5511
  42 => 'Nikkor Z 180-600mm f/5.6-6.3 VR', #30
5511
- 43 => 'NIKKOR Z DX 24mm f/1.7', #28
5512
- 44 => 'NIKKOR Z 70-180mm f/2.8', #28
5513
- 45 => 'NIKKOR Z 600mm f/6.3 VR S', #28
5512
+ 43 => 'Nikkor Z DX 24mm f/1.7', #28
5513
+ 44 => 'Nikkor Z 70-180mm f/2.8', #28
5514
+ 45 => 'Nikkor Z 600mm f/6.3 VR S', #28
5514
5515
  46 => 'Nikkor Z 135mm f/1.8 S Plena', #28
5516
+ 48 => 'Nikkor Z 28-400mm f/4-8 VR', #30
5515
5517
  32768 => 'Nikkor Z 400mm f/2.8 TC VR S TC-1.4x', #28
5516
5518
  32769 => 'Nikkor Z 600mm f/4 TC VR S TC-1.4x', #28
5517
5519
  },
@@ -48,7 +48,7 @@ use Image::ExifTool qw(:DataAccess :Utils);
48
48
  use Image::ExifTool::Exif;
49
49
  use Image::ExifTool::GPS;
50
50
 
51
- $VERSION = '2.94';
51
+ $VERSION = '2.96';
52
52
 
53
53
  sub ProcessMOV($$;$);
54
54
  sub ProcessKeys($$$);
@@ -59,6 +59,9 @@ sub ProcessSampleDesc($$$);
59
59
  sub ProcessHybrid($$$);
60
60
  sub ProcessRights($$$);
61
61
  sub ProcessNextbase($$$);
62
+ sub Process_mrlh($$$);
63
+ sub Process_mrlv($$$);
64
+ sub Process_mrld($$$);
62
65
  # ++vvvvvvvvvvvv++ (in QuickTimeStream.pl)
63
66
  sub Process_mebx($$$);
64
67
  sub Process_3gf($$$);
@@ -6491,6 +6494,9 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
6491
6494
  },
6492
6495
  ownr => 'Owner', #PH (obscure) (ref ChrisAdan private communication)
6493
6496
  'xid ' => 'ISRC', #PH
6497
+ # found in DJI Osmo Action4 video
6498
+ tnal => { Name => 'ThumbnailImage', Groups => { 2 => 'Preview' } },
6499
+ snal => { Name => 'PreviewImage', Groups => { 2 => 'Preview' } },
6494
6500
  );
6495
6501
 
6496
6502
  # tag decoded from timed face records
@@ -7870,20 +7876,9 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
7870
7876
  #
7871
7877
  ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7872
7878
  name => { Name => 'OtherName', Format => 'undef', ValueConv => 'substr($val, 4)' },
7873
- # mrlh = GM header?
7874
- # mrlv = GM data
7875
- # mrld = GM data (448-byte records):
7876
- # 0 - int32u count
7877
- # 4 - int32u ? (related to units) 0=none,1=m/km,2=L,3=kph,4=C,7=deg,8=rpm,9=kPa,10=G,11=V,15=Nm,16=%
7878
- # 8 - int32u ? (0,1,3,4,5)
7879
- # 12 - string[64] units
7880
- # 76 - int32u ? (1,3,7,15)
7881
- # 80 - int32u 0
7882
- # 84 - undef[4] ?
7883
- # 88 - int16u[6] ?
7884
- # 100 - undef[32] ?
7885
- # 132 - string[64] measurement name
7886
- # 196 - string[64] measurement name
7879
+ mrlh => { Name => 'MarlinHeader', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlh' } },
7880
+ mrlv => { Name => 'MarlinValues', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlv' } },
7881
+ mrld => { Name => 'MarlinDictionary',SubDirectory => { TagTable => 'Image::ExifTool::GM::mrld' } },
7887
7882
  );
7888
7883
 
7889
7884
  # MP4 data information box (ref 5)
@@ -9478,7 +9473,7 @@ sub ProcessMOV($$;$)
9478
9473
  my $dirID = $$dirInfo{DirID} || '';
9479
9474
  my $charsetQuickTime = $et->Options('CharsetQuickTime');
9480
9475
  my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9481
- my ($dirEnd, $unkOpt, %saveOptions, $atomCount);
9476
+ my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr);
9482
9477
 
9483
9478
  my $topLevel = not $$et{InQuickTime};
9484
9479
  $$et{InQuickTime} = 1;
@@ -9558,6 +9553,7 @@ sub ProcessMOV($$;$)
9558
9553
  $index = $$tagTablePtr{VARS}{START_INDEX};
9559
9554
  $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9560
9555
  }
9556
+ my $lastTag = '';
9561
9557
  for (;;) {
9562
9558
  my ($eeTag, $ignore);
9563
9559
  last if defined $atomCount and --$atomCount < 0;
@@ -9584,22 +9580,22 @@ sub ProcessMOV($$;$)
9584
9580
  }
9585
9581
  last;
9586
9582
  }
9587
- $size == 1 or $et->Warn('Invalid atom size'), last;
9583
+ $size == 1 or $warnStr = 'Invalid atom size', last;
9588
9584
  # read extended atom size
9589
- $raf->Read($buff, 8) == 8 or $et->Warn('Truncated atom header'), last;
9585
+ $raf->Read($buff, 8) == 8 or $warnStr = 'Truncated atom header', last;
9590
9586
  $dataPos += 8;
9591
9587
  my ($hi, $lo) = unpack('NN', $buff);
9592
9588
  if ($hi or $lo > 0x7fffffff) {
9593
9589
  if ($hi > 0x7fffffff) {
9594
- $et->Warn('Invalid atom size');
9590
+ $warnStr = 'Invalid atom size';
9595
9591
  last;
9596
9592
  } elsif (not $et->Options('LargeFileSupport')) {
9597
- $et->Warn('End of processing at large atom (LargeFileSupport not enabled)');
9593
+ $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
9598
9594
  last;
9599
9595
  }
9600
9596
  }
9601
9597
  $size = $hi * 4294967296 + $lo - 16;
9602
- $size < 0 and $et->Warn('Invalid extended size'), last;
9598
+ $size < 0 and $warnStr = 'Invalid extended size', last;
9603
9599
  } else {
9604
9600
  $size -= 8;
9605
9601
  }
@@ -9755,7 +9751,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
9755
9751
  my $missing = $size - $raf->Read($val, $size);
9756
9752
  if ($missing) {
9757
9753
  my $t = PrintableTagID($tag,2);
9758
- $et->Warn("Truncated '${t}' data (missing $missing bytes)");
9754
+ $warnStr = "Truncated '${t}' data (missing $missing bytes)";
9759
9755
  last;
9760
9756
  }
9761
9757
  # use value to get tag info if necessary
@@ -10070,16 +10066,28 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10070
10066
  ) if $verbose;
10071
10067
  if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
10072
10068
  my $t = PrintableTagID($tag,2);
10073
- $et->Warn("Truncated '${t}' data");
10069
+ $warnStr = "Truncated '${t}' data";
10074
10070
  last;
10075
10071
  }
10076
10072
  }
10077
10073
  $dataPos += $size + 8; # point to start of next atom data
10078
10074
  last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10079
10075
  $raf->Read($buff, 8) == 8 or last;
10076
+ $lastTag = $tag if $$tagTablePtr{$tag};
10080
10077
  ($size, $tag) = unpack('Na4', $buff);
10081
10078
  ++$index if defined $index;
10082
10079
  }
10080
+ if ($warnStr) {
10081
+ # assume this is an unknown trailer if it comes immediately after
10082
+ # mdat or moov and has a tag name we don't recognize
10083
+ if (($lastTag eq 'mdat' or $lastTag eq 'moov') and (not $$tagTablePtr{$tag} or
10084
+ ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10085
+ {
10086
+ $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10087
+ } else {
10088
+ $et->Warn($warnStr);
10089
+ }
10090
+ }
10083
10091
  # tweak file type based on track content ("iso*" and "dash" ftyp only)
10084
10092
  if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
10085
10093
  $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash)/ and
@@ -22,12 +22,12 @@ use Image::ExifTool qw(:DataAccess :Utils);
22
22
  use Image::ExifTool::QuickTime;
23
23
 
24
24
  sub Process_tx3g($$$);
25
- sub Process_marl($$$);
26
25
  sub Process_mebx($$$);
27
26
  sub Process_text($$$;$);
28
27
  sub ProcessFreeGPS($$$);
29
28
  sub Process360Fly($$$);
30
29
  sub ProcessFMAS($$$);
30
+ sub ProcessWolfbox($$$);
31
31
  sub ProcessCAMM($$$);
32
32
 
33
33
  # QuickTime data types that have ExifTool equivalents
@@ -109,7 +109,7 @@ my %insvLimit = (
109
109
  The tags below are extracted from timed metadata in QuickTime and other
110
110
  formats of video files when the ExtractEmbedded option is used. Although
111
111
  most of these tags are combined into the single table below, ExifTool
112
- currently reads 72 different formats of timed GPS metadata from video files.
112
+ currently reads 74 different formats of timed GPS metadata from video files.
113
113
  },
114
114
  VARS => { NO_ID => 1 },
115
115
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -207,6 +207,13 @@ my %insvLimit = (
207
207
  TagTable => 'Image::ExifTool::QuickTime::Stream',
208
208
  ProcessProc => \&ProcessFMAS,
209
209
  },
210
+ },{
211
+ Name => 'gpmd_Wolfbox', # Wolfbox G900 Dashcam
212
+ Condition => '$$valPt =~ /^.{136}0{16}HYTH/s',
213
+ SubDirectory => {
214
+ TagTable => 'Image::ExifTool::QuickTime::Stream',
215
+ ProcessProc => \&ProcessWolfbox,
216
+ },
210
217
  },{
211
218
  Name => 'gpmd_GoPro',
212
219
  SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
@@ -223,7 +230,7 @@ my %insvLimit = (
223
230
  },
224
231
  marl => {
225
232
  Name => 'marl',
226
- SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::marl' },
233
+ SubDirectory => { TagTable => 'Image::ExifTool::GM::marl' },
227
234
  },
228
235
  CTMD => { # (Canon Timed MetaData)
229
236
  Name => 'CTMD',
@@ -326,6 +333,14 @@ my %insvLimit = (
326
333
  Groups => { 0 => 'Trailer', 1 => 'Insta360' }, # (so these groups will appear in the -listg options)
327
334
  SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::INSV_MakerNotes' },
328
335
  },
336
+ ssmd => { # Chigee AIO-5 dashcam
337
+ Name => 'PreviewImage',
338
+ Groups => { 2 => 'Preview' },
339
+ RawConv => '$self->ValidateImage(\$val,$tag)',
340
+ },
341
+ # djmd - DJI AC003 Osmo Action 4 cam
342
+ #TODO djmd => { SubDirectory => { TagTable => 'Image::ExifTool::DJI::djmd', ByteOrder => 'Little-Endian' } },
343
+ # dbgi - DJI AC003 Osmo Action 4 cam -- lots more unknown stuff
329
344
  Unknown00 => { Unknown => 1 },
330
345
  Unknown01 => { Unknown => 1 },
331
346
  Unknown02 => { Unknown => 1 },
@@ -741,13 +756,6 @@ my %insvLimit = (
741
756
  10 => { Name => 'FusionYPR', Format => 'float[3]' },
742
757
  );
743
758
 
744
- # tags found in 'marl' ctbx timed metadata (ref PH)
745
- %Image::ExifTool::QuickTime::marl = (
746
- PROCESS_PROC => \&Process_marl,
747
- GROUPS => { 2 => 'Other' },
748
- NOTES => 'Tags extracted from the marl ctbx timed metadata of GM cars.',
749
- );
750
-
751
759
  #------------------------------------------------------------------------------
752
760
  # Save information from keys in OtherSampleDesc directory for processing timed metadata
753
761
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -2219,26 +2227,6 @@ sub Process_tx3g($$$)
2219
2227
  return 1;
2220
2228
  }
2221
2229
 
2222
- #------------------------------------------------------------------------------
2223
- # Process GM 'marl' ctbx metadata (ref PH)
2224
- # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2225
- # Returns: 1 on success
2226
- sub Process_marl($$$)
2227
- {
2228
- my ($et, $dirInfo, $tagTablePtr) = @_;
2229
- my $dataPt = $$dirInfo{DataPt};
2230
- return 0 if length $$dataPt < 2;
2231
-
2232
- # 8-byte records:
2233
- # byte 0 seems to be tag ID (0=timestamp in sec * 1e7)
2234
- # bytes 1-3 seem to be 24-bit signed integer (unknown meaning)
2235
- # bytes 4-7 are an int32u value, usually a multiple of 10000
2236
-
2237
- $et->WarnOnce("Can't yet decode timed GM data", 1);
2238
- # (see https://exiftool.org/forum/index.php?topic=11335.msg61393#msg61393)
2239
- return 1;
2240
- }
2241
-
2242
2230
  #------------------------------------------------------------------------------
2243
2231
  # Process QuickTime 'mebx' timed metadata
2244
2232
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
@@ -2443,7 +2431,9 @@ sub Process_nbmt($$$)
2443
2431
  $$et{DOC_NUM} = $$et{DOC_COUNT} + 1;
2444
2432
  delete $$et{UnknownTextCount};
2445
2433
  delete $$et{NoMoreTextDecoding};
2434
+ $$et{SET_GROUP1} = 'Nextbase';
2446
2435
  Process_text($et, $dataPt, $tagTbl, 1);
2436
+ delete $$et{SET_GROUP1};
2447
2437
  delete $$et{UnknownTextCount};
2448
2438
  delete $$et{NoMoreTextDecoding};
2449
2439
  delete $$et{DOC_NUM};
@@ -3229,6 +3219,52 @@ sub ProcessFMAS($$$)
3229
3219
  return 1;
3230
3220
  }
3231
3221
 
3222
+ #------------------------------------------------------------------------------
3223
+ # Process GPS from Wolfbox G900 Dashcam
3224
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
3225
+ # Returns: 1 on success
3226
+ sub ProcessWolfbox($$$)
3227
+ {
3228
+ my ($et, $dirInfo, $tagTbl) = @_;
3229
+ my $dataPt = $$dirInfo{DataPt};
3230
+ return 0 if length($$dataPt) < 0xc8;
3231
+ $et->VerboseDir('Wolfbox', undef, length($$dataPt));
3232
+ # 0000: 65 00 00 00 00 00 00 00 31 01 01 00 e3 ff 00 00 [e.......1.......]
3233
+ # 0010: 04 00 00 00 10 00 00 00 2a 00 00 00 00 00 00 00 [........*.......]
3234
+ # 0020: 01 00 00 00 00 00 00 00 8b 33 ff 51 00 00 00 00 [.........3.Q....]
3235
+ # 0030: a0 86 01 00 00 00 00 00 4d 5e 07 fa ff ff ff ff [........M^......]
3236
+ # 0040: a0 86 01 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
3237
+ # 0050: 64 00 00 00 00 00 00 00 90 21 00 00 00 00 00 00 [d........!......]
3238
+ # 0060: 64 00 00 00 00 00 00 00 18 00 00 00 03 00 00 00 [d...............]
3239
+ # 0070: e8 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
3240
+ # 0080: 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 30 [........00000000]
3241
+ # 0090: 30 30 30 30 30 30 30 30 48 59 54 48 00 00 00 00 [00000000HYTH....]
3242
+ # 00a0: 0c 00 00 00 10 00 00 00 2a 00 00 00 00 00 00 00 [........*.......]
3243
+ # 00b0: 4f 3f 0c 1f 00 00 00 00 a0 86 01 00 00 00 00 00 [O?..............]
3244
+ # 00c0: 7f cf 2d ff ff ff ff ff a0 86 01 00 00 00 00 00 [..-.............]
3245
+ # 00d0: 01 00 00 00 08 00 00 00 0a 00 00 00 00 00 00 00 [................]
3246
+ # 00e0: 0a 00 00 00 00 00 00 00 e8 03 00 00 00 00 00 00 [................]
3247
+ # 00f0: 0a 00 00 00 00 00 00 00 4d 00 00 00 00 00 00 00 [........M.......]
3248
+ # lat/lon at 0xb0/0xc0 and 0x128/0x138
3249
+ # h/m/s at 0x10 and 0xa0 and 0x148 (the first imprinted on the video, the latter 2 presumed UTC)
3250
+ # spd at 0x48, dir at 0x58, alt at 0xe8
3251
+ SetByteOrder('II');
3252
+ my ($spd,$dir,$d,$mo,$yr,$h,$m,$s) = unpack('x72Vx12Vx12V3x44V3',$$dataPt);
3253
+ # offset 0xa0 also stores hh mm ss, but is out by 8 hours!
3254
+ my $time = sprintf '%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $yr, $mo, $d, $h, $m, $s;
3255
+ my ($lat, $lon) = (Get32s($dataPt, 0xb0) / 1e5, Get32s($dataPt, 0xc0) / 1e5);
3256
+ my $alt = Get32s($dataPt, 0xe8);
3257
+ ConvertLatLon($lat, $lon);
3258
+ $et->HandleTag($tagTbl, GPSDateTime => $time);
3259
+ $et->HandleTag($tagTbl, GPSLatitude => $lat);
3260
+ $et->HandleTag($tagTbl, GPSLongitude => $lon);
3261
+ $et->HandleTag($tagTbl, GPSSpeed => $spd * $knotsToKph / 100);
3262
+ $et->HandleTag($tagTbl, GPSTrack => $dir / 100);
3263
+ $et->HandleTag($tagTbl, GPSAltitude => $alt / 10); # (NC)
3264
+ SetByteOrder('MM');
3265
+ return 1;
3266
+ }
3267
+
3232
3268
  #------------------------------------------------------------------------------
3233
3269
  # Scan media data for "freeGPS" metadata if not found already (ref PH)
3234
3270
  # Inputs: 0) ExifTool ref