File Coverage

blib/lib/Image/ExifTool/DJI.pm
Criterion Covered Total %
statement 21 47 44.6
branch 0 8 0.0
condition 0 11 0.0
subroutine 7 9 77.7
pod 0 2 0.0
total 28 77 36.3


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: DJI.pm
3             #
4             # Description: DJI Phantom maker notes tags
5             #
6             # Revisions: 2016-07-25 - P. Harvey Created
7             # 2017-06-23 - PH Added XMP tags
8             # 2024-12-04 - PH Added protobuf tags
9             #------------------------------------------------------------------------------
10              
11             package Image::ExifTool::DJI;
12              
13 11     11   80 use strict;
  11         30  
  11         598  
14 11     11   67 use vars qw($VERSION %knownProtocol);
  11         20  
  11         694  
15 11     11   58 use Image::ExifTool qw(:DataAccess :Utils);
  11         24  
  11         3372  
16 11     11   88 use Image::ExifTool::Exif;
  11         23  
  11         323  
17 11     11   2602 use Image::ExifTool::XMP;
  11         30  
  11         994  
18 11     11   69 use Image::ExifTool::GPS;
  11         22  
  11         280  
19 11     11   9508 use Image::ExifTool::Protobuf;
  11         42  
  11         34294  
20              
21             $VERSION = '1.16';
22              
23             sub ProcessDJIInfo($$$);
24             sub ProcessSettings($$$);
25              
26             %knownProtocol = (
27             'dvtm_ac203.proto' => 1, # Osmo Action 4
28             'dvtm_ac204.proto' => 1, # Osmo Action 5
29             'dvtm_AVATA2.proto' => 1, # Avata 2
30             'dvtm_wm265e.proto' => 1, # Mavic 3
31             'dvtm_pm320.proto' => 1, # Matrice 30
32             'dvtm_Mini4_Pro.proto' => 1, # Mini 4
33             'dvtm_dji_neo.proto' => 1, # Neo
34             'dvtm_Air3.proto' => 1, # Air 3
35             'dvtm_Air3s.proto' => 1, # Air 3s
36             'dvtm_PP-101.proto' => 1, # Osmo Pocket 3
37             'dvtm_oq101.proto' => 1, # Osmo 360
38             'dvtm_wa345e.proto' => 1, # Matrice 4E
39             'dvtm_wm261.proto' => 1, # Mavic Pro 3
40             # dvtm_wm169.proto seems to be DJI O3 but the structure is unknown
41             );
42              
43              
44             my %convFloat2 = (
45             PrintConv => 'sprintf("%+.2f", $val)',
46             PrintConvInv => '$val',
47             );
48              
49             # DJI maker notes (ref PH, mostly educated guesses based on DJI QuickTime::UserData tags)
50             %Image::ExifTool::DJI::Main = (
51             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
52             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
53             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
54             NOTES => q{
55             This table lists tags found in the maker notes of images from some DJI
56             Phantom drones.
57             },
58             0x01 => { Name => 'Make', Writable => 'string' },
59             # 0x02 - int8u[4]: "1 0 0 0", "1 1 0 0"
60             0x03 => { Name => 'SpeedX', Writable => 'float', %convFloat2 }, # (guess)
61             0x04 => { Name => 'SpeedY', Writable => 'float', %convFloat2 }, # (guess)
62             0x05 => { Name => 'SpeedZ', Writable => 'float', %convFloat2 }, # (guess)
63             0x06 => { Name => 'Pitch', Writable => 'float', %convFloat2 },
64             0x07 => { Name => 'Yaw', Writable => 'float', %convFloat2 },
65             0x08 => { Name => 'Roll', Writable => 'float', %convFloat2 },
66             0x09 => { Name => 'CameraPitch',Writable => 'float', %convFloat2 },
67             0x0a => { Name => 'CameraYaw', Writable => 'float', %convFloat2 },
68             0x0b => { Name => 'CameraRoll', Writable => 'float', %convFloat2 },
69             );
70              
71             # DJI debug maker notes
72             %Image::ExifTool::DJI::Info = (
73             PROCESS_PROC => \&ProcessDJIInfo,
74             GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
75             NOTES => 'Tags written by some DJI drones.',
76             VARS => { LONG_TAGS => 2 },
77             ae_dbg_info => { Name => 'AEDebugInfo' },
78             ae_histogram_info => { Name => 'AEHistogramInfo' },
79             ae_local_histogram => { Name => 'AELocalHistogram' },
80             ae_liveview_histogram_info => { Name => 'AELiveViewHistogramInfo' },
81             ae_liveview_local_histogram => { Name => 'AELiveViewLocalHistogram' },
82             awb_dbg_info => { Name => 'AWBDebugInfo' },
83             af_dbg_info => { Name => 'AFDebugInfo' },
84             hiso => { Name => 'Histogram' },
85             xidiri => { Name => 'Xidiri' },
86             'GimbalDegree(Y,P,R)'=> { Name => 'GimbalDegree' },
87             'FlightDegree(Y,P,R)'=> { Name => 'FlightDegree' },
88             adj_dbg_info => { Name => 'ADJDebugInfo' },
89             sensor_id => { Name => 'SensorID' },
90             'FlightSpeed(X,Y,Z)' => { Name => 'FlightSpeed' },
91             hyperlapse_dbg_info => { Name => 'HyperlapsDebugInfo' },
92             );
93              
94             # thermal parameters in APP4 of DJI ZH20T images (ref forum11401)
95             %Image::ExifTool::DJI::ThermalParams = (
96             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
97             GROUPS => { 0 => 'APP4', 2 => 'Image' },
98             NOTES => 'Thermal parameters extracted from APP4 of DJI RJPEG files from the ZH20T.',
99             # 0x00 - 0xaa551206 - temperature header magic number
100             0x24 => { Name => 'K1', Format => 'float' },
101             0x28 => { Name => 'K2', Format => 'float' },
102             0x2c => { Name => 'K3', Format => 'float' },
103             0x30 => { Name => 'K4', Format => 'float' },
104             0x34 => { Name => 'KF', Format => 'float' },
105             0x38 => { Name => 'B1', Format => 'float' },
106             0x3c => { Name => 'B2', Format => 'float' },
107             0x44 => { Name => 'ObjectDistance', Format => 'int16u' },
108             0x46 => { Name => 'RelativeHumidity', Format => 'int16u' },
109             0x48 => { Name => 'Emissivity', Format => 'int16u' },
110             0x4a => { Name => 'Reflection', Format => 'int16u', },
111             0x4c => { Name => 'AmbientTemperature', Format => 'int16u' }, # (aka D1)
112             0x50 => { Name => 'D2', Format => 'int32s' },
113             0x54 => { Name => 'KJ', Format => 'int16u' },
114             0x56 => { Name => 'DB', Format => 'int16u' },
115             0x58 => { Name => 'KK', Format => 'int16u' },
116             # 0x500 - 0x55aa1206 - device header magic number
117             # (nothing yet decoded from device header)
118             );
119              
120             # thermal parameters in APP4 of DJI M3T, H20N, M2EA and some M30T images (ref PH/forum11401)
121             %Image::ExifTool::DJI::ThermalParams2 = (
122             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
123             GROUPS => { 0 => 'APP4', 2 => 'Image' },
124             NOTES => 'Thermal parameters extracted from APP4 of DJI M3T RJPEG files.',
125             0x00 => { Name => 'AmbientTemperature', Format => 'float', PrintConv => 'sprintf("%.1f C",$val)' }, # (NC)
126             0x04 => { Name => 'ObjectDistance', Format => 'float', PrintConv => 'sprintf("%.1f m",$val)' },
127             0x08 => { Name => 'Emissivity', Format => 'float', PrintConv => 'sprintf("%.2f",$val)' },
128             0x0c => { Name => 'RelativeHumidity', Format => 'float', PrintConv => 'sprintf("%g %%",$val*100)' },
129             0x10 => { Name => 'ReflectedTemperature',Format => 'float', PrintConv => 'sprintf("%.1f C",$val)' },
130             0x65 => { Name => 'IDString', Format => 'string[16]' }, # (NC)
131             );
132              
133             # thermal parameters in APP4 of some DJI M30T images (ref PH)
134             %Image::ExifTool::DJI::ThermalParams3 = (
135             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
136             GROUPS => { 0 => 'APP4', 2 => 'Image' },
137             NOTES => 'Thermal parameters extracted from APP4 of some DJI RJPEG files.',
138             # 0x00 - 0xaa553800 - params3 magic number
139             0x04 => { Name => 'RelativeHumidity', Format => 'int16u' },
140             0x06 => { Name => 'ObjectDistance', Format => 'int16u', ValueConv => '$val / 10' },
141             0x08 => { Name => 'Emissivity', Format => 'int16u', ValueConv => '$val / 100' },
142             0x0a => { Name => 'ReflectedTemperature',Format => 'int16u', ValueConv => '$val / 10' },
143             );
144              
145             %Image::ExifTool::DJI::XMP = (
146             %Image::ExifTool::XMP::xmpTableDefaults,
147             GROUPS => { 0 => 'XMP', 1 => 'XMP-drone-dji', 2 => 'Location' },
148             NAMESPACE => 'drone-dji',
149             TABLE_DESC => 'XMP DJI',
150             VARS => { ID_FMT => 'none' },
151             NOTES => 'XMP tags used by DJI for images from drones.',
152             AbsoluteAltitude => { Writable => 'real' },
153             RelativeAltitude => { Writable => 'real' },
154             GimbalRollDegree => { Writable => 'real' },
155             GimbalYawDegree => { Writable => 'real' },
156             GimbalPitchDegree => { Writable => 'real' },
157             FlightRollDegree => { Writable => 'real' },
158             FlightYawDegree => { Writable => 'real' },
159             FlightPitchDegree => { Writable => 'real' },
160             GpsLatitude => {
161             Name => 'GPSLatitude',
162             Writable => 'real',
163             Avoid => 1,
164             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
165             PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lat")',
166             },
167             GpsLongtitude => { # [sic] (misspelt in DJI original file)
168             Name => 'GPSLongtitude',
169             Writable => 'real',
170             Avoid => 1, # (in case someone tries to write "GPSLong*")
171             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
172             PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lon")',
173             },
174             GpsLongitude => { #PH (NC)
175             Name => 'GPSLongitude',
176             Writable => 'real',
177             Avoid => 1,
178             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
179             PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lon")',
180             },
181             FlightXSpeed => { Writable => 'real' },
182             FlightYSpeed => { Writable => 'real' },
183             FlightZSpeed => { Writable => 'real' },
184             CamReverse => { }, # integer?
185             GimbalReverse => { }, # integer?
186             SelfData => { Groups => { 2 => 'Image' } },
187             CalibratedFocalLength => { Writable => 'real', Groups => { 2 => 'Image' } },
188             CalibratedOpticalCenterX => { Writable => 'real', Groups => { 2 => 'Image' } },
189             CalibratedOpticalCenterY => { Writable => 'real', Groups => { 2 => 'Image' } },
190             RtkFlag => { }, # integer?
191             RtkStdLon => { Writable => 'real' },
192             RtkStdLat => { Writable => 'real' },
193             RtkStdHgt => { Writable => 'real' },
194             DewarpData => { Groups => { 2 => 'Image' } },
195             DewarpFlag => { Groups => { 2 => 'Image' } }, # integer?
196             Latitude => {
197             Name => 'Latitude',
198             Writable => 'real',
199             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
200             PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lat")',
201             },
202             Longitude => {
203             Name => 'Longitude',
204             Writable => 'real',
205             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
206             PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1, "lon")',
207             },
208             );
209              
210             %Image::ExifTool::DJI::Glamour = (
211             GROUPS => { 0 => 'QuickTime', 1 => 'DJI', 2 => 'Image' },
212             PROCESS_PROC => \&Image::ExifTool::DJI::ProcessSettings,
213             NOTES => 'Glamour settings used by some DJI models.',
214             beauty_enable => 'BeautyEnable',
215             smoother => 'Smoother',
216             whitening => 'Whitening',
217             face_slimming => 'FaceSlimming',
218             eye_enlarge => 'EyeEnlarge',
219             nose_slimming => 'NoseSlimming',
220             mouth_beautify => 'MouthModify',
221             teeth_whitening => 'TeethWhitening',
222             leg_longer => 'LegLonger',
223             head_shrinking => 'HeadShrinking',
224             lipstick => 'Lipstick',
225             blush => 'Blush',
226             dark_circle => 'DarkCircle',
227             acne_spot_removal=>'AcneSpotRemoval',
228             eyebrows => 'Eyebrows',
229             );
230              
231             # metadata in protobuf format (djmd and dbgi meta types, ref PH)
232             %Image::ExifTool::DJI::Protobuf = (
233             GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Camera' },
234             TAG_PREFIX => '',
235             PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
236             NOTES => q{
237             Tags found in protobuf-format DJI djmd and dbgi timed metadata. The known
238             tags listed below are extracted by default, but unknown djmd tags may be
239             extracted as well by setting the Unknown option to 1, or 2 to also extract
240             unknown dbgi debug tags. Tag ID's are composed of the corresponding .proto
241             file name combined with the hierarchical protobuf field numbers.
242              
243             ExifTool currently extracts timed GPS plus a few other tags from DJI devices
244             which use the following protocols: dvtm_AVATA2.proto (Avata 2),
245             dvtm_ac203.proto (Osmo Action 4), dvtm_ac204.proto (Osmo Action 5),
246             dvtm_wm265e.proto (Mavic 3), dvtm_pm320.proto (Matrice 30),
247             dvtm_Mini4_Pro.proto (Mini 4 Pro), dvtm_dji_neo.proto (DJI Neo),
248             dvtm_Air3.proto (Air 3), dvtm_Air3s.proto (Air 3s), dvtm_PP-101.proto (Osmo
249             Pocket 3), dvtm_oq101.proto (Osmo 360), dvtm_wa345e.proto (Matrice 4E) and
250             dvtm_wm261.proto (Mavic Pro 3).
251              
252             Note that with the protobuf format, numerical tags missing from the output
253             for a given protocol should be considered to have the default value of 0.
254             },
255             Protocol => {
256             Notes => "typically protobuf field 1-1-1, but ExifTool doesn't rely on this",
257             RawConv => q{
258             unless ($Image::ExifTool::DJI::knownProtocol{$val}) {
259             $self->Warn("Unknown protocol $val (please submit sample for testing)");
260             }
261             return $val;
262             },
263             },
264             #
265             # Osmo Action 4
266             #
267             'dvtm_ac203_1-1-5' => { Name => 'SerialNumber', Notes => 'Osmo Action 4' }, # (NC)
268             # dvtm_ac203_1-1-6 - some version number
269             'dvtm_ac203_1-1-10' => 'Model',
270             'dvtm_ac203_2-3' => {
271             Name => 'FrameInfo',
272             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
273             },
274             'dvtm_ac203_3-2-2-1' => { Name => 'ISO', Format => 'float' },
275             'dvtm_ac203_3-2-4-1' => { # (NC)
276             Name => 'ShutterSpeed',
277             Format => 'rational',
278             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
279             },
280             'dvtm_ac203_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
281             # dvtm_ac203_3-2-9-1 - looks like Z accerometer measurement, but 2 and 3 don't look like other components
282             'dvtm_ac203_3-2-10-2' => { Name => 'AccelerometerX', Format => 'float' } , # (NC) left/right
283             'dvtm_ac203_3-2-10-3' => { Name => 'AccelerometerY', Format => 'float' } , # (NC) front/back
284             'dvtm_ac203_3-2-10-4' => { Name => 'AccelerometerZ', Format => 'float' } , # (NC) up/down
285             # dvtm_ac203_3-4-1-4 - model code?
286             'dvtm_ac203_3-4-2-1' => {
287             Name => 'GPSInfo',
288             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
289             },
290             'dvtm_ac203_3-4-2-2' => {
291             Name => 'GPSAltitude',
292             Groups => { 2 => 'Location' },
293             Format => 'unsigned',
294             ValueConv => '$val / 1000',
295             },
296             'dvtm_ac203_3-4-2-6-1' => {
297             Name => 'GPSDateTime',
298             Format => 'string',
299             Groups => { 2 => 'Time' },
300             RawConv => '$$self{GPSDateTime} = $val',
301             ValueConv => '$val =~ tr/-/:/; $val',
302             PrintConv => '$self->ConvertDateTime($val)',
303             },
304             #
305             # Osmo Action 5
306             #
307             'dvtm_ac204_1-1-5' => { Name => 'SerialNumber', Notes => 'Osmo Action 5' }, # (NC)
308             # dvtm_ac204_1-1-6 - some version number
309             'dvtm_ac204_1-1-10' => 'Model',
310             'dvtm_ac204_2-3' => {
311             Name => 'FrameInfo',
312             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
313             },
314             'dvtm_ac204_3-2-4-1' => { # (NC)
315             Name => 'ShutterSpeed',
316             Format => 'rational',
317             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
318             },
319             'dvtm_ac204_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
320             'dvtm_ac204_3-2-10-2' => { Name => 'AccelerometerX', Format => 'float' } , # (NC) left/right
321             'dvtm_ac204_3-2-10-3' => { Name => 'AccelerometerY', Format => 'float' } , # (NC) front/back
322             'dvtm_ac204_3-2-10-4' => { Name => 'AccelerometerZ', Format => 'float' } , # (NC) up/down
323             # dvtm_ac204_3-4-1-4 - model code?
324             'dvtm_ac204_3-4-2-1' => {
325             Name => 'GPSInfo',
326             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
327             },
328             'dvtm_ac204_3-4-2-2' => {
329             Name => 'GPSAltitude',
330             Groups => { 2 => 'Location' },
331             Format => 'unsigned',
332             ValueConv => '$val / 1000',
333             },
334             'dvtm_ac204_3-4-2-6-1' => {
335             Name => 'GPSDateTime',
336             Format => 'string',
337             Groups => { 2 => 'Time' },
338             RawConv => '$$self{GPSDateTime} = $val',
339             ValueConv => '$val =~ tr/-/:/; $val',
340             PrintConv => '$self->ConvertDateTime($val)',
341             },
342             #
343             # Avata 2
344             #
345             # dvtm_AVATA2_1-1-2 - some version number
346             # dvtm_AVATA2_1-1-3 - some version number
347             'dvtm_AVATA2_1-1-5' => { Name => 'SerialNumber', Notes => 'Avata 2' }, # (NC)
348             'dvtm_AVATA2_1-1-10' => 'Model',
349             # dvtm_AVATA2_2-2-1-4 - model code?
350             'dvtm_AVATA2_2-2-3-1' => 'SerialNumber2', # (NC)
351             'dvtm_AVATA2_2-3' => {
352             Name => 'FrameInfo',
353             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
354             },
355             # dvtm_AVATA2_3-1-1 - frame number (starting at 1)
356             'dvtm_AVATA2_3-1-2' => { # (also 3-2-1-6 and 3-4-1-6)
357             Name => 'TimeStamp',
358             Groups => { 2 => 'Time' },
359             Format => 'unsigned',
360             # milliseconds, but I don't know what the zero is
361             ValueConv => '$val / 1e6',
362             },
363             # dvtm_AVATA2_3-2-1-4 - model code?
364             # dvtm_AVATA2_3-2-1-5 - frame rate?
365             'dvtm_AVATA2_3-2-2-1' => { Name => 'ISO', Format => 'float' }, # (NC)
366             'dvtm_AVATA2_3-2-4-1' => {
367             Name => 'ShutterSpeed',
368             Format => 'rational',
369             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
370             },
371             'dvtm_AVATA2_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
372             'dvtm_AVATA2_3-2-10-1' => { # (NC)
373             Name => 'FNumber',
374             Format => 'rational',
375             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
376             },
377             # dvtm_AVATA2_3-4-1-4 - model code?
378             'dvtm_AVATA2_3-4-3' => { # (NC)
379             Name => 'DroneInfo',
380             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
381             },
382             'dvtm_AVATA2_3-4-4-1' => {
383             Name => 'GPSInfo',
384             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
385             },
386             'dvtm_AVATA2_3-4-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' }, # (NC)
387             'dvtm_AVATA2_3-4-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' }, # (NC)
388             #
389             # Mavic 3
390             #
391             'dvtm_wm265e_1-1-5' => { Name => 'SerialNumber', Notes => 'Mavic 3' }, # (confirmed)
392             'dvtm_wm265e_1-1-10' => 'Model',
393             'dvtm_wm265e_2-2' => {
394             Name => 'FrameInfo',
395             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
396             },
397             # dvtm_wm265e_3-2-1-4 - model code?
398             'dvtm_wm265e_3-2-2-1' => { Name => 'ISO', Format => 'float' },
399             'dvtm_wm265e_3-2-3-1' => {
400             Name => 'ShutterSpeed',
401             Format => 'rational',
402             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
403             },
404             # dvtm_wm265e_3-2-5-1 - unknown rational (xxxx / 1000)
405             'dvtm_wm265e_3-2-6-1' => { Name => 'DigitalZoom', Format => 'float' },
406             'dvtm_wm265e_3-3-4-1' => {
407             Name => 'GPSInfo',
408             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
409             },
410             'dvtm_wm265e_3-3-3' => {
411             Name => 'DroneInfo',
412             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
413             },
414             'dvtm_wm265e_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
415             'dvtm_wm265e_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
416             'dvtm_wm265e_3-4-3' => {
417             Name => 'GimbalInfo',
418             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
419             },
420             #
421             # Matrice 30
422             #
423             'dvtm_pm320_1-1-5' => { Name => 'SerialNumber', Notes => 'Matrice 30' },
424             'dvtm_pm320_1-1-10' => 'Model',
425             'dvtm_pm320_2-2' => {
426             Name => 'FrameInfo',
427             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
428             },
429             'dvtm_pm320_3-2-2-1' => { Name => 'ISO', Format => 'float' },
430             'dvtm_pm320_3-2-3-1' => {
431             Name => 'ShutterSpeed',
432             Format => 'rational',
433             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
434             },
435             'dvtm_pm320_3-2-4-1' => { # (NC)
436             Name => 'FNumber',
437             Format => 'rational',
438             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
439             },
440             'dvtm_pm320_3-2-6-1' => { Name => 'DigitalZoom', Format => 'float' },
441             'dvtm_pm320_3-3-4-1' => {
442             Name => 'GPSInfo',
443             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
444             },
445             'dvtm_pm320_3-3-3' => {
446             Name => 'DroneInfo',
447             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
448             },
449             'dvtm_pm320_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
450             'dvtm_pm320_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
451             'dvtm_pm320_3-4-3' => {
452             Name => 'GimbalInfo',
453             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
454             },
455             #
456             # Mini 4 Pro
457             #
458             'dvtm_Mini4_Pro_1-1-5' => { Name => 'SerialNumber', Notes => 'Mini 4 Pro' },
459             'dvtm_Mini4_Pro_1-1-10' => 'Model',
460             'dvtm_Mini4_Pro_2-3' => {
461             Name => 'FrameInfo',
462             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
463             },
464             'dvtm_Mini4_Pro_3-2-7-1' => { Name => 'ISO', Format => 'float' },
465             'dvtm_Mini4_Pro_3-2-10-1' => {
466             Name => 'ShutterSpeed',
467             Format => 'rational',
468             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
469             },
470             'dvtm_Mini4_Pro_3-2-11-1' => {
471             Name => 'FNumber',
472             Format => 'rational',
473             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
474             },
475             'dvtm_Mini4_Pro_3-2-32-1' => { Name => 'ColorTemperature', Format => 'unsigned' },
476             # dvtm_Mini4_Pro_3-2-37-1 - something to do with battery level or time remaining?
477             'dvtm_Mini4_Pro_3-3-4-1' => {
478             Name => 'GPSInfo',
479             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
480             },
481             'dvtm_Mini4_Pro_3-3-3' => {
482             Name => 'DroneInfo',
483             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
484             },
485             'dvtm_Mini4_Pro_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
486             'dvtm_Mini4_Pro_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' }, # (NC)
487             'dvtm_Mini4_Pro_3-4-3' => {
488             Name => 'GimbalInfo',
489             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
490             },
491             #
492             # DJI Neo (very similar to AVATA2)
493             #
494             # dvtm_dji_neo_1-1-2 - some version number
495             # dvtm_dji_neo_1-1-3 - some version number
496             'dvtm_dji_neo_1-1-5' => { Name => 'SerialNumber', Notes => 'DJI Neo' }, # (NC)
497             'dvtm_dji_neo_1-1-10' => 'Model',
498             # dvtm_dji_neo_2-2-1-4 - model code?
499             # dvtm_dji_neo_2-2-2-1 - some firmware version?
500             # dvtm_dji_neo_2-2-2-2 - some version number?
501             'dvtm_dji_neo_2-2-3-1' => 'SerialNumber2', # (NC)
502             'dvtm_dji_neo_2-3' => {
503             Name => 'FrameInfo',
504             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
505             },
506             # dvtm_dji_neo_3-1-1 - frame number (starting at 1)
507             'dvtm_dji_neo_3-1-2' => { # (also 3-2-1-6 and 3-4-1-6)
508             Name => 'TimeStamp',
509             Groups => { 2 => 'Time' },
510             Format => 'unsigned',
511             # milliseconds, but I don't know what the zero is
512             ValueConv => '$val / 1e6',
513             },
514             # dvtm_dji_neo_3-2-1-4 - model code?
515             # dvtm_dji_neo_3-2-1-5 - frame rate?
516             'dvtm_dji_neo_3-2-2-1' => { Name => 'ISO', Format => 'float' }, # (NC)
517             'dvtm_dji_neo_3-2-4-1' => {
518             Name => 'ShutterSpeed',
519             Format => 'rational',
520             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
521             },
522             'dvtm_dji_neo_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' }, # (NC)
523             'dvtm_dji_neo_3-2-10-1' => { # (NC)
524             Name => 'FNumber',
525             Format => 'rational',
526             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
527             },
528             # dvtm_dji_neo_3-4-1-4 - model code?
529             'dvtm_dji_neo_3-4-3' => { # (NC)
530             Name => 'DroneInfo',
531             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
532             },
533             'dvtm_dji_neo_3-4-4-1' => {
534             Name => 'GPSInfo',
535             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
536             },
537             'dvtm_dji_neo_3-4-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' }, # (NC)
538             #
539             # Air 3
540             #
541             'dvtm_Air3_1-1-5' => { Name => 'SerialNumber', Notes => 'Air 3' },
542             'dvtm_Air3_2-3' => {
543             Name => 'FrameInfo',
544             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
545             },
546             'dvtm_Air3_3-1-2' => {
547             Name => 'TimeStamp',
548             Groups => { 2 => 'Time' },
549             Format => 'unsigned',
550             ValueConv => '$val / 1e6',
551             },
552             'dvtm_Air3_3-2-7-1' => { Name => 'ISO', Format => 'float' },
553             'dvtm_Air3_3-2-10-1' => {
554             Name => 'ShutterSpeed',
555             Format => 'rational',
556             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
557             },
558             'dvtm_Air3_3-2-32-1' => { Name => 'ColorTemperature', Format => 'unsigned' },
559             'dvtm_Air3_3-2-11-1' => {
560             Name => 'FNumber',
561             Format => 'rational',
562             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
563             },
564             'dvtm_Air3_3-3-3' => {
565             Name => 'DroneInfo',
566             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
567             },
568             'dvtm_Air3_3-3-4-1' => {
569             Name => 'GPSInfo',
570             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
571             },
572             'dvtm_Air3_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
573             'dvtm_Air3_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
574             'dvtm_Air3_3-4-3' => {
575             Name => 'GimbalInfo',
576             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
577             },
578             #
579             # Air 3s (same structure as Air 3)
580             #
581             'dvtm_Air3s_1-1-5' => { Name => 'SerialNumber', Notes => 'Air 3s' },
582             'dvtm_Air3s_2-3' => {
583             Name => 'FrameInfo',
584             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
585             },
586             'dvtm_Air3s_3-1-2' => {
587             Name => 'TimeStamp',
588             Groups => { 2 => 'Time' },
589             Format => 'unsigned',
590             ValueConv => '$val / 1e6',
591             },
592             'dvtm_Air3s_3-2-7-1' => { Name => 'ISO', Format => 'float' },
593             'dvtm_Air3s_3-2-10-1' => {
594             Name => 'ShutterSpeed',
595             Format => 'rational',
596             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
597             },
598             'dvtm_Air3s_3-2-32-1' => { Name => 'ColorTemperature', Format => 'unsigned' },
599             'dvtm_Air3s_3-2-11-1' => {
600             Name => 'FNumber',
601             Format => 'rational',
602             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
603             },
604             'dvtm_Air3s_3-3-3' => {
605             Name => 'DroneInfo',
606             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
607             },
608             'dvtm_Air3s_3-3-4-1' => {
609             Name => 'GPSInfo',
610             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
611             },
612             'dvtm_Air3s_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
613             'dvtm_Air3s_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
614             'dvtm_Air3s_3-4-3' => {
615             Name => 'GimbalInfo',
616             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
617             },
618             #
619             # Osmo 360 (similar to Action 4/5)
620             #
621             'dvtm_oq101_1-1-5' => { Name => 'SerialNumber', Notes => 'Osmo 360' },
622             'dvtm_oq101_1-1-10' => 'Model',
623             'dvtm_oq101_3-1-2' => {
624             Name => 'TimeStamp',
625             Groups => { 2 => 'Time' },
626             Format => 'unsigned',
627             ValueConv => '$val / 1e6',
628             },
629             'dvtm_oq101_3-2-3-1' => { Name => 'ISO', Format => 'float' },
630             'dvtm_oq101_3-2-4-1' => {
631             Name => 'ShutterSpeed',
632             Format => 'rational',
633             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
634             },
635             'dvtm_oq101_3-2-6-1' => { Name => 'ColorTemperature', Format => 'unsigned' },
636             'dvtm_oq101_1-14-1' => {
637             Name => 'FNumber',
638             Format => 'rational',
639             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
640             },
641             'dvtm_oq101_3-4-2-1' => {
642             Name => 'GPSInfo',
643             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
644             },
645             'dvtm_oq101_3-4-2-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
646             'dvtm_oq101_3-4-2-6-1' => {
647             Name => 'GPSDateTime',
648             Format => 'string',
649             Groups => { 2 => 'Time' },
650             RawConv => '$$self{GPSDateTime} = $val',
651             ValueConv => '$val =~ tr/-/:/; $val',
652             PrintConv => '$self->ConvertDateTime($val)',
653             },
654             'dvtm_oq101_3-2-10-2' => { Name => 'AccelerometerX', Format => 'float' },
655             'dvtm_oq101_3-2-10-3' => { Name => 'AccelerometerY', Format => 'float' },
656             'dvtm_oq101_3-2-10-4' => { Name => 'AccelerometerZ', Format => 'float' },
657             #
658             # Osmo Pocket 3
659             #
660             'dvtm_PP-101_1-1-5' => { Name => 'SerialNumber', Notes => 'Osmo Pocket 3' },
661             'dvtm_PP-101_1-1-10' => 'Model',
662             'dvtm_PP-101_2-3' => {
663             Name => 'FrameInfo',
664             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
665             },
666             'dvtm_PP-101_3-1-2' => {
667             Name => 'TimeStamp',
668             Groups => { 2 => 'Time' },
669             Format => 'unsigned',
670             ValueConv => '$val / 1e6',
671             },
672             'dvtm_PP-101_3-2-9-1' => { Name => 'ISO', Format => 'float' },
673             'dvtm_PP-101_3-2-10-1' => {
674             Name => 'ShutterSpeed',
675             Format => 'rational',
676             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
677             },
678             'dvtm_PP-101_3-2-24-1' => { Name => 'ColorTemperature', Format => 'unsigned' },
679             #
680             # Matrice 4E
681             #
682             'dvtm_wa345e_1-1-5' => { Name => 'SerialNumber', Notes => 'Matrice 4E' },
683             'dvtm_wa345e_1-1-10' => 'Model',
684             'dvtm_wa345e_2-2' => {
685             Name => 'FrameInfo',
686             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
687             },
688             'dvtm_wa345e_3-1-2' => {
689             Name => 'TimeStamp',
690             Groups => { 2 => 'Time' },
691             Format => 'unsigned',
692             ValueConv => '$val / 1e6',
693             },
694             'dvtm_wa345e_3-3-3' => {
695             Name => 'DroneInfo',
696             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
697             },
698             'dvtm_wa345e_3-3-4-1' => {
699             Name => 'GPSInfo',
700             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
701             },
702             'dvtm_wa345e_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
703             'dvtm_wa345e_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
704             'dvtm_wa345e_3-4-3' => {
705             Name => 'GimbalInfo',
706             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
707             },
708             'dvtm_wa345e_3-3-4-6-1' => {
709             Name => 'GPSDateTime',
710             Format => 'string',
711             Groups => { 2 => 'Time' },
712             RawConv => '$$self{GPSDateTime} = $val',
713             ValueConv => '$val =~ tr/-/:/; $val',
714             PrintConv => '$self->ConvertDateTime($val)',
715             },
716             #
717             # Mavic Pro 3
718             #
719             'dvtm_wm261_1-1-5' => { Name => 'SerialNumber', Notes => 'Mavic Pro 3' },
720             'dvtm_wm261_1-1-10' => 'Model',
721             'dvtm_wm261_2-3' => {
722             Name => 'FrameInfo',
723             SubDirectory => { TagTable => 'Image::ExifTool::DJI::FrameInfo' },
724             },
725             'dvtm_wm261_3-1-2' => {
726             Name => 'TimeStamp',
727             Groups => { 2 => 'Time' },
728             Format => 'unsigned',
729             ValueConv => '$val / 1e6',
730             },
731             'dvtm_wm261_3-2-9-1' => { Name => 'ISO', Format => 'float' },
732             'dvtm_wm261_3-2-10-1' => {
733             Name => 'ShutterSpeed',
734             Format => 'rational',
735             PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
736             },
737             'dvtm_wm261_3-2-11-1'=> {
738             Name => 'FNumber',
739             Format => 'rational',
740             PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
741             },
742             'dvtm_wm261_3-3-3' => {
743             Name => 'DroneInfo',
744             SubDirectory => { TagTable => 'Image::ExifTool::DJI::DroneInfo' },
745             },
746             'dvtm_wm261_3-3-4-1' => {
747             Name => 'GPSInfo',
748             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GPSInfo' },
749             },
750             'dvtm_wm261_3-3-4-2' => { Name => 'AbsoluteAltitude', Format => 'int64s', ValueConv => '$val / 1000' },
751             'dvtm_wm261_3-3-5-1' => { Name => 'RelativeAltitude', Format => 'float', ValueConv => '$val / 1000' },
752             'dvtm_wm261_3-4-3' => {
753             Name => 'GimbalInfo',
754             SubDirectory => { TagTable => 'Image::ExifTool::DJI::GimbalInfo' },
755             },
756             'dvtm_wm261_3-3-4-6-1' => {
757             Name => 'GPSDateTime',
758             Format => 'string',
759             Groups => { 2 => 'Time' },
760             RawConv => '$$self{GPSDateTime} = $val',
761             ValueConv => '$val =~ tr/-/:/; $val',
762             PrintConv => '$self->ConvertDateTime($val)',
763             },
764             );
765              
766             %Image::ExifTool::DJI::DroneInfo = (
767             GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Camera' },
768             PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
769             VARS => { ID_FMT => 'dec', ID_LABEL => 'Field #' },
770             1 => { Name => 'DroneRoll', Format => 'int64s', ValueConv => '$val / 10' },
771             2 => { Name => 'DronePitch', Format => 'int64s', ValueConv => '$val / 10' },
772             3 => { Name => 'DroneYaw', Format => 'int64s', ValueConv => '$val / 10' },
773             );
774              
775             %Image::ExifTool::DJI::GimbalInfo = (
776             GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Camera' },
777             PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
778             VARS => { ID_FMT => 'dec', ID_LABEL => 'Field #' },
779             1 => { Name => 'GimbalPitch',Format => 'int64s', ValueConv => '$val / 10' },
780             2 => { Name => 'GimbalRoll', Format => 'int64s', ValueConv => '$val / 10' }, # usually 0, so missing
781             3 => { Name => 'GimbalYaw', Format => 'int64s', ValueConv => '$val / 10' },
782             );
783              
784             %Image::ExifTool::DJI::FrameInfo = (
785             GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Video' },
786             PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
787             VARS => { ID_FMT => 'dec', ID_LABEL => 'Field #' },
788             1 => { Name => 'FrameWidth', Format => 'unsigned' },
789             2 => { Name => 'FrameHeight', Format => 'unsigned' },
790             3 => { Name => 'FrameRate', Format => 'float' },
791             # 4-8: seen these values respectively for DJI Neo: 1,8,4,1,4
792             );
793              
794             %Image::ExifTool::DJI::GPSInfo = (
795             GROUPS => { 0 => 'Protobuf', 1 => 'DJI', 2 => 'Location' },
796             PROCESS_PROC => \&Image::ExifTool::Protobuf::ProcessProtobuf,
797             VARS => { ID_FMT => 'dec', ID_LABEL => 'Field #' },
798             1 => {
799             Name => 'CoordinateUnits',
800             Format => 'unsigned',
801             Notes => 'not extracted, but used internally to convert coordinates to degrees',
802             # don't extract this -- just convert to degrees
803             RawConv => '$$self{CoordUnits} = $val; undef',
804             # PrintConv => { 0 => 'Radians', 1 => 'Degrees' },
805             },
806             2 => {
807             Name => 'GPSLatitude',
808             Format => 'double',
809             # set ExifTool GPSLatitude/GPSLongitude members so GPSDateTime will be generated if necessary
810             RawConv => '$$self{GPSLatitude} = $$self{CoordUnits} ? $val : $val * 180 / 3.141592653589793', # (NC)
811             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
812             },
813             3 => {
814             Name => 'GPSLongitude',
815             Format => 'double',
816             RawConv => '$$self{GPSLongitude} = $$self{CoordUnits} ? $val : $val * 180 / 3.141592653589793', # (NC)
817             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
818             },
819             );
820              
821             #------------------------------------------------------------------------------
822             # Process DJI beauty settings (ref PH)
823             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
824             # Returns: 1 on success
825             sub ProcessSettings($$$)
826             {
827 0     0 0   my ($et, $dirInfo, $tagTbl) = @_;
828 0           $et->VerboseDir($$dirInfo{DirName}, undef, length(${$$dirInfo{DataPt}}));
  0            
829 0           foreach (split /;/, ${$$dirInfo{DataPt}}) {
  0            
830 0           my ($tag, $val) = split /=/;
831 0 0 0       next unless defined $tag and defined $val;
832 0           $et->HandleTag($tagTbl, $tag, $val, MakeTagInfo => 1);
833             }
834 0           return 1;
835             }
836              
837             #------------------------------------------------------------------------------
838             # Process DJI info (ref PH)
839             # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref
840             # Returns: 1 on success
841             sub ProcessDJIInfo($$$)
842             {
843 0     0 0   my ($et, $dirInfo, $tagTbl) = @_;
844 0           my $dataPt = $$dirInfo{DataPt};
845 0   0       my $dirStart = $$dirInfo{DirStart} || 0;
846 0   0       my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart);
847 0 0         if ($dirStart) {
848 0           my $buff = substr($$dataPt, $dirStart, $dirLen);
849 0           $dataPt = \$buff;
850             }
851 0           $et->VerboseDir('DJIInfo', undef, length $$dataPt);
852 0           while ($$dataPt =~ /\G\[(.*?)\](?=(\[|$))/sg) {
853 0           my ($tag, $val) = split /:/, $1, 2;
854 0 0 0       next unless defined $tag and defined $val;
855 0 0         if ($val =~ /^([\x20-\x7e]+)\0*$/) {
856 0           $val = $1;
857             } else {
858 0           my $buff = $val;
859 0           $val = \$buff;
860             }
861 0           $et->HandleTag($tagTbl, $tag, $val, MakeTagInfo => 1);
862             }
863 0           return 1;
864             }
865              
866             __END__