exiftool_vendored 12.81.0 → 12.82.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,9 +34,10 @@
34
34
  # 5 int8u - index of country in country list
35
35
  # 6 int8u - 0xf0 = population E exponent (in format "N.Fe+0E"), 0x0f = population N digit
36
36
  # 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
37
+ # 9 int16u - v1.02: 0x7fff = index in subregion (admin2), 0x8000 = high bit of time zone
38
+ # 9 int16u - v1.03: index in subregion (admin2)
38
39
  # 11 int8u - low byte of time zone index
39
- # 12 int8u - 0x0f - feature code index (see below)
40
+ # 12 int8u - 0x0f = feature code index (see below), v1.03: 0x80 = high bit of time zone
40
41
  # 13 string - UTF8 City name, terminated by newline
41
42
  # "\0\0\0\0\x01"
42
43
  # Country entries:
@@ -55,7 +56,7 @@
55
56
  #
56
57
  # Feature Codes: (see http://www.geonames.org/export/codes.html#P for descriptions)
57
58
  #
58
- # 0. Other 3. PPLA2 6. PPLA5 9. PPLF 12. PPLR
59
+ # 0. Other 3. PPLA2 6. PPLA5 9. PPLF 12. PPLR 15. PPLX
59
60
  # 1. PPL 4. PPLA3 7. PPLC 10. PPLG 13. PPLS
60
61
  # 2. PPLA 5. PPLA4 8. PPLCH 11. PPLL 14. STLMT
61
62
  #------------------------------------------------------------------------------
@@ -65,9 +66,7 @@ package Image::ExifTool::Geolocation;
65
66
  use strict;
66
67
  use vars qw($VERSION $geoDir $altDir $dbInfo);
67
68
 
68
- $VERSION = '1.03';
69
-
70
- my $databaseVersion = '1.02';
69
+ $VERSION = '1.04'; # (this is the module version number, not the database version)
71
70
 
72
71
  my $debug; # set to output processing time for testing
73
72
 
@@ -81,12 +80,13 @@ my (@cityList, @countryList, @regionList, @subregionList, @timezoneList);
81
80
  my (%countryNum, %regionNum, %subregionNum, %timezoneNum); # reverse lookups
82
81
  my (@sortOrder, @altNames, %langLookup, $nCity);
83
82
  my ($lastArgs, %lastFound, @lastByPop, @lastByLat); # cached city matches
83
+ my $dbVer = '1.03';
84
84
  my $sortedBy = 'Latitude';
85
85
  my $pi = 3.1415926536;
86
86
  my $earthRadius = 6371; # earth radius in km
87
87
 
88
- my @featureCodes = qw(Other PPL PPLA PPLA2 PPLA3 PPLA4 PPLA5
89
- PPLC PPLCH PPLF PPLG PPLL PPLR PPLS STLMT ?);
88
+ my @featureCodes = qw(Other PPL PPLA PPLA2 PPLA3 PPLA4 PPLA5 PPLC
89
+ PPLCH PPLF PPLG PPLL PPLR PPLS STLMT PPLX);
90
90
  my $i = 0;
91
91
  my %featureCodes = map { lc($_) => $i++ } @featureCodes;
92
92
 
@@ -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
147
  defined $comment and $comment =~ /(\d+)/ or close(DATFILE), return 0;
148
- $dbInfo = "$datfile v$databaseVersion: $nCity cities with population > $1";
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
@@ -268,11 +268,12 @@ sub SortDatabase($)
268
268
  # Add cities to the Geolocation database
269
269
  # Inputs: 0-8) city,region,subregion,country_code,country,timezone,feature_code,population,lat,lon,altNames
270
270
  # eg. AddEntry('Sinemorets','Burgas','Obshtina Tsarevo','BG','Bulgaria','Europe/Sofia','',400,42.06115,27.97833)
271
+ # Returns: true on success, otherwise issues warning
271
272
  sub AddEntry(@)
272
273
  {
273
274
  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;
275
+ @_ < 10 and warn("Too few arguments in $city definition (check for updated format)\n"), return 0;
276
+ length($cc) != 2 and warn("Country code '${cc}' is not 2 characters\n"), return 0;
276
277
  $fc = $featureCodes{lc $fc} || 0;
277
278
  chomp $lon; # (just in case it was read from file)
278
279
  # create reverse lookups for country/region/subregion/timezone if not done already
@@ -286,6 +287,7 @@ sub AddEntry(@)
286
287
  }
287
288
  my $cn = $countryNum{lc $cc};
288
289
  unless (defined $cn) {
290
+ $#countryList >= 0xff and warn("AddEntry: Too many countries\n"), return 0;
289
291
  push @countryList, "$cc$country";
290
292
  $cn = $countryNum{lc $cc} = $#countryList;
291
293
  } elsif ($country) {
@@ -293,16 +295,20 @@ sub AddEntry(@)
293
295
  }
294
296
  my $tn = $timezoneNum{lc $timezone};
295
297
  unless (defined $tn) {
298
+ $#timezoneList >= 0x1ff and warn("AddEntry: Too many time zones\n"), return 0;
296
299
  push @timezoneList, $timezone;
297
300
  $tn = $timezoneNum{lc $timezone} = $#timezoneList;
298
301
  }
299
302
  my $rn = $regionNum{lc $region};
300
303
  unless (defined $rn) {
304
+ $#regionList >= 0xfff and warn("AddEntry: Too many regions\n"), return 0;
301
305
  push @regionList, $region;
302
306
  $rn = $regionNum{lc $region} = $#regionList;
303
307
  }
304
308
  my $sn = $subregionNum{lc $subregion};
305
309
  unless (defined $sn) {
310
+ my $max = $dbVer eq '1.02' ? 0x0fff : 0xffff;
311
+ $#subregionList >= $max and warn("AddEntry: Too many subregions\n"), return 0;
306
312
  push @subregionList, $subregion;
307
313
  $sn = $subregionNum{lc $subregion} = $#subregionList;
308
314
  }
@@ -310,7 +316,14 @@ sub AddEntry(@)
310
316
  # pack CC index, population and region index into a 32-bit integer
311
317
  my $code = ($cn << 24) | (substr($pop,-1,1)<<20) | (substr($pop,0,1)<<16) | (substr($pop,2,1)<<12) | $rn;
312
318
  # store high bit of timezone index
313
- $tn > 255 and $sn |= 0x8000, $tn -= 256;
319
+ if ($tn > 255) {
320
+ if ($dbVer eq '1.02') {
321
+ $sn |= 0x8000;
322
+ } else {
323
+ $fc |= 0x80;
324
+ }
325
+ $tn -= 256;
326
+ }
314
327
  $lat = int(($lat + 90) / 180 * 0x100000 + 0.5) & 0xfffff;
315
328
  $lon = int(($lon + 180) / 360 * 0x100000 + 0.5) & 0xfffff;
316
329
  my $hdr = pack('nCnNnCC', $lat>>4, (($lat&0x0f)<<4)|($lon&0x0f), $lon>>4, $code, $sn, $tn, $fc);
@@ -328,6 +341,7 @@ sub AddEntry(@)
328
341
  }
329
342
  $sortedBy = '';
330
343
  undef $lastArgs; # (faster than ClearLastArgs)
344
+ return 1;
331
345
  }
332
346
 
333
347
  #------------------------------------------------------------------------------
@@ -341,14 +355,18 @@ sub GetEntry($;$$)
341
355
  my ($entryNum, $lang, $sort) = @_;
342
356
  return() if $entryNum > $#cityList;
343
357
  $entryNum = $sortOrder[$entryNum] if $sort and @sortOrder > $entryNum;
344
- my ($lt,$f,$ln,$code,$sb,$tn,$fc) = unpack('nCnNnCC', $cityList[$entryNum]);
358
+ my ($lt,$f,$ln,$code,$sn,$tn,$fc) = unpack('nCnNnCC', $cityList[$entryNum]);
345
359
  my $city = substr($cityList[$entryNum],13);
346
360
  my $ctry = $countryList[$code >> 24];
347
361
  my $rgn = $regionList[$code & 0x0fff];
348
- my $sub = $subregionList[$sb & 0x7fff];
362
+ if ($dbVer eq '1.02') {
363
+ $sn & 0x8000 and $tn += 256, $sn &= 0x7fff;
364
+ } else {
365
+ $fc & 0x80 and $tn += 256;
366
+ }
367
+ my $sub = $subregionList[$sn];
349
368
  # convert population digits back into exponent format
350
369
  my $pop = (($code>>16 & 0x0f) . '.' . ($code>>12 & 0x0f) . 'e+' . ($code>>20 & 0x0f)) + 0;
351
- $tn += 256 if $sb & 0x8000;
352
370
  $lt = sprintf('%.4f', (($lt<<4)|($f >> 4)) * 180 / 0x100000 - 90);
353
371
  $ln = sprintf('%.4f', (($ln<<4)|($f & 0x0f))* 360 / 0x100000 - 180);
354
372
  $fc = $featureCodes[$fc & 0x0f];
@@ -511,9 +529,10 @@ Entry: for (; $i<@cityList; ++$i) {
511
529
  if ($regex{8}) { $cty =~ $_ or next Entry foreach @{$regex{8}} }
512
530
  if ($regex{18}) { $cty !~ $_ or next Entry foreach @{$regex{18}} }
513
531
  # test other arguments
514
- my ($cd,$sb) = unpack('x5Nn', $cityList[$i]);
532
+ my ($cd,$sn) = unpack('x5Nn', $cityList[$i]);
515
533
  my $ct = $countryList[$cd >> 24];
516
- my @geo = (substr($ct,0,2), substr($ct,2), $regionList[$cd & 0x0fff], $subregionList[$sb & 0x7fff]);
534
+ $sn &= 0x7fff if $dbVer eq '1.02';
535
+ my @geo = (substr($ct,0,2), substr($ct,2), $regionList[$cd & 0x0fff], $subregionList[$sn]);
517
536
  if (@exact) {
518
537
  # make quick lookup for all names at this location
519
538
  my %geoLkup;
@@ -746,6 +765,10 @@ database, then the database entry is updated with the new country name.
746
765
 
747
766
  10) Optional comma-separated list of alternate names for the city
748
767
 
768
+ =item Return Value:
769
+
770
+ 1 on success, otherwise sends a warning message to stderr
771
+
749
772
  =back
750
773
 
751
774
  =head2 GetEntry
@@ -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.32';
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,7 +5499,7 @@ 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
@@ -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.95';
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($$$);
@@ -7870,20 +7873,9 @@ my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
7870
7873
  #
7871
7874
  ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7872
7875
  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
7876
+ mrlh => { Name => 'MarlinHeader', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlh' } },
7877
+ mrlv => { Name => 'MarlinValues', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlv' } },
7878
+ mrld => { Name => 'MarlinDictionary',SubDirectory => { TagTable => 'Image::ExifTool::GM::mrld' } },
7887
7879
  );
7888
7880
 
7889
7881
  # MP4 data information box (ref 5)
@@ -9478,7 +9470,7 @@ sub ProcessMOV($$;$)
9478
9470
  my $dirID = $$dirInfo{DirID} || '';
9479
9471
  my $charsetQuickTime = $et->Options('CharsetQuickTime');
9480
9472
  my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9481
- my ($dirEnd, $unkOpt, %saveOptions, $atomCount);
9473
+ my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr);
9482
9474
 
9483
9475
  my $topLevel = not $$et{InQuickTime};
9484
9476
  $$et{InQuickTime} = 1;
@@ -9558,6 +9550,7 @@ sub ProcessMOV($$;$)
9558
9550
  $index = $$tagTablePtr{VARS}{START_INDEX};
9559
9551
  $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9560
9552
  }
9553
+ my $lastTag = '';
9561
9554
  for (;;) {
9562
9555
  my ($eeTag, $ignore);
9563
9556
  last if defined $atomCount and --$atomCount < 0;
@@ -9584,22 +9577,22 @@ sub ProcessMOV($$;$)
9584
9577
  }
9585
9578
  last;
9586
9579
  }
9587
- $size == 1 or $et->Warn('Invalid atom size'), last;
9580
+ $size == 1 or $warnStr = 'Invalid atom size', last;
9588
9581
  # read extended atom size
9589
- $raf->Read($buff, 8) == 8 or $et->Warn('Truncated atom header'), last;
9582
+ $raf->Read($buff, 8) == 8 or $warnStr = 'Truncated atom header', last;
9590
9583
  $dataPos += 8;
9591
9584
  my ($hi, $lo) = unpack('NN', $buff);
9592
9585
  if ($hi or $lo > 0x7fffffff) {
9593
9586
  if ($hi > 0x7fffffff) {
9594
- $et->Warn('Invalid atom size');
9587
+ $warnStr = 'Invalid atom size';
9595
9588
  last;
9596
9589
  } elsif (not $et->Options('LargeFileSupport')) {
9597
- $et->Warn('End of processing at large atom (LargeFileSupport not enabled)');
9590
+ $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
9598
9591
  last;
9599
9592
  }
9600
9593
  }
9601
9594
  $size = $hi * 4294967296 + $lo - 16;
9602
- $size < 0 and $et->Warn('Invalid extended size'), last;
9595
+ $size < 0 and $warnStr = 'Invalid extended size', last;
9603
9596
  } else {
9604
9597
  $size -= 8;
9605
9598
  }
@@ -9755,7 +9748,7 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
9755
9748
  my $missing = $size - $raf->Read($val, $size);
9756
9749
  if ($missing) {
9757
9750
  my $t = PrintableTagID($tag,2);
9758
- $et->Warn("Truncated '${t}' data (missing $missing bytes)");
9751
+ $warnStr = "Truncated '${t}' data (missing $missing bytes)";
9759
9752
  last;
9760
9753
  }
9761
9754
  # use value to get tag info if necessary
@@ -10070,16 +10063,28 @@ ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
10070
10063
  ) if $verbose;
10071
10064
  if ($size and (not $raf->Seek($size-1, 1) or $raf->Read($buff, 1) != 1)) {
10072
10065
  my $t = PrintableTagID($tag,2);
10073
- $et->Warn("Truncated '${t}' data");
10066
+ $warnStr = "Truncated '${t}' data";
10074
10067
  last;
10075
10068
  }
10076
10069
  }
10077
10070
  $dataPos += $size + 8; # point to start of next atom data
10078
10071
  last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10079
10072
  $raf->Read($buff, 8) == 8 or last;
10073
+ $lastTag = $tag if $$tagTablePtr{$tag};
10080
10074
  ($size, $tag) = unpack('Na4', $buff);
10081
10075
  ++$index if defined $index;
10082
10076
  }
10077
+ if ($warnStr) {
10078
+ # assume this is an unknown trailer if it comes immediately after
10079
+ # mdat or moov and has a tag name we don't recognize
10080
+ if (($lastTag eq 'mdat' or $lastTag eq 'moov') and (not $$tagTablePtr{$tag} or
10081
+ ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10082
+ {
10083
+ $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10084
+ } else {
10085
+ $et->Warn($warnStr);
10086
+ }
10087
+ }
10083
10088
  # tweak file type based on track content ("iso*" and "dash" ftyp only)
10084
10089
  if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
10085
10090
  $$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,11 @@ 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
+ },
329
341
  Unknown00 => { Unknown => 1 },
330
342
  Unknown01 => { Unknown => 1 },
331
343
  Unknown02 => { Unknown => 1 },
@@ -741,13 +753,6 @@ my %insvLimit = (
741
753
  10 => { Name => 'FusionYPR', Format => 'float[3]' },
742
754
  );
743
755
 
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
756
  #------------------------------------------------------------------------------
752
757
  # Save information from keys in OtherSampleDesc directory for processing timed metadata
753
758
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -2219,26 +2224,6 @@ sub Process_tx3g($$$)
2219
2224
  return 1;
2220
2225
  }
2221
2226
 
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
2227
  #------------------------------------------------------------------------------
2243
2228
  # Process QuickTime 'mebx' timed metadata
2244
2229
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
@@ -3229,6 +3214,52 @@ sub ProcessFMAS($$$)
3229
3214
  return 1;
3230
3215
  }
3231
3216
 
3217
+ #------------------------------------------------------------------------------
3218
+ # Process GPS from Wolfbox G900 Dashcam
3219
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
3220
+ # Returns: 1 on success
3221
+ sub ProcessWolfbox($$$)
3222
+ {
3223
+ my ($et, $dirInfo, $tagTbl) = @_;
3224
+ my $dataPt = $$dirInfo{DataPt};
3225
+ return 0 if length($$dataPt) < 0xc8;
3226
+ $et->VerboseDir('Wolfbox', undef, length($$dataPt));
3227
+ # 0000: 65 00 00 00 00 00 00 00 31 01 01 00 e3 ff 00 00 [e.......1.......]
3228
+ # 0010: 04 00 00 00 10 00 00 00 2a 00 00 00 00 00 00 00 [........*.......]
3229
+ # 0020: 01 00 00 00 00 00 00 00 8b 33 ff 51 00 00 00 00 [.........3.Q....]
3230
+ # 0030: a0 86 01 00 00 00 00 00 4d 5e 07 fa ff ff ff ff [........M^......]
3231
+ # 0040: a0 86 01 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
3232
+ # 0050: 64 00 00 00 00 00 00 00 90 21 00 00 00 00 00 00 [d........!......]
3233
+ # 0060: 64 00 00 00 00 00 00 00 18 00 00 00 03 00 00 00 [d...............]
3234
+ # 0070: e8 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
3235
+ # 0080: 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 30 [........00000000]
3236
+ # 0090: 30 30 30 30 30 30 30 30 48 59 54 48 00 00 00 00 [00000000HYTH....]
3237
+ # 00a0: 0c 00 00 00 10 00 00 00 2a 00 00 00 00 00 00 00 [........*.......]
3238
+ # 00b0: 4f 3f 0c 1f 00 00 00 00 a0 86 01 00 00 00 00 00 [O?..............]
3239
+ # 00c0: 7f cf 2d ff ff ff ff ff a0 86 01 00 00 00 00 00 [..-.............]
3240
+ # 00d0: 01 00 00 00 08 00 00 00 0a 00 00 00 00 00 00 00 [................]
3241
+ # 00e0: 0a 00 00 00 00 00 00 00 e8 03 00 00 00 00 00 00 [................]
3242
+ # 00f0: 0a 00 00 00 00 00 00 00 4d 00 00 00 00 00 00 00 [........M.......]
3243
+ # lat/lon at 0xb0/0xc0 and 0x128/0x138
3244
+ # h/m/s at 0x10 and 0xa0 and 0x148 (the first imprinted on the video, the latter 2 presumed UTC)
3245
+ # spd at 0x48, dir at 0x58, alt at 0xe8
3246
+ SetByteOrder('II');
3247
+ my ($spd,$dir,$d,$mo,$yr,$h,$m,$s) = unpack('x72Vx12Vx12V3x44V3',$$dataPt);
3248
+ # offset 0xa0 also stores hh mm ss, but is out by 8 hours!
3249
+ my $time = sprintf '%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ', $yr, $mo, $d, $h, $m, $s;
3250
+ my ($lat, $lon) = (Get32s($dataPt, 0xb0) / 1e5, Get32s($dataPt, 0xc0) / 1e5);
3251
+ my $alt = Get32s($dataPt, 0xe8);
3252
+ ConvertLatLon($lat, $lon);
3253
+ $et->HandleTag($tagTbl, GPSDateTime => $time);
3254
+ $et->HandleTag($tagTbl, GPSLatitude => $lat);
3255
+ $et->HandleTag($tagTbl, GPSLongitude => $lon);
3256
+ $et->HandleTag($tagTbl, GPSSpeed => $spd * $knotsToKph / 100);
3257
+ $et->HandleTag($tagTbl, GPSTrack => $dir / 100);
3258
+ $et->HandleTag($tagTbl, GPSAltitude => $alt / 10); # (NC)
3259
+ SetByteOrder('MM');
3260
+ return 1;
3261
+ }
3262
+
3232
3263
  #------------------------------------------------------------------------------
3233
3264
  # Scan media data for "freeGPS" metadata if not found already (ref PH)
3234
3265
  # Inputs: 0) ExifTool ref
@@ -1132,6 +1132,8 @@ keys in structure hashes are:
1132
1132
  GROUPS : Same as in tag table, but only the family 2 group name is used,
1133
1133
  as the default for the flattened tags.
1134
1134
 
1135
+ SORT_ORDER : Order for sorting fields in documentation.
1136
+
1135
1137
  The contained structure field information hashes are similar to tag information
1136
1138
  hashes, except that only the following elements are used:
1137
1139
 
@@ -2083,6 +2083,7 @@ my %tagLookup = (
2083
2083
  'cropcirclex' => { 112 => 0xd7 },
2084
2084
  'cropcircley' => { 112 => 0xd8 },
2085
2085
  'cropconstraintowarp' => { 511 => 'CropConstrainToWarp', 513 => 'CropConstrainToWarp' },
2086
+ 'cropflag' => { 130 => 0x1051 },
2086
2087
  'croph' => { 505 => 'CropH' },
2087
2088
  'cropheight' => { 104 => 0x6, 111 => 0x24c, 328 => 0x615, 332 => 0x615, 511 => 'CropHeight', 513 => 'CropHeight' },
2088
2089
  'crophispeed' => { 239 => 0x1b },
@@ -2098,7 +2099,6 @@ my %tagLookup = (
2098
2099
  'cropoutputscale' => { 292 => 0xbe },
2099
2100
  'cropoutputwidth' => { 292 => 0xc6 },
2100
2101
  'cropoutputwidthinches' => { 292 => 0x8e },
2101
- 'cropped' => { 130 => 0x1051 },
2102
2102
  'croppedareaimageheightpixels' => { 499 => 'CroppedAreaImageHeightPixels', 500 => 'CroppedAreaImageHeightPixels' },
2103
2103
  'croppedareaimagewidthpixels' => { 499 => 'CroppedAreaImageWidthPixels', 500 => 'CroppedAreaImageWidthPixels' },
2104
2104
  'croppedarealeftpixels' => { 499 => 'CroppedAreaLeftPixels', 500 => 'CroppedAreaLeftPixels' },
@@ -8330,6 +8330,21 @@ my %tagExists = (
8330
8330
  'cfagreenthreshold2' => 1,
8331
8331
  'cfalayout' => 1,
8332
8332
  'cfaplanecolor' => 1,
8333
+ 'channel01' => 1,
8334
+ 'channel01description' => 1,
8335
+ 'channel01dispmax' => 1,
8336
+ 'channel01dispmin' => 1,
8337
+ 'channel01flags' => 1,
8338
+ 'channel01id' => 1,
8339
+ 'channel01interval' => 1,
8340
+ 'channel01max' => 1,
8341
+ 'channel01min' => 1,
8342
+ 'channel01multiplier' => 1,
8343
+ 'channel01name' => 1,
8344
+ 'channel01num' => 1,
8345
+ 'channel01offset' => 1,
8346
+ 'channel01type' => 1,
8347
+ 'channel01units' => 1,
8333
8348
  'channel0lagkernel' => 1,
8334
8349
  'channel1coordinates' => 1,
8335
8350
  'channel1flags' => 1,
@@ -8707,6 +8722,7 @@ my %tagExists = (
8707
8722
  'crgbtoerimm9spline' => 1,
8708
8723
  'cropdata' => 1,
8709
8724
  'cropinfo' => 1,
8725
+ 'cropped' => 1,
8710
8726
  'cropxcommonoffset' => 1,
8711
8727
  'cropxoffset' => 1,
8712
8728
  'cropxoffset2' => 1,
@@ -8818,6 +8834,8 @@ my %tagExists = (
8818
8834
  'datasize64' => 1,
8819
8835
  'datatype' => 1,
8820
8836
  'datawindow' => 1,
8837
+ 'date1' => 1,
8838
+ 'date2' => 1,
8821
8839
  'dateaccessed' => 1,
8822
8840
  'datearchived' => 1,
8823
8841
  'datecompleted' => 1,
@@ -9652,6 +9670,7 @@ my %tagExists = (
9652
9670
  'gpmd_gopro' => 1,
9653
9671
  'gpmd_kingslim' => 1,
9654
9672
  'gpmd_rove' => 1,
9673
+ 'gpmd_wolfbox' => 1,
9655
9674
  'gps' => 1,
9656
9675
  'gps360fly' => 1,
9657
9676
  'gpsaltituderaw' => 1,
@@ -10467,6 +10486,10 @@ my %tagExists = (
10467
10486
  'markerid' => 1,
10468
10487
  'markinfo' => 1,
10469
10488
  'marl' => 1,
10489
+ 'marlindataversion' => 1,
10490
+ 'marlindictionary' => 1,
10491
+ 'marlinheader' => 1,
10492
+ 'marlinvalues' => 1,
10470
10493
  'masksubarea' => 1,
10471
10494
  'masteredby' => 1,
10472
10495
  'mastergainadjustment' => 1,
@@ -12517,6 +12540,8 @@ my %tagExists = (
12517
12540
  'tilegaindeterminationtable' => 1,
12518
12541
  'tileoffsets' => 1,
12519
12542
  'tiles' => 1,
12543
+ 'time1' => 1,
12544
+ 'time2' => 1,
12520
12545
  'timeanddate' => 1,
12521
12546
  'timecode' => 1,
12522
12547
  'timecodeindex' => 1,