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.
- checksums.yaml +4 -4
- data/bin/Changes +119 -2
- data/bin/MANIFEST +5 -0
- data/bin/META.json +1 -1
- data/bin/META.yml +1 -1
- data/bin/README +32 -32
- data/bin/exiftool +55 -50
- data/bin/lib/Image/ExifTool.pm +155 -109
- data/bin/lib/Image/ExifTool.pod +103 -77
- data/bin/lib/Image/ExifTool/AIFF.pm +2 -2
- data/bin/lib/Image/ExifTool/APE.pm +2 -2
- data/bin/lib/Image/ExifTool/BuildTagLookup.pm +11 -6
- data/bin/lib/Image/ExifTool/Canon.pm +2 -1
- data/bin/lib/Image/ExifTool/CanonCustom.pm +82 -16
- data/bin/lib/Image/ExifTool/DPX.pm +56 -2
- data/bin/lib/Image/ExifTool/DarwinCore.pm +16 -3
- data/bin/lib/Image/ExifTool/Exif.pm +15 -6
- data/bin/lib/Image/ExifTool/Font.pm +9 -2
- data/bin/lib/Image/ExifTool/GIF.pm +5 -0
- data/bin/lib/Image/ExifTool/GeoTiff.pm +2 -0
- data/bin/lib/Image/ExifTool/GoPro.pm +10 -1
- data/bin/lib/Image/ExifTool/H264.pm +1 -1
- data/bin/lib/Image/ExifTool/ID3.pm +86 -12
- data/bin/lib/Image/ExifTool/Lang/de.pm +3 -1
- data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
- data/bin/lib/Image/ExifTool/M2TS.pm +1 -1
- data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
- data/bin/lib/Image/ExifTool/Minolta.pm +3 -2
- data/bin/lib/Image/ExifTool/Nikon.pm +134 -15
- data/bin/lib/Image/ExifTool/Olympus.pm +34 -17
- data/bin/lib/Image/ExifTool/PNG.pm +14 -3
- data/bin/lib/Image/ExifTool/PPM.pm +5 -5
- data/bin/lib/Image/ExifTool/Panasonic.pm +147 -13
- data/bin/lib/Image/ExifTool/PanasonicRaw.pm +33 -0
- data/bin/lib/Image/ExifTool/Parrot.pm +2 -1
- data/bin/lib/Image/ExifTool/Pentax.pm +2 -1
- data/bin/lib/Image/ExifTool/Photoshop.pm +2 -1
- data/bin/lib/Image/ExifTool/QuickTime.pm +204 -27
- data/bin/lib/Image/ExifTool/QuickTimeStream.pl +355 -19
- data/bin/lib/Image/ExifTool/README +20 -19
- data/bin/lib/Image/ExifTool/Ricoh.pm +19 -1
- data/bin/lib/Image/ExifTool/Shift.pl +1 -0
- data/bin/lib/Image/ExifTool/SigmaRaw.pm +40 -33
- data/bin/lib/Image/ExifTool/Sony.pm +376 -11
- data/bin/lib/Image/ExifTool/TagLookup.pm +1949 -1872
- data/bin/lib/Image/ExifTool/TagNames.pod +329 -53
- data/bin/lib/Image/ExifTool/Validate.pm +4 -4
- data/bin/lib/Image/ExifTool/WriteExif.pl +1 -0
- data/bin/lib/Image/ExifTool/WriteQuickTime.pl +23 -15
- data/bin/lib/Image/ExifTool/Writer.pl +44 -21
- data/bin/lib/Image/ExifTool/XMP.pm +41 -4
- data/bin/lib/Image/ExifTool/XMPStruct.pl +3 -1
- data/bin/lib/Image/ExifTool/ZISRAW.pm +123 -0
- data/bin/perl-Image-ExifTool.spec +31 -31
- data/lib/exiftool_vendored/version.rb +1 -1
- 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
|
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 (
|
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
|
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
|
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 =>
|
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
|
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 =>
|
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,
|
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})
|
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
|
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
|
-
|
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
|
-
#
|
1226
|
-
#
|
1227
|
-
#
|
1228
|
-
#
|
1229
|
-
#
|
1230
|
-
#
|
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
|