exiftool-vendored.exe 12.99.0 → 13.16.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 (209) hide show
  1. package/bin/exiftool.exe +0 -0
  2. package/bin/exiftool_files/exiftool.pl +183 -70
  3. package/bin/exiftool_files/lib/File/RandomAccess.pm +1 -1
  4. package/bin/exiftool_files/lib/File/RandomAccess.pod +2 -2
  5. package/bin/exiftool_files/lib/Image/ExifTool/AAC.pm +1 -1
  6. package/bin/exiftool_files/lib/Image/ExifTool/AES.pm +1 -1
  7. package/bin/exiftool_files/lib/Image/ExifTool/AFCP.pm +6 -6
  8. package/bin/exiftool_files/lib/Image/ExifTool/AIFF.pm +2 -2
  9. package/bin/exiftool_files/lib/Image/ExifTool/APE.pm +2 -2
  10. package/bin/exiftool_files/lib/Image/ExifTool/APP12.pm +1 -1
  11. package/bin/exiftool_files/lib/Image/ExifTool/ASF.pm +2 -2
  12. package/bin/exiftool_files/lib/Image/ExifTool/Apple.pm +11 -9
  13. package/bin/exiftool_files/lib/Image/ExifTool/Audible.pm +1 -1
  14. package/bin/exiftool_files/lib/Image/ExifTool/BMP.pm +1 -1
  15. package/bin/exiftool_files/lib/Image/ExifTool/BPG.pm +1 -1
  16. package/bin/exiftool_files/lib/Image/ExifTool/BZZ.pm +1 -1
  17. package/bin/exiftool_files/lib/Image/ExifTool/BigTIFF.pm +1 -1
  18. package/bin/exiftool_files/lib/Image/ExifTool/BuildTagLookup.pm +36 -22
  19. package/bin/exiftool_files/lib/Image/ExifTool/CBOR.pm +5 -2
  20. package/bin/exiftool_files/lib/Image/ExifTool/Canon.pm +66 -27
  21. package/bin/exiftool_files/lib/Image/ExifTool/CanonCustom.pm +1 -1
  22. package/bin/exiftool_files/lib/Image/ExifTool/CanonRaw.pm +1 -1
  23. package/bin/exiftool_files/lib/Image/ExifTool/CanonVRD.pm +1 -1
  24. package/bin/exiftool_files/lib/Image/ExifTool/CaptureOne.pm +1 -1
  25. package/bin/exiftool_files/lib/Image/ExifTool/Casio.pm +1 -1
  26. package/bin/exiftool_files/lib/Image/ExifTool/Charset.pm +1 -1
  27. package/bin/exiftool_files/lib/Image/ExifTool/DICOM.pm +1 -1
  28. package/bin/exiftool_files/lib/Image/ExifTool/DJI.pm +196 -30
  29. package/bin/exiftool_files/lib/Image/ExifTool/DNG.pm +1 -1
  30. package/bin/exiftool_files/lib/Image/ExifTool/DPX.pm +1 -1
  31. package/bin/exiftool_files/lib/Image/ExifTool/DV.pm +1 -1
  32. package/bin/exiftool_files/lib/Image/ExifTool/DarwinCore.pm +1 -1
  33. package/bin/exiftool_files/lib/Image/ExifTool/DjVu.pm +1 -1
  34. package/bin/exiftool_files/lib/Image/ExifTool/EXE.pm +138 -33
  35. package/bin/exiftool_files/lib/Image/ExifTool/Exif.pm +30 -17
  36. package/bin/exiftool_files/lib/Image/ExifTool/FITS.pm +3 -3
  37. package/bin/exiftool_files/lib/Image/ExifTool/FLAC.pm +1 -1
  38. package/bin/exiftool_files/lib/Image/ExifTool/FLIF.pm +3 -3
  39. package/bin/exiftool_files/lib/Image/ExifTool/FLIR.pm +1 -1
  40. package/bin/exiftool_files/lib/Image/ExifTool/Fixup.pm +1 -1
  41. package/bin/exiftool_files/lib/Image/ExifTool/Flash.pm +1 -1
  42. package/bin/exiftool_files/lib/Image/ExifTool/FlashPix.pm +17 -21
  43. package/bin/exiftool_files/lib/Image/ExifTool/Font.pm +2 -2
  44. package/bin/exiftool_files/lib/Image/ExifTool/FotoStation.pm +1 -1
  45. package/bin/exiftool_files/lib/Image/ExifTool/FujiFilm.pm +1 -1
  46. package/bin/exiftool_files/lib/Image/ExifTool/GE.pm +1 -1
  47. package/bin/exiftool_files/lib/Image/ExifTool/GIF.pm +144 -93
  48. package/bin/exiftool_files/lib/Image/ExifTool/GIMP.pm +1 -1
  49. package/bin/exiftool_files/lib/Image/ExifTool/GM.pm +1 -1
  50. package/bin/exiftool_files/lib/Image/ExifTool/GPS.pm +34 -30
  51. package/bin/exiftool_files/lib/Image/ExifTool/GeoTiff.pm +1 -1
  52. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.dat +0 -0
  53. package/bin/exiftool_files/lib/Image/ExifTool/Geolocation.pm +19 -9
  54. package/bin/exiftool_files/lib/Image/ExifTool/Geotag.pm +49 -14
  55. package/bin/exiftool_files/lib/Image/ExifTool/GoPro.pm +120 -8
  56. package/bin/exiftool_files/lib/Image/ExifTool/H264.pm +1 -1
  57. package/bin/exiftool_files/lib/Image/ExifTool/HP.pm +2 -2
  58. package/bin/exiftool_files/lib/Image/ExifTool/HTML.pm +1 -1
  59. package/bin/exiftool_files/lib/Image/ExifTool/HtmlDump.pm +1 -1
  60. package/bin/exiftool_files/lib/Image/ExifTool/ICC_Profile.pm +81 -2
  61. package/bin/exiftool_files/lib/Image/ExifTool/ICO.pm +1 -1
  62. package/bin/exiftool_files/lib/Image/ExifTool/ID3.pm +8 -8
  63. package/bin/exiftool_files/lib/Image/ExifTool/IPTC.pm +10 -7
  64. package/bin/exiftool_files/lib/Image/ExifTool/ISO.pm +1 -1
  65. package/bin/exiftool_files/lib/Image/ExifTool/ITC.pm +1 -1
  66. package/bin/exiftool_files/lib/Image/ExifTool/Import.pm +5 -4
  67. package/bin/exiftool_files/lib/Image/ExifTool/InDesign.pm +2 -2
  68. package/bin/exiftool_files/lib/Image/ExifTool/InfiRay.pm +1 -1
  69. package/bin/exiftool_files/lib/Image/ExifTool/JPEG.pm +32 -5
  70. package/bin/exiftool_files/lib/Image/ExifTool/JPEGDigest.pm +1 -1
  71. package/bin/exiftool_files/lib/Image/ExifTool/JSON.pm +1 -1
  72. package/bin/exiftool_files/lib/Image/ExifTool/JVC.pm +1 -1
  73. package/bin/exiftool_files/lib/Image/ExifTool/Jpeg2000.pm +10 -9
  74. package/bin/exiftool_files/lib/Image/ExifTool/Kodak.pm +1 -1
  75. package/bin/exiftool_files/lib/Image/ExifTool/KyoceraRaw.pm +1 -1
  76. package/bin/exiftool_files/lib/Image/ExifTool/LIF.pm +1 -1
  77. package/bin/exiftool_files/lib/Image/ExifTool/LNK.pm +2 -2
  78. package/bin/exiftool_files/lib/Image/ExifTool/Lang/cs.pm +1 -1
  79. package/bin/exiftool_files/lib/Image/ExifTool/Lang/de.pm +1 -1
  80. package/bin/exiftool_files/lib/Image/ExifTool/Lang/en_ca.pm +1 -1
  81. package/bin/exiftool_files/lib/Image/ExifTool/Lang/en_gb.pm +1 -1
  82. package/bin/exiftool_files/lib/Image/ExifTool/Lang/es.pm +1 -1
  83. package/bin/exiftool_files/lib/Image/ExifTool/Lang/fi.pm +1 -1
  84. package/bin/exiftool_files/lib/Image/ExifTool/Lang/fr.pm +1 -1
  85. package/bin/exiftool_files/lib/Image/ExifTool/Lang/it.pm +1 -1
  86. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ja.pm +1 -1
  87. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ko.pm +1 -1
  88. package/bin/exiftool_files/lib/Image/ExifTool/Lang/nl.pm +1 -1
  89. package/bin/exiftool_files/lib/Image/ExifTool/Lang/pl.pm +1 -1
  90. package/bin/exiftool_files/lib/Image/ExifTool/Lang/ru.pm +1 -1
  91. package/bin/exiftool_files/lib/Image/ExifTool/Lang/sk.pm +1 -1
  92. package/bin/exiftool_files/lib/Image/ExifTool/Lang/sv.pm +1 -1
  93. package/bin/exiftool_files/lib/Image/ExifTool/Lang/tr.pm +1 -1
  94. package/bin/exiftool_files/lib/Image/ExifTool/Lang/zh_cn.pm +1 -1
  95. package/bin/exiftool_files/lib/Image/ExifTool/Lang/zh_tw.pm +1 -1
  96. package/bin/exiftool_files/lib/Image/ExifTool/Leaf.pm +1 -1
  97. package/bin/exiftool_files/lib/Image/ExifTool/LigoGPS.pm +409 -0
  98. package/bin/exiftool_files/lib/Image/ExifTool/Lytro.pm +1 -1
  99. package/bin/exiftool_files/lib/Image/ExifTool/M2TS.pm +58 -19
  100. package/bin/exiftool_files/lib/Image/ExifTool/MIE.pm +15 -6
  101. package/bin/exiftool_files/lib/Image/ExifTool/MIEUnits.pod +1 -1
  102. package/bin/exiftool_files/lib/Image/ExifTool/MIFF.pm +1 -1
  103. package/bin/exiftool_files/lib/Image/ExifTool/MISB.pm +1 -1
  104. package/bin/exiftool_files/lib/Image/ExifTool/MNG.pm +1 -1
  105. package/bin/exiftool_files/lib/Image/ExifTool/MOI.pm +1 -1
  106. package/bin/exiftool_files/lib/Image/ExifTool/MPC.pm +1 -1
  107. package/bin/exiftool_files/lib/Image/ExifTool/MPEG.pm +1 -1
  108. package/bin/exiftool_files/lib/Image/ExifTool/MPF.pm +1 -1
  109. package/bin/exiftool_files/lib/Image/ExifTool/MRC.pm +1 -1
  110. package/bin/exiftool_files/lib/Image/ExifTool/MWG.pm +1 -1
  111. package/bin/exiftool_files/lib/Image/ExifTool/MXF.pm +3 -3
  112. package/bin/exiftool_files/lib/Image/ExifTool/MacOS.pm +3 -2
  113. package/bin/exiftool_files/lib/Image/ExifTool/MakerNotes.pm +1 -1
  114. package/bin/exiftool_files/lib/Image/ExifTool/Matroska.pm +22 -6
  115. package/bin/exiftool_files/lib/Image/ExifTool/Microsoft.pm +2 -2
  116. package/bin/exiftool_files/lib/Image/ExifTool/Minolta.pm +1 -1
  117. package/bin/exiftool_files/lib/Image/ExifTool/MinoltaRaw.pm +1 -1
  118. package/bin/exiftool_files/lib/Image/ExifTool/Motorola.pm +1 -1
  119. package/bin/exiftool_files/lib/Image/ExifTool/Nikon.pm +495 -39
  120. package/bin/exiftool_files/lib/Image/ExifTool/NikonCapture.pm +1 -1
  121. package/bin/exiftool_files/lib/Image/ExifTool/NikonCustom.pm +2 -2
  122. package/bin/exiftool_files/lib/Image/ExifTool/NikonSettings.pm +1 -1
  123. package/bin/exiftool_files/lib/Image/ExifTool/Nintendo.pm +1 -1
  124. package/bin/exiftool_files/lib/Image/ExifTool/OOXML.pm +8 -8
  125. package/bin/exiftool_files/lib/Image/ExifTool/Ogg.pm +1 -1
  126. package/bin/exiftool_files/lib/Image/ExifTool/Olympus.pm +1 -1
  127. package/bin/exiftool_files/lib/Image/ExifTool/OpenEXR.pm +1 -1
  128. package/bin/exiftool_files/lib/Image/ExifTool/Opus.pm +1 -1
  129. package/bin/exiftool_files/lib/Image/ExifTool/Other.pm +1 -1
  130. package/bin/exiftool_files/lib/Image/ExifTool/PCX.pm +1 -1
  131. package/bin/exiftool_files/lib/Image/ExifTool/PDF.pm +49 -18
  132. package/bin/exiftool_files/lib/Image/ExifTool/PGF.pm +1 -1
  133. package/bin/exiftool_files/lib/Image/ExifTool/PICT.pm +1 -1
  134. package/bin/exiftool_files/lib/Image/ExifTool/PLIST.pm +4 -4
  135. package/bin/exiftool_files/lib/Image/ExifTool/PLUS.pm +1 -1
  136. package/bin/exiftool_files/lib/Image/ExifTool/PNG.pm +20 -8
  137. package/bin/exiftool_files/lib/Image/ExifTool/PPM.pm +12 -3
  138. package/bin/exiftool_files/lib/Image/ExifTool/PSP.pm +1 -1
  139. package/bin/exiftool_files/lib/Image/ExifTool/Palm.pm +1 -1
  140. package/bin/exiftool_files/lib/Image/ExifTool/Panasonic.pm +27 -3
  141. package/bin/exiftool_files/lib/Image/ExifTool/PanasonicRaw.pm +1 -1
  142. package/bin/exiftool_files/lib/Image/ExifTool/Parrot.pm +1 -1
  143. package/bin/exiftool_files/lib/Image/ExifTool/Pentax.pm +1 -1
  144. package/bin/exiftool_files/lib/Image/ExifTool/PhaseOne.pm +6 -5
  145. package/bin/exiftool_files/lib/Image/ExifTool/PhotoCD.pm +1 -1
  146. package/bin/exiftool_files/lib/Image/ExifTool/PhotoMechanic.pm +1 -1
  147. package/bin/exiftool_files/lib/Image/ExifTool/Photoshop.pm +65 -4
  148. package/bin/exiftool_files/lib/Image/ExifTool/PostScript.pm +1 -1
  149. package/bin/exiftool_files/lib/Image/ExifTool/PrintIM.pm +1 -1
  150. package/bin/exiftool_files/lib/Image/ExifTool/Protobuf.pm +270 -0
  151. package/bin/exiftool_files/lib/Image/ExifTool/Qualcomm.pm +1 -1
  152. package/bin/exiftool_files/lib/Image/ExifTool/QuickTime.pm +326 -88
  153. package/bin/exiftool_files/lib/Image/ExifTool/QuickTimeStream.pl +266 -200
  154. package/bin/exiftool_files/lib/Image/ExifTool/README +12 -2
  155. package/bin/exiftool_files/lib/Image/ExifTool/RIFF.pm +21 -6
  156. package/bin/exiftool_files/lib/Image/ExifTool/RSRC.pm +1 -1
  157. package/bin/exiftool_files/lib/Image/ExifTool/RTF.pm +2 -2
  158. package/bin/exiftool_files/lib/Image/ExifTool/Radiance.pm +1 -1
  159. package/bin/exiftool_files/lib/Image/ExifTool/Rawzor.pm +1 -1
  160. package/bin/exiftool_files/lib/Image/ExifTool/Real.pm +1 -1
  161. package/bin/exiftool_files/lib/Image/ExifTool/Reconyx.pm +1 -1
  162. package/bin/exiftool_files/lib/Image/ExifTool/Red.pm +1 -1
  163. package/bin/exiftool_files/lib/Image/ExifTool/Ricoh.pm +4 -4
  164. package/bin/exiftool_files/lib/Image/ExifTool/Samsung.pm +2 -2
  165. package/bin/exiftool_files/lib/Image/ExifTool/Sanyo.pm +1 -1
  166. package/bin/exiftool_files/lib/Image/ExifTool/Scalado.pm +1 -1
  167. package/bin/exiftool_files/lib/Image/ExifTool/Shift.pl +1 -1
  168. package/bin/exiftool_files/lib/Image/ExifTool/Shortcuts.pm +1 -1
  169. package/bin/exiftool_files/lib/Image/ExifTool/Sigma.pm +1 -1
  170. package/bin/exiftool_files/lib/Image/ExifTool/SigmaRaw.pm +1 -1
  171. package/bin/exiftool_files/lib/Image/ExifTool/Sony.pm +5 -4
  172. package/bin/exiftool_files/lib/Image/ExifTool/SonyIDC.pm +1 -1
  173. package/bin/exiftool_files/lib/Image/ExifTool/Stim.pm +1 -1
  174. package/bin/exiftool_files/lib/Image/ExifTool/TagInfoXML.pm +6 -5
  175. package/bin/exiftool_files/lib/Image/ExifTool/TagLookup.pm +7025 -6967
  176. package/bin/exiftool_files/lib/Image/ExifTool/TagNames.pod +477 -46
  177. package/bin/exiftool_files/lib/Image/ExifTool/Text.pm +4 -3
  178. package/bin/exiftool_files/lib/Image/ExifTool/Theora.pm +1 -1
  179. package/bin/exiftool_files/lib/Image/ExifTool/Torrent.pm +3 -3
  180. package/bin/exiftool_files/lib/Image/ExifTool/Unknown.pm +1 -1
  181. package/bin/exiftool_files/lib/Image/ExifTool/VCard.pm +3 -3
  182. package/bin/exiftool_files/lib/Image/ExifTool/Validate.pm +6 -6
  183. package/bin/exiftool_files/lib/Image/ExifTool/Vivo.pm +124 -0
  184. package/bin/exiftool_files/lib/Image/ExifTool/Vorbis.pm +1 -1
  185. package/bin/exiftool_files/lib/Image/ExifTool/WPG.pm +1 -1
  186. package/bin/exiftool_files/lib/Image/ExifTool/WTV.pm +1 -1
  187. package/bin/exiftool_files/lib/Image/ExifTool/WriteCanonRaw.pl +1 -1
  188. package/bin/exiftool_files/lib/Image/ExifTool/WriteExif.pl +3 -3
  189. package/bin/exiftool_files/lib/Image/ExifTool/WriteIPTC.pl +1 -1
  190. package/bin/exiftool_files/lib/Image/ExifTool/WritePDF.pl +1 -1
  191. package/bin/exiftool_files/lib/Image/ExifTool/WritePNG.pl +1 -1
  192. package/bin/exiftool_files/lib/Image/ExifTool/WritePhotoshop.pl +1 -1
  193. package/bin/exiftool_files/lib/Image/ExifTool/WritePostScript.pl +1 -1
  194. package/bin/exiftool_files/lib/Image/ExifTool/WriteQuickTime.pl +166 -79
  195. package/bin/exiftool_files/lib/Image/ExifTool/WriteRIFF.pl +17 -6
  196. package/bin/exiftool_files/lib/Image/ExifTool/WriteXMP.pl +3 -3
  197. package/bin/exiftool_files/lib/Image/ExifTool/Writer.pl +89 -96
  198. package/bin/exiftool_files/lib/Image/ExifTool/XISF.pm +1 -1
  199. package/bin/exiftool_files/lib/Image/ExifTool/XMP.pm +39 -14
  200. package/bin/exiftool_files/lib/Image/ExifTool/XMP2.pl +103 -1
  201. package/bin/exiftool_files/lib/Image/ExifTool/XMPStruct.pl +2 -3
  202. package/bin/exiftool_files/lib/Image/ExifTool/ZIP.pm +2 -2
  203. package/bin/exiftool_files/lib/Image/ExifTool/ZISRAW.pm +1 -1
  204. package/bin/exiftool_files/lib/Image/ExifTool/iWork.pm +1 -1
  205. package/bin/exiftool_files/lib/Image/ExifTool.pm +338 -166
  206. package/bin/exiftool_files/lib/Image/ExifTool.pod +119 -73
  207. package/bin/exiftool_files/readme_windows.txt +8 -13
  208. package/bin/exiftool_files/windows_exiftool.txt +102 -53
  209. package/package.json +11 -11
@@ -29,6 +29,7 @@ sub Process360Fly($$$);
29
29
  sub ProcessFMAS($$$);
30
30
  sub ProcessWolfbox($$$);
31
31
  sub ProcessCAMM($$$);
32
+ sub OrderCipherDigits($$$;$);
32
33
 
33
34
  # QuickTime data types that have ExifTool equivalents
34
35
  # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35)
@@ -94,6 +95,7 @@ my %insvDataLen = (
94
95
  # 0xb00 => 10, # ? (Insta360 X3)
95
96
  # 0xd00 => 10, # ? (Insta360 Ace Pro)
96
97
  # 0x1200 ? # ? (Insta360 Ace Pro)
98
+ # 0x1600 ? # ? (?)
97
99
  );
98
100
 
99
101
  # limit the default amount of data we read for some record types
@@ -109,7 +111,7 @@ my %insvLimit = (
109
111
  The tags below are extracted from timed metadata in QuickTime and other
110
112
  formats of video files when the ExtractEmbedded option is used. Although
111
113
  most of these tags are combined into the single table below, ExifTool
112
- currently reads 78 different formats of timed GPS metadata from video files.
114
+ currently reads 100 different types of timed GPS metadata from video files.
113
115
  },
114
116
  VARS => { NO_ID => 1 },
115
117
  GPSLatitude => { PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")', RawConv => '$$self{FoundGPSLatitude} = 1; $val' },
@@ -145,7 +147,7 @@ my %insvLimit = (
145
147
  CameraDateTime=>{ PrintConv => '$self->ConvertDateTime($val)', Groups => { 2 => 'Time' } },
146
148
  DateTimeStamp =>{ PrintConv => '$self->ConvertDateTime($val)', Groups => { 2 => 'Time' } },
147
149
  VideoTimeStamp => { Groups => { 2 => 'Video' } },
148
- Accelerometer=> { Notes => '3-axis acceleration in units of g' },
150
+ Accelerometer=> { Notes => '3-axis acceleration, usually in units of g' },
149
151
  AccelerometerData => { },
150
152
  AngularVelocity => { },
151
153
  GSensor => { },
@@ -339,14 +341,21 @@ my %insvLimit = (
339
341
  Groups => { 2 => 'Preview' },
340
342
  RawConv => '$self->ValidateImage(\$val,$tag)',
341
343
  },
342
- # djmd - DJI AC003 Osmo Action 4 cam
343
- #TODO djmd => { SubDirectory => { TagTable => 'Image::ExifTool::DJI::djmd', ByteOrder => 'Little-Endian' } },
344
- # (also DJI_20240615181302_0006_D.LRF)
345
- # dbgi - DJI AC003 Osmo Action 4 cam -- lots more unknown stuff
344
+ djmd => { # (DJI AC003 Osmo Action 4 cam)
345
+ Name => 'DJIMetadata',
346
+ SubDirectory => { TagTable => 'Image::ExifTool::DJI::Protobuf' },
347
+ },
348
+ dbgi => { # (DJI AC003 Osmo Action 4 cam)
349
+ Name => 'DJIDebug',
350
+ Unknown => 2,
351
+ Notes => 'extracted only if Unknown option is 2 or greater',
352
+ SubDirectory => { TagTable => 'Image::ExifTool::DJI::Protobuf' },
353
+ },
346
354
  Unknown00 => { Unknown => 1 },
347
355
  Unknown01 => { Unknown => 1 },
348
356
  Unknown02 => { Unknown => 1 },
349
357
  Unknown03 => { Unknown => 1 },
358
+ MagneticVariation => { }, # (from LIGOGPSINFO)
350
359
  );
351
360
 
352
361
  # tags found in 'camm' type 0 timed metadata (ref 4)
@@ -591,8 +600,8 @@ my %insvLimit = (
591
600
  GROUPS => { 2 => 'Location' },
592
601
  FIRST_ENTRY => 0,
593
602
  NOTES => q{
594
- Tags extracted from the tx3g sbtl timed metadata of Yuneec drones, and
595
- subtitle text in some other videos.
603
+ Tags extracted from the tx3g sbtl timed metadata of Yuneec and Autel drones,
604
+ and subtitle text in some other videos.
596
605
  },
597
606
  Lat => {
598
607
  Name => 'GPSLatitude',
@@ -619,6 +628,32 @@ my %insvLimit = (
619
628
  PrintConv => '$self->ConvertDateTime($val)',
620
629
  },
621
630
  Text => { Groups => { 2 => 'Other' } },
631
+ # the following tags are extracted from Autel Evo II drone videos
632
+ GPSDateTime => {
633
+ Groups => { 2 => 'Time' },
634
+ Description => 'GPS Date/Time',
635
+ PrintConv => '$self->ConvertDateTime($val)',
636
+ },
637
+ HomeLat => {
638
+ Name => 'GPSHomeLatitude',
639
+ RawConv => '$$self{FoundGPSLatitude} = 1; $val',
640
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
641
+ },
642
+ HomeLon => {
643
+ Name => 'GPSHomeLongitude',
644
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
645
+ },
646
+ ISO => { },
647
+ SHUTTER => {
648
+ Name => 'ExposureTime',
649
+ ValueConv => '1 / $val',
650
+ PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
651
+ },
652
+ 'F-NUM' => {
653
+ Name => 'FNumber',
654
+ PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
655
+ },
656
+ EV => 'ExposureCompensation',
622
657
  );
623
658
 
624
659
  %Image::ExifTool::QuickTime::INSV_MakerNotes = (
@@ -868,7 +903,7 @@ sub FoundSomething($$;$$)
868
903
  #------------------------------------------------------------------------------
869
904
  # Approximate GPSDateTime value from sample time and CreateDate
870
905
  # Inputs: 0) ExifTool ref, 1) tag table ptr, 2) sample time (s)
871
- # 3) true if CreateDate is at end of video, 4) flag if CreateDate is UTC
906
+ # 3) true if CreateDate is UTC
872
907
  # Notes: Uses ExifTool CreateDateAtEnd as flag to subtract video duration
873
908
  sub SetGPSDateTime($$$;$)
874
909
  {
@@ -879,9 +914,9 @@ sub SetGPSDateTime($$$;$)
879
914
  if ($$et{CreateDateAtEnd}) { # adjust if CreateDate is at end of video
880
915
  return unless $$value{TimeScale} and $$value{Duration};
881
916
  $sampleTime -= $$value{Duration} / $$value{TimeScale};
882
- $et->WarnOnce('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
917
+ $et->Warn('Approximating GPSDateTime as CreateDate - Duration + SampleTime', 1);
883
918
  } else {
884
- $et->WarnOnce('Approximating GPSDateTime as CreateDate + SampleTime', 1);
919
+ $et->Warn('Approximating GPSDateTime as CreateDate + SampleTime', 1);
885
920
  }
886
921
  my $utc = $et->Options('QuickTimeUTC');
887
922
  $utc = $isUTC unless defined $utc; # (allow QuickTimeUTC=0 to override $isUTC default)
@@ -1242,7 +1277,7 @@ sub ProcessSamples($)
1242
1277
  ($startChunk, $samplesPerChunk, $descIdx) = @{shift @$stsc};
1243
1278
  $nextChunk = $$stsc[0][0] if @$stsc;
1244
1279
  }
1245
- @$size < @$start + $samplesPerChunk and $et->WarnOnce('Sample size error'), last;
1280
+ @$size < @$start + $samplesPerChunk and $et->Warn('Sample size error'), last;
1246
1281
  last unless defined $chunkStart and length $chunkStart;
1247
1282
  my $sampleStart = $chunkStart;
1248
1283
  my $chunkSize = 0;
@@ -1270,7 +1305,7 @@ Sample: for ($i=0; ; ) {
1270
1305
  push @chunkSize, $chunkSize;
1271
1306
  ++$iChunk;
1272
1307
  }
1273
- @$start == @$size or $et->WarnOnce('Incorrect sample start/size count'), return;
1308
+ @$start == @$size or $et->Warn('Incorrect sample start/size count'), return;
1274
1309
  # process as chunks if we are only interested in calculating hash
1275
1310
  if ($type eq 'soun' or $type eq 'vide') {
1276
1311
  $start = $stco;
@@ -1300,7 +1335,7 @@ Sample: for ($i=0; ; ) {
1300
1335
  $hdrFmt = ($hdrLen == 4 ? 'N' : $hdrLen == 2 ? 'n' : 'C');
1301
1336
  require Image::ExifTool::H264;
1302
1337
  }
1303
-
1338
+
1304
1339
  # loop through all samples
1305
1340
  for ($i=0; $i<@$start and $i<@$size; ++$i) {
1306
1341
 
@@ -1320,11 +1355,11 @@ Sample: for ($i=0; ; ) {
1320
1355
  }
1321
1356
  }
1322
1357
  # read the sample data
1323
- $raf->Seek($$start[$i], 0) or $et->WarnOnce("Seek error in $type data"), next;
1358
+ $raf->Seek($$start[$i], 0) or $et->Warn("Seek error in $type data"), next;
1324
1359
  my $buff;
1325
1360
  my $n = $raf->Read($buff, $size);
1326
1361
  unless ($n == $size) {
1327
- $et->WarnOnce("Error reading $type data");
1362
+ $et->Warn("Error reading $type data");
1328
1363
  next unless $n;
1329
1364
  $size = $n;
1330
1365
  }
@@ -1406,7 +1441,7 @@ Sample: for ($i=0; ; ) {
1406
1441
 
1407
1442
  if ($$tagTbl{$metaFormat}) {
1408
1443
  my $tagInfo = $et->GetTagInfo($tagTbl, $metaFormat, \$buff);
1409
- if ($tagInfo) {
1444
+ if ($tagInfo and (not $$tagInfo{Unknown} or $$et{OPTIONS}{Unknown} >= $$tagInfo{Unknown})) {
1410
1445
  FoundSomething($et, $tagTbl, $time[$i], $dur[$i]);
1411
1446
  $$et{ee} = $ee; # need ee information for 'keys'
1412
1447
  $et->HandleTag($tagTbl, $metaFormat, undef,
@@ -1416,6 +1451,15 @@ Sample: for ($i=0; ; ) {
1416
1451
  TagInfo => $tagInfo,
1417
1452
  );
1418
1453
  delete $$et{ee};
1454
+ # synthesize GPSDateTime if necessary for djmd metadata
1455
+ if ($metaFormat eq 'djmd') {
1456
+ if (defined $$et{GPSLatitude} and defined $$et{GPSLongitude} and not $$et{GPSDateTime}) {
1457
+ SetGPSDateTime($et, $tagTbl, $time[$i], 1); # (NC)
1458
+ }
1459
+ delete $$et{GPSLatitude};
1460
+ delete $$et{GPSLongitude};
1461
+ delete $$et{GPSDateTime};
1462
+ }
1419
1463
  } elsif ($metaFormat eq 'camm' and $buff =~ /^X/) {
1420
1464
  # seen 'camm' metadata in this format (X/Y/Z acceleration and G force? + GPRMC + ?)
1421
1465
  # "X0000.0000Y0000.0000Z0000.0000G0000.0000$GPRMC,000125,V,,,,,000.0,,280908,002.1,N*71~, 794021 \x0a"
@@ -1468,7 +1512,7 @@ Sample: for ($i=0; ; ) {
1468
1512
  # clean up
1469
1513
  $raf->Seek($tell, 0); # restore original file position
1470
1514
  delete $$et{DOC_NUM};
1471
- $$et{HandlerType} = $$et{HanderDesc} = '';
1515
+ $$et{HandlerType} = '';
1472
1516
  }
1473
1517
 
1474
1518
  #------------------------------------------------------------------------------
@@ -1519,7 +1563,7 @@ sub ProcessFreeGPS($$$)
1519
1563
  my ($et, $dirInfo, $tagTbl) = @_;
1520
1564
  my $dataPt = $$dirInfo{DataPt};
1521
1565
  my $dirLen = length $$dataPt;
1522
- my ($yr, $mon, $day, $hr, $min, $sec, $stat, $lbl, $ddd);
1566
+ my ($yr, $mon, $day, $hr, $min, $sec, $ss, $stat, $lbl, $ddd, $done);
1523
1567
  my ($lat, $latRef, $lon, $lonRef, $spd, $trk, $alt, @acc, @xtra);
1524
1568
 
1525
1569
  return 0 if $dirLen < 82;
@@ -1644,7 +1688,7 @@ sub ProcessFreeGPS($$$)
1644
1688
  }
1645
1689
  if ($notEnc and $notStr) {
1646
1690
 
1647
- $debug and $et->FoundTag(GPSType => '3a');
1691
+ $debug and $et->FoundTag(GPSType => 3);
1648
1692
  # decode freeGPS from ViofoA119v3 dashcam (similar to Novatek GPS format)
1649
1693
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1650
1694
  # 0010: 05 00 00 00 2f 00 00 00 03 00 00 00 13 00 00 00 [..../...........]
@@ -1658,7 +1702,7 @@ sub ProcessFreeGPS($$$)
1658
1702
  ($sec,$min,$hr,$day,$mon,$yr) = gmtime($time);
1659
1703
  $yr += 1900;
1660
1704
  ++$mon;
1661
- $et->WarnOnce('Converting GPSDateTime to UTC based on local time zone',1);
1705
+ $et->Warn('Converting GPSDateTime to UTC based on local time zone',1);
1662
1706
  }
1663
1707
  $lat = GetFloat($dataPt, 0x2c);
1664
1708
  $lon = GetFloat($dataPt, 0x30);
@@ -1672,7 +1716,7 @@ sub ProcessFreeGPS($$$)
1672
1716
 
1673
1717
  } else {
1674
1718
 
1675
- $debug and $et->FoundTag(GPSType => '3b');
1719
+ $debug and $et->FoundTag(GPSType => 4);
1676
1720
  # decode freeGPS from E-ACE B44 dashcam
1677
1721
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1678
1722
  # 0010: 08 00 00 00 22 00 00 00 01 00 00 00 18 00 00 00 [...."...........]
@@ -1703,40 +1747,76 @@ sub ProcessFreeGPS($$$)
1703
1747
  ($lon = DecryptLucky($ln, $key)) =~ /^\d{1,5}\.\d+$/ or undef($lon), next;
1704
1748
  last;
1705
1749
  }
1706
- $lon or $et->WarnOnce('Unknown encryption for latitude/longitude');
1750
+ $lon or $et->Warn('Unknown encryption for latitude/longitude');
1707
1751
  }
1708
1752
  }
1709
1753
 
1710
- } elsif ($$dataPt =~ /^.{21}\0\0\0A([NS])([EW])/s) {
1754
+ } elsif ($$dataPt =~ /^(.{16}|.{48}|.{80})LIGOGPSINFO\0/s and length($$dataPt) >= length($1) + 0x84) {
1711
1755
 
1712
- $debug and $et->FoundTag(GPSType => 4);
1713
- # also decode 'gpmd' chunk from Kingslim D4 dashcam videos
1714
- # 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
1715
- # 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
1716
- # 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
1717
- # 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
1718
- # 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
1719
- # 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1720
- # 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
1721
- # 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
1722
- ($latRef, $lonRef) = ($1, $2);
1723
- ($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
1724
- # lat/lon aren't decoded properly, but spd,trk,acc are
1725
- $lat = GetFloat($dataPt, 0x1c);
1726
- $lon = GetFloat($dataPt, 0x20);
1727
- $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
1728
- $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1729
- $lat = abs $lat;
1730
- $lon = abs $lon;
1731
- $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
1732
- $trk = GetFloat($dataPt, 0x28);
1733
- $acc[0] = GetFloat($dataPt, 0x2c);
1734
- $acc[1] = GetFloat($dataPt, 0x30);
1735
- $acc[2] = GetFloat($dataPt, 0x34);
1756
+ $debug and $et->FoundTag(GPSType => 5);
1757
+ my $pos = length $1;
1758
+ # iiway s1 dual dash cam - offset 16, encrypted and fuzzed with scale 1
1759
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1760
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1761
+ # 0020: 0a 00 00 00 23 23 23 23 6a 00 00 00 c0 20 20 20 [....####j.... ]
1762
+ # 0030: 20 f0 12 10 12 22 e1 0e 10 12 2f 90 10 13 02 f2 [ ...."..../.....]
1763
+ # XGODY 12" 4K Dashcam - offset 16, encrypted and fuzzed with scale 1
1764
+ # 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
1765
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1766
+ # 0020: cd 61 00 00 23 23 23 23 6d 00 00 00 c1 ec 41 20 [.a..####m.....A ]
1767
+ # 0030: 20 f0 12 10 12 24 e5 0e 10 11 2f 92 10 12 00 f6 [ ....$..../.....]
1768
+ # ABASK A8 4K Dashcam - offset 16, encrypted and fuzzed with scale 3
1769
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1770
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1771
+ # 0020: 00 00 00 00 23 23 23 23 69 00 00 00 c0 20 20 20 [....####i.... ]
1772
+ # 0030: 20 f0 12 10 12 23 e5 0e 10 12 2f 99 10 11 02 f2 [ ....#..../.....]
1773
+ # Unknown dashcam (forum16060) - offset 16, enciphered and fuzzed with scale 1
1774
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 98 00 00 00 [..@.freeGPS ....]
1775
+ # 0010: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 0d [LIGOGPSINFO.....]
1776
+ # 0020: 0a 00 00 00 23 23 23 23 3b 00 a0 34 46 44 46 31 [....####;..4FDF1]
1777
+ # 0030: 2f 44 39 2f 45 38 20 44 3d 4c 47 4a 4c 39 38 20 [/D9/E8 D=LGJL98 ]
1778
+ # Rexing dashcam V1GW-4K - offset 48, encrypted and fuzzed with scale 1
1779
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
1780
+ # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1781
+ # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
1782
+ # 0030: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1783
+ # 0040: 01 00 00 00 23 23 23 23 73 00 00 00 c0 20 20 20 [....####s.... ]
1784
+ # 0050: 20 f0 12 10 12 23 e5 0e 10 12 2f 95 10 12 01 f3 [ ....#..../.....]
1785
+ # Kingslim D4 dashcam - offset 80, encrypted and fuzzed with scale 1
1786
+ # 0000: 0a 00 00 00 0b 00 00 00 07 00 00 00 e5 07 00 00 [................]
1787
+ # 0010: 06 00 00 00 03 00 00 00 41 4e 57 31 91 52 83 45 [........ANW1.R.E]
1788
+ # 0020: 15 70 fe c5 29 5c c3 41 ae c7 af 42 00 00 d1 be [.p..)\.A...B....]
1789
+ # 0030: 00 00 80 3b 00 00 2c 3e 00 00 00 00 00 00 00 00 [...;..,>........]
1790
+ # 0040: 00 00 00 00 00 00 00 00 00 00 00 00 26 26 26 26 [............&&&&]
1791
+ # 0050: 4c 49 47 4f 47 50 53 49 4e 46 4f 00 00 00 00 05 [LIGOGPSINFO.....]
1792
+ # 0060: 01 00 00 00 23 23 23 23 75 00 00 00 c0 22 20 20 [....####u...." ]
1793
+ # 0070: 20 f0 12 10 12 21 e5 0e 10 12 2f 90 10 13 01 f2 [ ....!..../.....]
1794
+ my %dirInfo = ( DataPt => $dataPt, DirStart => $pos, DirName => "LigoGPS_$pos" );
1795
+ # (this is weak, but the only difference I could find between these 2 headers)
1796
+ # (NOTE: ../testpics/gps_video/forum16229.mp4 uses this word for a counter!)
1797
+ $$et{LigoGPSScale} = 3 if $pos == 16 and $$dataPt =~ /^.{12}\xf0\x03\0\0.{16}\0{4}/s;
1798
+ Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tagTbl);
1799
+ $done = 1;
1800
+
1801
+ # also... when offset is 0x50 (Kingslim), the GPS also exists in this format:
1802
+ # ($latRef, $lonRef) = ($1, $2);
1803
+ # ($hr,$min,$sec,$yr,$mon,$day) = unpack("V6", $$dataPt);
1804
+ # # lat/lon aren't decoded properly, but spd,trk,acc are
1805
+ # $lat = GetFloat($dataPt, 0x1c);
1806
+ # $lon = GetFloat($dataPt, 0x20);
1807
+ # $et->VPrint(0, sprintf("Raw lat/lon = %.9f %.9f\n", $lat, $lon));
1808
+ # $et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1809
+ # $lat = abs $lat;
1810
+ # $lon = abs $lon;
1811
+ # $spd = GetFloat($dataPt, 0x24) * $knotsToKph; # (convert knots to km/h)
1812
+ # $trk = GetFloat($dataPt, 0x28);
1813
+ # $acc[0] = GetFloat($dataPt, 0x2c);
1814
+ # $acc[1] = GetFloat($dataPt, 0x30);
1815
+ # $acc[2] = GetFloat($dataPt, 0x34);
1736
1816
 
1737
1817
  } elsif ($$dataPt =~ /^.{60}A\0{3}.{4}([NS])\0{3}.{4}([EW])\0{3}/s) {
1738
1818
 
1739
- $debug and $et->FoundTag(GPSType => 5);
1819
+ $debug and $et->FoundTag(GPSType => 6);
1740
1820
  # decode freeGPS from Akaso dashcam
1741
1821
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 60 00 00 00 [....freeGPS `...]
1742
1822
  # 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
@@ -1770,7 +1850,7 @@ sub ProcessFreeGPS($$$)
1770
1850
 
1771
1851
  } elsif ($$dataPt =~ /^.{60}4W`b]S</s and length($$dataPt) >= 140) {
1772
1852
 
1773
- $debug and $et->FoundTag(GPSType => 6);
1853
+ $debug and $et->FoundTag(GPSType => 7);
1774
1854
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 01 00 00 [..@.freeGPS ....]
1775
1855
  # 0010: 5a 58 53 42 4e 58 59 53 00 00 00 00 00 00 00 00 [ZXSBNXYS........]
1776
1856
  # 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
@@ -1780,18 +1860,18 @@ sub ProcessFreeGPS($$$)
1780
1860
  # 0060: 42 3e 49 49 40 42 45 3c 55 3c 45 47 3e 45 43 41 [B>II@BE<U<EG>ECA]
1781
1861
  # decipher $GPRMC by subtracting 16 from each character value
1782
1862
  $_ = pack 'C*', map { $_>=16 and $_-=16 } unpack('x60C80', $$dataPt);
1783
- unless (/[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+)/) {
1784
- SetByteOrder($oldOrder);
1785
- return 0;
1863
+ if (/[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+)/) {
1864
+ ($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
1865
+ $yr += ($yr >= 70 ? 1900 : 2000);
1866
+ $spd = $9 * $knotsToKph if length $9;
1867
+ $trk = $10 if length $10;
1868
+ } else {
1869
+ $done = 1;
1786
1870
  }
1787
- ($yr,$mon,$day,$hr,$min,$sec,$lat,$latRef,$lon,$lonRef) = ($13,$12,$11,$1,$2,$3,$5,$6,$7,$8);
1788
- $yr += ($yr >= 70 ? 1900 : 2000);
1789
- $spd = $9 * $knotsToKph if length $9;
1790
- $trk = $10 if length $10;
1791
1871
 
1792
1872
  } elsif ($$dataPt =~ /^.{64}[\x01-\x0c]\0{3}[\x01-\x1f]\0{3}A[NS][EW]\0{5}/s) {
1793
1873
 
1794
- $debug and $et->FoundTag(GPSType => 7);
1874
+ $debug and $et->FoundTag(GPSType => 8);
1795
1875
  # Akaso V1 dascham
1796
1876
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
1797
1877
  # 0010: 59 6e 64 41 6b 61 73 6f 43 61 72 00 00 00 00 00 [YndAkasoCar.....]
@@ -1816,7 +1896,7 @@ sub ProcessFreeGPS($$$)
1816
1896
  ($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
1817
1897
  unpack('x48V6a1a1a1x1', $$dataPt);
1818
1898
 
1819
- $et->WarnOnce('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1899
+ $et->Warn('GPSLatitude/Longitude encryption is not yet known, so these will be wrong');
1820
1900
  # (see https://exiftool.org/forum/index.php?topic=11320.0)
1821
1901
 
1822
1902
  $spd = GetFloat($dataPt, 0x60);
@@ -1824,11 +1904,11 @@ sub ProcessFreeGPS($$$)
1824
1904
  $lat = GetDouble($dataPt, 0x50); # latitude is here, but encrypted somehow
1825
1905
  $lon = GetDouble($dataPt, 0x58); # longitude is here, but encrypted somehow
1826
1906
  $ddd = 1; # don't convert until we know what the format is
1827
- #my $serialNum = substr($$dataPt, 0x68, 20);
1907
+ #my $serialNum = substr($$dataPt, 0x68, 20); # (confirmed)
1828
1908
 
1829
1909
  } elsif ($$dataPt =~ /^.{12}\xac\0\0\0.{44}(.{72})/s) {
1830
1910
 
1831
- $debug and $et->FoundTag(GPSType => 8);
1911
+ $debug and $et->FoundTag(GPSType => 9);
1832
1912
  # EACHPAI dash cam
1833
1913
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 ac 00 00 00 [....freeGPS ....]
1834
1914
  # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
@@ -1840,19 +1920,18 @@ sub ProcessFreeGPS($$$)
1840
1920
  # 0070: 43 41 3c 40 42 40 46 42 40 3c 3c 3c 51 3a 47 46 [CA<@B@FB@<<<Q:GF]
1841
1921
  # 0080: 00 2a 36 35 00 00 00 00 00 00 00 00 00 00 00 00 [.*65............]
1842
1922
 
1843
- $et->WarnOnce("Can't yet decrypt EACHPAI timed GPS", 1);
1923
+ $et->Warn("Can't yet decrypt EACHPAI timed GPS", 1);
1844
1924
  # (see https://exiftool.org/forum/index.php?topic=5095.msg61266#msg61266)
1845
- SetByteOrder($oldOrder);
1846
- return 1;
1925
+ $done = 1;
1847
1926
 
1848
- my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
1849
- # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1850
- substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1851
- # (other values are currently unknown)
1927
+ # my $time = pack 'C*', map { $_ ^= 0 } unpack 'C*', $1;
1928
+ # # bytes 7-12 are the timestamp in ASCII HHMMSS after xor-ing with 0x70
1929
+ # substr($time,7,6) = pack 'C*', map { $_ ^= 0x70 } unpack 'C*', substr($time,7,6);
1930
+ # # (other values are currently unknown)
1852
1931
 
1853
1932
  } elsif ($$dataPt =~ /^.{64}A([NS])([EW])\0/s) {
1854
1933
 
1855
- $debug and $et->FoundTag(GPSType => 9);
1934
+ $debug and $et->FoundTag(GPSType => 10);
1856
1935
  # Vantrue S1 dashcam
1857
1936
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 00 00 00 [....freeGPS x...]
1858
1937
  # 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
@@ -1864,21 +1943,21 @@ sub ProcessFreeGPS($$$)
1864
1943
  # 0070: 05 00 00 00 7f 00 00 00 07 01 00 00 00 00 00 00 [................]
1865
1944
  ($latRef, $lonRef) = ($1, $2);
1866
1945
  ($yr,$mon,$day,$hr,$min,$sec,@acc) = unpack('x68V6x20V3', $$dataPt);
1867
- unless ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
1868
- SetByteOrder($oldOrder);
1869
- return 0;
1946
+ if ($mon>=1 and $mon<=12 and $day>=1 and $day<=31) {
1947
+ # (not sure about acc scaling)
1948
+ @acc = map { SignedInt32 / 1000 } @acc;
1949
+ $lon = GetFloat($dataPt, 0x5c);
1950
+ $lat = GetFloat($dataPt, 0x60);
1951
+ $spd = GetFloat($dataPt, 0x64) * $knotsToKph;
1952
+ $trk = GetFloat($dataPt, 0x68);
1953
+ $alt = GetFloat($dataPt, 0x6c);
1954
+ } else {
1955
+ $done = 1;
1870
1956
  }
1871
- # (not sure about acc scaling)
1872
- @acc = map { SignedInt32 / 1000 } @acc;
1873
- $lon = GetFloat($dataPt, 0x5c);
1874
- $lat = GetFloat($dataPt, 0x60);
1875
- $spd = GetFloat($dataPt, 0x64) * $knotsToKph;
1876
- $trk = GetFloat($dataPt, 0x68);
1877
- $alt = GetFloat($dataPt, 0x6c);
1878
1957
 
1879
1958
  } elsif (substr($$dataPt,0x45,3) eq 'ATC') {
1880
1959
 
1881
- $debug and $et->FoundTag(GPSType => 10);
1960
+ $debug and $et->FoundTag(GPSType => 11);
1882
1961
  # header looks like this: (sample 1)
1883
1962
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 38 06 00 00 [....freeGPS 8...]
1884
1963
  # 0010: 49 51 53 32 30 31 33 30 33 30 36 42 00 00 00 00 [IQS20130306B....]
@@ -1919,7 +1998,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1919
1998
  my $i;
1920
1999
  for ($i=0; $i<@dateMax; ++$i) {
1921
2000
  next if $now[$i] <= $dateMax[$i];
1922
- $et->WarnOnce('Invalid GPS date/time');
2001
+ $et->Warn('Invalid GPS date/time');
1923
2002
  next ATCRec; # ignore this record
1924
2003
  }
1925
2004
  # look for next ATC record in temporal sequence
@@ -1986,12 +2065,11 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
1986
2065
  }
1987
2066
  # save position of most recent record (needed when parsing the next freeGPS block)
1988
2067
  $$et{FreeGPS2}{RecentRecPos} = $lastRecPos;
1989
- SetByteOrder($oldOrder);
1990
- return 1;
2068
+ $done = 1;
1991
2069
 
1992
2070
  } elsif ($$dataPt =~ /^.{60}A\0.{10}([NS])\0.{14}([EW])\0/s and $dirLen >= 0x88) {
1993
2071
 
1994
- $debug and $et->FoundTag(GPSType => 11);
2072
+ $debug and $et->FoundTag(GPSType => 12);
1995
2073
  # header looks like this in my sample:
1996
2074
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 08 01 00 00 [....freeGPS ....]
1997
2075
  # 0010: 32 30 31 33 30 38 31 35 2e 30 31 00 00 00 00 00 [20130815.01.....]
@@ -2022,9 +2100,14 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2022
2100
 
2023
2101
  } elsif ($$dataPt =~ /^.{16}A([NS])([EW])\0/s) {
2024
2102
 
2025
- $debug and $et->FoundTag(GPSType => 12);
2103
+ $debug and $et->FoundTag(GPSType => 13);
2026
2104
  # INNOVV MP4 video (same format as INNOVV TS)
2027
- while ($$dataPt =~ /(A[NS][EW]\0.{28})/g) {
2105
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
2106
+ # 0010: 41 4e 45 00 e4 56 96 45 86 b1 ca 44 5c 8f e2 40 [ANE..V.E...D\..@]
2107
+ # 0020: 33 33 58 43 c3 00 00 00 30 00 00 00 a0 fe ff ff [33XC....0.......]
2108
+ # 0030: 41 4e 45 00 e3 56 96 45 82 b1 ca 44 5c 8f fa 40 [ANE..V.E...D\..@]
2109
+ # 0040: c3 75 56 43 8c ff ff ff 8c 00 00 00 c3 fd ff ff [.uVC............]
2110
+ while ($$dataPt =~ /(A[NS][EW]\0.{28})/sg) {
2028
2111
  my $dat = $1;
2029
2112
  $lat = abs(GetFloat(\$dat, 4)); # (abs just to be safe)
2030
2113
  $lon = abs(GetFloat(\$dat, 8)); # (abs just to be safe)
@@ -2039,12 +2122,35 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2039
2122
  $et->HandleTag($tagTbl, GPSTrack => $trk);
2040
2123
  $et->HandleTag($tagTbl, Accelerometer => "@acc");
2041
2124
  }
2042
- SetByteOrder($oldOrder);
2043
- return 1;
2125
+ $done = 1;
2126
+
2127
+ } elsif ($$dataPt =~ /^.{20}[\0-\x18][\0-\x3b]{2}[\0-\x09]A([NS])([EW])/s) {
2128
+
2129
+ $debug and $et->FoundTag(GPSType => 14);
2130
+ # XBHT motorcycle dashcam Model XB702
2131
+ # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
2132
+ # 0010: 00 17 05 11 0d 25 18 00 41 4e 45 64 83 3f 00 00 [.....%..ANEd.?..]
2133
+ # 0020: 44 3d c5 02 48 6d ff 07 df 03 00 00 6b 00 00 00 [D=..Hm......k...]
2134
+ # 0030: 00 00 00 00 00 17 05 11 0d 25 18 01 41 4e 45 64 [.........%..ANEd]
2135
+ # 0040: 8b 3f 00 00 30 3d c5 02 50 6d ff 07 df 03 00 00 [.?..0=..Pm......]
2136
+ while ($$dataPt =~ /(.{7}[\0-\x09]A[NS][EW].{25})/sg) {
2137
+ my $dat = $1;
2138
+ ($yr,$mon,$day,$hr,$min,$sec,$ss,$latRef,$lonRef,$lat,$lon,$spd) =
2139
+ unpack('xC7xCCx5VVx4v', $dat);
2140
+ $yr += 2000; $lat /= 1e4; $lon /= 1e4;
2141
+ ConvertLatLon($lat, $lon);
2142
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2143
+ my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d.%d',$yr,$mon,$day,$hr,$min,$sec,$ss);
2144
+ $et->HandleTag($tagTbl, GPSDateTime => $time);
2145
+ $et->HandleTag($tagTbl, GPSLatitude => $lat * ($latRef eq 'S' ? -1 : 1));
2146
+ $et->HandleTag($tagTbl, GPSLongitude => $lon * ($lonRef eq 'W' ? -1 : 1));
2147
+ $et->HandleTag($tagTbl, GPSSpeed => $spd);
2148
+ }
2149
+ $done = 1;
2044
2150
 
2045
2151
  } elsif ($$dataPt =~ /^.{28}A.{11}([NS]).{15}([EW])/s) {
2046
2152
 
2047
- $debug and $et->FoundTag(GPSType => 13);
2153
+ $debug and $et->FoundTag(GPSType => 15);
2048
2154
  # Vantrue N4 dashcam
2049
2155
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 f0 03 00 00 [..@.freeGPS ....]
2050
2156
  # 0010: 0d 00 00 00 16 00 00 00 1e 00 00 00 41 00 00 00 [............A...]
@@ -2101,7 +2207,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2101
2207
  ($hr,$min,$sec,$yr,$mon,$day,$stat,$latRef,$lonRef) =
2102
2208
  unpack('x48V6a1a1a1x1V4', $$dataPt);
2103
2209
  if (substr($$dataPt, 16, 3) eq 'IQS') {
2104
- $debug and $et->FoundTag(GPSType => 14);
2210
+ $debug and $et->FoundTag(GPSType => 16);
2105
2211
  # Type 3b (ref PH)
2106
2212
  # header looks like this in my sample:
2107
2213
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 4c 00 00 00 [....freeGPS L...]
@@ -2113,7 +2219,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2113
2219
  $spd = Get32s($dataPt, 0x54) / 100 * $mpsToKph;
2114
2220
  $alt = GetFloat($dataPt, 0x58) / 1000; # (NC)
2115
2221
  } else {
2116
- $debug and $et->FoundTag(GPSType => 15);
2222
+ $debug and $et->FoundTag(GPSType => 17);
2117
2223
  $lat = GetFloat($dataPt, 0x4c);
2118
2224
  $lon = GetFloat($dataPt, 0x50);
2119
2225
  $spd = GetFloat($dataPt, 0x54) * $knotsToKph;
@@ -2133,7 +2239,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2133
2239
 
2134
2240
  } elsif ($$dataPt =~ m<^.{23}(\d{4})/(\d{2})/(\d{2}) (\d{2}):(\d{2}):(\d{2}) [N|S]>s) {
2135
2241
 
2136
- $debug and $et->FoundTag(GPSType => 16);
2242
+ $debug and $et->FoundTag(GPSType => 18);
2137
2243
  # XGODY 12" 4K Dashcam
2138
2244
  # 0000: 00 00 00 a8 66 72 65 65 47 50 53 20 98 00 00 00 [....freeGPS ....]
2139
2245
  # 0010: 6e 6f 72 6d 61 6c 3a 32 30 32 34 2f 30 35 2f 32 [normal:2024/05/2]
@@ -2164,8 +2270,8 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2164
2270
  }
2165
2271
 
2166
2272
  } elsif ($$dataPt =~ m/^.{30}A.{20}VV/) {
2167
-
2168
- $debug and $et->FoundTag(GPSType => 17);
2273
+
2274
+ $debug and $et->FoundTag(GPSType => 19);
2169
2275
  # 70mai A810 dashcam (note: no timestamps in the samples I have)
2170
2276
  # 0000: 00 00 40 00 66 72 65 65 47 50 53 20 ed 01 00 00 [..@.freeGPS ....]
2171
2277
  # 0010: 03 00 ed 01 00 00 00 0f 00 00 70 08 00 00 41 66 [..........p...Af]
@@ -2182,7 +2288,7 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2182
2288
 
2183
2289
  } else {
2184
2290
 
2185
- $debug and $et->FoundTag(GPSType => 18);
2291
+ $debug and $et->FoundTag(GPSType => 20);
2186
2292
  # (look for binary GPS as stored by Nextbase 512G, ref PH)
2187
2293
  # 0000: 00 00 80 00 66 72 65 65 47 50 53 20 78 01 00 00 [....freeGPS x...]
2188
2294
  # 0010: 78 2e 78 78 00 00 00 00 00 00 00 00 00 00 00 00 [x.xx............]
@@ -2228,14 +2334,14 @@ ATCRec: for ($recPos = 0x30; $recPos + 52 < $dirLen; $recPos += 52) {
2228
2334
  $et->HandleTag($tagTbl, GPSTrack => $trk);
2229
2335
  last if $pos += 0x20 > length($$dataPt) - 0x1e;
2230
2336
  }
2231
- SetByteOrder($oldOrder);
2232
- return $$et{DOC_NUM} ? 1 : 0; # return 0 if nothing extracted
2337
+ $done = 1;
2233
2338
  }
2339
+ SetByteOrder($oldOrder);
2340
+ return $$et{DOC_NUM} ? 1 : 0 if $done;
2341
+ return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
2234
2342
  #
2235
2343
  # save tag values extracted by above code
2236
2344
  #
2237
- SetByteOrder($oldOrder);
2238
- return 0 if defined $yr and $mon < 1 or $mon > 12; # quick sanity check
2239
2345
  FoundSomething($et, $tagTbl, $$dirInfo{SampleTime}, $$dirInfo{SampleDuration});
2240
2346
  $sec = '0' . $sec unless $sec =~ /^\d{2}/; # pad integer part of seconds to 2 digits
2241
2347
  if (defined $yr) {
@@ -2361,7 +2467,7 @@ sub ParseTag($$$)
2361
2467
  }
2362
2468
 
2363
2469
  #------------------------------------------------------------------------------
2364
- # Process Yuneec 'tx3g' sbtl metadata (ref PH)
2470
+ # Process Yuneec 'tx3g' and Autel sbtl metadata (ref PH)
2365
2471
  # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
2366
2472
  # Returns: 1 on success
2367
2473
  sub Process_tx3g($$$)
@@ -2369,13 +2475,49 @@ sub Process_tx3g($$$)
2369
2475
  my ($et, $dirInfo, $tagTablePtr) = @_;
2370
2476
  my $dataPt = $$dirInfo{DataPt};
2371
2477
  return 0 if length $$dataPt < 2;
2372
- pos($$dataPt) = 2; # skip 2-byte length word
2373
2478
  $et->VerboseDir('tx3g', undef, length($$dataPt)-2);
2374
- $et->HandleTag($tagTablePtr, 'Text', substr($$dataPt, 2));
2375
- if ($$dataPt =~ /^..\w{3} (\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}:\d{2}) ?([-+])(\d{2}):?(\d{2})$/s) {
2479
+ my $text = substr($$dataPt, 2); # remove 2-byte length word
2480
+ $et->HandleTag($tagTablePtr, 'Text', $text);
2481
+ if ($text =~ /^HOME\(/) {
2482
+ # --- sample text from Autel Evo II drone ---
2483
+ # HOME(W: 109.318642, N: 40.769371) 2023-09-12 10:28:07
2484
+ # GPS(W: 109.339287, N: 40.768574, 2371.76m)
2485
+ # HDR ISO:100 SHUTTER:1000 EV:-0.7 F-NUM:1.8
2486
+ # F.PRY (1.0\xc2\xb0, -3.7\xc2\xb0, -59.0\xc2\xb0), G.PRY (-51.1\xc2\xb0, 0.0\xc2\xb0, -58.9\xc2\xb0)
2487
+ my $line;
2488
+ foreach $line (split /\x0a/, $text) {
2489
+ if ($line =~ /^HOME\(([EW]):\s*(\d+\.\d+),\s*([NS]):\s*(\d+\.\d+)\)\s*(.*)/) {
2490
+ my ($lon, $lat, $time) = ($2, $4, $5);
2491
+ $lon = -$lon if $1 eq 'W';
2492
+ $lat = -$lat if $3 eq 'S';
2493
+ $time =~ tr/-/:/; # (likely local time zone, but not confirmed)
2494
+ $et->HandleTag($tagTablePtr, GPSDateTime => $time);
2495
+ $et->HandleTag($tagTablePtr, HomeLat => $lat);
2496
+ $et->HandleTag($tagTablePtr, HomeLon => $lon);
2497
+ } elsif ($line =~ /^GPS\(([EW]):\s*(\d+\.\d+),\s*([NS]):\s*(\d+\.\d+),\s*(.*)m/) {
2498
+ my ($lon, $lat, $alt) = ($2, $4, $5);
2499
+ $lon = -$lon if $1 eq 'W';
2500
+ $lat = -$lat if $3 eq 'S';
2501
+ $et->HandleTag($tagTablePtr, Lat => $lat);
2502
+ $et->HandleTag($tagTablePtr, Lon => $lon);
2503
+ $et->HandleTag($tagTablePtr, Alt => $alt);
2504
+ } elsif ($line =~ /^F\.PRY\s*\((-?[\d.]+)\xc2\xb0,\s*(-?[\d.]+)\xc2\xb0,\s*(-?[\d.]+)\xc2\xb0/) {
2505
+ $et->HandleTag($tagTablePtr, Yaw => $1);
2506
+ $et->HandleTag($tagTablePtr, Pitch => $2);
2507
+ $et->HandleTag($tagTablePtr, Roll => $3);
2508
+ if ($line =~ /G\.PRY\s*\((-?[\d.]+)\xc2\xb0,\s*(-?[\d.]+)\xc2\xb0,\s*(-?[\d.]+)\xc2\xb0/) {
2509
+ $et->HandleTag($tagTablePtr, GimYaw => $1);
2510
+ $et->HandleTag($tagTablePtr, GimPitch => $2);
2511
+ $et->HandleTag($tagTablePtr, GimRoll => $3);
2512
+ }
2513
+ } else {
2514
+ $et->HandleTag($tagTablePtr, $1, $2) while $line =~ /([-\w]+):([^:]*[^:\s])(\s|$)/sg;
2515
+ }
2516
+ }
2517
+ } elsif ($text =~ /^\w{3} (\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}:\d{2}) ?([-+])(\d{2}):?(\d{2})$/s) {
2376
2518
  $et->HandleTag($tagTablePtr, 'DateTime', "$1:$2:$3 $4$5$6:$7");
2377
2519
  } else {
2378
- $et->HandleTag($tagTablePtr, $1, $2) while $$dataPt =~ /(\w+):([^:]*[^:\s])(\s|$)/sg;
2520
+ $et->HandleTag($tagTablePtr, $1, $2) while $text =~ /(\w+):([^:]*[^:\s])(\s|$)/sg;
2379
2521
  }
2380
2522
  return 1;
2381
2523
  }
@@ -2417,7 +2559,7 @@ sub Process_mebx($$$)
2417
2559
  Size => $len - 8,
2418
2560
  );
2419
2561
  } else {
2420
- $et->WarnOnce('No key information for mebx ID ' . PrintableTagID($id,1));
2562
+ $et->Warn('No key information for mebx ID ' . PrintableTagID($id,1));
2421
2563
  }
2422
2564
  }
2423
2565
  return 1;
@@ -2540,7 +2682,7 @@ sub Process_gdat($$$)
2540
2682
  {
2541
2683
  my ($et, $dirInfo, $tagTbl) = @_;
2542
2684
  unless ($$et{OPTIONS}{ExtractEmbedded}) {
2543
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
2685
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
2544
2686
  return 0;
2545
2687
  }
2546
2688
  my $dataPt = $$dirInfo{DataPt};
@@ -2591,87 +2733,11 @@ sub Process_nbmt($$$)
2591
2733
  delete $$et{NoMoreTextDecoding};
2592
2734
  delete $$et{DOC_NUM};
2593
2735
  } else {
2594
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData',3);
2736
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData',3);
2595
2737
  }
2596
2738
  return 1;
2597
2739
  }
2598
2740
 
2599
- #------------------------------------------------------------------------------
2600
- # Process LIGOGPS JSON-format GPS from Yada RoadCam Pro 4K BT58189
2601
- # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
2602
- # Returns: 1 on success
2603
- # Sample data (chained 512-byte records starting like this):
2604
- # 0000: 4c 49 47 4f 47 50 53 49 4e 46 4f 20 7b 22 48 6f [LIGOGPSINFO {"Ho]
2605
- # 0010: 75 72 22 3a 20 22 32 33 22 2c 20 22 4d 69 6e 75 [ur": "23", "Minu]
2606
- # 0020: 74 65 22 3a 20 22 31 30 22 2c 20 22 53 65 63 6f [te": "10", "Seco]
2607
- # 0030: 6e 64 22 3a 20 22 32 32 22 2c 20 22 59 65 61 72 [nd": "22", "Year]
2608
- # 0040: 22 3a 20 22 32 30 32 33 22 2c 20 22 4d 6f 6e 74 [": "2023", "Mont]
2609
- # 0050: 68 22 3a 20 22 31 32 22 2c 20 22 44 61 79 22 3a [h": "12", "Day":]
2610
- # 0060: 20 22 32 38 22 2c 20 22 73 74 61 74 75 73 22 3a [ "28", "status":]
2611
- sub ProcessLIGO_JSON($$$)
2612
- {
2613
- my ($et, $dirInfo, $tagTbl) = @_;
2614
- my $dataPt = $$dirInfo{DataPt};
2615
- my $dirLen = $$dirInfo{DirLen};
2616
- require Image::ExifTool::Import;
2617
- $et->VerboseDir('LIGO_JSON', undef, length($$dataPt));
2618
- while ($$dataPt =~ /LIGOGPSINFO (\{.*?\})/g) {
2619
- my $json = $1;
2620
- my %dbase;
2621
- Image::ExifTool::Import::ReadJSON(\$json, \%dbase);
2622
- my $info = $dbase{'*'} or next;
2623
- # my sample contains the following JSON fields (in this order):
2624
- # Hour Minute Second Year Month Day (GPS UTC time)
2625
- # status NS EW Latitude Longitude Speed (speed in knots)
2626
- # GsensorX GsensorY GsensorZ (units? - only seen "000" for all)
2627
- # MHour MMinute MSecond MYear MMonth MDay (local dashcam clock time)
2628
- # OLatitude OLongitude (? same values as Latitude/Longitude)
2629
- next unless defined $$info{status} and $$info{status} eq 'A'; # only read if GPS is active
2630
- $$et{DOC_NUM} = ++$$et{DOC_COUNT};
2631
- my $num = 0;
2632
- defined $$info{$_} and ++$num foreach qw(Year Month Day Hour Minute Second);
2633
- if ($num == 6) {
2634
- # this is the GPS time in UTC
2635
- my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2dZ',@$info{qw{Year Month Day Hour Minute Second}});
2636
- $et->HandleTag($tagTbl, GPSDateTime => $time);
2637
- }
2638
- if ($$info{Latitude} and $$info{Longitude}) {
2639
- my $lat = $$info{Latitude};
2640
- $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
2641
- my $lon = $$info{Longitude};
2642
- $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
2643
- $et->HandleTag($tagTbl, GPSLatitude => $lat);
2644
- $et->HandleTag($tagTbl, GPSLongitude => $lon);
2645
- }
2646
- $et->HandleTag($tagTbl, GPSSpeed => $$info{Speed} * $knotsToKph) if defined $$info{Speed};
2647
- if (defined $$info{GsensorX} and defined $$info{GsensorY} and defined $$info{GsensorZ}) {
2648
- # (don't know conversion factor for accel data, so leave it raw for now)
2649
- $et->HandleTag($tagTbl, Accelerometer => "$$info{GsensorX} $$info{GsensorY} $$info{GsensorZ}");
2650
- }
2651
- $num = 0;
2652
- defined $$info{$_} and ++$num foreach qw(MYear MMonth MDay MHour MMinute MSecond);
2653
- if ($num == 6) {
2654
- # this is the dashcam clock time (local time zone)
2655
- my $time = sprintf('%.4d:%.2d:%.2d %.2d:%.2d:%.2d',@$info{qw{MYear MMonth MDay MHour MMinute MSecond}});
2656
- $et->HandleTag($tagTbl, DateTimeOriginal => $time);
2657
- }
2658
- if (defined $$info{OLatitude} and defined $$info{OLongitude}) {
2659
- my $lat = $$info{OLatitude};
2660
- $lat = -$lat if $$info{NS} and $$info{NS} eq 'S';
2661
- my $lon = $$info{OLongitude};
2662
- $lon = -$lon if $$info{EW} and $$info{EW} eq 'W';
2663
- $et->HandleTag($tagTbl, GPSLatitude2 => $lat);
2664
- $et->HandleTag($tagTbl, GPSLongitude2 => $lon);
2665
- }
2666
- unless ($et->Options('ExtractEmbedded')) {
2667
- $et->WarnOnce('Use the ExtractEmbedded option to extract all timed GPS',3);
2668
- last;
2669
- }
2670
- }
2671
- delete $$et{DOC_NUM};
2672
- return 1;
2673
- }
2674
-
2675
2741
  #------------------------------------------------------------------------------
2676
2742
  # Process Kenwood drv-a301w dashcam 'udta' atom (ref PH)
2677
2743
  # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
@@ -2711,7 +2777,7 @@ sub ProcessKenwood($$$)
2711
2777
  }
2712
2778
  $et->HandleTag($tagTbl, Accelerometer => "@acc") if @acc;
2713
2779
  unless ($et->Options('ExtractEmbedded')) {
2714
- $et->WarnOnce('Use the ExtractEmbedded option to extract all timed GPS',3);
2780
+ $et->Warn('Use the ExtractEmbedded option to extract all timed GPS',3);
2715
2781
  last;
2716
2782
  }
2717
2783
  }
@@ -2834,7 +2900,7 @@ sub ProcessKenwoodTrailer($$$)
2834
2900
  $raf->Read($buff, 14) and $buff eq 'CCCCCCCCCCCCCC' or return 0;
2835
2901
  $et->VerboseDir('Kenwood trailer', undef, undef);
2836
2902
  unless ($$et{OPTIONS}{ExtractEmbedded}) {
2837
- $et->WarnOnce('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
2903
+ $et->Warn('Use the ExtractEmbedded option to extract timed GPSData from Kenwood trailer',3);
2838
2904
  return 1;
2839
2905
  }
2840
2906
  while ($raf->Read($buff, 121) and $buff =~ /^GPSDATA--(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/) {
@@ -3057,7 +3123,7 @@ sub ProcessTTAD($$$)
3057
3123
  $et->HandleTag($tagTbl, "Unknown0$type" => "@a");
3058
3124
  }
3059
3125
  } else {
3060
- $et->WarnOnce("Unknown TTAD record type $type",1);
3126
+ $et->Warn("Unknown TTAD record type $type",1);
3061
3127
  }
3062
3128
  # without -ee, stop after we find types 0,3,5 (ie. bitmask 0x29)
3063
3129
  $eeOpt or ($found & 0x29) != 0x29 or EEWarn($et), last;
@@ -3088,16 +3154,18 @@ sub ProcessInsta360($;$)
3088
3154
  my $verbose = $et->Options('Verbose');
3089
3155
  my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
3090
3156
  my $fileEnd = $raf->Tell();
3157
+ my $trailEnd = $fileEnd - $offset;
3091
3158
  my $trailerLen = unpack('x38V', $buff);
3092
- $trailerLen > $fileEnd and $et->Warn('Bad Insta360 trailer size'), return 0;
3159
+ $trailerLen > $trailEnd and $et->Warn('Bad Insta360 trailer size'), return 0;
3093
3160
  if ($dirInfo) {
3094
3161
  $$dirInfo{DirLen} = $trailerLen;
3095
- $$dirInfo{DataPos} = $fileEnd - $trailerLen;
3162
+ $$dirInfo{DataPos} = $trailEnd - $trailerLen;
3096
3163
  if ($$dirInfo{OutFile}) {
3097
3164
  if ($$et{DEL_GROUP}{Insta360}) {
3098
3165
  ++$$et{CHANGED};
3166
+ return 1;
3099
3167
  # just copy the trailer when writing
3100
- } elsif ($trailerLen > $fileEnd or not $raf->Seek($$dirInfo{DataPos}, 0) or
3168
+ } elsif ($trailerLen > $trailEnd or not $raf->Seek($$dirInfo{DataPos}, 0) or
3101
3169
  $raf->Read(${$$dirInfo{OutFile}}, $trailerLen) != $trailerLen)
3102
3170
  {
3103
3171
  return 0;
@@ -3109,13 +3177,13 @@ sub ProcessInsta360($;$)
3109
3177
  }
3110
3178
  unless ($et->Options('ExtractEmbedded')) {
3111
3179
  # can arrive here when reading Insta360 trailer on JPEG image (INSP file)
3112
- $et->WarnOnce('Use ExtractEmbedded option to extract timed metadata from Insta360 trailer',3);
3180
+ $et->Warn('Use ExtractEmbedded option to extract timed metadata from Insta360 trailer',3);
3113
3181
  return 1;
3114
3182
  }
3115
3183
 
3116
3184
  my $unknown = $et->Options('Unknown');
3117
3185
  # position relative to end of trailer (avoids using large offsets for files > 2 GB)
3118
- my $epos = -78-$offset;
3186
+ my $epos = -78;
3119
3187
  my ($i, $p);
3120
3188
  $$et{SET_GROUP0} = 'Trailer';
3121
3189
  $$et{SET_GROUP1} = 'Insta360';
@@ -3124,7 +3192,7 @@ sub ProcessInsta360($;$)
3124
3192
  for (;;) {
3125
3193
  my ($id, $len) = unpack('vV', $buff);
3126
3194
  ($epos -= $len) + $trailerLen < 0 and last;
3127
- $raf->Seek($epos, 2) or last;
3195
+ $raf->Seek($epos-$offset, 2) or last;
3128
3196
  if ($verbose) {
3129
3197
  $et->VPrint(0, sprintf("Insta360 Record 0x%x (offset 0x%x, %d bytes):\n", $id, $fileEnd + $epos, $len));
3130
3198
  }
@@ -3152,7 +3220,7 @@ sub ProcessInsta360($;$)
3152
3220
  $dlen = 20;
3153
3221
  }
3154
3222
  }
3155
- $raf->Seek($epos, 2) or last;
3223
+ $raf->Seek($epos-$offset, 2) or last;
3156
3224
  }
3157
3225
  } elsif ($id == 0x200) {
3158
3226
  $dlen = $len;
@@ -3281,8 +3349,8 @@ sub ProcessInsta360($;$)
3281
3349
  } else {
3282
3350
  ($epos -= 6) + $trailerLen < 0 and last; # step back to previous record
3283
3351
  }
3284
- $raf->Seek($epos, 2) or last; # seek to start of next footer
3285
- $raf->Read($buff, 6) == 6 or last; # read footer
3352
+ $raf->Seek($epos-$offset, 2) or last; # seek to start of next footer
3353
+ $raf->Read($buff, 6) == 6 or last; # read footer
3286
3354
  }
3287
3355
  delete $$et{DOC_NUM};
3288
3356
  SetByteOrder('MM');
@@ -3306,8 +3374,8 @@ sub ProcessCAMM($$$)
3306
3374
  my $rtnVal = 0;
3307
3375
  while ($pos + 4 < $end) {
3308
3376
  my $type = Get16u($dataPt, $pos + 2);
3309
- my $size = $size{$type} or $et->WarnOnce("Unknown camm record type $type"), last;
3310
- $pos + $size > $end and $et->WarnOnce("Truncated camm record $type"), last;
3377
+ my $size = $size{$type} or $et->Warn("Unknown camm record type $type"), last;
3378
+ $pos + $size > $end and $et->Warn("Truncated camm record $type"), last;
3311
3379
  my $tagTbl = GetTagTable("Image::ExifTool::QuickTime::camm$type");
3312
3380
  $$dirInfo{DirStart} = $pos;
3313
3381
  $$dirInfo{DirLen} = $size;
@@ -3541,8 +3609,6 @@ sub ScanMediaData($)
3541
3609
  $et->VPrint(0, "--------------------------\n");
3542
3610
  $$et{INDENT} = substr $$et{INDENT}, 0, -2;
3543
3611
  }
3544
- # process Insta360 trailer if it exists
3545
- ProcessInsta360($et);
3546
3612
  }
3547
3613
 
3548
3614
  1; # end
@@ -3564,7 +3630,7 @@ information like GPS tracks from MOV, MP4 and INSV media data.
3564
3630
 
3565
3631
  =head1 AUTHOR
3566
3632
 
3567
- Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
3633
+ Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
3568
3634
 
3569
3635
  This library is free software; you can redistribute it and/or modify it
3570
3636
  under the same terms as Perl itself.