Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconyx HyperFire 2 Support #550

Merged
merged 2 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Source/com/drew/metadata/exif/ExifTiffHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,10 @@ private boolean processMakernote(final int makernoteOffset,
ReconyxUltraFireMakernoteDirectory directory = new ReconyxUltraFireMakernoteDirectory();
_metadata.addDirectory(directory);
processReconyxUltraFireMakernote(directory, makernoteOffset, reader);
} else if (firstNineChars.equalsIgnoreCase("RECONYXH2")) {
ReconyxHyperFire2MakernoteDirectory directory = new ReconyxHyperFire2MakernoteDirectory();
_metadata.addDirectory(directory);
processReconyxHyperFire2Makernote(directory, makernoteOffset, reader);
} else if ("SAMSUNG".equalsIgnoreCase(cameraMake)) {
// Only handles Type2 notes correctly. Others aren't implemented, and it's complex to determine which ones to use
pushDirectory(SamsungType2MakernoteDirectory.class);
Expand Down Expand Up @@ -798,6 +802,82 @@ private static void processReconyxHyperFireMakernote(@NotNull final ReconyxHyper
directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, 44, Charsets.UTF_8));
}

private static void processReconyxHyperFire2Makernote(@NotNull final ReconyxHyperFire2MakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
{

int major = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION);
int minor = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 2);
int revision = reader.getUInt16(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_FIRMWARE_VERSION + 4);
String buildYear = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 6));
String buildDate = String.format("%04X", reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION + 8));
String buildYearAndDate = buildYear + buildDate;
Integer build;
try {
build = Integer.parseInt(buildYearAndDate);
} catch (NumberFormatException e) {
build = null;
}

if (build != null) {
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d.%s", major, minor, revision, build));
} else {
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_FIRMWARE_VERSION, String.format("%d.%d.%d", major, minor, revision));
directory.addError("Error processing Reconyx HyperFire 2 makernote data: build '" + buildYearAndDate + "' is not in the expected format and will be omitted from Firmware Version.");
}

directory.setIntArray(ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE,
new int[]
{
reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE),
reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SEQUENCE + 2)
});

int eventNumberHigh = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER);
int eventNumberLow = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER + 2);
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_EVENT_NUMBER, (eventNumberHigh << 16) + eventNumberLow);

int seconds = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL);
int minutes = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 2);
int hour = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 4);
int month = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 6);
int day = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 8);
int year = reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL + 10);

if ((seconds >= 0 && seconds < 60) &&
(minutes >= 0 && minutes < 60) &&
(hour >= 0 && hour < 24) &&
(month >= 1 && month < 13) &&
(day >= 1 && day < 32) &&
(year >= 1 && year <= 9999)) {
directory.setString(ReconyxHyperFire2MakernoteDirectory.TAG_DATE_TIME_ORIGINAL,
String.format("%4d:%2d:%2d %2d:%2d:%2d", year, month, day, hour, minutes, seconds));
} else {
directory.addError("Error processing Reconyx HyperFire 2 makernote data: Date/Time Original " + year + "-" + month + "-" + day + " " + hour + ":" + minutes + ":" + seconds + " is not a valid date/time.");
}

directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_MOON_PHASE, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_MOON_PHASE));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, reader.getInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE_FAHRENHEIT));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE, reader.getInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_TEMPERATURE));

directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_CONTRAST, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_CONTRAST));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_BRIGHTNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BRIGHTNESS));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_SHARPNESS, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SHARPNESS));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_SATURATION, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SATURATION));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_FLASH, reader.getByte(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_FLASH));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_INFRARED, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_INFRARED));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_LIGHT, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_AMBIENT_LIGHT));
directory.setInt(ReconyxHyperFire2MakernoteDirectory.TAG_MOTION_SENSITIVITY, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_MOTION_SENSITIVITY));
directory.setDouble(ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE) / 1000.0);
directory.setDouble(ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE_AVG, reader.getUInt16(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_BATTERY_VOLTAGE_AVG) / 1000.0);


directory.setString(ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, reader.getNullTerminatedString(makernoteOffset + ReconyxHyperFireMakernoteDirectory.TAG_USER_LABEL, 44, Charsets.UTF_8));
directory.setStringValue(ReconyxHyperFire2MakernoteDirectory.TAG_SERIAL_NUMBER, new StringValue(reader.getBytes(makernoteOffset + ReconyxHyperFire2MakernoteDirectory.TAG_SERIAL_NUMBER, 28), Charsets.UTF_16LE));
// two unread bytes: the serial number's terminating null
}



private static void processReconyxUltraFireMakernote(@NotNull final ReconyxUltraFireMakernoteDirectory directory, final int makernoteOffset, @NotNull final RandomAccessReader reader) throws IOException
{
directory.setString(ReconyxUltraFireMakernoteDirectory.TAG_LABEL, reader.getString(makernoteOffset, 9, Charsets.UTF_8));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/

package com.drew.metadata.exif.makernotes;

import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.StringValue;
import com.drew.metadata.TagDescriptor;

import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import static com.drew.metadata.exif.makernotes.ReconyxHyperFire2MakernoteDirectory.*;

/**
* Provides human-readable string representations of tag values stored in a {@link ReconyxHyperFire2MakernoteDirectory}.
*/
@SuppressWarnings("WeakerAccess")
public class ReconyxHyperFire2MakernoteDescriptor extends TagDescriptor<ReconyxHyperFire2MakernoteDirectory>
{
public ReconyxHyperFire2MakernoteDescriptor(@NotNull ReconyxHyperFire2MakernoteDirectory directory)
{
super(directory);
}

@Override
@Nullable
public String getDescription(int tagType)
{
switch (tagType) {

case TAG_FILE_NUMBER:
return String.format("%d", _directory.getInteger(tagType));

case TAG_DIRECTORY_NUMBER:
return String.format("%d", _directory.getInteger(tagType));

case TAG_FIRMWARE_VERSION:
return _directory.getString(tagType);

case TAG_FIRMWARE_DATE:
String dtFirm = _directory.getString(tagType);
try {
DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
return parser.format(parser.parse(dtFirm));
} catch (ParseException e) {
return null;
}

case TAG_TRIGGER_MODE:
return _directory.getString(tagType);

case TAG_SEQUENCE:
int[] sequence = _directory.getIntArray(tagType);
if (sequence == null)
return null;
return String.format("%d/%d", sequence[0], sequence[1]);

case TAG_EVENT_NUMBER:
return String.format("%d", _directory.getInteger(tagType));

case TAG_DATE_TIME_ORIGINAL:
String date = _directory.getString(tagType);
try {
DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
return parser.format(parser.parse(date));
} catch (ParseException e) {
return null;
}

case TAG_DAY_OF_WEEK:
return getIndexedDescription(tagType, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");

case TAG_MOON_PHASE:
return getIndexedDescription(tagType, "New", "Waxing Crescent", "First Quarter", "Waxing Gibbous", "Full", "Waning Gibbous", "Last Quarter", "Waning Crescent");

case TAG_AMBIENT_TEMPERATURE_FAHRENHEIT:
case TAG_AMBIENT_TEMPERATURE:
return String.format("%d", _directory.getInteger(tagType));

case TAG_CONTRAST:
case TAG_BRIGHTNESS:
case TAG_SHARPNESS:
case TAG_SATURATION:
return String.format("%d", _directory.getInteger(tagType));

case TAG_FLASH:
return getIndexedDescription(tagType, "Off", "On");

// ?????
case TAG_AMBIENT_INFRARED:
case TAG_AMBIENT_LIGHT:
return String.format("%d", _directory.getInteger(tagType));

case TAG_MOTION_SENSITIVITY:
return String.format("%d", _directory.getInteger(tagType));

case TAG_BATTERY_VOLTAGE:
case TAG_BATTERY_VOLTAGE_AVG:
Double value = _directory.getDoubleObject(tagType);
DecimalFormat formatter = new DecimalFormat("0.000");
return value == null ? null : formatter.format(value);

case TAG_BATTERY_TYPE:
return String.format("%d", _directory.getInteger(tagType));

case TAG_USER_LABEL:
return _directory.getString(tagType);
case TAG_SERIAL_NUMBER:
// default is UTF_16LE
StringValue svalue = _directory.getStringValue(tagType);
if(svalue == null)
return null;
return svalue.toString();

default:
return super.getDescription(tagType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/

package com.drew.metadata.exif.makernotes;

import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Directory;

import java.util.HashMap;

/**
* Describes tags specific to Reconyx HyperFire 2 cameras.
*/
@SuppressWarnings("WeakerAccess")
public class ReconyxHyperFire2MakernoteDirectory extends Directory
{
public static final int TAG_FILE_NUMBER = 16;
public static final int TAG_DIRECTORY_NUMBER = 18;
public static final int TAG_FIRMWARE_VERSION = 42;
public static final int TAG_FIRMWARE_DATE = 48;
public static final int TAG_TRIGGER_MODE = 52;
public static final int TAG_SEQUENCE = 54;
public static final int TAG_EVENT_NUMBER = 58;
public static final int TAG_DATE_TIME_ORIGINAL = 62;
public static final int TAG_DAY_OF_WEEK = 74;
public static final int TAG_MOON_PHASE = 76;
public static final int TAG_AMBIENT_TEMPERATURE_FAHRENHEIT = 78;
public static final int TAG_AMBIENT_TEMPERATURE = 80;
public static final int TAG_CONTRAST = 82;
public static final int TAG_BRIGHTNESS = 84;
public static final int TAG_SHARPNESS = 86;
public static final int TAG_SATURATION = 88;
public static final int TAG_FLASH = 90;
public static final int TAG_AMBIENT_INFRARED = 92;
public static final int TAG_AMBIENT_LIGHT = 94;
public static final int TAG_MOTION_SENSITIVITY = 96;
public static final int TAG_BATTERY_VOLTAGE = 98;
public static final int TAG_BATTERY_VOLTAGE_AVG = 100;
public static final int TAG_BATTERY_TYPE = 102;
public static final int TAG_USER_LABEL = 104;
public static final int TAG_SERIAL_NUMBER = 126;

@NotNull
private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();

static
{
_tagNameMap.put(TAG_FILE_NUMBER, "File Number");
_tagNameMap.put(TAG_DIRECTORY_NUMBER, "Directory Number");
_tagNameMap.put(TAG_FIRMWARE_VERSION, "Firmware Version");
_tagNameMap.put(TAG_FIRMWARE_DATE, "Firmware Date");
_tagNameMap.put(TAG_TRIGGER_MODE, "Trigger Mode");
_tagNameMap.put(TAG_SEQUENCE, "Sequence");
_tagNameMap.put(TAG_EVENT_NUMBER, "Event Number");
_tagNameMap.put(TAG_DATE_TIME_ORIGINAL, "Date/Time Original");
_tagNameMap.put(TAG_DAY_OF_WEEK, "DaY of Week");
_tagNameMap.put(TAG_MOON_PHASE, "Moon Phase");
_tagNameMap.put(TAG_AMBIENT_TEMPERATURE_FAHRENHEIT, "Ambient Temperature Fahrenheit");
_tagNameMap.put(TAG_AMBIENT_TEMPERATURE, "Ambient Temperature");
_tagNameMap.put(TAG_CONTRAST, "Contrast");
_tagNameMap.put(TAG_BRIGHTNESS, "Brightness");
_tagNameMap.put(TAG_SHARPNESS, "Sharpness");
_tagNameMap.put(TAG_SATURATION, "Saturation");
_tagNameMap.put(TAG_FLASH, "Flash");
_tagNameMap.put(TAG_AMBIENT_INFRARED, "Ambient Infrared");
_tagNameMap.put(TAG_AMBIENT_LIGHT, "Ambient Light");
_tagNameMap.put(TAG_MOTION_SENSITIVITY, "Motion Sensitivity");
_tagNameMap.put(TAG_BATTERY_VOLTAGE, "Battery Voltage");
_tagNameMap.put(TAG_BATTERY_VOLTAGE_AVG, "Battery Voltage Avg");
_tagNameMap.put(TAG_BATTERY_TYPE, "Battery Type");
_tagNameMap.put(TAG_USER_LABEL, "User Label");
_tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number");
}

public ReconyxHyperFire2MakernoteDirectory()
{
this.setDescriptor(new ReconyxHyperFire2MakernoteDescriptor(this));
}

@Override
@NotNull
public String getName()
{
return "Reconyx HyperFire2 Makernote";
}

@Override
@NotNull
protected HashMap<Integer, String> getTagNameMap()
{
return _tagNameMap;
}
}