Skip to content

Commit 55b51b6

Browse files
authored
Merge pull request #550 from IntelliDevPeep/patch-1
Reconyx HyperFire 2 Support
2 parents 4cb07f0 + d76909c commit 55b51b6

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

Source/com/drew/metadata/exif/ExifTiffHandler.java

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,10 @@ private boolean processMakernote(final int makernoteOffset,
598598
ReconyxUltraFireMakernoteDirectory directory = new ReconyxUltraFireMakernoteDirectory();
599599
_metadata.addDirectory(directory);
600600
processReconyxUltraFireMakernote(directory, makernoteOffset, reader);
601+
} else if (firstNineChars.equalsIgnoreCase("RECONYXH2")) {
602+
ReconyxHyperFire2MakernoteDirectory directory = new ReconyxHyperFire2MakernoteDirectory();
603+
_metadata.addDirectory(directory);
604+
processReconyxHyperFire2Makernote(directory, makernoteOffset, reader);
601605
} else if ("SAMSUNG".equalsIgnoreCase(cameraMake)) {
602606
// Only handles Type2 notes correctly. Others aren't implemented, and it's complex to determine which ones to use
603607
pushDirectory(SamsungType2MakernoteDirectory.class);
@@ -798,6 +802,82 @@ private static void processReconyxHyperFireMakernote(@NotNull final ReconyxHyper
798802
directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, 44, Charsets.UTF_8));
799803
}
800804

805+
private static void processReconyxHyperFire2Makernote(@NotNull final ReconyxHyperFire2MakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
806+
{
807+
808+
int major = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION);
809+
int minor = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 2);
810+
int revision = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 4);
811+
String buildYear = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 6));
812+
String buildDate = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 8));
813+
String buildYearAndDate = buildYear + buildDate;
814+
Integer build;
815+
try {
816+
build = Integer.parseInt(buildYearAndDate);
817+
} catch (NumberFormatException e) {
818+
build = null;
819+
}
820+
821+
if (build != null) {
822+
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d.%s", major, minor, revision, build));
823+
} else {
824+
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d", major, minor, revision));
825+
directory.addError("Error processing Reconyx HyperFire 2 makernote data: build '" + buildYearAndDate + "' is not in the expected format and will be omitted from Firmware Version.");
826+
}
827+
828+
directory.setIntArray(ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE,
829+
new int[]
830+
{
831+
reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE),
832+
reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE + 2)
833+
});
834+
835+
int eventNumberHigh = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER);
836+
int eventNumberLow = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER + 2);
837+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER, (eventNumberHigh << 16) + eventNumberLow);
838+
839+
int seconds = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL);
840+
int minutes = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 2);
841+
int hour = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 4);
842+
int month = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 6);
843+
int day = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 8);
844+
int year = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 10);
845+
846+
if ((seconds >= 0 && seconds < 60) &&
847+
(minutes >= 0 && minutes < 60) &&
848+
(hour >= 0 && hour < 24) &&
849+
(month >= 1 && month < 13) &&
850+
(day >= 1 && day < 32) &&
851+
(year >= 1 && year <= 9999)) {
852+
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL,
853+
String.format("%4d:%2d:%2d %2d:%2d:%2d", year, month, day, hour, minutes, seconds));
854+
} else {
855+
directory.addError("Error processing Reconyx HyperFire 2 makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time.");
856+
}
857+
858+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_MOON_PHASE, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_MOON_PHASE));
859+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, reader.getInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT));
860+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE, reader.getInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE));
861+
862+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_CONTRAST, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_CONTRAST));
863+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_BRIGHTNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BRIGHTNESS));
864+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_SHARPNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SHARPNESS));
865+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_SATURATION, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SATURATION));
866+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_FLASH, reader.getByte(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FLASH));
867+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_INFRARED, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_INFRARED));
868+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_LIGHT, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_LIGHT));
869+
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_MOTION_SENSITIVITY, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_MOTION_SENSITIVITY));
870+
directory.setDouble(ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE) / 1000.0);
871+
directory.setDouble(ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE_AVG, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE_AVG) / 1000.0);
872+
873+
874+
directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, 44, Charsets.UTF_8));
875+
directory.setStringValue(ReconyxHyperFire2MakernoteDirectory.TAG_SERIAL_NUMBER, new StringValue(reader.getBytes(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SERIAL_NUMBER, 28), Charsets.UTF_16LE));
876+
// two unread bytes: the serial number's terminating null
877+
}
878+
879+
880+
801881
private static void processReconyxUltraFireMakernote(@NotNull final ReconyxUltraFireMakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
802882
{
803883
directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_LABEL, reader.getString(makernoteOffset, 9, Charsets.UTF_8));
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2002-2019 Drew Noakes and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* More information about this project is available at:
17+
*
18+
* https://drewnoakes.com/code/exif/
19+
* https://github.com/drewnoakes/metadata-extractor
20+
*/
21+
22+
package com.drew.metadata.exif.makernotes;
23+
24+
import com.drew.lang.annotations.NotNull;
25+
import com.drew.lang.annotations.Nullable;
26+
import com.drew.metadata.StringValue;
27+
import com.drew.metadata.TagDescriptor;
28+
29+
import java.text.DateFormat;
30+
import java.text.DecimalFormat;
31+
import java.text.ParseException;
32+
import java.text.SimpleDateFormat;
33+
34+
import static com.drew.metadata.exif.makernotes.ReconyxHyperFire2MakernoteDirectory.*;
35+
36+
/**
37+
* Provides human-readable string representations of tag values stored in a {@link ReconyxHyperFire2MakernoteDirectory}.
38+
*/
39+
@SuppressWarnings("WeakerAccess")
40+
public class ReconyxHyperFire2MakernoteDescriptor extends TagDescriptor<ReconyxHyperFire2MakernoteDirectory>
41+
{
42+
public ReconyxHyperFire2MakernoteDescriptor(@NotNull ReconyxHyperFire2MakernoteDirectory directory)
43+
{
44+
super(directory);
45+
}
46+
47+
@Override
48+
@Nullable
49+
public String getDescription(int tagType)
50+
{
51+
switch (tagType) {
52+
53+
case TAG_FILE_NUMBER:
54+
return String.format("%d", _directory.getInteger(tagType));
55+
56+
case TAG_DIRECTORY_NUMBER:
57+
return String.format("%d", _directory.getInteger(tagType));
58+
59+
case TAG_FIRMWARE_VERSION:
60+
return _directory.getString(tagType);
61+
62+
case TAG_FIRMWARE_DATE:
63+
String dtFirm = _directory.getString(tagType);
64+
try {
65+
DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
66+
return parser.format(parser.parse(dtFirm));
67+
} catch (ParseException e) {
68+
return null;
69+
}
70+
71+
case TAG_TRIGGER_MODE:
72+
return _directory.getString(tagType);
73+
74+
case TAG_SEQUENCE:
75+
int[] sequence = _directory.getIntArray(tagType);
76+
if (sequence == null)
77+
return null;
78+
return String.format("%d/%d", sequence[0], sequence[1]);
79+
80+
case TAG_EVENT_NUMBER:
81+
return String.format("%d", _directory.getInteger(tagType));
82+
83+
case TAG_DATE_TIME_ORIGINAL:
84+
String date = _directory.getString(tagType);
85+
try {
86+
DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
87+
return parser.format(parser.parse(date));
88+
} catch (ParseException e) {
89+
return null;
90+
}
91+
92+
case TAG_DAY_OF_WEEK:
93+
return getIndexedDescription(tagType, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");
94+
95+
case TAG_MOON_PHASE:
96+
return getIndexedDescription(tagType, "New", "Waxing Crescent", "First Quarter", "Waxing Gibbous", "Full", "Waning Gibbous", "Last Quarter", "Waning Crescent");
97+
98+
case TAG_AMBIENT_TEMPERATURE_FAHRENHEIT:
99+
case TAG_AMBIENT_TEMPERATURE:
100+
return String.format("%d", _directory.getInteger(tagType));
101+
102+
case TAG_CONTRAST:
103+
case TAG_BRIGHTNESS:
104+
case TAG_SHARPNESS:
105+
case TAG_SATURATION:
106+
return String.format("%d", _directory.getInteger(tagType));
107+
108+
case TAG_FLASH:
109+
return getIndexedDescription(tagType, "Off", "On");
110+
111+
// ?????
112+
case TAG_AMBIENT_INFRARED:
113+
case TAG_AMBIENT_LIGHT:
114+
return String.format("%d", _directory.getInteger(tagType));
115+
116+
case TAG_MOTION_SENSITIVITY:
117+
return String.format("%d", _directory.getInteger(tagType));
118+
119+
case TAG_BATTERY_VOLTAGE:
120+
case TAG_BATTERY_VOLTAGE_AVG:
121+
Double value = _directory.getDoubleObject(tagType);
122+
DecimalFormat formatter = new DecimalFormat("0.000");
123+
return value == null ? null : formatter.format(value);
124+
125+
case TAG_BATTERY_TYPE:
126+
return String.format("%d", _directory.getInteger(tagType));
127+
128+
case TAG_USER_LABEL:
129+
return _directory.getString(tagType);
130+
case TAG_SERIAL_NUMBER:
131+
// default is UTF_16LE
132+
StringValue svalue = _directory.getStringValue(tagType);
133+
if(svalue == null)
134+
return null;
135+
return svalue.toString();
136+
137+
default:
138+
return super.getDescription(tagType);
139+
}
140+
}
141+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2002-2019 Drew Noakes and contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* More information about this project is available at:
17+
*
18+
* https://drewnoakes.com/code/exif/
19+
* https://github.com/drewnoakes/metadata-extractor
20+
*/
21+
22+
package com.drew.metadata.exif.makernotes;
23+
24+
import com.drew.lang.annotations.NotNull;
25+
import com.drew.metadata.Directory;
26+
27+
import java.util.HashMap;
28+
29+
/**
30+
* Describes tags specific to Reconyx HyperFire 2 cameras.
31+
*/
32+
@SuppressWarnings("WeakerAccess")
33+
public class ReconyxHyperFire2MakernoteDirectory extends Directory
34+
{
35+
public static final int TAG_FILE_NUMBER = 16;
36+
public static final int TAG_DIRECTORY_NUMBER = 18;
37+
public static final int TAG_FIRMWARE_VERSION = 42;
38+
public static final int TAG_FIRMWARE_DATE = 48;
39+
public static final int TAG_TRIGGER_MODE = 52;
40+
public static final int TAG_SEQUENCE = 54;
41+
public static final int TAG_EVENT_NUMBER = 58;
42+
public static final int TAG_DATE_TIME_ORIGINAL = 62;
43+
public static final int TAG_DAY_OF_WEEK = 74;
44+
public static final int TAG_MOON_PHASE = 76;
45+
public static final int TAG_AMBIENT_TEMPERATURE_FAHRENHEIT = 78;
46+
public static final int TAG_AMBIENT_TEMPERATURE = 80;
47+
public static final int TAG_CONTRAST = 82;
48+
public static final int TAG_BRIGHTNESS = 84;
49+
public static final int TAG_SHARPNESS = 86;
50+
public static final int TAG_SATURATION = 88;
51+
public static final int TAG_FLASH = 90;
52+
public static final int TAG_AMBIENT_INFRARED = 92;
53+
public static final int TAG_AMBIENT_LIGHT = 94;
54+
public static final int TAG_MOTION_SENSITIVITY = 96;
55+
public static final int TAG_BATTERY_VOLTAGE = 98;
56+
public static final int TAG_BATTERY_VOLTAGE_AVG = 100;
57+
public static final int TAG_BATTERY_TYPE = 102;
58+
public static final int TAG_USER_LABEL = 104;
59+
public static final int TAG_SERIAL_NUMBER = 126;
60+
61+
@NotNull
62+
private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();
63+
64+
static
65+
{
66+
_tagNameMap.put(TAG_FILE_NUMBER, "File Number");
67+
_tagNameMap.put(TAG_DIRECTORY_NUMBER, "Directory Number");
68+
_tagNameMap.put(TAG_FIRMWARE_VERSION, "Firmware Version");
69+
_tagNameMap.put(TAG_FIRMWARE_DATE, "Firmware Date");
70+
_tagNameMap.put(TAG_TRIGGER_MODE, "Trigger Mode");
71+
_tagNameMap.put(TAG_SEQUENCE, "Sequence");
72+
_tagNameMap.put(TAG_EVENT_NUMBER, "Event Number");
73+
_tagNameMap.put(TAG_DATE_TIME_ORIGINAL, "Date/Time Original");
74+
_tagNameMap.put(TAG_DAY_OF_WEEK, "DaY of Week");
75+
_tagNameMap.put(TAG_MOON_PHASE, "Moon Phase");
76+
_tagNameMap.put(TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, "Ambient Temperature Fahrenheit");
77+
_tagNameMap.put(TAG_AMBIENT_TEMPERATURE, "Ambient Temperature");
78+
_tagNameMap.put(TAG_CONTRAST, "Contrast");
79+
_tagNameMap.put(TAG_BRIGHTNESS, "Brightness");
80+
_tagNameMap.put(TAG_SHARPNESS, "Sharpness");
81+
_tagNameMap.put(TAG_SATURATION, "Saturation");
82+
_tagNameMap.put(TAG_FLASH, "Flash");
83+
_tagNameMap.put(TAG_AMBIENT_INFRARED, "Ambient Infrared");
84+
_tagNameMap.put(TAG_AMBIENT_LIGHT, "Ambient Light");
85+
_tagNameMap.put(TAG_MOTION_SENSITIVITY, "Motion Sensitivity");
86+
_tagNameMap.put(TAG_BATTERY_VOLTAGE, "Battery Voltage");
87+
_tagNameMap.put(TAG_BATTERY_VOLTAGE_AVG, "Battery Voltage Avg");
88+
_tagNameMap.put(TAG_BATTERY_TYPE, "Battery Type");
89+
_tagNameMap.put(TAG_USER_LABEL, "User Label");
90+
_tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
91+
}
92+
93+
public ReconyxHyperFire2MakernoteDirectory()
94+
{
95+
this.setDescriptor(new ReconyxHyperFire2MakernoteDescriptor(this));
96+
}
97+
98+
@Override
99+
@NotNull
100+
public String getName()
101+
{
102+
return "Reconyx HyperFire2 Makernote";
103+
}
104+
105+
@Override
106+
@NotNull
107+
protected HashMap<Integer, String> getTagNameMap()
108+
{
109+
return _tagNameMap;
110+
}
111+
}

0 commit comments

Comments
 (0)