Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 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
150 changes: 150 additions & 0 deletions MetadataExtractor/Formats/Exif/ExifTiffHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,12 @@ private bool ProcessMakernote(in TiffReaderContext context, int makernoteOffset)
// Always in Motorola byte order
TiffReader.ProcessIfd(this, context.WithByteOrder(isMotorolaByteOrder: true).WithShiftedBaseOffset(makernoteOffset), ifdOffset: 14);
}
else if (headerString.StartsWith("RECONYXHF4K", StringComparison.OrdinalIgnoreCase))
{
var directory = new ReconyxHyperFire4KMakernoteDirectory();
Directories.Add(directory);
ProcessReconyxHyperFire4KMakernote(directory, context.Reader.WithShiftedBaseOffset(makernoteOffset));
}
else if (context.Reader.GetUInt16(makernoteOffset) == ReconyxHyperFireMakernoteDirectory.MakernoteVersion)
{
var directory = new ReconyxHyperFireMakernoteDirectory();
Expand Down Expand Up @@ -1131,5 +1137,149 @@ private static void ProcessReconyxHyperFire2Makernote(ReconyxHyperFire2Makernote
// two unread bytes: terminating null of unicode string
directory.Set(ReconyxHyperFire2MakernoteDirectory.TagSerialNumber, reader.GetString(ReconyxHyperFire2MakernoteDirectory.TagSerialNumber, 28, Encoding.Unicode));
}

private static void ProcessReconyxHyperFire4KMakernote(ReconyxHyperFire4KMakernoteDirectory directory, IndexedReader reader)
{
try
{
// Read the identifier string
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteIdentifier,
reader.GetNullTerminatedString(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteIdentifier, 12));

// Read aggregate makernote structure information
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteVersion,
reader.GetUInt32(ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteVersion));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteSize,
reader.GetUInt32(ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteSize));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoVersion,
reader.GetUInt32(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoVersion));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoSize,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoSize));

// Read camera firmware information
byte cameraFirmwareMajor = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMajor);
byte cameraFirmwareMinor = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMinor);
ushort cameraFirmwareBuildYear = reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildYear);
byte cameraFirmwareBuildMonth = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildMonth);
byte cameraFirmwareBuildDay = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildDay);
byte cameraFirmwareRevision = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareRevision);

directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMajor, cameraFirmwareMajor);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMinor, cameraFirmwareMinor);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildYear, cameraFirmwareBuildYear);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildMonth, cameraFirmwareBuildMonth);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildDay, cameraFirmwareBuildDay);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareRevision, cameraFirmwareRevision);

// Read UIB firmware information
byte uibFirmwareMajor = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMajor);
byte uibFirmwareMinor = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMinor);
ushort uibFirmwareBuildYear = reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildYear);
byte uibFirmwareBuildMonth = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildMonth);
byte uibFirmwareBuildDay = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildDay);
byte uibFirmwareRevision = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareRevision);

directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMajor, uibFirmwareMajor);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMinor, uibFirmwareMinor);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildYear, uibFirmwareBuildYear);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildMonth, uibFirmwareBuildMonth);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildDay, uibFirmwareBuildDay);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareRevision, uibFirmwareRevision);

// Read event information
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagEventType,
reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagEventType));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagEventSequenceNumber,
reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagEventSequenceNumber));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMaxEventSequenceNumber,
reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagMaxEventSequenceNumber));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagEventNumber,
reader.GetUInt32(ReconyxHyperFire4KMakernoteDirectory.TagEventNumber));

// Read timestamp information
byte timeSeconds = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagTimeSeconds);
byte timeMinutes = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagTimeMinutes);
byte timeHours = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagTimeHours);
byte dateDay = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagDateDay);
byte dateMonth = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagDateMonth);
ushort dateYear = reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagDateYear);
byte dateDayOfWeek = reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagDateDayOfWeek);

directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagTimeSeconds, timeSeconds);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagTimeMinutes, timeMinutes);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagTimeHours, timeHours);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagDateDay, dateDay);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagDateMonth, dateMonth);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagDateYear, dateYear);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagDateDayOfWeek, dateDayOfWeek);

// Validate and create DateTime if valid
if (timeSeconds < 60 &&
timeMinutes < 60 &&
timeHours < 24 &&
dateMonth is >= 1 and <= 12 &&
dateDay is >= 1 and <= 31 &&
dateYear >= DateTime.MinValue.Year && dateYear <= DateTime.MaxValue.Year)
{
try
{
// Validate that the DateTime can be constructed, but don't store it to avoid overwriting raw values
var _ = new DateTime(dateYear, dateMonth, dateDay, timeHours, timeMinutes, timeSeconds, DateTimeKind.Unspecified);
}
catch (ArgumentOutOfRangeException)
{
directory.AddError($"Invalid date/time: {dateYear}-{dateMonth:D2}-{dateDay:D2} {timeHours:D2}:{timeMinutes:D2}:{timeSeconds:D2}");
}
}

// Read environmental information
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMoonPhase,
reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagMoonPhase));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagTemperatureFahrenheit,
reader.GetInt16(ReconyxHyperFire4KMakernoteDirectory.TagTemperatureFahrenheit));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagTemperatureCelsius,
reader.GetInt16(ReconyxHyperFire4KMakernoteDirectory.TagTemperatureCelsius));

// Read camera settings
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagContrast,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagContrast));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagBrightness,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagBrightness));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagSharpness,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagSharpness));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagSaturation,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagSaturation));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagFlash,
reader.GetByte(ReconyxHyperFire4KMakernoteDirectory.TagFlash));

// Read sensor information
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagAmbientLightReading,
reader.GetUInt32(ReconyxHyperFire4KMakernoteDirectory.TagAmbientLightReading));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagMotionSensorSensitivity,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagMotionSensorSensitivity));

// Read battery information
ushort batteryVoltageInstantaneous = reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageInstantaneous);
ushort batteryVoltageAverage = reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageAverage);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageInstantaneous, batteryVoltageInstantaneous / 1000.0);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageAverage, batteryVoltageAverage / 1000.0);
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagBatteryType,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagBatteryType));

// Read device identification
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagUserLabel,
reader.GetNullTerminatedString(ReconyxHyperFire4KMakernoteDirectory.TagUserLabel, 51));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagCameraSerialNumber,
reader.GetNullTerminatedString(ReconyxHyperFire4KMakernoteDirectory.TagCameraSerialNumber, 15));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagRECNXDirectoryNumber,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagRECNXDirectoryNumber));
directory.Set(ReconyxHyperFire4KMakernoteDirectory.TagFileNumber,
reader.GetUInt16(ReconyxHyperFire4KMakernoteDirectory.TagFileNumber));
}
catch (Exception ex)
{
directory.AddError("Error processing Reconyx HyperFire 4K makernote data: " + ex.Message);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System.Globalization;

namespace MetadataExtractor.Formats.Exif.Makernotes
{
/// <summary>
/// Provides human-readable string representations of tag values stored in a <see cref="ReconyxHyperFire4KMakernoteDirectory"/>.
/// </summary>
/// <remarks>Reconyx HyperFire 4K uses a fixed makernote block. Tag values are the byte offset within the makernote structure.</remarks>
public sealed class ReconyxHyperFire4KMakernoteDescriptor(ReconyxHyperFire4KMakernoteDirectory directory)
: TagDescriptor<ReconyxHyperFire4KMakernoteDirectory>(directory)
{
public override string? GetDescription(int tagType)
{
switch (tagType)
{
case ReconyxHyperFire4KMakernoteDirectory.TagMakernoteIdentifier:
return Directory.GetString(tagType);

case ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteVersion:
case ReconyxHyperFire4KMakernoteDirectory.TagAggregateMakernoteSize:
case ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoVersion:
return Directory.GetUInt32(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagMakernoteInfoSize:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMajor:
case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareMinor:
case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildMonth:
case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildDay:
return Directory.GetByte(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareBuildYear:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagCameraFirmwareRevision:
var cameraRevision = Directory.GetByte(tagType);
return cameraRevision != 0 ? ((char)cameraRevision).ToString() : "";

case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMajor:
case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareMinor:
case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildMonth:
case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildDay:
return Directory.GetByte(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareBuildYear:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagUIBFirmwareRevision:
var uibRevision = Directory.GetByte(tagType);
return uibRevision != 0 ? ((char)uibRevision).ToString() : "";

case ReconyxHyperFire4KMakernoteDirectory.TagEventType:
var eventType = Directory.GetByte(tagType);
return eventType != 0 ? ((char)eventType).ToString() : "";

case ReconyxHyperFire4KMakernoteDirectory.TagEventSequenceNumber:
case ReconyxHyperFire4KMakernoteDirectory.TagMaxEventSequenceNumber:
return Directory.GetByte(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagEventNumber:
return Directory.GetUInt32(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagTimeSeconds:
case ReconyxHyperFire4KMakernoteDirectory.TagTimeMinutes:
case ReconyxHyperFire4KMakernoteDirectory.TagTimeHours:
case ReconyxHyperFire4KMakernoteDirectory.TagDateDay:
case ReconyxHyperFire4KMakernoteDirectory.TagDateMonth:
return Directory.GetByte(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagDateYear:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagDateDayOfWeek:
return GetIndexedDescription(tagType, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");

case ReconyxHyperFire4KMakernoteDirectory.TagMoonPhase:
return GetIndexedDescription(tagType, "New", "Waxing Crescent", "First Quarter", "Waxing Gibbous", "Full", "Waning Gibbous", "Last Quarter", "Waning Crescent");

case ReconyxHyperFire4KMakernoteDirectory.TagTemperatureFahrenheit:
return $"{Directory.GetInt16(tagType)}°F";

case ReconyxHyperFire4KMakernoteDirectory.TagTemperatureCelsius:
return $"{Directory.GetInt16(tagType)}°C";

case ReconyxHyperFire4KMakernoteDirectory.TagContrast:
case ReconyxHyperFire4KMakernoteDirectory.TagBrightness:
case ReconyxHyperFire4KMakernoteDirectory.TagSharpness:
case ReconyxHyperFire4KMakernoteDirectory.TagSaturation:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagFlash:
return GetIndexedDescription(tagType, "Off", "On");

case ReconyxHyperFire4KMakernoteDirectory.TagAmbientLightReading:
return Directory.GetUInt32(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagMotionSensorSensitivity:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageInstantaneous:
case ReconyxHyperFire4KMakernoteDirectory.TagBatteryVoltageAverage:
return Directory.GetDouble(tagType).ToString("0.000");

case ReconyxHyperFire4KMakernoteDirectory.TagBatteryType:
return Directory.GetUInt16(tagType).ToString();

case ReconyxHyperFire4KMakernoteDirectory.TagUserLabel:
case ReconyxHyperFire4KMakernoteDirectory.TagCameraSerialNumber:
return Directory.GetString(tagType);

case ReconyxHyperFire4KMakernoteDirectory.TagRECNXDirectoryNumber:
case ReconyxHyperFire4KMakernoteDirectory.TagFileNumber:
return Directory.GetUInt16(tagType).ToString();

default:
return base.GetDescription(tagType);
}
}
}
}
Loading