exiftool_vendored 13.10.0 → 13.14.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.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +82 -19
  3. data/bin/MANIFEST +6 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +3 -3
  7. data/bin/arg_files/exif2xmp.args +4 -0
  8. data/bin/arg_files/xmp2exif.args +2 -1
  9. data/bin/build_geolocation +1 -1
  10. data/bin/exiftool +208 -150
  11. data/bin/lib/File/RandomAccess.pm +1 -1
  12. data/bin/lib/File/RandomAccess.pod +2 -2
  13. data/bin/lib/Image/ExifTool/AAC.pm +1 -1
  14. data/bin/lib/Image/ExifTool/AES.pm +1 -1
  15. data/bin/lib/Image/ExifTool/AFCP.pm +6 -6
  16. data/bin/lib/Image/ExifTool/AIFF.pm +1 -1
  17. data/bin/lib/Image/ExifTool/APE.pm +1 -1
  18. data/bin/lib/Image/ExifTool/APP12.pm +1 -1
  19. data/bin/lib/Image/ExifTool/ASF.pm +1 -1
  20. data/bin/lib/Image/ExifTool/Apple.pm +1 -1
  21. data/bin/lib/Image/ExifTool/Audible.pm +1 -1
  22. data/bin/lib/Image/ExifTool/BMP.pm +1 -1
  23. data/bin/lib/Image/ExifTool/BPG.pm +1 -1
  24. data/bin/lib/Image/ExifTool/BZZ.pm +1 -1
  25. data/bin/lib/Image/ExifTool/BigTIFF.pm +1 -1
  26. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +29 -18
  27. data/bin/lib/Image/ExifTool/CBOR.pm +1 -1
  28. data/bin/lib/Image/ExifTool/Canon.pm +8 -4
  29. data/bin/lib/Image/ExifTool/CanonCustom.pm +1 -1
  30. data/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  31. data/bin/lib/Image/ExifTool/CanonVRD.pm +1 -1
  32. data/bin/lib/Image/ExifTool/CaptureOne.pm +1 -1
  33. data/bin/lib/Image/ExifTool/Casio.pm +1 -1
  34. data/bin/lib/Image/ExifTool/Charset.pm +1 -1
  35. data/bin/lib/Image/ExifTool/DICOM.pm +1 -1
  36. data/bin/lib/Image/ExifTool/DJI.pm +150 -46
  37. data/bin/lib/Image/ExifTool/DNG.pm +1 -1
  38. data/bin/lib/Image/ExifTool/DPX.pm +1 -1
  39. data/bin/lib/Image/ExifTool/DV.pm +1 -1
  40. data/bin/lib/Image/ExifTool/DarwinCore.pm +1 -1
  41. data/bin/lib/Image/ExifTool/DjVu.pm +1 -1
  42. data/bin/lib/Image/ExifTool/EXE.pm +123 -32
  43. data/bin/lib/Image/ExifTool/Exif.pm +7 -4
  44. data/bin/lib/Image/ExifTool/FITS.pm +1 -1
  45. data/bin/lib/Image/ExifTool/FLAC.pm +1 -1
  46. data/bin/lib/Image/ExifTool/FLIF.pm +1 -1
  47. data/bin/lib/Image/ExifTool/FLIR.pm +1 -1
  48. data/bin/lib/Image/ExifTool/Fixup.pm +1 -1
  49. data/bin/lib/Image/ExifTool/Flash.pm +1 -1
  50. data/bin/lib/Image/ExifTool/FlashPix.pm +1 -1
  51. data/bin/lib/Image/ExifTool/Font.pm +1 -1
  52. data/bin/lib/Image/ExifTool/FotoStation.pm +1 -1
  53. data/bin/lib/Image/ExifTool/FujiFilm.pm +1 -1
  54. data/bin/lib/Image/ExifTool/GE.pm +1 -1
  55. data/bin/lib/Image/ExifTool/GIF.pm +1 -1
  56. data/bin/lib/Image/ExifTool/GIMP.pm +1 -1
  57. data/bin/lib/Image/ExifTool/GM.pm +1 -1
  58. data/bin/lib/Image/ExifTool/GPS.pm +1 -1
  59. data/bin/lib/Image/ExifTool/GeoTiff.pm +1 -1
  60. data/bin/lib/Image/ExifTool/Geolocation.pm +17 -8
  61. data/bin/lib/Image/ExifTool/Geotag.pm +1 -1
  62. data/bin/lib/Image/ExifTool/GoPro.pm +1 -1
  63. data/bin/lib/Image/ExifTool/H264.pm +1 -1
  64. data/bin/lib/Image/ExifTool/HP.pm +1 -1
  65. data/bin/lib/Image/ExifTool/HTML.pm +1 -1
  66. data/bin/lib/Image/ExifTool/HtmlDump.pm +1 -1
  67. data/bin/lib/Image/ExifTool/ICC_Profile.pm +1 -1
  68. data/bin/lib/Image/ExifTool/ICO.pm +1 -1
  69. data/bin/lib/Image/ExifTool/ID3.pm +1 -1
  70. data/bin/lib/Image/ExifTool/IPTC.pm +8 -5
  71. data/bin/lib/Image/ExifTool/ISO.pm +1 -1
  72. data/bin/lib/Image/ExifTool/ITC.pm +1 -1
  73. data/bin/lib/Image/ExifTool/Import.pm +5 -4
  74. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  75. data/bin/lib/Image/ExifTool/InfiRay.pm +1 -1
  76. data/bin/lib/Image/ExifTool/JPEG.pm +6 -2
  77. data/bin/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  78. data/bin/lib/Image/ExifTool/JSON.pm +1 -1
  79. data/bin/lib/Image/ExifTool/JVC.pm +1 -1
  80. data/bin/lib/Image/ExifTool/Jpeg2000.pm +1 -1
  81. data/bin/lib/Image/ExifTool/Kodak.pm +1 -1
  82. data/bin/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  83. data/bin/lib/Image/ExifTool/LIF.pm +1 -1
  84. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  85. data/bin/lib/Image/ExifTool/Lang/cs.pm +1 -1
  86. data/bin/lib/Image/ExifTool/Lang/de.pm +1 -1
  87. data/bin/lib/Image/ExifTool/Lang/en_ca.pm +1 -1
  88. data/bin/lib/Image/ExifTool/Lang/en_gb.pm +1 -1
  89. data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  90. data/bin/lib/Image/ExifTool/Lang/fi.pm +1 -1
  91. data/bin/lib/Image/ExifTool/Lang/fr.pm +1 -1
  92. data/bin/lib/Image/ExifTool/Lang/it.pm +1 -1
  93. data/bin/lib/Image/ExifTool/Lang/ja.pm +1 -1
  94. data/bin/lib/Image/ExifTool/Lang/ko.pm +1 -1
  95. data/bin/lib/Image/ExifTool/Lang/nl.pm +1 -1
  96. data/bin/lib/Image/ExifTool/Lang/pl.pm +1 -1
  97. data/bin/lib/Image/ExifTool/Lang/ru.pm +1 -1
  98. data/bin/lib/Image/ExifTool/Lang/sk.pm +1 -1
  99. data/bin/lib/Image/ExifTool/Lang/sv.pm +1 -1
  100. data/bin/lib/Image/ExifTool/Lang/tr.pm +1 -1
  101. data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  102. data/bin/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  103. data/bin/lib/Image/ExifTool/Leaf.pm +1 -1
  104. data/bin/lib/Image/ExifTool/LigoGPS.pm +409 -0
  105. data/bin/lib/Image/ExifTool/Lytro.pm +1 -1
  106. data/bin/lib/Image/ExifTool/M2TS.pm +22 -13
  107. data/bin/lib/Image/ExifTool/MIE.pm +1 -1
  108. data/bin/lib/Image/ExifTool/MIEUnits.pod +1 -1
  109. data/bin/lib/Image/ExifTool/MIFF.pm +1 -1
  110. data/bin/lib/Image/ExifTool/MISB.pm +1 -1
  111. data/bin/lib/Image/ExifTool/MNG.pm +1 -1
  112. data/bin/lib/Image/ExifTool/MOI.pm +1 -1
  113. data/bin/lib/Image/ExifTool/MPC.pm +1 -1
  114. data/bin/lib/Image/ExifTool/MPEG.pm +1 -1
  115. data/bin/lib/Image/ExifTool/MPF.pm +1 -1
  116. data/bin/lib/Image/ExifTool/MRC.pm +1 -1
  117. data/bin/lib/Image/ExifTool/MWG.pm +1 -1
  118. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  119. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  120. data/bin/lib/Image/ExifTool/MakerNotes.pm +1 -1
  121. data/bin/lib/Image/ExifTool/Matroska.pm +12 -4
  122. data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
  123. data/bin/lib/Image/ExifTool/Minolta.pm +1 -1
  124. data/bin/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  125. data/bin/lib/Image/ExifTool/Motorola.pm +1 -1
  126. data/bin/lib/Image/ExifTool/Nikon.pm +166 -35
  127. data/bin/lib/Image/ExifTool/NikonCapture.pm +1 -1
  128. data/bin/lib/Image/ExifTool/NikonCustom.pm +1 -1
  129. data/bin/lib/Image/ExifTool/NikonSettings.pm +1 -1
  130. data/bin/lib/Image/ExifTool/Nintendo.pm +1 -1
  131. data/bin/lib/Image/ExifTool/OOXML.pm +1 -1
  132. data/bin/lib/Image/ExifTool/Ogg.pm +1 -1
  133. data/bin/lib/Image/ExifTool/Olympus.pm +1 -1
  134. data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
  135. data/bin/lib/Image/ExifTool/Opus.pm +1 -1
  136. data/bin/lib/Image/ExifTool/Other.pm +1 -1
  137. data/bin/lib/Image/ExifTool/PCX.pm +1 -1
  138. data/bin/lib/Image/ExifTool/PDF.pm +1 -1
  139. data/bin/lib/Image/ExifTool/PGF.pm +1 -1
  140. data/bin/lib/Image/ExifTool/PICT.pm +1 -1
  141. data/bin/lib/Image/ExifTool/PLIST.pm +1 -1
  142. data/bin/lib/Image/ExifTool/PLUS.pm +1 -1
  143. data/bin/lib/Image/ExifTool/PNG.pm +1 -1
  144. data/bin/lib/Image/ExifTool/PPM.pm +1 -1
  145. data/bin/lib/Image/ExifTool/PSP.pm +1 -1
  146. data/bin/lib/Image/ExifTool/Palm.pm +1 -1
  147. data/bin/lib/Image/ExifTool/Panasonic.pm +20 -2
  148. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +1 -1
  149. data/bin/lib/Image/ExifTool/Parrot.pm +1 -1
  150. data/bin/lib/Image/ExifTool/Pentax.pm +1 -1
  151. data/bin/lib/Image/ExifTool/PhaseOne.pm +1 -1
  152. data/bin/lib/Image/ExifTool/PhotoCD.pm +1 -1
  153. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  154. data/bin/lib/Image/ExifTool/Photoshop.pm +1 -1
  155. data/bin/lib/Image/ExifTool/PostScript.pm +1 -1
  156. data/bin/lib/Image/ExifTool/PrintIM.pm +1 -1
  157. data/bin/lib/Image/ExifTool/Protobuf.pm +48 -20
  158. data/bin/lib/Image/ExifTool/Qualcomm.pm +1 -1
  159. data/bin/lib/Image/ExifTool/QuickTime.pm +206 -66
  160. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +21 -303
  161. data/bin/lib/Image/ExifTool/README +4 -0
  162. data/bin/lib/Image/ExifTool/RIFF.pm +1 -1
  163. data/bin/lib/Image/ExifTool/RSRC.pm +1 -1
  164. data/bin/lib/Image/ExifTool/RTF.pm +1 -1
  165. data/bin/lib/Image/ExifTool/Radiance.pm +1 -1
  166. data/bin/lib/Image/ExifTool/Rawzor.pm +1 -1
  167. data/bin/lib/Image/ExifTool/Real.pm +1 -1
  168. data/bin/lib/Image/ExifTool/Reconyx.pm +1 -1
  169. data/bin/lib/Image/ExifTool/Red.pm +1 -1
  170. data/bin/lib/Image/ExifTool/Ricoh.pm +1 -1
  171. data/bin/lib/Image/ExifTool/Samsung.pm +2 -2
  172. data/bin/lib/Image/ExifTool/Sanyo.pm +1 -1
  173. data/bin/lib/Image/ExifTool/Scalado.pm +1 -1
  174. data/bin/lib/Image/ExifTool/Shift.pl +1 -1
  175. data/bin/lib/Image/ExifTool/Shortcuts.pm +1 -1
  176. data/bin/lib/Image/ExifTool/Sigma.pm +1 -1
  177. data/bin/lib/Image/ExifTool/SigmaRaw.pm +1 -1
  178. data/bin/lib/Image/ExifTool/Sony.pm +1 -1
  179. data/bin/lib/Image/ExifTool/SonyIDC.pm +1 -1
  180. data/bin/lib/Image/ExifTool/Stim.pm +1 -1
  181. data/bin/lib/Image/ExifTool/TagInfoXML.pm +2 -2
  182. data/bin/lib/Image/ExifTool/TagLookup.pm +3449 -3435
  183. data/bin/lib/Image/ExifTool/TagNames.pod +164 -47
  184. data/bin/lib/Image/ExifTool/Text.pm +1 -1
  185. data/bin/lib/Image/ExifTool/Theora.pm +1 -1
  186. data/bin/lib/Image/ExifTool/Torrent.pm +3 -3
  187. data/bin/lib/Image/ExifTool/Unknown.pm +1 -1
  188. data/bin/lib/Image/ExifTool/VCard.pm +1 -1
  189. data/bin/lib/Image/ExifTool/Validate.pm +1 -1
  190. data/bin/lib/Image/ExifTool/Vivo.pm +124 -0
  191. data/bin/lib/Image/ExifTool/Vorbis.pm +1 -1
  192. data/bin/lib/Image/ExifTool/WPG.pm +1 -1
  193. data/bin/lib/Image/ExifTool/WTV.pm +1 -1
  194. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  195. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  196. data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  197. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -1
  198. data/bin/lib/Image/ExifTool/WritePNG.pl +1 -1
  199. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  200. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -1
  201. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +74 -43
  202. data/bin/lib/Image/ExifTool/WriteRIFF.pl +1 -1
  203. data/bin/lib/Image/ExifTool/WriteXMP.pl +1 -1
  204. data/bin/lib/Image/ExifTool/Writer.pl +21 -16
  205. data/bin/lib/Image/ExifTool/XISF.pm +1 -1
  206. data/bin/lib/Image/ExifTool/XMP.pm +1 -1
  207. data/bin/lib/Image/ExifTool/XMP2.pl +1 -1
  208. data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -1
  209. data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
  210. data/bin/lib/Image/ExifTool/ZISRAW.pm +1 -1
  211. data/bin/lib/Image/ExifTool/iWork.pm +1 -1
  212. data/bin/lib/Image/ExifTool.pm +34 -12
  213. data/bin/lib/Image/ExifTool.pod +57 -46
  214. data/bin/perl-Image-ExifTool.spec +1 -1
  215. data/lib/exiftool_vendored/version.rb +1 -1
  216. metadata +4 -2
@@ -1258,7 +1258,7 @@ and values.
1258
1258
 
1259
1259
  =head1 AUTHOR
1260
1260
 
1261
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
1261
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
1262
1262
 
1263
1263
  This library is free software; you can redistribute it and/or modify it
1264
1264
  under the same terms as Perl itself.
@@ -5718,7 +5718,7 @@ and values.
5718
5718
 
5719
5719
  =head1 AUTHOR
5720
5720
 
5721
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
5721
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
5722
5722
 
5723
5723
  This library is free software; you can redistribute it and/or modify it
5724
5724
  under the same terms as Perl itself.
@@ -1789,7 +1789,7 @@ and values.
1789
1789
 
1790
1790
  =head1 AUTHOR
1791
1791
 
1792
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
1792
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
1793
1793
 
1794
1794
  This library is free software; you can redistribute it and/or modify it
1795
1795
  under the same terms as Perl itself.
@@ -620,7 +620,7 @@ and values.
620
620
 
621
621
  =head1 AUTHOR
622
622
 
623
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
623
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
624
624
 
625
625
  This library is free software; you can redistribute it and/or modify it
626
626
  under the same terms as Perl itself.
@@ -528,7 +528,7 @@ and values.
528
528
 
529
529
  =head1 AUTHOR
530
530
 
531
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
531
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
532
532
 
533
533
  This library is free software; you can redistribute it and/or modify it
534
534
  under the same terms as Perl itself.
@@ -1313,7 +1313,7 @@ and values.
1313
1313
 
1314
1314
  =head1 AUTHOR
1315
1315
 
1316
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
1316
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
1317
1317
 
1318
1318
  This library is free software; you can redistribute it and/or modify it
1319
1319
  under the same terms as Perl itself.
@@ -784,7 +784,7 @@ and values.
784
784
 
785
785
  =head1 AUTHOR
786
786
 
787
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
787
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
788
788
 
789
789
  This library is free software; you can redistribute it and/or modify it
790
790
  under the same terms as Perl itself.
@@ -504,7 +504,7 @@ Capture.
504
504
 
505
505
  =head1 AUTHOR
506
506
 
507
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
507
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
508
508
 
509
509
  This library is free software; you can redistribute it and/or modify it
510
510
  under the same terms as Perl itself.
@@ -0,0 +1,409 @@
1
+ #------------------------------------------------------------------------------
2
+ # File: LigoGPS.pm
3
+ #
4
+ # Description: Read LIGOGPSINFO timed GPS records
5
+ #
6
+ # Revisions: 2024-12-30 - P. Harvey Created
7
+ #------------------------------------------------------------------------------
8
+ package Image::ExifTool::LigoGPS;
9
+
10
+ use strict;
11
+ use vars qw($VERSION);
12
+ use Image::ExifTool;
13
+
14
+ $VERSION = '1.02';
15
+
16
+ sub ProcessLigoGPS($$$;$);
17
+ sub ProcessLigoJSON($$$);
18
+ sub OrderCipherDigits($$$;$);
19
+
20
+ my $knotsToKph = 1.852; # knots --> km/h
21
+
22
+ #------------------------------------------------------------------------------
23
+ # Clean up cipher variables and print warning if deciphering was unsuccessful
24
+ # Inputs: 0) ExifTool ref
25
+ sub CleanupCipher($)
26
+ {
27
+ my $et = shift;
28
+ if ($$et{LigoCipher} and $$et{LigoCipher}{'next'}) {
29
+ $et->Warn('Not enough GPS points to determine cipher for decoding LIGOGPSINFO');
30
+ }
31
+ delete $$et{LigoCipher};
32
+ }
33
+
34
+ #------------------------------------------------------------------------------
35
+ # Un-do LIGOGPS fuzzing
36
+ # Inputs: 0) fuzzed latitude, 1) fuzzed longitude, 2) scale factor
37
+ # Returns: 0) latitude, 1) longitude
38
+ sub UnfuzzLigoGPS($$$)
39
+ {
40
+ my ($lat, $lon, $scl) = @_;
41
+ my $lat2 = int($lat / 10) * 10;
42
+ my $lon2 = int($lon / 10) * 10;
43
+ return($lat2 + ($lon - $lon2) * $scl, $lon2 + ($lat - $lat2) * $scl);
44
+ }
45
+
46
+ #------------------------------------------------------------------------------
47
+ # Decrypt LIGOGPSINFO record (starting with "####")
48
+ # Inputs: 0) encrypted GPS record incuding 8-byte header
49
+ # Returns: decrypted record including 4-byte uint32 header, or undef on error
50
+ sub DecryptLigoGPS($)
51
+ {
52
+ my $str = shift;
53
+ my $num = unpack('x4V',$str);
54
+ return undef if $num < 4;
55
+ $num = 0x84 if $num > 0x84; # (be safe)
56
+ my @in = unpack("x8C$num",$str);
57
+ my @out;
58
+ while (@in) {
59
+ my $b = shift @in; # get next byte in data
60
+ # upper 3 bits steer the decryption for this round
61
+ my $steeringBits = $b & 0xe0;
62
+ if ($steeringBits >= 0xc0) {
63
+ return undef if @in < 4; # next 4 bytes are encrypted data
64
+ push @out, (shift(@in) | $b & 0x01) ^ 0x20,
65
+ (shift(@in) | $b & 0x02) ^ 0x20,
66
+ (shift(@in) | $b & 0x0c) ^ 0x20,
67
+ shift(@in) ^ 0x20 | $b & 0x30;
68
+ } elsif ($steeringBits >= 0x40) {
69
+ return undef if @in < 3; # next 3 bytes are encrypted data
70
+ if ($steeringBits == 0x40) {
71
+ push @out, 0x20,
72
+ (shift(@in) | $b & 0x01) ^ 0x20,
73
+ (shift(@in) | $b & 0x06) ^ 0x20,
74
+ (shift(@in) | $b & 0x18) ^ 0x20;
75
+ } elsif ($steeringBits == 0x60) {
76
+ push @out, (shift(@in) | $b & 0x03) ^ 0x20,
77
+ 0x20,
78
+ (shift(@in) | $b & 0x04) ^ 0x20,
79
+ (shift(@in) | $b & 0x18) ^ 0x20;
80
+ } elsif ($steeringBits == 0x80) {
81
+ push @out, (shift(@in) | $b & 0x03) ^ 0x20,
82
+ (shift(@in) | $b & 0x0c) ^ 0x20,
83
+ 0x20,
84
+ (shift(@in) | $b & 0x10) ^ 0x20;
85
+ } else {
86
+ push @out, (shift(@in) | $b & 0x01) ^ 0x20,
87
+ (shift(@in) | $b & 0x06) ^ 0x20,
88
+ (shift(@in) | $b & 0x18) ^ 0x20,
89
+ 0x20;
90
+ }
91
+ } elsif ($steeringBits == 0x00) {
92
+ return undef if @in < 1; # next byte is encrypted data
93
+ push @out, shift(@in) | $b & 0x13;
94
+ } else {
95
+ return undef; # (shouldn't happen)
96
+ }
97
+ }
98
+ return pack 'C*', @out;
99
+ }
100
+
101
+ #------------------------------------------------------------------------------
102
+ # Determine correct ordering of enciphered digits (unit digits of seconds)
103
+ # Inputs: 0) starting character code, 1) lookup for next character(s) in sequence
104
+ # 2) i/o list of ordered characters, 3) hash of used characters
105
+ # Returns: true if a consistent ordering was found
106
+ # - loops through all possible orders based on $next sequence until a complete
107
+ # cycle is established
108
+ # - this complexity is necessary because GPS may skip some seconds
109
+ sub OrderCipherDigits($$$;$)
110
+ {
111
+ my ($ch, $next, $order, $did) = @_;
112
+ $did or $did = { };
113
+ while ($$next{$ch}) {
114
+ if (@$order < 10) {
115
+ last if $$did{$ch};
116
+ } else {
117
+ # success if we have cycled through all 10 digits and back to the first
118
+ return 1 if @$order == 10 and $ch eq $$order[0];
119
+ last;
120
+ }
121
+ push @$order, $ch;
122
+ $$did{$ch} = 1;
123
+ # continue with next character if there is only one possibility
124
+ @{$$next{$ch}} == 1 and $ch = $$next{$ch}[0], next;
125
+ # otherwise, test all possibilities
126
+ my $n = $#$order;
127
+ foreach (@{$$next{$ch}}) {
128
+ my %did = %$did; # make a copy of the used-character lookup
129
+ return 1 if OrderCipherDigits($_, $next, $order, \%did);
130
+ $#$order = $n; # restore order and try next possibility
131
+ }
132
+ last;
133
+ }
134
+ return 0; # failure
135
+ }
136
+
137
+ #------------------------------------------------------------------------------
138
+ # Decipher and parse LIGOGPSINFO record (starting with "####")
139
+ # Inputs: 0) ExifTool ref, 1) enciphered string, 2) tag table ref
140
+ # 3) true if GPS coordinates don't need de-fuzzing
141
+ # Returns: true if this looked like an enciphered string
142
+ # Notes: handles contained tags, but may defer handling until full cipher is known
143
+ sub DecipherLigoGPS($$$;$)
144
+ {
145
+ my ($et, $str, $tagTbl, $noFuzz) = @_;
146
+
147
+ # (enciphered characters must be in the range 0x30-0x5f ('0' - '_'))
148
+ $str =~ m[^####.{4}([0-_])[0-_]{3}/[0-_]{2}/[0-_]{2} ..([0-_])..([0-_]).([0-_]) ]s or return undef;
149
+ return undef unless $2 eq $3; # (colons in time string must be the same)
150
+
151
+ my $cipherInfo = $$et{LigoCipher};
152
+ unless ($cipherInfo) {
153
+ $cipherInfo = $$et{LigoCipher} = { cache => [ ], 'next' => { } };
154
+ $et->AddCleanup(\&CleanupCipher);
155
+ };
156
+ my $decipher = $$cipherInfo{decipher};
157
+ my $cache = $$cipherInfo{cache};
158
+
159
+ # determine the cipher code table based on the advancing 1's digit of seconds
160
+ unless ($decipher) {
161
+ push @$cache, $str; # cache records until we can decipher them
162
+ my $next = $$cipherInfo{next};
163
+ my ($millennium, $colon, $ch2) = ($1, $2, $4);
164
+ # determine the cipher lookup table
165
+ # (only characters in range 0x30-0x5f are encrypted)
166
+ my $ch1 = $$cipherInfo{ch1};
167
+ $$cipherInfo{ch1} = $ch2;
168
+ return 1 if not defined $ch1 or $ch1 eq $ch2; # ignore duplicate sequential digits
169
+ if ($$next{$ch1}) {
170
+ return 1 if grep /\Q$ch2\E/, @{$$next{$ch1}}; # don't add twice
171
+ push @{$$next{$ch1}}, $ch2;
172
+ } else {
173
+ $$next{$ch1} = [ $ch2 ];
174
+ }
175
+ # must wait until the lookup contains all 10 digits
176
+ scalar(keys %$next) < 10 and return 1;
177
+ # protect against trying to decipher bad data
178
+ scalar(keys %$next) > 10 and $$cipherInfo{'next'} = { }, return 1;
179
+ my (@order, $two);
180
+ return 1 unless OrderCipherDigits($ch1, $next, \@order);
181
+ # get index of enciphered "2" in ordered array
182
+ $order[$_] eq $millennium and $two = $_, last foreach 0..9;
183
+ defined $two or $et->Warn('Problem deciphering LIGOGPSINFO'), return 1;
184
+ delete $$cipherInfo{'next'}; # all done with 'next' lookup
185
+ my %decipher = ( $colon => ':' ); # (':' is the time separator)
186
+ foreach (0..9) {
187
+ my $ch = $order[($_ + $two - 2 + 10) % 10];
188
+ $decipher{$ch} = chr($_ + 0x30);
189
+ }
190
+ # may also know the lat/lon quadrant from the signs of the coordinates
191
+ if ($str =~ / ([0-_])$colon(-?).*? ([0-_])$colon(-?)/) {
192
+ @decipher{$1,$3} = ($2 ? 'S' : 'N', $4 ? 'W' : 'E');
193
+ unless ($2 or $4) {
194
+ my ($ns, $ew) = ($1, $3);
195
+ if ($$et{OPTIONS}{GPSQuadrant} and $$et{OPTIONS}{GPSQuadrant} =~ /^([NS])([EW])$/i) {
196
+ @decipher{$ns,$ew} = (uc($1), uc($2));
197
+ } else {
198
+ $et->Warn('May need to set API GPSQuadrant option (eg. "NW")');
199
+ }
200
+ }
201
+ }
202
+ # fill in unknown entries with '?' (only chars 0x30-0x5f are enciphered)
203
+ defined $decipher{$_} or $decipher{$_} = '?' foreach map(chr, 0x30..0x5f);
204
+ $decipher = $$cipherInfo{decipher} = \%decipher;
205
+ $str = shift @$cache; # start deciphering at oldest cache entry
206
+ }
207
+
208
+ # apply reverse cipher and extract GPS information
209
+ do {
210
+ my $pre = substr($str, 4, 4); # save second 4 bytes of header
211
+ ($str = substr($str,8)) =~ s/\0+$//; # remove 8-byte header and null padding
212
+ $str =~ s/([0-_])/$$decipher{$1}/g; # decipher
213
+ if ($$et{OPTIONS}{Verbose} > 1) {
214
+ $et->VPrint(1, "$$et{INDENT}\(Deciphered: ".unpack('H8',$pre)." $str)\n");
215
+ }
216
+ # add back leading 4 bytes (int16u counter plus 2 unknown bytes), and parse
217
+ ParseLigoGPS($et, "$pre$str", $tagTbl, $noFuzz);
218
+ } while $str = shift @$cache;
219
+
220
+ return 1;
221
+ }
222
+
223
+ #------------------------------------------------------------------------------
224
+ # Parse decrypted/deciphered (but not defuzzed) LIGOGPSINFO record
225
+ # (record starts with 4-byte int32u counter followed by date/time, etc)
226
+ # Inputs: 0) ExifTool ref, 1) GPS string, 2) tag table ref, 3) not fuzzed
227
+ # Returns: nothing
228
+ sub ParseLigoGPS($$$;$)
229
+ {
230
+ my ($et, $str, $tagTbl, $noFuzz) = @_;
231
+
232
+ # example string input
233
+ # "....2022/09/19 12:45:24 N:31.285065 W:124.759483 46.93 km/h x:-0.000 y:-0.000 z:-0.000"
234
+ unless ($str=~ /^.{4}(\S+ \S+)\s+([NS?]):(-?)([.\d]+)\s+([EW?]):(-?)([\.\d]+)\s+([.\d]+)/s) {
235
+ $et->Warn('LIGOGPSINFO format error');
236
+ return;
237
+ }
238
+ my ($time,$latRef,$latNeg,$lat,$lonRef,$lonNeg,$lon,$spd) = ($1,$2,$3,$4,$5,$6,$7,$8);
239
+ my %gpsScl = ( 1 => 1.524855137, 2 => 1.456027985, 3 => 1.15368 );
240
+ my $spdScl = $noFuzz ? $knotsToKph : 1.85407333;
241
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
242
+ $time =~ tr(/)(:);
243
+ # convert from DDMM.MMMMMM to DD.DDDDDD if necessary
244
+ # (speed wasn't scaled in my 1 sample with this format)
245
+ $lat =~ /^\d{3}/ and Image::ExifTool::QuickTime::ConvertLatLon($lat,$lon), $spdScl = 1;
246
+ unless ($noFuzz) { # unfuzz the coordinates if necessary
247
+ my $scl = $$et{OPTIONS}{LigoGPSScale} || $$et{LigoGPSScale} || 1;
248
+ $scl = $gpsScl{$scl} if $gpsScl{$scl};
249
+ ($lat, $lon) = UnfuzzLigoGPS($lat, $lon, $scl);
250
+ }
251
+ # a final sanity check
252
+ ($lat > 90 or $lon > 180) and $et->Warn('LIGOGPSINFO coordinates out of range'), return;
253
+ $$et{SET_GROUP1} = 'LIGO';
254
+ $et->HandleTag($tagTbl, 'GPSDateTime', $time);
255
+ # (ignore N/S/E/W if coordinate is signed)
256
+ $et->HandleTag($tagTbl, 'GPSLatitude', $lat * (($latNeg or $latRef eq 'S') ? -1 : 1));
257
+ $et->HandleTag($tagTbl, 'GPSLongitude', $lon * (($lonNeg or $lonRef eq 'W') ? -1 : 1));
258
+ $et->HandleTag($tagTbl, 'GPSSpeed', $spd * $spdScl);
259
+ $et->HandleTag($tagTbl, 'GPSTrack', $1) if $str =~ /\bA:(\S+)/;
260
+ $et->HandleTag($tagTbl, 'GPSAltitude', $1) if $str =~ /\bH:(\S+)/;
261
+ $et->HandleTag($tagTbl, 'MagneticVariation', $1) if $str =~ /\bM:(\S+)/;
262
+ # (have a sample where tab is used to separate acc components)
263
+ $et->HandleTag($tagTbl, 'Accelerometer',"$1 $2 $3") if $str =~ /x:(\S+)\sy:(\S+)\sz:(\S+)/;
264
+ delete $$et{SET_GROUP1};
265
+ }
266
+
267
+ #------------------------------------------------------------------------------
268
+ # Process LIGOGPSINFO data (non-JSON format)
269
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
270
+ # 3) 1=LIGOGPS lat/lon/spd weren't fuzzed
271
+ # Returns: 1 on success
272
+ # Notes: The directory data should start with the string "LIGOGPSINFO\0"
273
+ sub ProcessLigoGPS($$$;$)
274
+ {
275
+ my ($et, $dirInfo, $tagTbl, $noFuzz) = @_;
276
+ my $dataPt = $$dirInfo{DataPt};
277
+ my $pos = ($$dirInfo{DirStart} || 0) + 0x14;
278
+ return undef if $pos > length $$dataPt;
279
+ my $cipherInfo = $$et{LigoCipher};
280
+ my $dirName = $$dirInfo{DirName} || 'LigoGPS';
281
+ push @{$$et{PATH}}, $dirName unless $$dirInfo{DirID};
282
+ # not fuzzed if header =~ /LIGOGPSINFO\0\0\0\0[\x01\x14]/ (\x01=BlueSkySeaDV688)
283
+ $noFuzz = 1 if substr($$dataPt, $pos-8, 4) =~ /^\0\0\0[\x01\x14]/;
284
+ $et->VerboseDir($dirName);
285
+ for (; $pos + 0x84 <= length($$dataPt); $pos+=0x84) {
286
+ my $dat = substr($$dataPt, $pos, 0x84);
287
+ $dat =~ /^####/ or next; # (have seen blank records filled with zeros, so keep trying)
288
+ # decipher if we already know the encryption
289
+ $cipherInfo and $$cipherInfo{decipher} and DecipherLigoGPS($et, $dat, $tagTbl, $noFuzz) and next;
290
+ my $str = DecryptLigoGPS($dat);
291
+ defined $str or DecipherLigoGPS($et, $dat, $tagTbl, $noFuzz), next; # try to decipher
292
+ $et->VPrint(1, "$$et{INDENT}\(Decrypted: ",unpack('V',$str),' ',substr($str,4),")\n") if $$et{OPTIONS}{Verbose} > 1;
293
+ ParseLigoGPS($et, $str, $tagTbl, $noFuzz);
294
+ }
295
+ pop @{$$et{PATH}} unless $$dirInfo{DirID};
296
+ delete $$et{DOC_NUM};
297
+ return 1;
298
+ }
299
+
300
+ #------------------------------------------------------------------------------
301
+ # Process LIGOGPSINFO JSON-format GPS (Yada RoadCam Pro 4K BT58189)
302
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
303
+ # Returns: 1 on success
304
+ # Sample data (chained 512-byte records starting like this):
305
+ # 0000: 4c 49 47 4f 47 50 53 49 4e 46 4f 20 7b 22 48 6f [LIGOGPSINFO {"Ho]
306
+ # 0010: 75 72 22 3a 20 22 32 33 22 2c 20 22 4d 69 6e 75 [ur": "23", "Minu]
307
+ # 0020: 74 65 22 3a 20 22 31 30 22 2c 20 22 53 65 63 6f [te": "10", "Seco]
308
+ # 0030: 6e 64 22 3a 20 22 32 32 22 2c 20 22 59 65 61 72 [nd": "22", "Year]
309
+ # 0040: 22 3a 20 22 32 30 32 33 22 2c 20 22 4d 6f 6e 74 [": "2023", "Mont]
310
+ # 0050: 68 22 3a 20 22 31 32 22 2c 20 22 44 61 79 22 3a [h": "12", "Day":]
311
+ # 0060: 20 22 32 38 22 2c 20 22 73 74 61 74 75 73 22 3a [ "28", "status":]
312
+ sub ProcessLigoJSON($$$)
313
+ {
314
+ my ($et, $dirInfo, $tagTbl) = @_;
315
+ my $dataPt = $$dirInfo{DataPt};
316
+ my $dirLen = $$dirInfo{DirLen};
317
+ require Image::ExifTool::Import;
318
+ $et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
319
+ $$et{SET_GROUP1} = 'LIGO';
320
+ while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
321
+ my $json = $1;
322
+ my %dbase;
323
+ Image::ExifTool::Import::ReadJSON(\$json, \%dbase);
324
+ my $info = $dbase{'*'} or next;
325
+ # my sample contains the following JSON fields (in this order):
326
+ # Hour Minute Second Year Month Day (GPS UTC time)
327
+ # status NS EW Latitude Longitude Speed (speed in knots)
328
+ # GsensorX GsensorY GsensorZ (units? - only seen "000" for all)
329
+ # MHour MMinute MSecond MYear MMonth MDay (local dashcam clock time)
330
+ # OLatitude OLongitude (? same values as Latitude/Longitude)
331
+ next unless defined $$info{status} and $$info{status} eq 'A'; # only read if GPS is active
332
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
333
+ my $num = 0;
334
+ defined $$info{$_} and ++$num foreach qw(Year Month Day Hour Minute Second);
335
+ if ($num == 6) {
336
+ # this is the GPS time in UTC
337
+ my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ',@$info{qw{Year Month Day Hour Minute Second}});
338
+ $et->HandleTag($tagTbl, GPSDateTime => $time);
339
+ }
340
+ if ($$info{Latitude} and $$info{Longitude}) {
341
+ my $lat = $$info{Latitude};
342
+ $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
343
+ my $lon = $$info{Longitude};
344
+ $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
345
+ $et->HandleTag($tagTbl, GPSLatitude => $lat);
346
+ $et->HandleTag($tagTbl, GPSLongitude => $lon);
347
+ }
348
+ $et->HandleTag($tagTbl, GPSSpeed => $$info{Speed} * $knotsToKph) if defined $$info{Speed};
349
+ if (defined $$info{GsensorX} and defined $$info{GsensorY} and defined $$info{GsensorZ}) {
350
+ # (don't know conversion factor for accel data, so leave it raw for now)
351
+ $et->HandleTag($tagTbl, Accelerometer => "$$info{GsensorX} $$info{GsensorY} $$info{GsensorZ}");
352
+ }
353
+ $num = 0;
354
+ defined $$info{$_} and ++$num foreach qw(MYear MMonth MDay MHour MMinute MSecond);
355
+ if ($num == 6) {
356
+ # this is the dashcam clock time (local time zone)
357
+ my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d',@$info{qw{MYear MMonth MDay MHour MMinute MSecond}});
358
+ $et->HandleTag($tagTbl, DateTimeOriginal => $time);
359
+ }
360
+ if (defined $$info{OLatitude} and defined $$info{OLongitude}) {
361
+ my $lat = $$info{OLatitude};
362
+ $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
363
+ my $lon = $$info{OLongitude};
364
+ $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
365
+ $et->HandleTag($tagTbl, GPSLatitude2 => $lat);
366
+ $et->HandleTag($tagTbl, GPSLongitude2 => $lon);
367
+ }
368
+ unless ($et->Options('ExtractEmbedded')) {
369
+ $et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
370
+ last;
371
+ }
372
+ }
373
+ delete $$et{DOC_NUM};
374
+ delete $$et{SET_GROUP1};
375
+ return 1;
376
+ }
377
+
378
+ 1; #end
379
+
380
+
381
+ __END__
382
+
383
+ =head1 NAME
384
+
385
+ Image::ExifTool::LigoGPS - Read LIGOGPSINFO timed GPS records
386
+
387
+ =head1 SYNOPSIS
388
+
389
+ This module is loaded automatically by Image::ExifTool when required.
390
+
391
+ =head1 DESCRIPTION
392
+
393
+ This module decrypts, deciphers and decodes timed GPS metadata from
394
+ LIGOGPSINFO records found in various locations of MP4 and M2TS videos from a
395
+ variety of dashcam makes and models.
396
+
397
+ =head1 AUTHOR
398
+
399
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
400
+
401
+ This library is free software; you can redistribute it and/or modify it
402
+ under the same terms as Perl itself.
403
+
404
+ =head1 SEE ALSO
405
+
406
+ L<Image::ExifTool::TagNames/QuickTime Stream Tags>,
407
+ L<Image::ExifTool(3pm)|Image::ExifTool>
408
+
409
+ =cut
@@ -192,7 +192,7 @@ from Lytro Light Field Picture (LFP) files.
192
192
 
193
193
  =head1 AUTHOR
194
194
 
195
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
195
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
196
196
 
197
197
  This library is free software; you can redistribute it and/or modify it
198
198
  under the same terms as Perl itself.
@@ -32,7 +32,7 @@ use strict;
32
32
  use vars qw($VERSION);
33
33
  use Image::ExifTool qw(:DataAccess :Utils);
34
34
 
35
- $VERSION = '1.28';
35
+ $VERSION = '1.29';
36
36
 
37
37
  # program map table "stream_type" lookup (ref 6/1/9)
38
38
  my %streamType = (
@@ -310,7 +310,7 @@ sub ParsePID($$$$$)
310
310
  if ($$dataPt =~ /^LIGOGPSINFO/s) {
311
311
  my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
312
312
  my %dirInfo = ( DataPt => $dataPt, DirName => 'Ligo0x0300' );
313
- Image::ExifTool::QuickTime::ProcessLigoGPS($et, \%dirInfo, $tbl, 1);
313
+ Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl, 1);
314
314
  $$et{FoundGoodGPS} = 1;
315
315
  $more = 1;
316
316
  }
@@ -437,13 +437,16 @@ sub ParsePID($$$$$)
437
437
  $more = 1;
438
438
  } elsif ($$dataPt =~ /\$GPRMC,/) {
439
439
  # Jomise T860S-GM dashcam
440
- # $GPRMC,hhmmss.ss,A,ddmm.mmmmm,N,dddmm.mmmmm,W,spd-kts,dir-dg,DDMMYY,,*cs
441
- # $GPRMC,172255.00,A,:985.95194,N,17170.14674,W,029.678,170.68,240822,,,D*7B
442
- # $GPRMC,172355.00,A,:984.76779,N,17170.00473,W,032.219,172.04,240822,,,D*7B
443
- # ddmm.mmmm: from 4742.2568 12209.2028 (should be)
444
- # to 4741.7696 12209.1056
445
- # stamped on video: 47.70428N, 122.15338W, 35mph (dd.ddddd)
446
- # to 47.69616N, 122.15176W, 37mph
440
+ # $GPRMC,hhmmss.ss,A,ddmm.mmmmm,N,dddmm.mmmmm,W,spd-kts,dir-dg,DDMMYY,,M*cs - lat,lon,spd from video
441
+ # $GPRMC,172255.00,A,:985.95194,N,17170.14674,W,029.678,170.68,240822,,,D*7B - N47.70428,W122.15338,35mph
442
+ # $GPRMC,192643.00,A,:987.94979,N,17171.07268,W,010.059,079.61,111122,,,A*73 - N47.71862,W122.16437,12mph
443
+ # $GPRMC,192743.00,A,:988.72110,N,17171.04873,W,017.477,001.03,111122,,,A*78 - N47.72421,W122.16408,20mph
444
+ # $GPRMC,192844.00,A,:989.43771,N,17171.03538,W,016.889,001.20,111122,,,A*7B - N47.72932,W122.16393,19mph
445
+ # $GPRMC,005241.00,A,:987.70873,N,17171.81293,W,000.284,354.78,141122,,,A*7F - N47.71687,W122.17318,0mph
446
+ # $GPRMC,005341.00,A,:987.90851,N,17171.85380,W,000.080,349.36,141122,,,A*7C - N47.71832,W122.17367,0mph
447
+ # $GPRMC,005441.00,A,:987.94538,N,17171.21783,W,029.686,091.09,141122,,,A*7A - N47.71859,W122.16630,35mph
448
+ # $GPRMC,002816.00,A,6820.67273,N,13424.26599,W,000.045,000.00,261122,,,A*79 - N29.52096,W95.55953,0mph (seattle)
449
+ # $GPRMC,035136.00,A,:981.47322,N,17170.14105,W,024.594,180.50,291122,,,D*79 - N47.67180,W122.15328,28mph
447
450
  my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
448
451
  while ($$dataPt =~ /\$[A-Z]{2}RMC,(\d{2})(\d{2})(\d+(\.\d*)?),A?,(.{2})(\d{2}\.\d+),([NS]),(.{3})(\d{2}\.\d+),([EW]),(\d*\.?\d*),(\d*\.?\d*),(\d{2})(\d{2})(\d+)/g and
449
452
  # do some basic sanity checks on the date
@@ -477,6 +480,11 @@ sub ParsePID($$$$$)
477
480
  $et->HandleTag($tagTbl, GPSLongitude => (($lo || 0) + (($9-70.14674)/1.460987654320988+9.2028)/60) * ($10 eq 'E' ? 1 : -1));
478
481
  }
479
482
  }
483
+ } elsif ($$dataPt =~ /\$GSENSORD,\s*(\d+),\s*(\d+),\s*(\d+),/) {
484
+ # Jomise T860S-GM dashcam
485
+ my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
486
+ $$et{DOC_NUM} = $$et{DOC_COUNT};
487
+ $et->HandleTag($tagTbl, Accelerometer => "$1 $2 $3"); # (NC - values range from 0 to 6)
480
488
  } elsif ($$dataPt =~ /^.{44}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s and length($$dataPt) >= 84) {
481
489
  #forum11320
482
490
  SetByteOrder('II');
@@ -531,7 +539,7 @@ sub ParsePID($$$$$)
531
539
  # only extract data from the first one)
532
540
  my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
533
541
  my %dirInfo = ( DataPt => $dataPt, DirStart => 8, DirName => sprintf('Ligo0x%.4x',$pid));
534
- Image::ExifTool::QuickTime::ProcessLigoGPS($et, \%dirInfo, $tbl, 1);
542
+ Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl, 1);
535
543
  $$et{FoundGoodGPS} = 1;
536
544
  } elsif ($$et{FoundGoodGPS}) {
537
545
  $more = 1;
@@ -592,7 +600,8 @@ sub ProcessM2TS($$)
592
600
  my %gpsPID = (
593
601
  0x0300 => 1, # Novatek INNOVV, DOD_LS600W
594
602
  0x01e4 => 1, # vsys a6l dashcam
595
- 0x0e1b => 1, # Jomise T860S-GM dashcam
603
+ 0x0e1b => 1, # Jomise T860S-GM dashcam GPS
604
+ 0x0e1a => 1, # Jomise T860S-GM dashcam accelerometer
596
605
  );
597
606
  my $pEnd = 0;
598
607
 
@@ -980,7 +989,7 @@ sub ProcessM2TS($$)
980
989
  if ($len < $raf->Tell() and $raf->Seek(-$len, 2) and $raf->Read($buff,$len) == $len) {
981
990
  my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
982
991
  my %dirInfo = ( DataPt => \$buff, DirStart => 8, DirName => 'LigoTrailer' );
983
- Image::ExifTool::QuickTime::ProcessLigoGPS($et, \%dirInfo, $tbl);
992
+ Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
984
993
  }
985
994
  }
986
995
 
@@ -1007,7 +1016,7 @@ video.
1007
1016
 
1008
1017
  =head1 AUTHOR
1009
1018
 
1010
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
1019
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
1011
1020
 
1012
1021
  This library is free software; you can redistribute it and/or modify it
1013
1022
  under the same terms as Perl itself.
@@ -2557,7 +2557,7 @@ tag name. For example:
2557
2557
 
2558
2558
  =head1 AUTHOR
2559
2559
 
2560
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
2560
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
2561
2561
 
2562
2562
  This library is free software; you can redistribute it and/or modify it
2563
2563
  under the same terms as Perl itself. The MIE format itself is also
@@ -356,7 +356,7 @@ A few examples of combined units strings:
356
356
 
357
357
  =head1 AUTHOR
358
358
 
359
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
359
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
360
360
 
361
361
  This library is free software; you can redistribute it and/or modify it
362
362
  under the same terms as Perl itself.
@@ -255,7 +255,7 @@ This module contains routines required by Image::ExifTool to read MIFF
255
255
 
256
256
  =head1 AUTHOR
257
257
 
258
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
258
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
259
259
 
260
260
  This library is free software; you can redistribute it and/or modify it
261
261
  under the same terms as Perl itself.
@@ -468,7 +468,7 @@ Board (MISB) KLV-format metadata from M2TS videos.
468
468
 
469
469
  =head1 AUTHOR
470
470
 
471
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
471
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
472
472
 
473
473
  This library is free software; you can redistribute it and/or modify it
474
474
  under the same terms as Perl itself.
@@ -661,7 +661,7 @@ This module contains definitions required by Image::ExifTool to read MNG
661
661
 
662
662
  =head1 AUTHOR
663
663
 
664
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
664
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
665
665
 
666
666
  This library is free software; you can redistribute it and/or modify it
667
667
  under the same terms as Perl itself.
@@ -137,7 +137,7 @@ information from MOI files.
137
137
 
138
138
  =head1 AUTHOR
139
139
 
140
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
140
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
141
141
 
142
142
  This library is free software; you can redistribute it and/or modify it
143
143
  under the same terms as Perl itself.
@@ -134,7 +134,7 @@ information from Musepack (MPC) audio files.
134
134
 
135
135
  =head1 AUTHOR
136
136
 
137
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
137
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
138
138
 
139
139
  This library is free software; you can redistribute it and/or modify it
140
140
  under the same terms as Perl itself.