exiftool_vendored 13.10.0 → 13.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +33 -0
  3. data/bin/MANIFEST +1 -0
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +3 -3
  7. data/bin/exiftool +206 -148
  8. data/bin/lib/File/RandomAccess.pm +1 -1
  9. data/bin/lib/File/RandomAccess.pod +2 -2
  10. data/bin/lib/Image/ExifTool/AAC.pm +1 -1
  11. data/bin/lib/Image/ExifTool/AES.pm +1 -1
  12. data/bin/lib/Image/ExifTool/AFCP.pm +1 -1
  13. data/bin/lib/Image/ExifTool/AIFF.pm +1 -1
  14. data/bin/lib/Image/ExifTool/APE.pm +1 -1
  15. data/bin/lib/Image/ExifTool/APP12.pm +1 -1
  16. data/bin/lib/Image/ExifTool/ASF.pm +1 -1
  17. data/bin/lib/Image/ExifTool/Apple.pm +1 -1
  18. data/bin/lib/Image/ExifTool/Audible.pm +1 -1
  19. data/bin/lib/Image/ExifTool/BMP.pm +1 -1
  20. data/bin/lib/Image/ExifTool/BPG.pm +1 -1
  21. data/bin/lib/Image/ExifTool/BZZ.pm +1 -1
  22. data/bin/lib/Image/ExifTool/BigTIFF.pm +1 -1
  23. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +6 -6
  24. data/bin/lib/Image/ExifTool/CBOR.pm +1 -1
  25. data/bin/lib/Image/ExifTool/Canon.pm +8 -4
  26. data/bin/lib/Image/ExifTool/CanonCustom.pm +1 -1
  27. data/bin/lib/Image/ExifTool/CanonRaw.pm +1 -1
  28. data/bin/lib/Image/ExifTool/CanonVRD.pm +1 -1
  29. data/bin/lib/Image/ExifTool/CaptureOne.pm +1 -1
  30. data/bin/lib/Image/ExifTool/Casio.pm +1 -1
  31. data/bin/lib/Image/ExifTool/Charset.pm +1 -1
  32. data/bin/lib/Image/ExifTool/DICOM.pm +1 -1
  33. data/bin/lib/Image/ExifTool/DJI.pm +96 -45
  34. data/bin/lib/Image/ExifTool/DNG.pm +1 -1
  35. data/bin/lib/Image/ExifTool/DPX.pm +1 -1
  36. data/bin/lib/Image/ExifTool/DV.pm +1 -1
  37. data/bin/lib/Image/ExifTool/DarwinCore.pm +1 -1
  38. data/bin/lib/Image/ExifTool/DjVu.pm +1 -1
  39. data/bin/lib/Image/ExifTool/EXE.pm +123 -32
  40. data/bin/lib/Image/ExifTool/Exif.pm +7 -4
  41. data/bin/lib/Image/ExifTool/FITS.pm +1 -1
  42. data/bin/lib/Image/ExifTool/FLAC.pm +1 -1
  43. data/bin/lib/Image/ExifTool/FLIF.pm +1 -1
  44. data/bin/lib/Image/ExifTool/FLIR.pm +1 -1
  45. data/bin/lib/Image/ExifTool/Fixup.pm +1 -1
  46. data/bin/lib/Image/ExifTool/Flash.pm +1 -1
  47. data/bin/lib/Image/ExifTool/FlashPix.pm +1 -1
  48. data/bin/lib/Image/ExifTool/Font.pm +1 -1
  49. data/bin/lib/Image/ExifTool/FotoStation.pm +1 -1
  50. data/bin/lib/Image/ExifTool/FujiFilm.pm +1 -1
  51. data/bin/lib/Image/ExifTool/GE.pm +1 -1
  52. data/bin/lib/Image/ExifTool/GIF.pm +1 -1
  53. data/bin/lib/Image/ExifTool/GIMP.pm +1 -1
  54. data/bin/lib/Image/ExifTool/GM.pm +1 -1
  55. data/bin/lib/Image/ExifTool/GPS.pm +1 -1
  56. data/bin/lib/Image/ExifTool/GeoTiff.pm +1 -1
  57. data/bin/lib/Image/ExifTool/Geolocation.pm +1 -1
  58. data/bin/lib/Image/ExifTool/Geotag.pm +1 -1
  59. data/bin/lib/Image/ExifTool/GoPro.pm +1 -1
  60. data/bin/lib/Image/ExifTool/H264.pm +1 -1
  61. data/bin/lib/Image/ExifTool/HP.pm +1 -1
  62. data/bin/lib/Image/ExifTool/HTML.pm +1 -1
  63. data/bin/lib/Image/ExifTool/HtmlDump.pm +1 -1
  64. data/bin/lib/Image/ExifTool/ICC_Profile.pm +1 -1
  65. data/bin/lib/Image/ExifTool/ICO.pm +1 -1
  66. data/bin/lib/Image/ExifTool/ID3.pm +1 -1
  67. data/bin/lib/Image/ExifTool/IPTC.pm +8 -5
  68. data/bin/lib/Image/ExifTool/ISO.pm +1 -1
  69. data/bin/lib/Image/ExifTool/ITC.pm +1 -1
  70. data/bin/lib/Image/ExifTool/Import.pm +5 -4
  71. data/bin/lib/Image/ExifTool/InDesign.pm +1 -1
  72. data/bin/lib/Image/ExifTool/InfiRay.pm +1 -1
  73. data/bin/lib/Image/ExifTool/JPEG.pm +1 -1
  74. data/bin/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  75. data/bin/lib/Image/ExifTool/JSON.pm +1 -1
  76. data/bin/lib/Image/ExifTool/JVC.pm +1 -1
  77. data/bin/lib/Image/ExifTool/Jpeg2000.pm +1 -1
  78. data/bin/lib/Image/ExifTool/Kodak.pm +1 -1
  79. data/bin/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  80. data/bin/lib/Image/ExifTool/LIF.pm +1 -1
  81. data/bin/lib/Image/ExifTool/LNK.pm +1 -1
  82. data/bin/lib/Image/ExifTool/Lang/cs.pm +1 -1
  83. data/bin/lib/Image/ExifTool/Lang/de.pm +1 -1
  84. data/bin/lib/Image/ExifTool/Lang/en_ca.pm +1 -1
  85. data/bin/lib/Image/ExifTool/Lang/en_gb.pm +1 -1
  86. data/bin/lib/Image/ExifTool/Lang/es.pm +1 -1
  87. data/bin/lib/Image/ExifTool/Lang/fi.pm +1 -1
  88. data/bin/lib/Image/ExifTool/Lang/fr.pm +1 -1
  89. data/bin/lib/Image/ExifTool/Lang/it.pm +1 -1
  90. data/bin/lib/Image/ExifTool/Lang/ja.pm +1 -1
  91. data/bin/lib/Image/ExifTool/Lang/ko.pm +1 -1
  92. data/bin/lib/Image/ExifTool/Lang/nl.pm +1 -1
  93. data/bin/lib/Image/ExifTool/Lang/pl.pm +1 -1
  94. data/bin/lib/Image/ExifTool/Lang/ru.pm +1 -1
  95. data/bin/lib/Image/ExifTool/Lang/sk.pm +1 -1
  96. data/bin/lib/Image/ExifTool/Lang/sv.pm +1 -1
  97. data/bin/lib/Image/ExifTool/Lang/tr.pm +1 -1
  98. data/bin/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  99. data/bin/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  100. data/bin/lib/Image/ExifTool/Leaf.pm +1 -1
  101. data/bin/lib/Image/ExifTool/LigoGPS.pm +408 -0
  102. data/bin/lib/Image/ExifTool/Lytro.pm +1 -1
  103. data/bin/lib/Image/ExifTool/M2TS.pm +22 -13
  104. data/bin/lib/Image/ExifTool/MIE.pm +1 -1
  105. data/bin/lib/Image/ExifTool/MIEUnits.pod +1 -1
  106. data/bin/lib/Image/ExifTool/MIFF.pm +1 -1
  107. data/bin/lib/Image/ExifTool/MISB.pm +1 -1
  108. data/bin/lib/Image/ExifTool/MNG.pm +1 -1
  109. data/bin/lib/Image/ExifTool/MOI.pm +1 -1
  110. data/bin/lib/Image/ExifTool/MPC.pm +1 -1
  111. data/bin/lib/Image/ExifTool/MPEG.pm +1 -1
  112. data/bin/lib/Image/ExifTool/MPF.pm +1 -1
  113. data/bin/lib/Image/ExifTool/MRC.pm +1 -1
  114. data/bin/lib/Image/ExifTool/MWG.pm +1 -1
  115. data/bin/lib/Image/ExifTool/MXF.pm +1 -1
  116. data/bin/lib/Image/ExifTool/MacOS.pm +1 -1
  117. data/bin/lib/Image/ExifTool/MakerNotes.pm +1 -1
  118. data/bin/lib/Image/ExifTool/Matroska.pm +12 -4
  119. data/bin/lib/Image/ExifTool/Microsoft.pm +1 -1
  120. data/bin/lib/Image/ExifTool/Minolta.pm +1 -1
  121. data/bin/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  122. data/bin/lib/Image/ExifTool/Motorola.pm +1 -1
  123. data/bin/lib/Image/ExifTool/Nikon.pm +164 -35
  124. data/bin/lib/Image/ExifTool/NikonCapture.pm +1 -1
  125. data/bin/lib/Image/ExifTool/NikonCustom.pm +1 -1
  126. data/bin/lib/Image/ExifTool/NikonSettings.pm +1 -1
  127. data/bin/lib/Image/ExifTool/Nintendo.pm +1 -1
  128. data/bin/lib/Image/ExifTool/OOXML.pm +1 -1
  129. data/bin/lib/Image/ExifTool/Ogg.pm +1 -1
  130. data/bin/lib/Image/ExifTool/Olympus.pm +1 -1
  131. data/bin/lib/Image/ExifTool/OpenEXR.pm +1 -1
  132. data/bin/lib/Image/ExifTool/Opus.pm +1 -1
  133. data/bin/lib/Image/ExifTool/Other.pm +1 -1
  134. data/bin/lib/Image/ExifTool/PCX.pm +1 -1
  135. data/bin/lib/Image/ExifTool/PDF.pm +1 -1
  136. data/bin/lib/Image/ExifTool/PGF.pm +1 -1
  137. data/bin/lib/Image/ExifTool/PICT.pm +1 -1
  138. data/bin/lib/Image/ExifTool/PLIST.pm +1 -1
  139. data/bin/lib/Image/ExifTool/PLUS.pm +1 -1
  140. data/bin/lib/Image/ExifTool/PNG.pm +1 -1
  141. data/bin/lib/Image/ExifTool/PPM.pm +1 -1
  142. data/bin/lib/Image/ExifTool/PSP.pm +1 -1
  143. data/bin/lib/Image/ExifTool/Palm.pm +1 -1
  144. data/bin/lib/Image/ExifTool/Panasonic.pm +20 -2
  145. data/bin/lib/Image/ExifTool/PanasonicRaw.pm +1 -1
  146. data/bin/lib/Image/ExifTool/Parrot.pm +1 -1
  147. data/bin/lib/Image/ExifTool/Pentax.pm +1 -1
  148. data/bin/lib/Image/ExifTool/PhaseOne.pm +1 -1
  149. data/bin/lib/Image/ExifTool/PhotoCD.pm +1 -1
  150. data/bin/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  151. data/bin/lib/Image/ExifTool/Photoshop.pm +1 -1
  152. data/bin/lib/Image/ExifTool/PostScript.pm +1 -1
  153. data/bin/lib/Image/ExifTool/PrintIM.pm +1 -1
  154. data/bin/lib/Image/ExifTool/Protobuf.pm +24 -14
  155. data/bin/lib/Image/ExifTool/Qualcomm.pm +1 -1
  156. data/bin/lib/Image/ExifTool/QuickTime.pm +46 -17
  157. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +20 -302
  158. data/bin/lib/Image/ExifTool/README +4 -0
  159. data/bin/lib/Image/ExifTool/RIFF.pm +1 -1
  160. data/bin/lib/Image/ExifTool/RSRC.pm +1 -1
  161. data/bin/lib/Image/ExifTool/RTF.pm +1 -1
  162. data/bin/lib/Image/ExifTool/Radiance.pm +1 -1
  163. data/bin/lib/Image/ExifTool/Rawzor.pm +1 -1
  164. data/bin/lib/Image/ExifTool/Real.pm +1 -1
  165. data/bin/lib/Image/ExifTool/Reconyx.pm +1 -1
  166. data/bin/lib/Image/ExifTool/Red.pm +1 -1
  167. data/bin/lib/Image/ExifTool/Ricoh.pm +1 -1
  168. data/bin/lib/Image/ExifTool/Samsung.pm +1 -1
  169. data/bin/lib/Image/ExifTool/Sanyo.pm +1 -1
  170. data/bin/lib/Image/ExifTool/Scalado.pm +1 -1
  171. data/bin/lib/Image/ExifTool/Shift.pl +1 -1
  172. data/bin/lib/Image/ExifTool/Shortcuts.pm +1 -1
  173. data/bin/lib/Image/ExifTool/Sigma.pm +1 -1
  174. data/bin/lib/Image/ExifTool/SigmaRaw.pm +1 -1
  175. data/bin/lib/Image/ExifTool/Sony.pm +1 -1
  176. data/bin/lib/Image/ExifTool/SonyIDC.pm +1 -1
  177. data/bin/lib/Image/ExifTool/Stim.pm +1 -1
  178. data/bin/lib/Image/ExifTool/TagInfoXML.pm +2 -2
  179. data/bin/lib/Image/ExifTool/TagLookup.pm +11 -5
  180. data/bin/lib/Image/ExifTool/TagNames.pod +84 -34
  181. data/bin/lib/Image/ExifTool/Text.pm +1 -1
  182. data/bin/lib/Image/ExifTool/Theora.pm +1 -1
  183. data/bin/lib/Image/ExifTool/Torrent.pm +1 -1
  184. data/bin/lib/Image/ExifTool/Unknown.pm +1 -1
  185. data/bin/lib/Image/ExifTool/VCard.pm +1 -1
  186. data/bin/lib/Image/ExifTool/Validate.pm +1 -1
  187. data/bin/lib/Image/ExifTool/Vorbis.pm +1 -1
  188. data/bin/lib/Image/ExifTool/WPG.pm +1 -1
  189. data/bin/lib/Image/ExifTool/WTV.pm +1 -1
  190. data/bin/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  191. data/bin/lib/Image/ExifTool/WriteExif.pl +1 -1
  192. data/bin/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  193. data/bin/lib/Image/ExifTool/WritePDF.pl +1 -1
  194. data/bin/lib/Image/ExifTool/WritePNG.pl +1 -1
  195. data/bin/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  196. data/bin/lib/Image/ExifTool/WritePostScript.pl +1 -1
  197. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +5 -3
  198. data/bin/lib/Image/ExifTool/WriteRIFF.pl +1 -1
  199. data/bin/lib/Image/ExifTool/WriteXMP.pl +1 -1
  200. data/bin/lib/Image/ExifTool/Writer.pl +9 -8
  201. data/bin/lib/Image/ExifTool/XISF.pm +1 -1
  202. data/bin/lib/Image/ExifTool/XMP.pm +1 -1
  203. data/bin/lib/Image/ExifTool/XMP2.pl +1 -1
  204. data/bin/lib/Image/ExifTool/XMPStruct.pl +1 -1
  205. data/bin/lib/Image/ExifTool/ZIP.pm +1 -1
  206. data/bin/lib/Image/ExifTool/ZISRAW.pm +1 -1
  207. data/bin/lib/Image/ExifTool/iWork.pm +1 -1
  208. data/bin/lib/Image/ExifTool.pm +13 -7
  209. data/bin/lib/Image/ExifTool.pod +14 -5
  210. data/bin/perl-Image-ExifTool.spec +1 -1
  211. data/lib/exiftool_vendored/version.rb +1 -1
  212. metadata +3 -2
@@ -0,0 +1,408 @@
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
+ sub ProcessLigoGPS($$$;$)
273
+ {
274
+ my ($et, $dirInfo, $tagTbl, $noFuzz) = @_;
275
+ my $dataPt = $$dirInfo{DataPt};
276
+ my $pos = ($$dirInfo{DirStart} || 0) + 0x14;
277
+ return undef if $pos > length $$dataPt;
278
+ my $cipherInfo = $$et{LigoCipher};
279
+ my $dirName = $$dirInfo{DirName} || 'LigoGPS';
280
+ push @{$$et{PATH}}, $dirName unless $$dirInfo{DirID};
281
+ # not fuzzed if header =~ /LIGOGPSINFO\0\0\0\0[\x01\x14]/ (\x01=BlueSkySeaDV688)
282
+ $noFuzz = 1 if substr($$dataPt, $pos-8, 4) =~ /^\0\0\0[\x01\x14]/;
283
+ $et->VerboseDir($dirName);
284
+ for (; $pos + 0x84 <= length($$dataPt); $pos+=0x84) {
285
+ my $dat = substr($$dataPt, $pos, 0x84);
286
+ $dat =~ /^####/ or next; # (have seen blank records filled with zeros, so keep trying)
287
+ # decipher if we already know the encryption
288
+ $cipherInfo and $$cipherInfo{decipher} and DecipherLigoGPS($et, $dat, $tagTbl, $noFuzz) and next;
289
+ my $str = DecryptLigoGPS($dat);
290
+ defined $str or DecipherLigoGPS($et, $dat, $tagTbl, $noFuzz), next; # try to decipher
291
+ $et->VPrint(1, "$$et{INDENT}\(Decrypted: ",unpack('V',$str),' ',substr($str,4),")\n") if $$et{OPTIONS}{Verbose} > 1;
292
+ ParseLigoGPS($et, $str, $tagTbl, $noFuzz);
293
+ }
294
+ pop @{$$et{PATH}} unless $$dirInfo{DirID};
295
+ delete $$et{DOC_NUM};
296
+ return 1;
297
+ }
298
+
299
+ #------------------------------------------------------------------------------
300
+ # Process LIGOGPSINFO JSON-format GPS (Yada RoadCam Pro 4K BT58189)
301
+ # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
302
+ # Returns: 1 on success
303
+ # Sample data (chained 512-byte records starting like this):
304
+ # 0000: 4c 49 47 4f 47 50 53 49 4e 46 4f 20 7b 22 48 6f [LIGOGPSINFO {"Ho]
305
+ # 0010: 75 72 22 3a 20 22 32 33 22 2c 20 22 4d 69 6e 75 [ur": "23", "Minu]
306
+ # 0020: 74 65 22 3a 20 22 31 30 22 2c 20 22 53 65 63 6f [te": "10", "Seco]
307
+ # 0030: 6e 64 22 3a 20 22 32 32 22 2c 20 22 59 65 61 72 [nd": "22", "Year]
308
+ # 0040: 22 3a 20 22 32 30 32 33 22 2c 20 22 4d 6f 6e 74 [": "2023", "Mont]
309
+ # 0050: 68 22 3a 20 22 31 32 22 2c 20 22 44 61 79 22 3a [h": "12", "Day":]
310
+ # 0060: 20 22 32 38 22 2c 20 22 73 74 61 74 75 73 22 3a [ "28", "status":]
311
+ sub ProcessLigoJSON($$$)
312
+ {
313
+ my ($et, $dirInfo, $tagTbl) = @_;
314
+ my $dataPt = $$dirInfo{DataPt};
315
+ my $dirLen = $$dirInfo{DirLen};
316
+ require Image::ExifTool::Import;
317
+ $et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
318
+ $$et{SET_GROUP1} = 'LIGO';
319
+ while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
320
+ my $json = $1;
321
+ my %dbase;
322
+ Image::ExifTool::Import::ReadJSON(\$json, \%dbase);
323
+ my $info = $dbase{'*'} or next;
324
+ # my sample contains the following JSON fields (in this order):
325
+ # Hour Minute Second Year Month Day (GPS UTC time)
326
+ # status NS EW Latitude Longitude Speed (speed in knots)
327
+ # GsensorX GsensorY GsensorZ (units? - only seen "000" for all)
328
+ # MHour MMinute MSecond MYear MMonth MDay (local dashcam clock time)
329
+ # OLatitude OLongitude (? same values as Latitude/Longitude)
330
+ next unless defined $$info{status} and $$info{status} eq 'A'; # only read if GPS is active
331
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
332
+ my $num = 0;
333
+ defined $$info{$_} and ++$num foreach qw(Year Month Day Hour Minute Second);
334
+ if ($num == 6) {
335
+ # this is the GPS time in UTC
336
+ my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ',@$info{qw{Year Month Day Hour Minute Second}});
337
+ $et->HandleTag($tagTbl, GPSDateTime => $time);
338
+ }
339
+ if ($$info{Latitude} and $$info{Longitude}) {
340
+ my $lat = $$info{Latitude};
341
+ $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
342
+ my $lon = $$info{Longitude};
343
+ $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
344
+ $et->HandleTag($tagTbl, GPSLatitude => $lat);
345
+ $et->HandleTag($tagTbl, GPSLongitude => $lon);
346
+ }
347
+ $et->HandleTag($tagTbl, GPSSpeed => $$info{Speed} * $knotsToKph) if defined $$info{Speed};
348
+ if (defined $$info{GsensorX} and defined $$info{GsensorY} and defined $$info{GsensorZ}) {
349
+ # (don't know conversion factor for accel data, so leave it raw for now)
350
+ $et->HandleTag($tagTbl, Accelerometer => "$$info{GsensorX} $$info{GsensorY} $$info{GsensorZ}");
351
+ }
352
+ $num = 0;
353
+ defined $$info{$_} and ++$num foreach qw(MYear MMonth MDay MHour MMinute MSecond);
354
+ if ($num == 6) {
355
+ # this is the dashcam clock time (local time zone)
356
+ my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d',@$info{qw{MYear MMonth MDay MHour MMinute MSecond}});
357
+ $et->HandleTag($tagTbl, DateTimeOriginal => $time);
358
+ }
359
+ if (defined $$info{OLatitude} and defined $$info{OLongitude}) {
360
+ my $lat = $$info{OLatitude};
361
+ $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
362
+ my $lon = $$info{OLongitude};
363
+ $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
364
+ $et->HandleTag($tagTbl, GPSLatitude2 => $lat);
365
+ $et->HandleTag($tagTbl, GPSLongitude2 => $lon);
366
+ }
367
+ unless ($et->Options('ExtractEmbedded')) {
368
+ $et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
369
+ last;
370
+ }
371
+ }
372
+ delete $$et{DOC_NUM};
373
+ delete $$et{SET_GROUP1};
374
+ return 1;
375
+ }
376
+
377
+ 1; #end
378
+
379
+
380
+ __END__
381
+
382
+ =head1 NAME
383
+
384
+ Image::ExifTool::LigoGPS - Read LIGOGPSINFO timed GPS records
385
+
386
+ =head1 SYNOPSIS
387
+
388
+ This module is loaded automatically by Image::ExifTool when required.
389
+
390
+ =head1 DESCRIPTION
391
+
392
+ This module decrypts, deciphers and decodes timed GPS metadata from
393
+ LIGOGPSINFO records found in various locations of MP4 and M2TS videos from a
394
+ variety of dashcam makes and models.
395
+
396
+ =head1 AUTHOR
397
+
398
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
399
+
400
+ This library is free software; you can redistribute it and/or modify it
401
+ under the same terms as Perl itself.
402
+
403
+ =head1 SEE ALSO
404
+
405
+ L<Image::ExifTool::TagNames/QuickTime Stream Tags>,
406
+ L<Image::ExifTool(3pm)|Image::ExifTool>
407
+
408
+ =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.
@@ -704,7 +704,7 @@ based on unofficial sources which may be incomplete, inaccurate or outdated.
704
704
 
705
705
  =head1 AUTHOR
706
706
 
707
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
707
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
708
708
 
709
709
  This library is free software; you can redistribute it and/or modify it
710
710
  under the same terms as Perl itself.
@@ -267,7 +267,7 @@ Format (MPF) information from JPEG images.
267
267
 
268
268
  =head1 AUTHOR
269
269
 
270
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
270
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
271
271
 
272
272
  This library is free software; you can redistribute it and/or modify it
273
273
  under the same terms as Perl itself.
@@ -315,7 +315,7 @@ metadata from Medical Research Council (MRC) images.
315
315
 
316
316
  =head1 AUTHOR
317
317
 
318
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
318
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
319
319
 
320
320
  This library is free software; you can redistribute it and/or modify it
321
321
  under the same terms as Perl itself.
@@ -757,7 +757,7 @@ must be loaded explicitly as described above.
757
757
 
758
758
  =head1 AUTHOR
759
759
 
760
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
760
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
761
761
 
762
762
  This library is free software; you can redistribute it and/or modify it
763
763
  under the same terms as Perl itself.
@@ -2987,7 +2987,7 @@ information from MXF (Material Exchange Format) files.
2987
2987
 
2988
2988
  =head1 AUTHOR
2989
2989
 
2990
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
2990
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
2991
2991
 
2992
2992
  This library is free software; you can redistribute it and/or modify it
2993
2993
  under the same terms as Perl itself.
@@ -737,7 +737,7 @@ Writable tags use "xattr", "setfile" or "osascript" for writing.
737
737
 
738
738
  =head1 AUTHOR
739
739
 
740
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
740
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
741
741
 
742
742
  This library is free software; you can redistribute it and/or modify it
743
743
  under the same terms as Perl itself.
@@ -1831,7 +1831,7 @@ maker notes in EXIF information.
1831
1831
 
1832
1832
  =head1 AUTHOR
1833
1833
 
1834
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
1834
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
1835
1835
 
1836
1836
  This library is free software; you can redistribute it and/or modify it
1837
1837
  under the same terms as Perl itself.