exiftool_vendored 11.99.0 → 12.06.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of exiftool_vendored might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +119 -2
  3. data/bin/MANIFEST +5 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +32 -32
  7. data/bin/exiftool +55 -50
  8. data/bin/lib/Image/ExifTool.pm +155 -109
  9. data/bin/lib/Image/ExifTool.pod +103 -77
  10. data/bin/lib/Image/ExifTool/AIFF.pm +2 -2
  11. data/bin/lib/Image/ExifTool/APE.pm +2 -2
  12. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +11 -6
  13. data/bin/lib/Image/ExifTool/Canon.pm +2 -1
  14. data/bin/lib/Image/ExifTool/CanonCustom.pm +82 -16
  15. data/bin/lib/Image/ExifTool/DPX.pm +56 -2
  16. data/bin/lib/Image/ExifTool/DarwinCore.pm +16 -3
  17. data/bin/lib/Image/ExifTool/Exif.pm +15 -6
  18. data/bin/lib/Image/ExifTool/Font.pm +9 -2
  19. data/bin/lib/Image/ExifTool/GIF.pm +5 -0
  20. data/bin/lib/Image/ExifTool/GeoTiff.pm +2 -0
  21. data/bin/lib/Image/ExifTool/GoPro.pm +10 -1
  22. data/bin/lib/Image/ExifTool/H264.pm +1 -1
  23. data/bin/lib/Image/ExifTool/ID3.pm +86 -12
  24. data/bin/lib/Image/ExifTool/Lang/de.pm +3 -1
  25. data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  26. data/bin/lib/Image/ExifTool/M2TS.pm +1 -1
  27. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  28. data/bin/lib/Image/ExifTool/Minolta.pm +3 -2
  29. data/bin/lib/Image/ExifTool/Nikon.pm +134 -15
  30. data/bin/lib/Image/ExifTool/Olympus.pm +34 -17
  31. data/bin/lib/Image/ExifTool/PNG.pm +14 -3
  32. data/bin/lib/Image/ExifTool/PPM.pm +5 -5
  33. data/bin/lib/Image/ExifTool/Panasonic.pm +147 -13
  34. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +33 -0
  35. data/bin/lib/Image/ExifTool/Parrot.pm +2 -1
  36. data/bin/lib/Image/ExifTool/Pentax.pm +2 -1
  37. data/bin/lib/Image/ExifTool/Photoshop.pm +2 -1
  38. data/bin/lib/Image/ExifTool/QuickTime.pm +204 -27
  39. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +355 -19
  40. data/bin/lib/Image/ExifTool/README +20 -19
  41. data/bin/lib/Image/ExifTool/Ricoh.pm +19 -1
  42. data/bin/lib/Image/ExifTool/Shift.pl +1 -0
  43. data/bin/lib/Image/ExifTool/SigmaRaw.pm +40 -33
  44. data/bin/lib/Image/ExifTool/Sony.pm +376 -11
  45. data/bin/lib/Image/ExifTool/TagLookup.pm +1949 -1872
  46. data/bin/lib/Image/ExifTool/TagNames.pod +329 -53
  47. data/bin/lib/Image/ExifTool/Validate.pm +4 -4
  48. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -0
  49. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +23 -15
  50. data/bin/lib/Image/ExifTool/Writer.pl +44 -21
  51. data/bin/lib/Image/ExifTool/XMP.pm +41 -4
  52. data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
  53. data/bin/lib/Image/ExifTool/ZISRAW.pm +123 -0
  54. data/bin/perl-Image-ExifTool.spec +31 -31
  55. data/lib/exiftool_vendored/version.rb +1 -1
  56. metadata +4 -3
@@ -19,9 +19,11 @@ use Image::ExifTool qw(:DataAccess :Utils);
19
19
  use Image::ExifTool::QuickTime;
20
20
 
21
21
  sub Process_tx3g($$$);
22
+ sub Process_marl($$$);
22
23
  sub Process_mebx($$$);
23
24
  sub ProcessFreeGPS($$$);
24
25
  sub ProcessFreeGPS2($$$);
26
+ sub Process360Fly($$$);
25
27
 
26
28
  # QuickTime data types that have ExifTool equivalents
27
29
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
@@ -70,6 +72,7 @@ my %processByMetaFormat = (
70
72
  meta => 1, # ('CTMD' in CR3 images, 'priv' unknown in DJI video)
71
73
  data => 1, # ('RVMI')
72
74
  sbtl => 1, # (subtitle; 'tx3g' in Yuneec drone videos)
75
+ ctbx => 1, # ('marl' in GM videos)
73
76
  );
74
77
 
75
78
  # data lengths for each INSV record type
@@ -91,7 +94,7 @@ my %insvLimit = (
91
94
  NOTES => q{
92
95
  Timed metadata extracted from QuickTime media data and some AVI videos when
93
96
  the ExtractEmbedded option is used. Although most of these tags are
94
- combined into the single table below, ExifTool currently reads 37 different
97
+ combined into the single table below, ExifTool currently reads 46 different
95
98
  formats of timed GPS metadata from video files.
96
99
  },
97
100
  VARS => { NO_ID => 1 },
@@ -173,6 +176,10 @@ my %insvLimit = (
173
176
  Name => 'rtmd',
174
177
  SubDirectory => { TagTable => 'Image::ExifTool::Sony::rtmd' },
175
178
  },
179
+ marl => {
180
+ Name => 'marl',
181
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::marl' },
182
+ },
176
183
  CTMD => { # (Canon Timed MetaData)
177
184
  Name => 'CTMD',
178
185
  SubDirectory => { TagTable => 'Image::ExifTool::Canon::CTMD' },
@@ -548,6 +555,137 @@ my %insvLimit = (
548
555
  },
549
556
  );
550
557
 
558
+ %Image::ExifTool::QuickTime::Tags360Fly = (
559
+ PROCESS_PROC => \&Process360Fly,
560
+ NOTES => 'Timed metadata found in MP4 videos from the 360Fly.',
561
+ 1 => {
562
+ Name => 'Accel360Fly',
563
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Accel360Fly' },
564
+ },
565
+ 2 => {
566
+ Name => 'Gyro360Fly',
567
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Gyro360Fly' },
568
+ },
569
+ 3 => {
570
+ Name => 'Mag360Fly',
571
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Mag360Fly' },
572
+ },
573
+ 5 => {
574
+ Name => 'GPS360Fly',
575
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GPS360Fly' },
576
+ },
577
+ 6 => {
578
+ Name => 'Rot360Fly',
579
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rot360Fly' },
580
+ },
581
+ 250 => {
582
+ Name => 'Fusion360Fly',
583
+ SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Fusion360Fly' },
584
+ },
585
+ );
586
+
587
+ %Image::ExifTool::QuickTime::Accel360Fly = (
588
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
589
+ GROUPS => { 2 => 'Location' },
590
+ 1 => { Name => 'AccelMode', Unknown => 1 }, # (always 2 in my sample)
591
+ 2 => {
592
+ Name => 'SampleTime',
593
+ Groups => { 2 => 'Video' },
594
+ Format => 'int64u',
595
+ ValueConv => '$val / 1e6',
596
+ PrintConv => 'ConvertDuration($val)',
597
+ },
598
+ 10 => { Name => 'AccelYPR', Format => 'float[3]' },
599
+ );
600
+
601
+ %Image::ExifTool::QuickTime::Gyro360Fly = (
602
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
603
+ GROUPS => { 2 => 'Location' },
604
+ 1 => { Name => 'GyroMode', Unknown => 1 }, # (always 1 in my sample)
605
+ 2 => {
606
+ Name => 'SampleTime',
607
+ Groups => { 2 => 'Video' },
608
+ Format => 'int64u',
609
+ ValueConv => '$val / 1e6',
610
+ PrintConv => 'ConvertDuration($val)',
611
+ },
612
+ 10 => { Name => 'GyroYPR', Format => 'float[3]' },
613
+ );
614
+
615
+ %Image::ExifTool::QuickTime::Mag360Fly = (
616
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
617
+ GROUPS => { 2 => 'Location' },
618
+ 1 => { Name => 'MagMode', Unknown => 1 }, # (always 1 in my sample)
619
+ 2 => {
620
+ Name => 'SampleTime',
621
+ Groups => { 2 => 'Video' },
622
+ Format => 'int64u',
623
+ ValueConv => '$val / 1e6',
624
+ PrintConv => 'ConvertDuration($val)',
625
+ },
626
+ 10 => { Name => 'MagnetometerXYZ', Format => 'float[3]' },
627
+ );
628
+
629
+ %Image::ExifTool::QuickTime::GPS360Fly = (
630
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
631
+ GROUPS => { 2 => 'Location' },
632
+ 1 => { Name => 'GPSMode', Unknown => 1 }, # (always 16 in my sample)
633
+ 2 => {
634
+ Name => 'SampleTime',
635
+ Groups => { 2 => 'Video' },
636
+ Format => 'int64u',
637
+ ValueConv => '$val / 1e6',
638
+ PrintConv => 'ConvertDuration($val)',
639
+ },
640
+ 10 => { Name => 'GPSLatitude', Format => 'float', PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")' },
641
+ 14 => { Name => 'GPSLongitude', Format => 'float', PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")' },
642
+ 18 => { Name => 'GPSAltitude', Format => 'float', PrintConv => '"$val m"' }, # (questionable accuracy)
643
+ 22 => {
644
+ Name => 'GPSSpeed',
645
+ Notes => 'converted to km/hr',
646
+ Format => 'int16u',
647
+ ValueConv => '$val * 0.036',
648
+ PrintConv => 'sprintf("%.1f",$val)',
649
+ },
650
+ 24 => { Name => 'GPSTrack', Format => 'int16u', ValueConv => '$val / 100' },
651
+ 26 => { Name => 'Acceleration', Format => 'int16u', ValueConv => '$val / 1000' },
652
+ );
653
+
654
+ %Image::ExifTool::QuickTime::Rot360Fly = (
655
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
656
+ GROUPS => { 2 => 'Location' },
657
+ 1 => { Name => 'RotMode', Unknown => 1 }, # (always 1 in my sample)
658
+ 2 => {
659
+ Name => 'SampleTime',
660
+ Groups => { 2 => 'Video' },
661
+ Format => 'int64u',
662
+ ValueConv => '$val / 1e6',
663
+ PrintConv => 'ConvertDuration($val)',
664
+ },
665
+ 10 => { Name => 'RotationXYZ', Format => 'float[3]' },
666
+ );
667
+
668
+ %Image::ExifTool::QuickTime::Fusion360Fly = (
669
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
670
+ GROUPS => { 2 => 'Location' },
671
+ 1 => { Name => 'FusionMode', Unknown => 1 }, # (always 0 in my sample)
672
+ 2 => {
673
+ Name => 'SampleTime',
674
+ Groups => { 2 => 'Video' },
675
+ Format => 'int64u',
676
+ ValueConv => '$val / 1e6',
677
+ PrintConv => 'ConvertDuration($val)',
678
+ },
679
+ 10 => { Name => 'FusionYPR', Format => 'float[3]' },
680
+ );
681
+
682
+ # tags found in 'marl' ctbx timed metadata (ref PH)
683
+ %Image::ExifTool::QuickTime::marl = (
684
+ PROCESS_PROC => \&Process_marl,
685
+ GROUPS => { 2 => 'Other' },
686
+ NOTES => 'Tags extracted from the marl ctbx timed metadata of GM cars.',
687
+ );
688
+
551
689
  #------------------------------------------------------------------------------
552
690
  # Save information from keys in OtherSampleDesc directory for processing timed metadata
553
691
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
@@ -648,7 +786,7 @@ sub FoundSomething($$;$$)
648
786
 
649
787
  #------------------------------------------------------------------------------
650
788
  # Approximate GPSDateTime value from sample time and CreateDate
651
- # Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (ms)
789
+ # Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (s)
652
790
  # 3) true if CreateDate is at end of video
653
791
  # Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
654
792
  sub SetGPSDateTime($$$)
@@ -696,7 +834,7 @@ sub HandleTextTags($$$)
696
834
 
697
835
  #------------------------------------------------------------------------------
698
836
  # Process subtitle 'text'
699
- # Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref, 3) optional sample time
837
+ # Inputs: 0) ExifTool ref, 1) tag table ref, 2) data ref
700
838
  sub Process_text($$$)
701
839
  {
702
840
  my ($et, $tagTbl, $buffPt) = @_;
@@ -876,7 +1014,28 @@ sub Process_text($$$)
876
1014
  return;
877
1015
  }
878
1016
 
879
- # check for Thinkware format, eg:
1017
+ # check for Roadhawk dashcam text
1018
+ # ".;;;;D?JL;6+;;;D;R?;4;;;;DBB;;O;;;=D;L;;HO71G>F;-?=J-F:FNJJ;DPP-JF3F;;PL=DBRLBF0F;=?DNF-RD-PF;N;?=JF;;?D=F:*6F~"
1019
+ # decoded:
1020
+ # "X0000.2340Y-000.0720Z0000.9900G0001.0400$GPRMC,082138,A,5330.6683,N,00641.9749,W,012.5,87.86,050213,002.1,A"
1021
+ # (note: "002.1" is magnetic variation and is not decoded; it should have ",E" or ",W" afterward for direction)
1022
+ if ($$buffPt =~ /\*[0-9A-F]{2}~$/) {
1023
+ # (ref https://reverseengineering.stackexchange.com/questions/11582/how-to-reverse-engineer-dash-cam-metadata)
1024
+ my @decode = unpack 'C*', '-I8XQWRVNZOYPUTA0B1C2SJ9K.L,M$D3E4F5G6H7';
1025
+ my @chars = unpack 'C*', substr($$buffPt, 0, -4);
1026
+ foreach (@chars) {
1027
+ my $n = $_ - 43;
1028
+ $_ = $decode[$n] if $n >= 0 and defined $decode[$n];
1029
+ }
1030
+ my $buff = pack 'C*', @chars;
1031
+ if ($buff =~ /X(.*?)Y(.*?)Z(.*?)G(.*?)\$/) {
1032
+ # yup. the decoding worked out
1033
+ $tags{Accelerometer} = "$1 $2 $3 $4";
1034
+ $$buffPt = $buff; # (process GPRMC below)
1035
+ }
1036
+ }
1037
+
1038
+ # check for Thinkware format (and other NMEA RMC), eg:
880
1039
  # "gsensori,4,512,-67,-12,100;GNRMC,161313.00,A,4529.87489,N,07337.01215,W,6.225,35.34,310819,,,A*52..;
881
1040
  # CAR,0,0,0,0.0,0,0,0,0,0,0,0,0"
882
1041
  if ($$buffPt =~ /[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(\d*?)(\d{1,2}\.\d+),([NS]),(\d*?)(\d{1,2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/ and
@@ -1080,8 +1239,8 @@ sub ProcessSamples($)
1080
1239
  $$et{ee} = $ee; # need ee information for 'keys'
1081
1240
  $et->HandleTag($tagTbl, $metaFormat, undef,
1082
1241
  DataPt => \$buff,
1083
- DataPos => $$start[$i],
1084
- Base => $$start[$i],
1242
+ DataPos => 0,
1243
+ Base => $$start[$i], # (Base must be set for CR3 files)
1085
1244
  TagInfo => $tagInfo,
1086
1245
  );
1087
1246
  delete $$et{ee};
@@ -1093,7 +1252,7 @@ sub ProcessSamples($)
1093
1252
  Process_text($et, $tagTbl, \$buff);
1094
1253
  }
1095
1254
  } elsif ($verbose) {
1096
- $et->VPrint(0, "Unknown meta format ($metaFormat)");
1255
+ $et->VPrint(0, "Unknown $type format ($metaFormat)");
1097
1256
  }
1098
1257
 
1099
1258
  } elsif ($type eq 'gps ') { # (ie. GPSDataList tag)
@@ -1115,8 +1274,8 @@ sub ProcessSamples($)
1115
1274
  FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
1116
1275
  $et->HandleTag($tagTbl, $type, undef,
1117
1276
  DataPt => \$buff,
1118
- DataPos => $$start[$i],
1119
- Base => $$start[$i],
1277
+ DataPos => 0,
1278
+ Base => $$start[$i], # (Base must be set for CR3 files)
1120
1279
  TagInfo => $tagInfo,
1121
1280
  );
1122
1281
  }
@@ -1150,13 +1309,35 @@ sub ProcessFreeGPS($$$)
1150
1309
 
1151
1310
  return 0 if $dirLen < 92;
1152
1311
 
1153
- if (substr($$dataPt,12,1) eq "\x05") {
1312
+ if (substr($$dataPt,18,8) eq "\xaa\xaa\xf2\xe1\xf0\xee\x54\x54") {
1154
1313
 
1314
+ # (this is very similar to the encrypted text format)
1155
1315
  # decode encrypted ASCII-based GPS (DashCam Azdome GS63H, ref 5)
1156
1316
  # header looks like this in my sample:
1157
1317
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 05 01 00 00 [....freeGPS ....]
1158
1318
  # 0010: 01 03 aa aa f2 e1 f0 ee 54 54 98 9a 9b 92 9a 93 [........TT......]
1159
1319
  # 0020: 98 9e 98 98 9e 93 98 92 a6 9f 9f 9c 9d ed fa 8a [................]
1320
+ # decrypted (from byte 18):
1321
+ # 0000: 00 00 58 4b 5a 44 fe fe 32 30 31 38 30 39 32 34 [..XKZD..20180924]
1322
+ # 0010: 32 32 34 39 32 38 0c 35 35 36 37 47 50 20 20 20 [224928.5567GP ]
1323
+ # 0020: 00 00 00 00 00 03 4e 34 30 34 36 34 33 35 30 57 [......N40464350W]
1324
+ # 0030: 30 30 37 30 34 30 33 30 38 30 30 30 30 30 30 30 [0070403080000000]
1325
+ # 0040: 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [7...............]
1326
+ # [...]
1327
+ # 00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 30 39 [.............+09]
1328
+ # 00b0: 33 2d 30 30 33 2d 30 30 35 00 00 00 00 00 00 00 [3-003-005.......]
1329
+ # header looks like this for EEEkit gps:
1330
+ # 0000: 00 00 04 00 66 72 65 65 47 50 53 20 f0 03 00 00 [....freeGPS ....]
1331
+ # 0010: 01 03 aa aa f2 e1 f0 ee 54 54 98 9a 98 9a 9a 9f [........TT......]
1332
+ # 0020: 9b 93 9b 9c 98 99 99 9f a6 9a 9a 98 9a 9a 9f 9b [................]
1333
+ # 0030: 93 9b 9c 98 99 99 9c a9 e4 99 9d 9e 9f 98 9e 9b [................]
1334
+ # 0040: 9c fd 9b 98 98 98 9f 9f 9a 9a 93 81 9a 9b 9d 9f [................]
1335
+ # decrypted (from byte 18):
1336
+ # 0000: 00 00 58 4b 5a 44 fe fe 32 30 32 30 30 35 31 39 [..XKZD..20200519]
1337
+ # 0010: 31 36 32 33 33 35 0c 30 30 32 30 30 35 31 39 31 [162335.002005191]
1338
+ # 0020: 36 32 33 33 36 03 4e 33 37 34 35 32 34 31 36 57 [62336.N37452416W]
1339
+ # 0030: 31 32 32 32 35 35 30 30 39 2b 30 31 37 35 30 31 [122255009+017501]
1340
+ # 0040: 31 2b 30 31 34 2b 30 30 32 2b 30 32 36 2b 30 31 [1+014+002+026+01]
1160
1341
  my $n = $dirLen - 18;
1161
1342
  $n = 0x101 if $n > 0x101;
1162
1343
  my $buf2 = pack 'C*', map { $_ ^ 0xaa } unpack 'C*', substr($$dataPt,18,$n);
@@ -1165,13 +1346,25 @@ sub ProcessFreeGPS($$$)
1165
1346
  $et->VerboseDump(\$buf2);
1166
1347
  }
1167
1348
  # (extract longitude as 9 digits, not 8, ref PH)
1168
- return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})/s;
1349
+ return 0 unless $buf2 =~ /^.{8}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).(.{15})([NS])(\d{8})([EW])(\d{9})(\d{8})?/s;
1169
1350
  ($yr,$mon,$day,$hr,$min,$sec,$lbl,$latRef,$lat,$lonRef,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8,$9/1e4,$10,$11/1e4,$12);
1170
- $spd += 0; # remove leading 0's
1351
+ if (defined $spd) { # (Azdome)
1352
+ $spd += 0; # remove leading 0's
1353
+ } elsif ($buf2 =~ /^.{57}([-+]\d{4})(\d{3})/s) { # (EEEkit)
1354
+ # $alt = $1 + 0; (doesn't look right for my sample, but the Ambarella A12 text has this)
1355
+ $spd = $2 + 0;
1356
+ }
1171
1357
  $lbl =~ s/\0.*//s; $lbl =~ s/\s+$//; # truncate at null and remove trailing spaces
1172
1358
  push @xtra, UserLabel => $lbl if length $lbl;
1173
1359
  # extract accelerometer data (ref PH)
1174
- @acc = ($1/100,$2/100,$3/100) if $buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s;
1360
+ if ($buf2 =~ /^.{65}(([-+]\d{3})([-+]\d{3})([-+]\d{3})([-+]\d{3})*)/s) {
1361
+ $_ = $1;
1362
+ @acc = ($2/100, $3/100, $4/100);
1363
+ s/([-+])/ $1/g; s/^ //;
1364
+ push @xtra, AccelerometerData => $_;
1365
+ } elsif ($buf2 =~ /^.{173}([-+]\d{3})([-+]\d{3})([-+]\d{3})/s) { # (Azdome)
1366
+ @acc = ($1/100, $2/100, $3/100);
1367
+ }
1175
1368
 
1176
1369
  } elsif ($$dataPt =~ /^.{52}(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/s) {
1177
1370
 
@@ -1222,12 +1415,12 @@ sub ProcessFreeGPS($$$)
1222
1415
 
1223
1416
  # decode freeGPS from Akaso dashcam
1224
1417
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 60 00 00 00 [....freeGPS `...]
1225
- # 0000: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
1226
- # 0000: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
1227
- # 0000: 12 00 00 00 2f 00 00 00 19 00 00 00 41 00 00 00 [..../.......A...]
1228
- # 0000: 13 b3 ca 44 4e 00 00 00 29 92 fb 45 45 00 00 00 [...DN...)..EE...]
1229
- # 0000: d9 ee b4 41 ec d1 d3 42 e4 07 00 00 01 00 00 00 [...A...B........]
1230
- # 0000: 0c 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 [................]
1418
+ # 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
1419
+ # 0020: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
1420
+ # 0030: 12 00 00 00 2f 00 00 00 19 00 00 00 41 00 00 00 [..../.......A...]
1421
+ # 0040: 13 b3 ca 44 4e 00 00 00 29 92 fb 45 45 00 00 00 [...DN...)..EE...]
1422
+ # 0050: d9 ee b4 41 ec d1 d3 42 e4 07 00 00 01 00 00 00 [...A...B........]
1423
+ # 0060: 0c 00 00 00 01 00 00 00 05 00 00 00 00 00 00 00 [................]
1231
1424
  ($latRef, $lonRef) = ($1, $2);
1232
1425
  ($hr, $min, $sec, $yr, $mon, $day) = unpack('x48V3x28V3', $$dataPt);
1233
1426
  SetByteOrder('II');
@@ -1238,6 +1431,56 @@ sub ProcessFreeGPS($$$)
1238
1431
  $trk -= 360 if $trk >= 360;
1239
1432
  SetByteOrder('MM');
1240
1433
 
1434
+ } elsif ($$dataPt =~ /^.{16}YndAkasoCar/s) {
1435
+
1436
+ # Akaso V1 dascham
1437
+ # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
1438
+ # 0010: 59 6e 64 41 6b 61 73 6f 43 61 72 00 00 00 00 00 [YndAkasoCar.....]
1439
+ # 0020: 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 [00000...........]
1440
+ # 0030: 0e 00 00 00 27 00 00 00 2c 00 00 00 e3 07 00 00 [....'...,.......]
1441
+ # 0040: 05 00 00 00 1d 00 00 00 41 4e 45 00 00 00 00 00 [........ANE.....]
1442
+ # 0050: f1 4e 3e 3d 90 df ca 40 e3 50 bf 0b 0b 31 a0 40 [.N>=...@.P...1.@]
1443
+ # 0060: 4b dc c8 41 9a 79 a7 43 34 58 43 31 4f 37 31 35 [K..A.y.C4XC1O715]
1444
+ # 0070: 35 31 32 36 36 35 37 35 59 4e 44 53 0d e7 cc f9 [51266575YNDS....]
1445
+ # 0080: 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 [................]
1446
+ ($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
1447
+ unpack('x48V6a1a1a1x1', $$dataPt);
1448
+ # ignore invalid fixes
1449
+ return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
1450
+ ($lonRef eq 'E' or $lonRef eq 'W');
1451
+
1452
+ $et->WarnOnce("Can't yet decrypt Akaso V1 timed GPS", 1);
1453
+ # (see https://exiftool.org/forum/index.php?topic=11320.0)
1454
+ return 1;
1455
+
1456
+ SetByteOrder('II');
1457
+ $lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
1458
+ $lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
1459
+ SetByteOrder('MM');
1460
+ #my $serialNum = substr($$dataPt, 0x68, 20);
1461
+
1462
+ } elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
1463
+
1464
+ # EACHPAI dash cam
1465
+ # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 ac 00 00 00 [....freeGPS ....]
1466
+ # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1467
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1468
+ # 0030: 00 00 00 00 00 00 00 00 00 00 00 00 34 57 60 62 [............4W`b]
1469
+ # 0040: 5d 53 3c 41 47 45 45 42 42 3e 40 40 40 3c 51 3c []S<AGEEBB>@@@<Q<]
1470
+ # 0050: 44 42 44 40 3e 48 46 43 45 3c 5e 3c 40 48 43 41 [DBD@>HFCE<^<@HCA]
1471
+ # 0060: 42 3e 46 42 47 48 3c 67 3c 40 3e 40 42 3c 43 3e [B>FBGH<g<@>@B<C>]
1472
+ # 0070: 43 41 3c 40 42 40 46 42 40 3c 3c 3c 51 3a 47 46 [CA<@B@FB@<<<Q:GF]
1473
+ # 0080: 00 2a 36 35 00 00 00 00 00 00 00 00 00 00 00 00 [.*65............]
1474
+
1475
+ $et->WarnOnce("Can't yet decrypt EACHPAI timed GPS", 1);
1476
+ # (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
1477
+ return 1;
1478
+
1479
+ my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
1480
+ # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1481
+ substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1482
+ # (other values are currently unknown)
1483
+
1241
1484
  } else {
1242
1485
 
1243
1486
  # decode binary GPS format (Viofo A119S, ref 2)
@@ -1252,6 +1495,21 @@ sub ProcessFreeGPS($$$)
1252
1495
  return 0 unless $stat eq 'A' and ($latRef eq 'N' or $latRef eq 'S') and
1253
1496
  ($lonRef eq 'E' or $lonRef eq 'W');
1254
1497
  ($lat,$lon,$spd,$trk) = unpack 'f*', pack 'L*', $lat, $lon, $spd, $trk;
1498
+ # lat/lon also stored as doubles by Transcend Driver Pro 230 (ref PH)
1499
+ SetByteOrder('II');
1500
+ my ($lat2, $lon2, $alt2) = (
1501
+ GetDouble($dataPt, 0x70),
1502
+ GetDouble($dataPt, 0x80),
1503
+ # GetDouble($dataPt, 0x98), # (don't know what this is)
1504
+ GetDouble($dataPt,0xa0),
1505
+ # GetDouble($dataPt,0xa8)) # (don't know what this is)
1506
+ );
1507
+ if (abs($lat2-$lat) < 0.001 and abs($lon2-$lon) < 0.001) {
1508
+ $lat = $lat2;
1509
+ $lon = $lon2;
1510
+ $alt = $alt2;
1511
+ }
1512
+ SetByteOrder('MM');
1255
1513
  $yr += 2000 if $yr < 2000;
1256
1514
  $spd *= $knotsToKph; # convert speed to km/h
1257
1515
  # ($trk is not confirmed; may be GPSImageDirection, ref PH)
@@ -1644,6 +1902,33 @@ sub ParseTag($$$)
1644
1902
  }
1645
1903
  $$et{HandlerType} = $tag; # fake handler type
1646
1904
  ProcessSamples($et); # we have all we need to process sample data now
1905
+ } elsif ($tag eq 'GPS ') {
1906
+ my $pos = 0;
1907
+ my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
1908
+ SetByteOrder('II');
1909
+ while ($pos + 36 < $dataLen) {
1910
+ my $dat = substr($$dataPt, $pos, 36);
1911
+ last if $dat eq "\x0" x 36;
1912
+ my @a = unpack 'VVVVCVCV', $dat;
1913
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
1914
+ # 0=1, 1=1, 2=secs, 3=?
1915
+ SetGPSDateTime($et, $tagTbl, $a[2]);
1916
+ my $lat = $a[5] / 1e3;
1917
+ my $lon = $a[7] / 1e3;
1918
+ my $deg = int($lat / 100);
1919
+ $lat = $deg + ($lat - $deg * 100) / 60;
1920
+ $deg = int($lon / 100);
1921
+ $lon = $deg + ($lon - $deg * 100) / 60;
1922
+ $lat = -$lat if $a[4] eq 'S';
1923
+ $lon = -$lon if $a[6] eq 'W';
1924
+ $et->HandleTag($tagTbl, GPSLatitude => $lat);
1925
+ $et->HandleTag($tagTbl, GPSLongitude => $lon);
1926
+ $et->HandleTag($tagTbl, GPSSpeed => $a[3] / 1e3);
1927
+ $et->HandleTag($tagTbl, GPSSpeedRef => 'K');
1928
+ $pos += 36;
1929
+ }
1930
+ SetByteOrder('MM');
1931
+ delete $$et{DOC_NUM};
1647
1932
  }
1648
1933
  }
1649
1934
 
@@ -1661,6 +1946,26 @@ sub Process_tx3g($$$)
1661
1946
  return 1;
1662
1947
  }
1663
1948
 
1949
+ #------------------------------------------------------------------------------
1950
+ # Process GM 'marl' ctbx metadata (ref PH)
1951
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
1952
+ # Returns: 1 on success
1953
+ sub Process_marl($$$)
1954
+ {
1955
+ my ($et, $dirInfo, $tagTablePtr) = @_;
1956
+ my $dataPt = $$dirInfo{DataPt};
1957
+ return 0 if length $$dataPt < 2;
1958
+
1959
+ # 8-byte records:
1960
+ # byte 0 seems to be tag ID (0=timestamp in sec * 1e7)
1961
+ # bytes 1-3 seem to be 24-bit signed integer (unknown meaning)
1962
+ # bytes 4-7 are an int32u value, usually a multiple of 10000
1963
+
1964
+ $et->WarnOnce("Can't yet decode timed GM data", 1);
1965
+ # (see https://exiftool.org/forum/index.php?topic=11335.msg61393#msg61393)
1966
+ return 1;
1967
+ }
1968
+
1664
1969
  #------------------------------------------------------------------------------
1665
1970
  # Process QuickTime 'mebx' timed metadata
1666
1971
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
@@ -2189,6 +2494,37 @@ sub ProcessInsta360($;$)
2189
2494
  return 1;
2190
2495
  }
2191
2496
 
2497
+ #------------------------------------------------------------------------------
2498
+ # Process 360Fly 'uuid' atom containing sensor data
2499
+ # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
2500
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2501
+ # Returns: 1 on success
2502
+ sub Process360Fly($$$)
2503
+ {
2504
+ my ($et, $dirInfo, $tagTbl) = @_;
2505
+ my $dataPt = $$dirInfo{DataPt};
2506
+ my $dataLen = length $$dataPt;
2507
+ my $pos = 16;
2508
+ my $lastTime = -1;
2509
+ my $streamTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
2510
+ while ($pos + 32 <= $dataLen) {
2511
+ my $type = ord substr $$dataPt, $pos, 1;
2512
+ my $time = Get64u($dataPt, $pos + 2); # (only valid for some types)
2513
+ if ($$tagTbl{$type}) {
2514
+ if ($time != $lastTime) {
2515
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2516
+ $lastTime = $time;
2517
+ }
2518
+ }
2519
+ $et->HandleTag($tagTbl, $type, undef, DataPt => $dataPt, Start => $pos, Size => 32);
2520
+ # synthesize GPSDateTime from the timestamp for GPS records
2521
+ SetGPSDateTime($et, $streamTbl, $time / 1e6) if $type == 5;
2522
+ $pos += 32;
2523
+ }
2524
+ delete $$et{DOC_NUM};
2525
+ return 1;
2526
+ }
2527
+
2192
2528
  #------------------------------------------------------------------------------
2193
2529
  # Scan media data for "freeGPS" metadata if not found already (ref PH)
2194
2530
  # Inputs: 0) ExifTool ref