File Coverage

blib/lib/Image/ExifTool/Apple.pm
Criterion Covered Total %
statement 21 23 91.3
branch 1 2 50.0
condition 1 3 33.3
subroutine 5 5 100.0
pod 0 1 0.0
total 28 34 82.3


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: Apple.pm
3             #
4             # Description: Apple EXIF maker notes tags
5             #
6             # Revisions: 2013-09-13 - P. Harvey Created
7             #
8             # References: 1) http://www.photoinvestigator.co/blog/the-mystery-of-maker-apple-metadata/
9             # 2) Frank Rupprecht private communication
10             #------------------------------------------------------------------------------
11              
12             package Image::ExifTool::Apple;
13              
14 7     7   8509 use strict;
  7         17  
  7         352  
15 7     7   42 use vars qw($VERSION);
  7         17  
  7         432  
16 7     7   1608 use Image::ExifTool::Exif;
  7         17  
  7         492  
17 7     7   6816 use Image::ExifTool::PLIST;
  7         25  
  7         6433  
18              
19             $VERSION = '1.15';
20              
21             sub ConvertPLIST($$);
22              
23             # Apple iPhone metadata (ref PH)
24             %Image::ExifTool::Apple::Main = (
25             WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
26             CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
27             WRITABLE => 1,
28             GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
29             NOTES => 'Tags extracted from the maker notes of iPhone images.',
30             0x0001 => { # (Version, ref 2)
31             Name => 'MakerNoteVersion',
32             Writable => 'int32s',
33             },
34             0x0002 => { #2
35             Name => 'AEMatrix',
36             Unknown => 1,
37             # (not currently writable)
38             ValueConv => \&ConvertPLIST,
39             },
40             0x0003 => { # (Timestamp, ref 2)
41             Name => 'RunTime', # (includes time plugged in, but not when suspended, ref 1)
42             SubDirectory => { TagTable => 'Image::ExifTool::Apple::RunTime' },
43             },
44             0x0004 => { #2
45             Name => 'AEStable',
46             Writable => 'int32s',
47             PrintConv => { 0 => 'No', 1 => 'Yes' },
48             },
49             0x0005 => { #2
50             Name => 'AETarget',
51             Writable => 'int32s',
52             },
53             0x0006 => { #2
54             Name => 'AEAverage',
55             Writable => 'int32s',
56             },
57             0x0007 => { #2
58             Name => 'AFStable',
59             Writable => 'int32s',
60             PrintConv => { 0 => 'No', 1 => 'Yes' },
61             },
62             0x0008 => { #1 (FocusAccelerometerVector, ref 2)
63             Name => 'AccelerationVector',
64             Groups => { 2 => 'Camera' },
65             Writable => 'rational64s',
66             Count => 3,
67             # Note: the directions are contrary to the Apple documentation (which have the
68             # signs of all axes reversed -- apparently the Apple geeks aren't very good
69             # with basic physics, and don't understand the concept of acceleration. See
70             # http://nscookbook.com/2013/03/ios-programming-recipe-19-using-core-motion-to-access-gyro-and-accelerometer/
71             # for one of the few correct descriptions of this). Note that this leads to
72             # a left-handed coordinate system for acceleration.
73             Notes => q{
74             XYZ coordinates of the acceleration vector in units of g. As viewed from
75             the front of the phone, positive X is toward the left side, positive Y is
76             toward the bottom, and positive Z points into the face of the phone
77             },
78             },
79             # 0x0009 - int32s: seen 19,275,531,4371 (SISMethod, ref 2)
80             0x000a => { # (HDRMethod, ref 2)
81             Name => 'HDRImageType',
82             Writable => 'int32s',
83             PrintConv => {
84             # 2 => ? (iPad mini 2)
85             3 => 'HDR Image',
86             4 => 'Original Image',
87             },
88             },
89             0x000b => { # (BurstUUID, ref 2)
90             Name => 'BurstUUID',
91             Writable => 'string',
92             Notes => 'unique ID for all images in a burst',
93             },
94             0x000c => { # ref forum13710 (Neal Krawetz) (SphereHealthTrackingError, ref 2)
95             Name => 'FocusDistanceRange',
96             Writable => 'rational64s',
97             Count => 2,
98             PrintConv => q{
99             my @a = split ' ', $val;
100             sprintf('%.2f - %.2f m', $a[0] <= $a[1] ? @a : reverse @a);
101             },
102             PrintConvInv => '$val =~ s/ - / /; $val =~ s/ ?m$//; $val',
103             },
104             # 0x000d - int32s: 0,1,6,20,24,32,40 (SphereHealthAverageCurrent, ref 2)
105             # 0x000e - int32s: 0,1,4,12 (Orientation? 0=landscape? 4=portrait? ref 1) (SphereMotionDataStatus, ref 2)
106             0x000f => { #2
107             Name => 'OISMode',
108             Writable => 'int32s',
109             # seen: 2,3,5
110             },
111             # 0x0010 - int32s: 1 (SphereStatus, ref 2)
112             0x0011 => { # (if defined, there is a live photo associated with the video, #forum13565) (AssetIdentifier, ref 2)
113             Name => 'ContentIdentifier',
114             Notes => 'called MediaGroupUUID when it appears as an XAttr',
115             # - originally called ContentIdentifier, forum8750
116             # - changed in 12.19 to MediaGroupUUID, NealKrawetz private communication
117             # - changed back to ContentIdentifier since Apple writes this to Keys content.identifier (forum14874)
118             Writable => 'string',
119             },
120             # 0x0012 - (QRMOutputType, ref 2)
121             # 0x0013 - (SphereExternalForceOffset, ref 2)
122             0x0014 => { # (StillImageCaptureType, ref 2)
123             Name => 'ImageCaptureType',
124             Writable => 'int32s',
125             # seen: 1,2,3,4,5,10,12
126             PrintConv => { #forum15096
127             1 => 'ProRAW',
128             2 => 'Portrait',
129             10 => 'Photo',
130             11 => 'Manual Focus', #forum16044
131             12 => 'Scene', #forum16044
132             },
133             },
134             0x0015 => { # (ImageGroupIdentifier, ref 2)
135             Name => 'ImageUniqueID',
136             Writable => 'string',
137             },
138             # 0x0016 - string[29]: "AXZ6pMTOh2L+acSh4Kg630XCScoO\0" (PhotosOriginatingSignature, ref 2)
139             0x0017 => { #forum13565 (only valid if MediaGroupUUID/ContentIdentifier exists) (StillImageCaptureFlags, ref 2)
140             Name => 'LivePhotoVideoIndex',
141             Notes => 'divide by RunTimeScale to get time in seconds',
142             },
143             # 0x0018 - (PhotosRenderOriginatingSignature, ref 2)
144             0x0019 => { # (StillImageProcessingFlags, ref 2)
145             Name => 'ImageProcessingFlags',
146             Writable => 'int32s',
147             Unknown => 1,
148             PrintConv => { BITMASK => { } },
149             },
150             0x001a => { # (PhotoTranscodeQualityHint, ref 2)
151             Name => 'QualityHint',
152             Writable => 'string',
153             Unknown => 1,
154             # seen: "q825s\0", "q750n\0", "q900n\0"
155             },
156             # 0x001b - (PhotosRenderEffect, ref 2)
157             # 0x001c - (BracketedCaptureSequenceNumber, ref 2)
158             # 0x001c - Flash, 2="On" (ref PH)
159             0x001d => { #2
160             Name => 'LuminanceNoiseAmplitude',
161             Writable => 'rational64s',
162             },
163             # 0x001e - (OriginatingAppID, ref 2)
164             0x001f => {
165             Name => 'PhotosAppFeatureFlags', #2
166             Notes => 'set if person or pet detected in image', #PH
167             Writable => 'int32s',
168             },
169             0x0020 => { # (ImageCaptureRequestIdentifier, ref 2)
170             Name => 'ImageCaptureRequestID',
171             Writable => 'string',
172             Unknown => 1,
173             },
174             0x0021 => { # (MeteorHeadroom, ref 2)
175             Name => 'HDRHeadroom',
176             Writable => 'rational64s',
177             },
178             # 0x0022 - (ARKitPhoto, ref 2)
179             0x0023 => {
180             Name => 'AFPerformance', #2
181             Writable => 'int32s',
182             Count => 2,
183             Notes => q{
184             first number maybe related to focus distance, last number maybe related to
185             focus accuracy
186             },
187             PrintConv => 'my @a=split " ",$val; sprintf("%d %d %d",$a[0],$a[1]>>28,$a[1]&0xfffffff)',
188             PrintConvInv => 'my @a=split " ",$val; sprintf("%d %d",$a[0],($a[1]<<28)+$a[2])',
189             },
190             # 0x0023 - int32s[2] (AFPerformance, ref 2)
191             # 0x0024 - (AFExternalOffset, ref 2)
192             0x0025 => { # (StillImageSceneFlags, ref 2)
193             Name => 'SceneFlags',
194             Writable => 'int32s',
195             Unknown => 1,
196             PrintConv => { BITMASK => { } },
197             },
198             0x0026 => { # (StillImageSNRType, ref 2)
199             Name => 'SignalToNoiseRatioType',
200             Writable => 'int32s',
201             Unknown => 1,
202             },
203             0x0027 => { # (StillImageSNR, ref 2)
204             Name => 'SignalToNoiseRatio',
205             Writable => 'rational64s',
206             },
207             # 0x0028 - int32s (UBMethod, ref 2)
208             # 0x0029 - string (SpatialOverCaptureGroupIdentifier, ref 2)
209             # 0x002A - (iCloudServerSoftwareVersionForDynamicallyGeneratedMedia, ref 2)
210             0x002b => {
211             Name => 'PhotoIdentifier', #2
212             Writable => 'string',
213             },
214             # 0x002C - (SpatialOverCaptureImageType, ref 2)
215             # 0x002D - (CCT, ref 2)
216             0x002d => { #PH
217             Name => 'ColorTemperature',
218             Writable => 'int32s',
219             },
220             # 0x002E - (ApsMode, ref 2)
221             0x002e => { #PH
222             Name => 'CameraType',
223             Writable => 'int32s',
224             PrintConv => {
225             0 => 'Back Wide Angle',
226             1 => 'Back Normal',
227             6 => 'Front',
228             },
229             },
230             # 0x002e - set to 0 for 0.5x (crop?) (ref PH)
231             0x002F => { #2
232             Name => 'FocusPosition',
233             Writable => 'int32s',
234             },
235             0x0030 => { # (MeteorPlusGainMap, ref 2)
236             Name => 'HDRGain',
237             Writable => 'rational64s',
238             },
239             # 0x0031 - (StillImageProcessingHomography, ref 2)
240             # 0x0032 - (IntelligentDistortionCorrection, ref 2)
241             # 0x0033 - (NRFStatus, ref 2)
242             # 0x0034 - (NRFInputBracketCount, ref 2)
243             # 0x0034 - 1 for flash on, otherwise doesn't exist (ref PH)
244             # 0x0035 - (NRFRegisteredBracketCount, ref 2)
245             # 0x0035 - 0 for flash on, otherwise doesn't exist (ref PH)
246             # 0x0036 - (LuxLevel, ref 2)
247             # 0x0037 - (LastFocusingMethod, ref 2)
248             0x0038 => { # (TimeOfFlightAssistedAutoFocusEstimatorMeasuredDepth, ref 2)
249             Name => 'AFMeasuredDepth',
250             Notes => 'from the time-of-flight-assisted auto-focus estimator',
251             Writable => 'int32s',
252             },
253             # 0x0039 - (TimeOfFlightAssistedAutoFocusEstimatorROIType, ref 2)
254             # 0x003A - (NRFSRLStatus, ref 2)
255             # 0x003a - non-zero if a person was in the image? (ref PH)
256             # 0x003B - (SystemPressureLevel, ref 2)
257             # 0x003C - (CameraControlsStatisticsMaster, ref 2)
258             # 0x003c - 4=rear cam, 1=front cam? (ref PH)
259             0x003D => { # (TimeOfFlightAssistedAutoFocusEstimatorSensorConfidence, ref 2)
260             Name => 'AFConfidence',
261             Writable => 'int32s',
262             },
263             0x003E => { # (ColorCorrectionMatrix, ref 2)
264             Name => 'ColorCorrectionMatrix',
265             Unknown => 1,
266             ValueConv => \&ConvertPLIST,
267             },
268             0x003F => { #2
269             Name => 'GreenGhostMitigationStatus',
270             Writable => 'int32s',
271             Unknown => 1,
272             },
273             0x0040 => { #2
274             Name => 'SemanticStyle',
275             Notes => '_1=Tone, _2=Warm, _3=1.Std,2.Vibrant,3.Rich Contrast,4.Warm,5.Cool', #PH
276             ValueConv => \&ConvertPLIST,
277             },
278             0x0041 => { # (SemanticStyleKey_RenderingVersion, ref 2)
279             Name => 'SemanticStyleRenderingVer',
280             ValueConv => \&ConvertPLIST,
281             },
282             0x0042 => { # (SemanticStyleKey_Preset, ref 2)
283             Name => 'SemanticStylePreset',
284             ValueConv => \&ConvertPLIST,
285             },
286             # 0x0043 - (SemanticStyleKey_ToneBias, ref 2)
287             # 0x0044 - (SemanticStyleKey_WarmthBias, ref 2)
288             # 0x0045 - (FrontFacing, ref 2) (not for iPhone15, ref PH)
289             # 0x0046 - (TimeOfFlightAssistedAutoFocusEstimatorContainsBlindSpot, ref 2)
290             # 0x0047 - (LeaderFollowerAutoFocusLeaderDepth, ref 2)
291             # 0x0048 - (LeaderFollowerAutoFocusLeaderFocusMethod, ref 2)
292             # 0x0049 - (LeaderFollowerAutoFocusLeaderConfidence, ref 2)
293             # 0x004a - (LeaderFollowerAutoFocusLeaderROIType, ref 2)
294             # 0x004a - 2=back normal, 4=back wide angle, 5=front (ref PH)
295             # 0x004b - (ZeroShutterLagFailureReason, ref 2)
296             # 0x004c - (TimeOfFlightAssistedAutoFocusEstimatorMSPMeasuredDepth, ref 2)
297             # 0x004d - (TimeOfFlightAssistedAutoFocusEstimatorMSPSensorConfidence, ref 2)
298             # 0x004e - (Camera, ref 2)
299             0x004e => {
300             Name => 'Apple_0x004e',
301             Unknown => 1,
302             # first number is 0 for front cam, 1 for either back cam (ref PH)
303             ValueConv => \&ConvertPLIST,
304             },
305             0x004f => {
306             Name => 'Apple_0x004f',
307             Unknown => 1,
308             ValueConv => \&ConvertPLIST,
309             },
310             0x0054 => {
311             Name => 'Apple_0x0054',
312             Unknown => 1,
313             ValueConv => \&ConvertPLIST,
314             },
315             0x005a => {
316             Name => 'Apple_0x005a',
317             Unknown => 1,
318             ValueConv => \&ConvertPLIST,
319             },
320             );
321              
322             # PLIST-format CMTime structure (ref PH)
323             # (CMTime ref https://developer.apple.com/library/ios/documentation/CoreMedia/Reference/CMTime/Reference/reference.html)
324             %Image::ExifTool::Apple::RunTime = (
325             PROCESS_PROC => \&Image::ExifTool::PLIST::ProcessBinaryPLIST,
326             GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
327             NOTES => q{
328             This PLIST-format information contains the elements of a CMTime structure
329             representing the amount of time the phone has been running since the last
330             boot, not including standby time.
331             },
332             timescale => { Name => 'RunTimeScale' }, # (seen 1000000000 --> ns)
333             epoch => { Name => 'RunTimeEpoch' }, # (seen 0)
334             value => { Name => 'RunTimeValue' }, # (should divide by RunTimeScale to get seconds)
335             flags => {
336             Name => 'RunTimeFlags',
337             PrintConv => { BITMASK => {
338             0 => 'Valid',
339             1 => 'Has been rounded',
340             2 => 'Positive infinity',
341             3 => 'Negative infinity',
342             4 => 'Indefinite',
343             }},
344             },
345             );
346              
347             # Apple composite tags
348             %Image::ExifTool::Apple::Composite = (
349             GROUPS => { 2 => 'Camera' },
350             RunTimeSincePowerUp => {
351             Require => {
352             0 => 'Apple:RunTimeValue',
353             1 => 'Apple:RunTimeScale',
354             },
355             ValueConv => '$val[1] ? $val[0] / $val[1] : undef',
356             PrintConv => 'ConvertDuration($val)',
357             },
358             );
359              
360             # add our composite tags
361             Image::ExifTool::AddCompositeTags('Image::ExifTool::Apple');
362              
363             #------------------------------------------------------------------------------
364             # Convert from binary PLIST format to a tag value we can use
365             # Inputs: 0) binary plist data, 1) ExifTool ref
366             # Returns: converted value
367             sub ConvertPLIST($$)
368             {
369 1     1 0 4 my ($val, $et) = @_;
370 1         6 my $dirInfo = { DataPt => \$val, NoVerboseDir => 1 };
371 1         8 my $oldOrder = $et->GetByteOrder();
372 1         11 require Image::ExifTool::PLIST;
373 1         9 Image::ExifTool::PLIST::ProcessBinaryPLIST($et, $dirInfo);
374 1         3 $val = $$dirInfo{Value};
375 1 50 33     7 if (ref $val eq 'HASH' and not $et->Options('Struct')) {
376 0         0 require 'Image/ExifTool/XMPStruct.pl';
377 0         0 $val = Image::ExifTool::XMP::SerializeStruct($et, $val);
378             }
379 1         7 $et->SetByteOrder($oldOrder);
380 1         7 return $val;
381             }
382              
383             1; # end
384              
385             __END__