495 lines
23 KiB
Perl
495 lines
23 KiB
Perl
#------------------------------------------------------------------------------
|
|
# File: MISB.pm
|
|
#
|
|
# Description: Read Motion Industry Standards Board metadata
|
|
#
|
|
# Revisions: 2022/10/08 - P. Harvey Created
|
|
#
|
|
# References: 1) https://dokumen.tips/documents/nato-standardization-agreement-stanag-4609-ed-3.html
|
|
# 2) https://upload.wikimedia.org/wikipedia/commons/1/19/MISB_Standard_0601.pdf
|
|
# 3) https://dokumen.tips/documents/misb-st-010211-standard-security-metadata-universal-standard-describes-the-use.html
|
|
#------------------------------------------------------------------------------
|
|
|
|
package Image::ExifTool::MISB;
|
|
|
|
use strict;
|
|
use vars qw($VERSION);
|
|
use Image::ExifTool qw(:DataAccess :Utils);
|
|
|
|
$VERSION = '1.00';
|
|
|
|
sub ProcessKLV($$$);
|
|
|
|
my %timeInfo = (
|
|
Groups => { 2 => 'Time' },
|
|
Format => 'int64u',
|
|
ValueConv => 'ConvertUnixTime($val/1e6, 0, 6) . "Z"',
|
|
PrintConv => '$self->ConvertDateTime($val)',
|
|
);
|
|
my %latInfo = (
|
|
Format => 'int32s',
|
|
ValueConv => '$val * 90 / 0x7fffffff',
|
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
|
);
|
|
my %lonInfo = (
|
|
Format => 'int32s',
|
|
ValueConv => '$val * 180 / 0x7fffffff',
|
|
PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
|
);
|
|
my %altInfo = (
|
|
Format => 'int16u',
|
|
ValueConv => '$val * 19900 / 0xffff - 900',
|
|
PrintConv => 'sprintf("%.2f m", $val)',
|
|
);
|
|
|
|
# default format based on size of unknown KLV information
|
|
my %defaultFormat = (
|
|
1 => 'int8u',
|
|
2 => 'int16u',
|
|
4 => 'int32u',
|
|
8 => 'int64u',
|
|
);
|
|
|
|
%Image::ExifTool::MISB::Main = (
|
|
GROUPS => { 0 => 'MISB', 1 => 'MISB', 2 => 'Other' },
|
|
VARS => { LONG_TAGS => 2 },
|
|
NOTES => q{
|
|
These tags are extracted from STANAG-4609 MISB (Motion Industry Standards
|
|
Board) KLV-format metadata in M2TS videos.
|
|
},
|
|
'060e2b34020b01010e01030101000000' => {
|
|
Name => 'UASDataLink',
|
|
SubDirectory => { TagTable => 'Image::ExifTool::MISB::UASDatalink' },
|
|
},
|
|
'060e2b3402030101434e415644494147' => { # "CNAVDIAG" written by ChurchillNavigation ION
|
|
Name => 'ChurchillNav',
|
|
SubDirectory => {
|
|
TagTable => 'Image::ExifTool::MISB::ChurchillNav',
|
|
ByteOrder => 'LittleEndian', # !!
|
|
},
|
|
},
|
|
'060E2B34030101010E01030302000000' => { # (NC)
|
|
Name => 'Security',
|
|
SubDirectory => { TagTable => 'Image::ExifTool::MISB::Security' },
|
|
},
|
|
'<other>' => {
|
|
Name => 'Unknown',
|
|
SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' },
|
|
},
|
|
);
|
|
|
|
# UAS datalink local set tags (ref 2, MISB ST 0601.11)
|
|
%Image::ExifTool::MISB::UASDatalink = (
|
|
GROUPS => { 0 => 'MISB', 1 => 'MISB', 2 => 'Location' },
|
|
PROCESS_PROC => \&ProcessKLV,
|
|
NOTES => 'Tags extracted from the MISB ST 0601.11 UAS Datalink local set.',
|
|
1 => { Name => 'Checksum', Format => 'int16u' },
|
|
2 => { Name => 'GPSDateTime', %timeInfo },
|
|
3 => { Name => 'MissionID', Format => 'string' },
|
|
4 => { Name => 'TailNumber', Format => 'string' },
|
|
5 => { Name => 'GPSTrack', Format => 'int16u', ValueConv => '$val * 360 / 0xffff' },
|
|
6 => { Name => 'PitchAngle', Format => 'int16s', ValueConv => '$val * 20 / 0x7fff' },
|
|
7 => { Name => 'RollAngle', Format => 'int16s', ValueConv => '$val * 50 / 0x7fff' },
|
|
8 => { Name => 'TrueAirspeed', Format => 'int8u', PrintConv => '"$val m/s"' },
|
|
9 => { Name => 'IndicatedAirspeed', Format => 'int8u', PrintConv => '"$val m/s"' },
|
|
10 => { Name => 'ProjectIDCode', Format => 'string' },
|
|
11 => { Name => 'SensorName', Format => 'string' },
|
|
12 => { Name => 'ImageCoordinateSystem', Format => 'string' },
|
|
13 => { Name => 'GPSLatitude', %latInfo },
|
|
14 => { Name => 'GPSLongitude', %lonInfo },
|
|
15 => { Name => 'GPSAltitude', %altInfo },
|
|
16 => { Name => 'HorizontalFieldOfView', Format => 'int16u', ValueConv => '$val * 180 / 0xffff' },
|
|
17 => { Name => 'VerticalFieldOfView', Format => 'int16u', ValueConv => '$val * 180 / 0xffff' },
|
|
18 => { Name => 'SensorRelativeAzimuthAngle',Format=> 'int32u', ValueConv => '$val * 360 / 0xffffffff' },
|
|
19 => { Name => 'SensorRelativeElevationAngle',Format=>'int32s',ValueConv => '$val * 180 / 0x7fffffff' },
|
|
20 => { Name => 'SensorRelativeRollAngle', Format => 'int32u', ValueConv => '$val * 360 / 0xffffffff' },
|
|
21 => { Name => 'SlantRange', Format => 'int32u', ValueConv => '$val * 5000000 / 0xffffffff' },
|
|
22 => { Name => 'TargetWidth', Format => 'int16u', ValueConv => '$val * 10000 / 0xffff' },
|
|
23 => { Name => 'FrameCenterLatitude', %latInfo },
|
|
24 => { Name => 'FrameCenterLongitude', %lonInfo },
|
|
25 => { Name => 'FrameCenterElevation', %altInfo },
|
|
26 => { Name => 'OffsetCornerLatitude1', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
27 => { Name => 'OffsetCornerLongitude1', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
28 => { Name => 'OffsetCornerLatitude2', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
29 => { Name => 'OffsetCornerLongitude2', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
30 => { Name => 'OffsetCornerLatitude3', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
31 => { Name => 'OffsetCornerLongitude3', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
32 => { Name => 'OffsetCornerLatitude4', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
33 => { Name => 'OffsetCornerLongitude4', Format => 'int16s', ValueConv => '$val * .075 / 0x7fff' },
|
|
34 => { Name => 'IcingDetected', Format => 'int8u', PrintConv => { 0 => 'n/a', 1 => 'No', 2 => 'Yes' } },
|
|
35 => { Name => 'WindDirection', Format => 'int16u', ValueConv => '$val * 360 / 0xffff' },
|
|
36 => { Name => 'WindSpeed', Format => 'int8u', ValueConv => '$val * 100 / 0xff', Notes => 'm/s' },
|
|
37 => { Name => 'StaticPressure', Format => 'int16u', ValueConv => '$val * 5000 / 0xffff', Notes => 'mbar' },
|
|
38 => { Name => 'DensityAltitude', Format => 'int16u', ValueConv => '$val * 19900 / 0xffff - 900' },
|
|
39 => { Name => 'AirTemperature', Format => 'int8s' },
|
|
40 => { Name => 'TargetLocationLatitude', %latInfo },
|
|
41 => { Name => 'TargetLocationLongitude', %lonInfo },
|
|
42 => { Name => 'TargetLocationElevation', %altInfo },
|
|
43 => { Name => 'TargetTrackGateWidth', Format => 'int8u' },
|
|
44 => { Name => 'TargetTrackGateHeight', Format => 'int8u' },
|
|
45 => { Name => 'TargetErrorEstimateCE90', Format => 'int16u' },
|
|
46 => { Name => 'TargetErrorEstimateLE90', Format => 'int16u' },
|
|
47 => { Name => 'GenericFlagData01',
|
|
Format => 'int8u',
|
|
PrintConv => { BITMASK => {
|
|
0 => 'Laser range',
|
|
1 => 'Auto-track',
|
|
2 => 'IR polarity black',
|
|
3 => 'Icing detected',
|
|
4 => 'Slant range measured',
|
|
5 => 'Image invalid',
|
|
}},
|
|
},
|
|
48 => { Name => 'SecurityLocalMetadataSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Security' } },
|
|
49 => { Name => 'DifferentialPressure', Format => 'int16u', ValueConv => '$val * 5000 / 0xffff' },
|
|
50 => { Name => 'AngleOfAttack', Format => 'int16s', ValueConv => '$val * 20 / 0x7fff' },
|
|
51 => { Name => 'VerticalSpeed', Format => 'int16s', ValueConv => '$val * 180 / 0x7fff', Notes => 'm/s' },
|
|
52 => { Name => 'SideslipAngle', Format => 'int16s', ValueConv => '$val * 20 / 0x7fff' },
|
|
53 => { Name => 'AirfieldBarometricPressure',Format=> 'int16u', ValueConv => '$val * 5000 / 0xffff' },
|
|
54 => { Name => 'AirfieldElevation', %altInfo },
|
|
55 => { Name => 'RelativeHumidity', Format => 'int8u', ValueConv => '$val * 100 / 0xff' },
|
|
56 => { Name => 'GPSSpeed', Format => 'int8u', Notes => 'm/s' },
|
|
57 => { Name => 'GroundRange', Format => 'int32u', ValueConv => '$val * 5000000 / 0xffffffff' },
|
|
58 => { Name => 'FuelRemaining', Format => 'int16u', ValueConv => '$val * 10000 / 0xffff', Notes => 'kg' },
|
|
59 => { Name => 'CallSign', Format => 'string' },
|
|
60 => { Name => 'WeaponLoad', Format => 'int16u', PrintConv => 'sprintf("0x%.4x",$val)' },
|
|
61 => { Name => 'WeaponFired', Format => 'int8u', PrintConv => 'sprintf("0x%.2x",$val)' },
|
|
62 => { Name => 'LaserPRFCode', Format => 'int16u' },
|
|
63 => { Name => 'SensorFieldOfViewName', Format => 'int8u',
|
|
PrintConv => {
|
|
0 => 'Ultranarrow',
|
|
1 => 'Narrow',
|
|
2 => 'Medium',
|
|
3 => 'Wide',
|
|
4 => 'Ultrawide',
|
|
5 => 'Narrow Medium',
|
|
6 => '2x Ultranarrow',
|
|
7 => '4x Ultranarrow',
|
|
},
|
|
},
|
|
64 => { Name => 'MagneticHeading', Format => 'int16u', ValueConv => '$val * 360 / 0xffff' },
|
|
65 => { Name => 'UAS_LSVersionNumber', Format => 'int8u' },
|
|
66 => { Name => 'TargetLocationCovarianceMatrix', Format => 'undef', ValueConv => '\$val' },
|
|
67 => { Name => 'AlternateLatitude', %latInfo },
|
|
68 => { Name => 'AlternateLongitude', %lonInfo },
|
|
69 => { Name => 'AlternateAltitude', %altInfo },
|
|
70 => { Name => 'AlternateName', Format => 'string' },
|
|
71 => { Name => 'AlternateHeading', Format => 'int16u', ValueConv => '$val * 360 / 0xffff' },
|
|
72 => { Name => 'EventStartTime', %timeInfo },
|
|
73 => { Name => 'RVTLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
74 => { Name => 'VMTIDataSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
75 => { Name => 'SensorEllipsoidHeight', %altInfo },
|
|
76 => { Name => 'AlternateEllipsoidHeight', %altInfo },
|
|
77 => { Name => 'OperationalMode', Format => 'int8u',
|
|
PrintConv => {
|
|
0 => 'Other',
|
|
1 => 'Operational',
|
|
2 => 'Training',
|
|
3 => 'Exercise',
|
|
4 => 'Maintenance',
|
|
},
|
|
},
|
|
78 => { Name => 'FrameCenterHeightAboveEllipsoid', %altInfo },
|
|
79 => { Name => 'SensorVelocityNorth', Format => 'int16s', ValueConv => '$val * 327 / 0x7fff' },
|
|
80 => { Name => 'SensorVelocityEast', Format => 'int16s', ValueConv => '$val * 327 / 0x7fff' },
|
|
81 => { Name => 'ImageHorizonPixelPack', Format => 'undef', ValueConv => '\$val' },
|
|
82 => { Name => 'CornerLatitude1', %latInfo },
|
|
83 => { Name => 'CornerLongitude1', %lonInfo },
|
|
84 => { Name => 'CornerLatitude2', %latInfo },
|
|
85 => { Name => 'CornerLongitude2', %lonInfo },
|
|
86 => { Name => 'CornerLatitude3', %latInfo },
|
|
87 => { Name => 'CornerLongitude3', %lonInfo },
|
|
88 => { Name => 'CornerLatitude4', %latInfo },
|
|
89 => { Name => 'CornerLongitude4', %lonInfo },
|
|
90 => { Name => 'FullPitchAngle', Format => 'int32s', ValueConv => '$val * 90 / 0x7fffffff' },
|
|
91 => { Name => 'FullRollAngle', Format => 'int32s', ValueConv => '$val * 90 / 0x7fffffff' },
|
|
92 => { Name => 'FullAngleOfAttack', Format => 'int32s', ValueConv => '$val * 90 / 0x7fffffff' },
|
|
93 => { Name => 'FullSideslipAngle', Format => 'int32s', ValueConv => '$val * 90 / 0x7fffffff' },
|
|
94 => { Name => 'MIISCoreIdentifier', Format => 'undef', ValueConv => '\$val' },
|
|
95 => { Name => 'SARMotionImageryData', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
96 => { Name => 'TargetWidthExtended', Format => 'undef', ValueConv => '\$val' }, # IMAPB format
|
|
97 => { Name => 'RangeImageLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
98 => { Name => 'GeoregistrationLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
99 => { Name => 'CompositeImagingLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
100=> { Name => 'SegmentLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
101=> { Name => 'AmendLocalSet', SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } },
|
|
102=> { Name => 'SDCC-FLP', Format => 'undef', ValueConv => '\$val' }, # IMAPB format
|
|
103=> { Name => 'DensityAltitudeExtended', Format => 'undef', ValueConv => '\$val' }, # IMAPB format
|
|
104=> { Name => 'SensorEllipsoidHeightExtended', Format => 'undef', ValueConv => '\$val' }, # IMAPB format
|
|
105=> { Name => 'AlternateEllipsoidHeightExtended', Format => 'undef', ValueConv => '\$val' }, # IMAPB format
|
|
);
|
|
|
|
# tags from MISB ST 0102.11 local set
|
|
%Image::ExifTool::MISB::Security = (
|
|
GROUPS => { 0 => 'MISB', 1 => 'MISB', 2 => 'Document' },
|
|
PROCESS_PROC => \&ProcessKLV,
|
|
NOTES => 'Tags extracted from the MISB ST 0102.11 Security Metadata local set.',
|
|
1 => { Name => 'SecurityClassification', PrintConv => {
|
|
1 => 'Unclassified',
|
|
2 => 'Restricted',
|
|
3 => 'Confidential',
|
|
4 => 'Secret',
|
|
5 => 'Top Secret',
|
|
}},
|
|
2 => { Name => 'ClassifyingCountryCodeMethod', PrintConv => {
|
|
0x01 => 'ISO-3166 Two Letter',
|
|
0x02 => 'ISO-3166 Three Letter',
|
|
0x03 => 'FIPS 10-4 Two Letter',
|
|
0x04 => 'FIPS 10-4 Four Letter',
|
|
0x05 => 'ISO-3166 Numeric',
|
|
0x06 => '1059 Two Letter',
|
|
0x07 => '1059 Three Letter',
|
|
0x0a => 'FIPS 10-4 Mixed',
|
|
0x0b => 'ISO 3166 Mixed',
|
|
0x0c => 'STANAG 1059 Mixed',
|
|
0x0d => 'GENC Two Letter',
|
|
0x0e => 'GENC Three Letter',
|
|
0x0f => 'GENC Numeric',
|
|
0x10 => 'GENC Mixed',
|
|
}},
|
|
3 => { Name => 'ClassifyingCountry', Format => 'string', PrintConv => '$val =~ s(^//)(); $val' },
|
|
4 => 'SecuritySCI-SHIInformation',
|
|
5 => { Name => 'Caveats', Format => 'string' },
|
|
6 => { Name => 'ReleasingInstructions',Format => 'string' },
|
|
7 => { Name => 'ClassifiedBy', Format => 'string' },
|
|
8 => { Name => 'DerivedFrom', Format => 'string' },
|
|
9 => { Name => 'ClassificationReason', Format => 'string' },
|
|
10 => {
|
|
Name => 'DeclassificationDate',
|
|
Format => 'string',
|
|
Groups => { 2 => 'Time' },
|
|
ValueConv => '$val =~ s/(\d{4})(\d{2})(\d{2})/$1:$2:$3/; $val',
|
|
},
|
|
11 => 'ClassificationAndMarkingSystem',
|
|
12 => { Name => 'ObjectCountryCodingMethod', PrintConv => {
|
|
0x01 => 'ISO-3166 Two Letter',
|
|
0x02 => 'ISO-3166 Three Letter',
|
|
0x03 => 'ISO-3166 Numeric',
|
|
0x04 => 'FIPS 10-4 Two Letter',
|
|
0x05 => 'FIPS 10-4 Four Letter',
|
|
0x06 => '1059 Two Letter',
|
|
0x07 => '1059 Three Letter',
|
|
0x0d => 'GENC Two Letter',
|
|
0x0e => 'GENC Three Letter',
|
|
0x0f => 'GENC Numeric',
|
|
0x40 => 'GENC AdminSub',
|
|
}},
|
|
13 => { Name => 'ObjectCountryCodes', Format => 'string', PrintConv => '$val =~ s(^//)(); $val' },
|
|
14 => { Name => 'ClassificationComments', Format => 'string' },
|
|
15 => 'UMID',
|
|
16 => 'StreamID',
|
|
17 => 'TransportStreamID',
|
|
21 => 'ItemDesignatorID',
|
|
22 => { Name => 'SecurityVersion', Format => 'int16u', PrintConv => '"0102.$val"' },
|
|
23 => {
|
|
Name => 'ClassifyingCountryCodingMethodDate',
|
|
Groups => { 2 => 'Time' },
|
|
Format => 'string',
|
|
ValueConv => '$val=~tr/-/:/; $val',
|
|
},
|
|
24 => {
|
|
Name => 'ObjectCountryCodingMethodDate',
|
|
Groups => { 2 => 'Time' },
|
|
Format => 'string',
|
|
ValueConv => '$val=~tr/-/:/; $val',
|
|
},
|
|
);
|
|
|
|
# I have seen these, but don't know what they are for - PH
|
|
# (they look interesting, but remain static through my sample video)
|
|
%Image::ExifTool::MISB::ChurchillNav = (
|
|
GROUPS => { 0 => 'MISB', 1 => 'MISB', 2 => 'Other' },
|
|
PROCESS_PROC => \&ProcessKLV,
|
|
TAG_PREFIX => 'ChurchillNav',
|
|
NOTES => q{
|
|
Proprietary tags used by Churchill Navigation units. These tags are all
|
|
currently unknown, but extracted with the Unknown option.
|
|
},
|
|
# Note: tag ID's are decimal (because the MISB specification uses decimal ID's)
|
|
1 => { Name => 'ChurchillNav_0x0001', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
2 => { Name => 'ChurchillNav_0x0002', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
3 => { Name => 'ChurchillNav_0x0003', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
4 => { Name => 'ChurchillNav_0x0004', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
5 => { Name => 'ChurchillNav_0x0005', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
6 => { Name => 'ChurchillNav_0x0006', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
9 => { Name => 'ChurchillNav_0x0009', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
10 => { Name => 'ChurchillNav_0x000a', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
11 => { Name => 'ChurchillNav_0x000b', Format => 'string', Unknown => 1, Hidden => 1 },
|
|
12 => { Name => 'ChurchillNav_0x000c', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
13 => { Name => 'ChurchillNav_0x000d', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
14 => { Name => 'ChurchillNav_0x000e', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
16 => { Name => 'ChurchillNav_0x0010', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
17 => { Name => 'ChurchillNav_0x0011', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
18 => { Name => 'ChurchillNav_0x0012', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
20 => { Name => 'ChurchillNav_0x0014', Format => 'double', Unknown => 1, Hidden => 1 },
|
|
);
|
|
|
|
%Image::ExifTool::MISB::Unknown = (
|
|
GROUPS => { 0 => 'MISB', 1 => 'MISB', 2 => 'Other' },
|
|
PROCESS_PROC => \&ProcessKLV,
|
|
NOTES => 'Other tags are extracted with the Unknown option.',
|
|
);
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Process KLV (Key-Length-Value) metadata
|
|
# Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
|
|
# Returns: 1 if anything was extracted
|
|
sub ProcessKLV($$$)
|
|
{
|
|
my ($et, $dirInfo, $tagTablePtr) = @_;
|
|
my $dataPt = $$dirInfo{DataPt};
|
|
my $dirStart = $$dirInfo{DirStart};
|
|
my $dirEnd = $dirStart + $$dirInfo{DirLen};
|
|
my $rtnVal = 0;
|
|
my $pos;
|
|
|
|
$et->VerboseDir($$dirInfo{DirName}, undef, $$dirInfo{DirLen});
|
|
|
|
# loop through KLV packets
|
|
for ($pos=$dirStart; $pos<$dirEnd-1; ) {
|
|
my $tag = Get8u($dataPt, $pos++);
|
|
my $len = Get8u($dataPt, $pos++);
|
|
if ($len & 0x80) {
|
|
my $n = $len & 0x7f;
|
|
last if $pos + $n > $dirEnd;
|
|
$len = 0;
|
|
$len = $len * 256 + Get8u($dataPt, $pos++) foreach 1..$n;
|
|
}
|
|
last if $pos + $len > $dirEnd;
|
|
# best guess at decoding the value
|
|
my $val;
|
|
my $tagInfo = $$tagTablePtr{$tag} || { };
|
|
my $format = $$tagInfo{Format} || $defaultFormat{$len};
|
|
if ($format) {
|
|
$val = ReadValue($dataPt, $pos, $format, undef, $len);
|
|
} else {
|
|
# treat as string or binary data
|
|
$val = substr($$dataPt, $pos, $len);
|
|
if ($val !~ /^[\t\n\r\x20-\x7e]*$/) {
|
|
my $dat = $val;
|
|
$val = \$val;
|
|
}
|
|
}
|
|
$et->HandleTag($tagTablePtr, $tag, $val,
|
|
DataPt => $dataPt,
|
|
Start => $pos,
|
|
Size => $len,
|
|
);
|
|
$rtnVal = 1;
|
|
$pos += $len;
|
|
}
|
|
return $rtnVal;
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Parse MISB metadata
|
|
# Inputs: 0) ExifTool ref, 1) data ref, 2) tag table ref
|
|
# Returns: 1 if something was extracted, 0 otherwise
|
|
sub ParseMISB($$$)
|
|
{
|
|
my ($et, $dataPt, $tagTablePtr) = @_;
|
|
my $end = length $$dataPt;
|
|
my $rtnVal = 0;
|
|
my $unknown = $$et{OPTIONS}{Unknown};
|
|
my $verbose = $$et{OPTIONS}{Verbose};
|
|
my $pos;
|
|
|
|
# increment document number in case we find any tags
|
|
$$et{DOC_NUM} = ++$$et{DOC_COUNT};
|
|
$$et{INDENT} .= '| ';
|
|
|
|
# skip the 5-byte header (ref 1 pg. 68)
|
|
# 0 - int8u: metadata service ID (0x00)
|
|
# 1 - int8u: sequence number (increments each packet)
|
|
# 2 - int8u: 0x0f (bits: cell fragmentation 00, decoder config 0, random access 0, reserved 1111)
|
|
# 3-4 - int16u: data length (ie. packet length - 5)
|
|
for ($pos = 5; $pos + 16 < $end; ) {
|
|
my $key = unpack('H*', substr($$dataPt, $pos, 16));
|
|
$pos += 16;
|
|
my $len = Get8u($dataPt, $pos);
|
|
++$pos;
|
|
if ($len & 0x80) { # is this a BER long form integer? (Basic Encoding Rules)
|
|
my $n = $len & 0x7f;
|
|
$len = 0;
|
|
return $rtnVal if $pos + $n > $end;
|
|
$len = $len * 256 + Get8u($dataPt, $pos++) foreach 1..$n;
|
|
}
|
|
my $tagInfo = $$tagTablePtr{$key};
|
|
unless ($tagInfo) {
|
|
if ($verbose or $unknown) {
|
|
# (assume this is a data set, but it maybe be a simple tag)
|
|
$tagInfo = { Name => "MISB_$key", SubDirectory => { TagTable => 'Image::ExifTool::MISB::Unknown' } };
|
|
$et->VPrint(0," [adding $$tagInfo{Name}]\n");
|
|
AddTagToTable($tagTablePtr, $key, $tagInfo);
|
|
} else {
|
|
# skip this record
|
|
$pos += $len;
|
|
next;
|
|
}
|
|
}
|
|
if ($pos + $len > $end) {
|
|
$len = $end - $pos;
|
|
$et->VPrint(0, "$$et{INDENT}(truncated record, only $len bytes available)\n");
|
|
}
|
|
my $dir = $$tagInfo{SubDirectory};
|
|
SetByteOrder($$dir{ByteOrder}) if $$dir{ByteOrder};
|
|
my %dirInfo = (
|
|
DataPt => $dataPt,
|
|
DirStart => $pos,
|
|
DirLen => $len,
|
|
DirName => $$tagInfo{Name},
|
|
);
|
|
ProcessKLV($et, \%dirInfo, GetTagTable($$dir{TagTable})) and $rtnVal = 1;
|
|
SetByteOrder('MM');
|
|
$pos += $len;
|
|
}
|
|
$$et{INDENT} = substr($$et{INDENT},0,-2);
|
|
delete $$et{DOC_NUM};
|
|
--$$et{DOC_COUNT} unless $rtnVal;
|
|
return $rtnVal;
|
|
}
|
|
|
|
1; # end
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Image::ExifTool::MISB - Read Motion Industry Standards Board metadata
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
This module is used by Image::ExifTool
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This module contains code to extract STANAG-4609 Motion Industry Standards
|
|
Board (MISB) KLV-format metadata from M2TS videos.
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright 2003-2025, Phil Harvey (philharvey66 at gmail.com)
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the same terms as Perl itself.
|
|
|
|
=head1 REFERENCES
|
|
|
|
=over 4
|
|
|
|
=item L<https://dokumen.tips/documents/nato-standardization-agreement-stanag-4609-ed-3.html>
|
|
|
|
=item L<https://upload.wikimedia.org/wikipedia/commons/1/19/MISB_Standard_0601.pdf>
|
|
|
|
=item L<https://dokumen.tips/documents/misb-st-010211-standard-security-metadata-universal-standard-describes-the-use.html>
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Image::ExifTool::TagNames/MISB Tags>,
|
|
L<Image::ExifTool(3pm)|Image::ExifTool>
|
|
|
|
=cut
|
|
|