exiftool_vendored 12.81.0 → 12.83.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/bin/Changes +39 -1
  3. data/bin/MANIFEST +4 -18
  4. data/bin/META.json +1 -1
  5. data/bin/META.yml +1 -1
  6. data/bin/README +3 -2
  7. data/bin/build_geolocation +867 -0
  8. data/bin/exiftool +37 -9
  9. data/bin/fmt_files/gpx.fmt +2 -1
  10. data/bin/fmt_files/gpx_wpt.fmt +2 -1
  11. data/bin/lib/Image/ExifTool/BuildTagLookup.pm +47 -31
  12. data/bin/lib/Image/ExifTool/CanonVRD.pm +6 -5
  13. data/bin/lib/Image/ExifTool/DJI.pm +29 -0
  14. data/bin/lib/Image/ExifTool/Exif.pm +19 -2
  15. data/bin/lib/Image/ExifTool/FujiFilm.pm +9 -2
  16. data/bin/lib/Image/ExifTool/GM.pm +552 -0
  17. data/bin/lib/Image/ExifTool/Geolocation.dat +0 -0
  18. data/bin/lib/Image/ExifTool/Geolocation.pm +90 -54
  19. data/bin/lib/Image/ExifTool/Nikon.pm +9 -7
  20. data/bin/lib/Image/ExifTool/QuickTime.pm +31 -23
  21. data/bin/lib/Image/ExifTool/QuickTimeStream.pl +66 -30
  22. data/bin/lib/Image/ExifTool/README +2 -0
  23. data/bin/lib/Image/ExifTool/Sony.pm +15 -6
  24. data/bin/lib/Image/ExifTool/TagLookup.pm +37 -6
  25. data/bin/lib/Image/ExifTool/TagNames.pod +407 -239
  26. data/bin/lib/Image/ExifTool/WriteQuickTime.pl +5 -2
  27. data/bin/lib/Image/ExifTool/Writer.pl +165 -133
  28. data/bin/lib/Image/ExifTool/XMP.pm +3 -2
  29. data/bin/lib/Image/ExifTool/XMP2.pl +3 -0
  30. data/bin/lib/Image/ExifTool.pm +38 -9
  31. data/bin/lib/Image/ExifTool.pod +31 -18
  32. data/bin/perl-Image-ExifTool.spec +1 -1
  33. data/lib/exiftool_vendored/version.rb +1 -1
  34. metadata +4 -20
  35. data/bin/lib/Image/ExifTool/GeoLang/cs.pm +0 -978
  36. data/bin/lib/Image/ExifTool/GeoLang/de.pm +0 -1975
  37. data/bin/lib/Image/ExifTool/GeoLang/en_ca.pm +0 -44
  38. data/bin/lib/Image/ExifTool/GeoLang/en_gb.pm +0 -124
  39. data/bin/lib/Image/ExifTool/GeoLang/es.pm +0 -2921
  40. data/bin/lib/Image/ExifTool/GeoLang/fi.pm +0 -1116
  41. data/bin/lib/Image/ExifTool/GeoLang/fr.pm +0 -3171
  42. data/bin/lib/Image/ExifTool/GeoLang/it.pm +0 -2750
  43. data/bin/lib/Image/ExifTool/GeoLang/ja.pm +0 -10256
  44. data/bin/lib/Image/ExifTool/GeoLang/ko.pm +0 -4499
  45. data/bin/lib/Image/ExifTool/GeoLang/nl.pm +0 -1270
  46. data/bin/lib/Image/ExifTool/GeoLang/pl.pm +0 -3019
  47. data/bin/lib/Image/ExifTool/GeoLang/ru.pm +0 -18220
  48. data/bin/lib/Image/ExifTool/GeoLang/sk.pm +0 -441
  49. data/bin/lib/Image/ExifTool/GeoLang/sv.pm +0 -714
  50. data/bin/lib/Image/ExifTool/GeoLang/tr.pm +0 -452
  51. data/bin/lib/Image/ExifTool/GeoLang/zh_cn.pm +0 -2225
  52. data/bin/lib/Image/ExifTool/GeoLang/zh_tw.pm +0 -72
@@ -0,0 +1,552 @@
1
+ #------------------------------------------------------------------------------
2
+ # File: GM.pm
3
+ #
4
+ # Description: Read GM PDR metadata from automobile videos
5
+ #
6
+ # Revisions: 2024-04-01 - P. Harvey Created
7
+ #
8
+ # References: 1) https://exiftool.org/forum/index.php?topic=11335
9
+ #------------------------------------------------------------------------------
10
+
11
+ package Image::ExifTool::GM;
12
+
13
+ use strict;
14
+ use vars qw($VERSION);
15
+ use Image::ExifTool qw(:DataAccess :Utils);
16
+ use Image::ExifTool::GPS;
17
+
18
+ $VERSION = '1.01';
19
+
20
+ sub Process_marl($$$);
21
+ sub Process_mrld($$$);
22
+ sub Process_mrlv($$$);
23
+ sub PrintCSV($;$);
24
+
25
+ # rename some units strings
26
+ my %convertUnits = (
27
+ "\xc2\xb0" => 'deg',
28
+ "\xc2\xb0C" => 'C',
29
+ "\xc2\xb0/sec" => 'deg/sec',
30
+ ltr => 'L',
31
+ );
32
+
33
+ my $pi = 3.141592653589793;
34
+
35
+ # offsets and scaling factors to convert to reasonable units
36
+ my %changeOffset = (
37
+ C => -273.15, # K to C
38
+ );
39
+ my %changeScale = (
40
+ G => 1 / 9.80665, # m/s2 to G
41
+ kph => 3.6, # m/s to km/h
42
+ deg => 180 / $pi, # radians to degrees
43
+ 'deg/sec' => 180 / $pi, # rad/s to deg/s
44
+ '%' => 100, # decimal to %
45
+ kPa => 1/1000, # Pa to kPa
46
+ rpm => 10, # ? (arbitrary factor of 10)
47
+ km => 1/1000, # m to km
48
+ L => 1000, # m3 to L
49
+ mm => 1000, # m to mm
50
+ );
51
+
52
+ # default print conversions for various types of units
53
+ my %printConv = (
54
+ rpm => 'sprintf("%.2f rpm", $val)',
55
+ '%' => 'sprintf("%.2f %%", $val)',
56
+ kPa => 'sprintf("%.2f kPa", $val)',
57
+ G => 'sprintf("%.3f G", $val)',
58
+ km => 'sprintf("%.3f km", $val)',
59
+ kph => 'sprintf("%.2f km/h", $val)',
60
+ deg => 'sprintf("%.2f deg", $val)',
61
+ 'deg/sec' => 'sprintf("%.2f deg/sec", $val)',
62
+ );
63
+
64
+ # channel parameters extracted from marl dictionary
65
+ my @channel = qw(
66
+ ID Type Num Units Flags Interval Min Max DispMin DispMax Multiplier Offset
67
+ Name Description
68
+ );
69
+ my %channelStruct = (
70
+ STRUCT_NAME => 'GM Channel',
71
+ NOTES => 'Information stored for each channel in the Marlin dictionary.',
72
+ SORT_ORDER => \@channel,
73
+ ID => { Writable => 0, Notes => 'channel ID number' },
74
+ Type => { Writable => 0, Notes => 'measurement type' },
75
+ Num => { Writable => 0, Notes => 'units ID number' },
76
+ Units => { Writable => 0, Notes => 'units string' },
77
+ Flags => { Writable => 0, Notes => 'channel flags' },
78
+ Interval=> { Writable => 0, Notes => 'measurement interval', ValueConv => '$val / 1e7', PrintConv => '"$val s"' },
79
+ Min => { Writable => 0, Notes => 'raw value minimum' },
80
+ Max => { Writable => 0, Notes => 'raw value maximum' },
81
+ DispMin => { Writable => 0, Notes => 'displayed value minimum' },
82
+ DispMax => { Writable => 0, Notes => 'displayed value maximum' },
83
+ Multiplier=>{Writable => 0, Notes => 'multiplier for raw value' },
84
+ Offset => { Writable => 0, Notes => 'offset for scaled value' },
85
+ Name => { Writable => 0, Notes => 'channel name' },
86
+ Description=>{Writable=> 0, Notes => 'channel description' },
87
+ );
88
+
89
+ # tags found in the 'mrlh' (marl header) atom
90
+ %Image::ExifTool::GM::mrlh = (
91
+ PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
92
+ NOTES => 'The Marlin PDR header.',
93
+ 0 => { Name => 'MarlinDataVersion', Format => 'int16u[2]', PrintConv => '$val =~ tr/ /./; $val' },
94
+ );
95
+
96
+ # tags found in the 'mrlv' (Marlin values) atom
97
+ %Image::ExifTool::GM::mrlv = (
98
+ PROCESS_PROC => \&Process_mrlv,
99
+ FORMAT => 'string',
100
+ NOTES => q{Tags found in the 'mrlv' (Marlin values) box.},
101
+ 'time'=> { Name => 'Time1', Groups => { 2 => 'Time' }, ValueConv => '$val =~ tr/-/:/; $val' },
102
+ date => { Name => 'Date1', Groups => { 2 => 'Time' }, ValueConv => '$val =~ tr/-/:/; $val' },
103
+ ltim => { Name => 'Time2', Groups => { 2 => 'Time' }, ValueConv => '$val =~ tr/-/:/; $val' },
104
+ ldat => { Name => 'Date2', Groups => { 2 => 'Time' }, ValueConv => '$val =~ tr/-/:/; $val' },
105
+ tstm => {
106
+ Name => 'StartTime',
107
+ Groups => { 2 => 'Time' },
108
+ Format => 'int64u',
109
+ RawConv => '$$self{GMStartTime} = $val / 1e7',
110
+ ValueConv => 'ConvertUnixTime($val, undef, 6)', # (likely UTC, but not sure so don't add time zone)
111
+ PrintConv => '$self->ConvertDateTime($val)',
112
+ },
113
+ zone => { Name => 'TimeZone', Groups => { 2 => 'Time' } },
114
+ lang => 'Language',
115
+ unit => { Name => 'Units', PrintConv => { usim => 'U.S. Imperial' } },
116
+ swvs => 'SoftwareVersion',
117
+ # id ? ""
118
+ # cntr ? ""
119
+ # flap ? ""
120
+ );
121
+
122
+ # tags found in the 'mrld' (Marlin dictionary) atom
123
+ %Image::ExifTool::GM::mrld = (
124
+ PROCESS_PROC => \&Process_mrld,
125
+ VARS => { ADD_FLATTENED => 1 },
126
+ WRITABLE => 0,
127
+ NOTES => q{
128
+ The Marlin dictionary. Only one channel is listed but all available
129
+ channels are extracted. Use the -struct (L<API Struct|../ExifTool.html#Struct>) option to extract the
130
+ channel information as structures.
131
+ },
132
+ Channel01 => { Struct => \%channelStruct },
133
+ );
134
+
135
+ # tags found in 'marl' ctbx timed metadata
136
+ %Image::ExifTool::GM::marl = (
137
+ PROCESS_PROC => \&Process_marl,
138
+ GROUPS => { 2 => 'Other' },
139
+ VARS => { NO_ID => 1, NO_LOOKUP => 1 },
140
+ NOTES => q{
141
+ Tags extracted from the 'ctbx' 'marl' (Marlin) box of timed PDR metadata
142
+ from GM cars. Use the -ee (L<API ExtractEmbedded|../ExifTool.html#ExtractEmbedded>) option to extract this
143
+ information, or the L<API PrintCSV|../ExifTool.html#PrintCSV> option to output in CSV format.
144
+ },
145
+ TimeStamp => { # (the marl timestamp)
146
+ Groups => { 2 => 'Time' },
147
+ Notes => q{
148
+ the numerical value is seconds since start of video, but the print
149
+ conversion adds StartTime to provide a date/time value. Extracted as
150
+ GPSDateTime if requested
151
+ },
152
+ ValueConv => '$val / 1e7',
153
+ PrintConv => q{
154
+ return "$val s" unless $$self{GMStartTime};
155
+ return $self->ConvertDateTime(ConvertUnixTime($val+$$self{GMStartTime},undef,6));
156
+ },
157
+ },
158
+ GPSDateTime => { # (alternative for TimeStamp)
159
+ Groups => { 2 => 'Time' },
160
+ Notes => 'generated from the TimeStamp only if specifically requested',
161
+ RawConv => '$$self{GMStartTime} ? $val : undef',
162
+ ValueConv => 'ConvertUnixTime($val / 1e7 + $$self{GMStartTime}) . "Z"',
163
+ PrintConv => '$self->ConvertDateTime($val,undef,6)',
164
+ },
165
+ Latitude => {
166
+ Name => 'GPSLatitude',
167
+ Description => 'GPS Latitude', # (need description so we don't set it from the mrld)
168
+ Groups => { 2 => 'Location' },
169
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
170
+ },
171
+ Longitude => {
172
+ Name => 'GPSLongitude',
173
+ Description => 'GPS Longitude',
174
+ Groups => { 2 => 'Location' },
175
+ PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
176
+ },
177
+ Altitude => {
178
+ Name => 'GPSAltitude',
179
+ Description => 'GPS Altitude',
180
+ Groups => { 2 => 'Location' },
181
+ },
182
+ Heading => {
183
+ Name => 'GPSTrack',
184
+ Description => 'GPS Track',
185
+ Groups => { 2 => 'Location' },
186
+ PrintConv => '$val > 360 ? "n/a" : sprintf("%.2f",$val)', # (seen 655.35)
187
+ },
188
+ ABSActive => { },
189
+ AccelPos => { },
190
+ BatteryVoltage => { },
191
+ Beacon => { },
192
+ BoostPressureInd => { },
193
+ BrakePos => { },
194
+ ClutchPos => { },
195
+ CoolantTemp => { },
196
+ CornerExitSetting => { },
197
+ CPUFree => { },
198
+ CPUIO => { },
199
+ CPUIRQ => { },
200
+ CPUSystem => { },
201
+ CPUUser => { },
202
+ DiskReadOperations => { },
203
+ DiskReadRate => { },
204
+ DiskReadTime => { },
205
+ DiskWriteOperations => { },
206
+ DiskWriteRate => { },
207
+ DiskWriteTime => { },
208
+ Distance => { },
209
+ DriverPerformanceMode => { },
210
+ EngineSpeedRequest => { },
211
+ EngineTorqureReq => { },
212
+ FuelCapacity => { },
213
+ FuelLevel => { },
214
+ Gear => {
215
+ Notes => q{
216
+ in the PrintCSV output, the value for Neutral is set to -1, and Reverse to
217
+ -100 for compatibility with RaceRender
218
+ },
219
+ CSVConv => { 13 => -1, 14 => -100 },
220
+ PrintConv => { 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 13=>'N', 14=>'R' }
221
+ },
222
+ GPSFix => { },
223
+ InfotainOpMode => { },
224
+ IntakeAirTemperature => { },
225
+ IntakeBoostPressure => { },
226
+ LateralAcceleration => { },
227
+ LFTyrePressure => { },
228
+ LFTyreTemp => { },
229
+ LongitudinalAcceleration => { },
230
+ LRTyrePressure => { },
231
+ LRTyreTemp => { },
232
+ OilPressure => { },
233
+ OilTemp => { },
234
+ OutsideAirTemperature => { },
235
+ RecordingEventOdometer => { },
236
+ RFTyrePressure => { },
237
+ RFTyreTemp => { },
238
+ RPM => { },
239
+ RRTyrePressure => { },
240
+ RRTyreTemp => { },
241
+ Speed => { Groups => { 2 => 'Location' } },
242
+ SpeedControlResponse => { },
243
+ SpeedRequestIntervention => { },
244
+ Steering1Switch => { },
245
+ Steering2Switch => { },
246
+ SteeringAngle => { },
247
+ SuspensionDisplacementLeftFront => { },
248
+ SuspensionDisplacementLeftRear => { },
249
+ SuspensionDisplacementRightFront => { },
250
+ SuspensionDisplacementRightRear => { },
251
+ SystemBackupPowerEnabled => { },
252
+ SystemBackupPowerMode => { },
253
+ SystemPowerMode => { },
254
+ TractionControlActive => { },
255
+ TransOilTemp => { },
256
+ TransportStorageMode => { },
257
+ ValetMode => { },
258
+ VehicleStabilityActive => { },
259
+ VerticalAcceleration => { },
260
+ WheelspeedLeftDriven => { },
261
+ 'WheelspeedLeftNon-Driven' => { },
262
+ WheelspeedRightDriven => { },
263
+ 'WheelspeedRightNon-Driven' => { },
264
+ YawRate => { },
265
+ );
266
+
267
+ #------------------------------------------------------------------------------
268
+ # Print a CSV row
269
+ # Inputs: 0) ExifTool ref, 1) time stamp
270
+ sub PrintCSV($;$)
271
+ {
272
+ my ($et, $ts) = @_;
273
+ my $csv = $$et{GMCsv} or return; # get the list of channels with measurements
274
+ @$csv or return;
275
+ my $vals = $$et{GMVals};
276
+ my $gmDict = $$et{GMDictionary};
277
+ my @items = ('') x scalar(@$gmDict);
278
+ $items[0] = ($ts || $$et{GMMaxTS}) / 1e7;
279
+ # fill in scaled measurements for this TimeStamp
280
+ foreach (@$csv) {
281
+ my $gmChan = $$gmDict[$_];
282
+ $items[$_] = $$vals[$_] * $$gmChan{Mult} + $$gmChan{Off};
283
+ # apply CSV conversion if applicable (ie. Gear)
284
+ next unless $$gmChan{Conv} and defined $$gmChan{Conv}{$items[$_]};
285
+ $items[$_] = $$gmChan{Conv}{$items[$_]};
286
+ }
287
+ my $out = $$et{OPTIONS}{TextOut};
288
+ print $out join(',',@items),"\n";
289
+ @$csv = (); # clear the channel list
290
+ }
291
+
292
+ #------------------------------------------------------------------------------
293
+ # Process GM Marlin values ('mrlv' box)
294
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
295
+ # Returns: 1 on success
296
+ sub Process_mrlv($$$)
297
+ {
298
+ my ($et, $dirInfo, $tagTablePtr) = @_;
299
+ my $dataPt = $$dirInfo{DataPt};
300
+ my $dataPos = $$dirInfo{DataPos};
301
+ my $dirLen = length $$dataPt;
302
+ my $pos = 0;
303
+ # data lengths for known formats
304
+ my %fmtLen = (
305
+ strs => 64, lang => 64, strl => 256, 'time' => 32, date => 32,
306
+ tmzn => 32, tstm => 8, focc => 4, "kvp\0" => 64+256,
307
+ );
308
+ $et->VerboseDir('mrlv', undef, $dirLen);
309
+ while ($pos + 8 <= $dirLen) {
310
+ my $tag = substr($$dataPt, $pos, 4);
311
+ my $fmt = substr($$dataPt, $pos + 4, 4);
312
+ my $len = $fmtLen{$fmt};
313
+ unless ($len) {
314
+ ($tag, $fmt) = (PrintableTagID($tag), PrintableTagID($fmt));
315
+ $et->Warn("Unknown format ($fmt) for tag $tag");
316
+ last;
317
+ }
318
+ $pos + 8 + $len > $dirLen and $et->Warn('Truncated mrlv data'), last;
319
+ $et->HandleTag($tagTablePtr, $tag, undef,
320
+ DataPt => $dataPt,
321
+ DataPos => $dataPos,
322
+ Start => $pos + 8,
323
+ Size => $len,
324
+ );
325
+ $pos += 8 + $len;
326
+ }
327
+ return 1;
328
+ }
329
+
330
+ #------------------------------------------------------------------------------
331
+ # Process GM Marlin dictionary ('mrld' box)
332
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
333
+ # Returns: 1 on success
334
+ sub Process_mrld($$$)
335
+ {
336
+ my ($et, $dirInfo, $tagTablePtr) = @_;
337
+ my $dataPt = $$dirInfo{DataPt};
338
+ my $dataPos = $$dirInfo{DataPos};
339
+ my $dirLen = length $$dataPt;
340
+ my $struct = $et->Options('Struct') || 0;
341
+ my $gmDict = $$et{GMDictionary} = [ ];
342
+ my $marl = GetTagTable('Image::ExifTool::GM::marl');
343
+ my ($pos, $item, $csv);
344
+
345
+ $et->VerboseDir('mrld', undef, $dirLen);
346
+ require 'Image/ExifTool/XMPStruct.pl';
347
+ Image::ExifTool::XMP::AddFlattenedTags($tagTablePtr);
348
+ $csv = [ ] if $et->Options('PrintCSV');
349
+
350
+ for ($pos=0; $pos+448<=$dirLen; $pos+=448) {
351
+ # unpack 448-byte records:
352
+ # 0. int32u - channel number
353
+ # 1. int32u - measurement type
354
+ # 2. int32u - units number
355
+ # 3. string[64] - units string
356
+ # 4. int32u - flags (0.visible, 1.linear conversion, 2.interpolation OK)
357
+ # 5. int64u - interval
358
+ # 6. int32s - min reading
359
+ # 7. int32s - max reading
360
+ # 8. double - disp min
361
+ # 9. double - disp max
362
+ # 10. double - multiplier
363
+ # 11. double - offset
364
+ # 12. string[64] - channel name
365
+ # 13. string[64] - channel description
366
+ my @a = unpack("x${pos}NNNZ64Na8N2a8a8a8a8Z64Z64", $$dataPt);
367
+ my $units = $convertUnits{$a[3]} || $a[3];
368
+ $a[3] = $et->Decode($a[3], 'UTF8'); # convert from UTF8
369
+ $_ & 0x8000000 and $_ -= 4294967296 foreach @a[6,7]; # convert signed ints
370
+ map { $_ = GetDouble(\$_,0) } @a[8,9,10,11]; # convert doubles
371
+ $a[5] = Get64u(\$a[5],0); # convert 64-bit int
372
+ my $chan = $a[0];
373
+ my $tag = sprintf('Channel%.2d', $chan);
374
+ my $tagInfo = $$tagTablePtr{$tag};
375
+ my $hash = { map { $channel[$_] => $a[$_] } 1..$#a };
376
+ unless ($tagInfo) {
377
+ $tagInfo = AddTagToTable($tagTablePtr, $tag, { Name => $tag, Struct => \%channelStruct });
378
+ Image::ExifTool::XMP::AddFlattenedTags($tagTablePtr, $tag);
379
+ }
380
+ # extract channel structure if specified
381
+ if ($struct) {
382
+ $$hash{_ordered_keys_} = [ @channel[1..$#channel] ];
383
+ $et->FoundTag($tagInfo, $hash);
384
+ }
385
+ # extract flattened channel elements
386
+ if ($struct == 0 or $struct == 2) {
387
+ $et->HandleTag($tagTablePtr, "$tag$channel[$_]", $a[$_]) foreach 1..$#a;
388
+ }
389
+ # add corresponding tag to marl table
390
+ my $name = Image::ExifTool::MakeTagName($a[12]);
391
+ $tagInfo = $$marl{$name};
392
+ unless ($tagInfo) {
393
+ $et->VPrint(0, $$et{INDENT}, "[adding $name]\n");
394
+ $tagInfo = AddTagToTable($marl, $name, { });
395
+ }
396
+ $$tagInfo{Description} = $a[13] unless $$tagInfo{Description};
397
+ unless ($$tagInfo{PrintConv}) {
398
+ # add a default print conversion
399
+ $units =~ tr/"\\//d; # (just to be safe, probably never happen)
400
+ $$tagInfo{PrintConv} = $printConv{$units} || qq("\$val $units");
401
+ }
402
+ # adjust multiplier/offset as necessary to scale to more appropriate units
403
+ # (ie. to the units actually specified in this dictionary -- d'oh)
404
+ my $mult = $a[10] * ($changeScale{$units} || 1);
405
+ my $off = $a[11] * ($changeScale{$units} || 1) + ($changeOffset{$units} || 0);
406
+ my $init = int(($a[6] + $a[7]) / 2); # initial value for difference readings
407
+ # save information about this channel necessary for processing the marl data
408
+ $$gmDict[$chan] = { Name => $name, Mult => $mult, Off => $off, Init => $init };
409
+ $$gmDict[$chan]{Conv} = $$tagInfo{CSVConv};
410
+ $csv and $$csv[$chan] = $a[12] . ($a[3] ? " ($a[3])" : '');
411
+ }
412
+ # channel 0 must not be defined because we use it for the TimeStamp
413
+ if (defined $$gmDict[0]) {
414
+ $et->Warn('Internal error: PDR channel 0 is used');
415
+ delete $$et{GMDictionary};
416
+ } elsif ($csv) {
417
+ $$csv[0] = 'Time (s)';
418
+ defined $_ or $_ = '' foreach @$csv;
419
+ my $out = $$et{OPTIONS}{TextOut};
420
+ print $out join(',',@$csv),"\n";
421
+ $$et{GMCsv} = [ ];
422
+ }
423
+ $et->AddCleanup(\&PrintCSV); # print last CSV line when we are done
424
+ # initialize variables for processing marl box
425
+ $$et{GMVals} = [ ];
426
+ $$et{GMMaxTS} = 0;
427
+ $$et{GMBadChan} = 0;
428
+ return 1;
429
+ }
430
+
431
+ #------------------------------------------------------------------------------
432
+ # Process GM 'marl' ctbx data (ref PH)
433
+ # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
434
+ # Returns: 1 on success
435
+ # (see https://exiftool.org/forum/index.php?topic=11335.msg61393#msg61393)
436
+ sub Process_marl($$$)
437
+ {
438
+ my ($et, $dirInfo, $tagTablePtr) = @_;
439
+ my $dataPt = $$dirInfo{DataPt};
440
+ my $dataPos = $$dirInfo{DataPos} + $$dirInfo{Base};
441
+ my $dataLen = length $$dataPt;
442
+ my $vals = $$et{GMVals}; # running values for each channel (0=TimeStamp)
443
+ my $chan = $$et{GMChan}; # running channel number
444
+ my $gmDict = $$et{GMDictionary};
445
+ my $csv = $$et{GMCsv};
446
+ my $maxTS = $$et{GMMaxTS};
447
+ my $reqGPSDateTime = $$et{REQ_TAG_LOOKUP}{gpsdatetime};
448
+ my $reqTimeStamp = $reqGPSDateTime ? $$et{REQ_TAG_LOOKUP}{timestamp} : 1;
449
+ my ($pos, $verbose2);
450
+
451
+ $et->VerboseDir('marl', undef, $dataLen);
452
+ $gmDict or $et->Warn('Missing marl dictionary'), return 0;
453
+ my $maxChan = $#$gmDict;
454
+ $verbose2 = 1 if $et->Options('Verbose') > 1;
455
+ $$vals[0] = -1 unless defined $$vals[0]; # (we use the 0th channel for the TimeStamp)
456
+ my $ts = $$vals[0];
457
+
458
+ for ($pos=0; $pos + 8 <= $dataLen; $pos += 8) {
459
+ my @a = unpack("x${pos}NN", $$dataPt);
460
+ my $ah = $a[0] >> 24;
461
+ my $a2 = $ah & 0xc0;
462
+ my ($val, $chanDiff, $valDiff, @ts, $gmChan);
463
+ if ($a2 == 0xc0) { # 16-byte full record?
464
+ last if $ah == 0xff; # exit at first empty record
465
+ $chan = $a[0] & 0x0fffffff;
466
+ $gmChan = $$gmDict[$chan] or next; # (shouldn't happen)
467
+ $val = $a[1] - ($a[1] & 0x80000000 ? 4294967296 : 0);
468
+ $$vals[$chan] = $val;
469
+ last if $pos + 16 > $dataLen; # (shouldn't happen)
470
+ $pos += 8; # point at time stamp
471
+ @ts = unpack("x${pos}NN", $$dataPt);
472
+ $ts = $ts[0] * 4294967296 + $ts[1];
473
+ } elsif ($a2 == 0x40) { # 8-byte difference record?
474
+ next unless defined $chan; # (shouldn't happen)
475
+ $ts += $a[1]; # increment time stamp
476
+ $chanDiff = ($ah & 0x3f) - ($ah & 0x20 ? 0x40 : 0);
477
+ $chan += $chanDiff; # increment the running channel number
478
+ $gmChan = $$gmDict[$chan] or next; # (shouldn't happen)
479
+ defined $$vals[$chan] or $$vals[$chan] = $$gmChan{Init}; # init if necessary
480
+ $valDiff = ($a[0] & 0x00ffffff) - ($a[0] & 0x00800000 ? 0x01000000 : 0);
481
+ $val = ($$vals[$chan] += $valDiff); # increment the running value for this channel
482
+ } else {
483
+ next; # (shouldn't happen)
484
+ }
485
+ # ensure that the timestamps are monotonically increasing
486
+ # (have seen backward steps up to 0.033 sec, so fudge these)
487
+ if ($ts > $maxTS) {
488
+ if ($csv) {
489
+ PrintCSV($et, $maxTS);
490
+ } else {
491
+ $$et{DOC_NUM} = ++$$et{DOC_COUNT};
492
+ $et->HandleTag($tagTablePtr, TimeStamp => $ts) if $reqTimeStamp;
493
+ $et->HandleTag($tagTablePtr, GPSDateTime => $ts) if $reqGPSDateTime;
494
+ }
495
+ $maxTS = $ts;
496
+ }
497
+ $csv and push(@$csv, $chan), next;
498
+ my $scaled = $val * $$gmChan{Mult} + $$gmChan{Off};
499
+ $et->HandleTag($tagTablePtr, $$gmChan{Name}, $scaled);
500
+ if ($verbose2) {
501
+ my $str = " * $$gmChan{Mult} + $$gmChan{Off} = $scaled";
502
+ my $p0 = $dataPos + $pos - ($a2 == 0xc0 ? 8 : 0);
503
+ my ($cd,$vd) = @ts ? ('','') : (sprintf('%+d',$chanDiff),sprintf('%+d',$valDiff));
504
+ printf "| %8.4x: %.8x %.8x chan$cd=%.2d $$gmChan{Name}$vd = $val$str\n", $p0, @a, $chan;
505
+ printf("| %8.4x: %.8x %.8x TimeStamp = %.6f sec\n", $dataPos + $pos, @ts, $ts / 1e7) if @ts;
506
+ }
507
+ }
508
+ $$vals[0] = $ts; # save last timestamp
509
+ $$et{GMChan} = $chan; # save last channel number
510
+ $$et{GMMaxTS} = $ts;
511
+ delete $$et{DOC_NUM};
512
+ return 1;
513
+ }
514
+
515
+ 1; # end
516
+
517
+ __END__
518
+
519
+ =head1 NAME
520
+
521
+ Image::ExifTool::GM - Read GM PDR Data from automobile videos
522
+
523
+ =head1 SYNOPSIS
524
+
525
+ This module is loaded automatically by Image::ExifTool when required.
526
+
527
+ =head1 DESCRIPTION
528
+
529
+ This module contains definitions required by Image::ExifTool to read PDR
530
+ metadata from videos written by some GM models such as Corvette and Camero.
531
+
532
+ =head1 AUTHOR
533
+
534
+ Copyright 2003-2024, Phil Harvey (philharvey66 at gmail.com)
535
+
536
+ This library is free software; you can redistribute it and/or modify it
537
+ under the same terms as Perl itself.
538
+
539
+ =head1 REFERENCES
540
+
541
+ =over 4
542
+
543
+ =item L<https://exiftool.org/forum/index.php?topic=11335>
544
+
545
+ =back
546
+
547
+ =head1 SEE ALSO
548
+
549
+ L<Image::ExifTool::TagNames/GM Tags>,
550
+ L<Image::ExifTool(3pm)|Image::ExifTool>
551
+
552
+ =cut
Binary file