Browse Source

Initial Metadata parsing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
bc9755473e
  1. 209
      src/ImageSharp/Formats/Heic/FourCharacterCode.cs
  2. 26
      src/ImageSharp/Formats/Heic/HeicBoxType.cs
  3. 2
      src/ImageSharp/Formats/Heic/HeicConstants.cs
  4. 363
      src/ImageSharp/Formats/Heic/HeicDecoderCore.cs
  5. 10
      src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs
  6. 32
      src/ImageSharp/Formats/Heic/HeicItem.cs
  7. 23
      src/ImageSharp/Formats/Heic/HeicItemPropertyType.cs
  8. 22
      src/ImageSharp/Formats/Heic/HeicMetaSubBoxType.cs
  9. 52
      src/ImageSharp/Formats/Heic/HeicNalUnitType.cs
  10. 9
      src/ImageSharp/Formats/Heic/Readme.md

209
src/ImageSharp/Formats/Heic/FourCharacterCode.cs

@ -0,0 +1,209 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Provides constants for 4 Character codes used in HEIC images.
/// </summary>
public static class FourCharacterCode
{
// TODO: Create T4 template for this file
/// <summary>
/// File Type
/// </summary>
public const uint ftyp = 0x66747970U,
/// <summary>
/// Metadata container
/// </summary>
public const uint meta = 0x6D657461U,
/// <summary>
/// Media Data
/// </summary>
public const uint mdat = 0x6D646174U,
/// <summary>
/// Item Information Entry
/// </summary>
public const uint infe = 0x696E6665U,
/// <summary>
/// Item Data
/// </summary>
public const uint idat = 0x69646174U,
/// <summary>
/// Item Location
/// </summary>
public const uint iloc = 0x696C6F63U,
/// <summary>
/// EXIF metadata
/// </summary>
public const uint Exif = 0x45786966U,
/// <summary>
/// Data Reference
/// </summary>
public const uint dref = 0x64726566U,
/// <summary>
/// Primary Item
/// </summary>
public const uint pitm = 0x7069746DU,
/// <summary>
/// Item Spatial Extent
/// </summary>
public const uint ispe = 0x69737064U,
/// <summary>
/// Alternative text
/// </summary>
public const uint altt = 0, // 'altt'
/// <summary>
/// Colour information
/// </summary>
public const uint colr = 0, // 'colr'
/// <summary>
/// HVC configuration
/// </summary>
public const uint hvcC = 0, // 'hvcC'
/// <summary>
/// Image Mirror
/// </summary>
public const uint imir = 0, // 'imir'
/// <summary>
/// Image Rotation
/// </summary>
public const uint irot = 0, // 'irot'
/// <summary>
/// Image Scaling
/// </summary>
public const uint iscl = 0, // 'iscl'
/// <summary>
/// Pixel Aspect Ration
/// </summary>
public const uint pasp = 0, // 'pasp'
/// <summary>
/// Pixel Information
/// </summary>
public const uint pixi = 0x70697869U,
/// <summary>
/// Reference Location
/// </summary>
public const uint rloc = 0, // 'rloc
/// <summary>
/// User Description
/// </summary>
public const uint udes = 0, // 'udes'
/// <summary>
/// Item Property Container
/// </summary>
public const uint ipco = 0,
/// <summary>
/// Item Property Association
/// </summary>
public const uint ipma = 0,
/// <summary>
/// High Efficient Image Coding
/// </summary>
public const uint heic = 0,
/// <summary>
/// High Efficiency Coding tile
/// </summary>
public const uint hvc1 = 0,
/// <summary>
/// Data Information
/// </summary>
public const uint dinf = 0,
/// <summary>
/// Group list
/// </summary>
public const uint grpl = 0,
/// <summary>
/// Handler
/// </summary>
public const uint hdlr = 0,
/// <summary>
/// Item Data
/// </summary>
public const uint idat = 0, // 'idat'
/// <summary>
/// Item Information
/// </summary>
public const uint iinf = 0, // 'iinf'
/// <summary>
/// Item Property
/// </summary>
public const uint iprp = 0, // 'iprp'
/// <summary>
/// Item Protection
/// </summary>
public const uint ipro = 0, // 'ipro'
/// <summary>
/// Item Reference
/// </summary>
public const uint iref = 0, // 'iref'
/// <summary>
/// Grid
/// </summary>
public const uint grid = 0, // 'grid'
/// <summary>
/// Derived Image
/// </summary>
public const uint dimg = 0, // 'dimg'
/// <summary>
/// Thumbnail
/// </summary>
public const uint thmb = 0, // 'thmb'
/// <summary>
/// Content Description
/// </summary>
public const uint cdsc = 0, // 'cdsc'
public static uint Parse(string code)
{
if (code.Length != 4)
{
throw new ImageFormatException();
}
Span<byte> span = Encoding.UTF8.GetBytes(code);
return BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
public static string ToString(uint fourcc)
{
Span<byte> span = stackalloc new byte[4];
BinaryPrimitives.WriteUInt32BigEndian(buffer, fourcc);
return Encoding.UTF8.GetString(span);
}
}

26
src/ImageSharp/Formats/Heic/HeicBoxType.cs

@ -0,0 +1,26 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Provides enumeration of supported ISO Base Format Box Types for HEIC.
/// </summary>
public enum HeicBoxType : uint
{
FileType = FourCharacterCode.ftyp,
Meta = FourCharacterCode.meta,
MediaData = FourCharacterCode.mdat,
ItemInfo = FourCharacterCode.infe
ItemData = FourCharacterCode.idat,
ItemLocation = FourCharacterCode.iloc,
Exif = FourCharacterCode.Exif,
ItemPropertyAssociation = FourCharacterCode.ipma,
DataReference = FourCharacterCode.dref,
PrimaryItemReference = FourCharacterCode.pitm,
ImageSpatialExtentsProperty = FourCharacterCode.ispe,
// Possible box types outside of HEIC images:
Movie = FourCharacterCode.moov,
Track = FourCharacterCode.trak,
}

2
src/ImageSharp/Formats/Heic/HeicConstants.cs

@ -8,6 +8,8 @@ namespace SixLabors.ImageSharp.Formats.Heic;
/// </summary>
internal static class HeicConstants
{
public const uint HeicBrand = FourCharacterCode.heic;
/// <summary>
/// The list of mimetypes that equate to a HEIC.
/// </summary>

363
src/ImageSharp/Formats/Heic/HeicDecoderCore.cs

@ -25,6 +25,10 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
/// </summary>
private ImageMetadata? metadata;
private List<HeicItem> items;
private List<HeicItemLink> itemLinks;
/// <summary>
/// Initializes a new instance of the <see cref="HeicDecoderCore" /> class.
/// </summary>
@ -45,28 +49,375 @@ internal sealed class HeicDecoderCore : IImageDecoderInternals
public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{
this.ProcessHeader(stream);
if (!this.CheckFileTypeBox(stream))
{
throw new InvalidImageFormatException();
}
while (!stream.Eof)
{
var length = ReadBoxHeader(stream, out var boxType);
switch (boxType)
{
case HeicBoxType.Meta:
ParseMetadata(stream, length);
break;
case HeicBoxType.MediaData:
ParseMediaData(stream, length);
break;
default:
throw new ImageFormatException($"Unknown box type of '{FourCharacterCode.ToString(boxType)}'");
}
}
var image = new Image<TPixel>(this.configuration, this.pixelSize.Width, this.pixelSize.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
// TODO: Implement
return image;
}
/// <inheritdoc/>
public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
{
this.ProcessHeader(stream);
this.CheckFileTypeBox(stream);
// TODO: Implement
while (!stream.Eof)
{
var length = ReadBoxHeader(stream, out var boxType);
var buffer = new byte[length];
stream.Read(buffer);
switch (boxType)
{
case HeicBoxType.Metadata:
ParseMetadata(buffer);
break;
default:
// Silently skip all other box types.
break;
}
}
return new ImageInfo(new PixelTypeInfo(bitsPerPixel), new(this.pixelSize.Width, this.pixelSize.Height), this.metadata);
}
private void ReadNals(BufferedReadStream stream) {
private bool CheckFileTypeBox(BufferedReadStream stream)
{
var boxLength = ReadBoxHeader(stream, out var boxType);
Span<byte> buffer = stackalloc new byte[boxLength];
stream.Read(buffer);
var majorBrand = BinaryPrimitives.ReadUInt32BigEndian(buf);
// TODO: Interpret minorVersion and compatible brands.
return boxTypepe == HeicBoxType.FileType && majorBrand == HeicConstants.HeicBrand;
}
private ulong ReadBoxHeader(BufferedReadStream stream, out uint boxType)
{
// Read 4 bytes of length of box
Span<byte> buf = stackalloc new byte[8];
stream.Read(buf);
ulong boxSize = BinaryPrimitives.ReadUInt32BigEndian(buf);
ulong headerSize = 8;
// Read 4 bytes of box type
boxType = BinaryPrimitives.ReadUInt32BigEndian(buf.Slice(4));
if (boxSize == 1)
{
stream.Read(buf);
boxSize = BinaryPrimitives.ReadUInt64BigEndian(buf);
headerSize += 8UL;
}
return boxSize - headerSize;
}
private uint ParseBoxHeader(Span<byte> buffer, out ulong length, out uint boxType)
{
ulong boxSize = BinaryPrimitives.ReadUInt32BigEndian(buffer);
ulong bytesRead = 4;
boxType = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
if (boxSize == 1)
{
boxSize = BinaryPrimitives.ReadUInt64BigEndian(buffer.Slice(bytesRead));
bytesRead += 8;
}
length = boxSize - bytesRead;
return bytesRead;
}
private void ParseMetadata(BufferedReadStream stream, uint boxLength)
{
var endPosition = stream.Position + boxLength;
while (stream.Position < endPosition)
{
var length = ReadBoxHeader(stream, out var boxType);
switch (boxType)
{
case HeicMetaSubBoxType.ItemProperty:
ParseItemPropertyContainer(stream, length);
break;
case HeicMetaSubBoxType.ItemInfo:
ParseItemInfo(stream, length);
break;
case HeicMetaSubBoxType.ItemReference:
ParseItemReference(stream, length);
break;
case HeicMetaSubBoxType.DataInformation:
case HeicMetaSubBoxType.GroupsList:
case HeicMetaSubBoxType.Handler:
case HeicMetaSubBoxType.ItemData:
case HeicMetaSubBoxType.ItemLocation:
case HeicMetaSubBoxType.ItemProtection:
case HeicMetaSubBoxType.PrimaryItem:
// TODO: Implement
break;
default:
throw new ImageFormatException($"Unknown metadata box type of '{FourCharacterCode.ToString(boxType)}'");
}
}
}
private void ParseItemInfo(BufferedReadStream stream, uint boxLength)
{
Span<byte> buffer = new byte[length];
stream.Read(buffer);
uint entryCount;
int bytesRead = 0;
if (buffer[bytesRead] == 0)
{
bytesRead += 4;
entryCount = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
}
else
{
bytesRead += 4;
entryCount = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
}
for(uint i = 0; i < entryCount; i++)
{
bytesRead += ParseBoxHeader(out var subBoxLength, out var boxType);
ParseItemInfoEntry(buffer.Slice(bytesRead, subBoxLength));
bytesRead += subBoxLength;
}
}
private void ParseItemInfoEntry(Span<byte> buffer, uint boxLength)
{
int bytesRead = 0;
var version = buffer[bytesRead];
bytesRead += 4;
var item = new HeicItem();
if (version == 0 || version == 1)
{
item.Id = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
item.ProtectionIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
item.Name = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.Name.Length + 1;
item.ContentType = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.ContentType.Length + 1;
// Optional field.
if (bytesRead < boxLength)
{
item.ContentEncoding = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.ContentEncoding.Length + 1;
}
}
if (version == 1)
{
// Optional fields.
if (bytesRead < boxLength)
{
item.ExtensionType = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
}
if (bytesRead < boxLength)
{
// TODO: Parse item.Extension
}
}
if (version >= 2)
{
if (getVersion() == 2)
{
item.Id = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
}
else if (getVersion() == 3)
{
item.Id = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
}
item.ProtectionIndex = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
item.Type = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
item.Name = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.Name.Length + 1;
if (item.Type == "mime")
{
item.ContentType = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.ContentType.Length + 1;
// Optional field.
if (bytesRead < boxLength)
{
item.ContentEncoding = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.ContentEncoding.Length + 1;
}
}
else if (item.Type == "uri ")
{
item.UriType = ReadNullTerminatedString(buffer.Slice(bytesRead));
bytesRead += item.ContentEncoding.Length + 1;
}
}
}
private void ParseItemReference(BufferedReadStream stream, uint boxLength)
{
Span<byte> buffer = new byte[length];
stream.Read(buffer);
int bytesRead = 0;
bool largeIds = buffer[bytesRead] != 0;
bytesRead += 4;
while(bytesRead < boxLength)
{
ParseBoxHeader(buffer.Slice(bytesRead), out var subBoxLength, out var linkType);
var link = new HeicItemLink();
link.Type = linkType;
if (largeIds)
{
link.Source = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
}
else
{
link.Source = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
}
var count = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
for(uint i = 0; i < count; i++)
{
uint destId;
if (largeIds)
{
destId = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(bytesRead));
bytesRead += 4;
}
else
{
destId = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(bytesRead));
bytesRead += 2;
}
link.Destinations.Add(destId);
}
itemLinks.Add(link);
}
}
private void ParseItemPropertyContainer(BufferedReadStream stream, uint boxLength)
{
var containerLength = ReadBoxHeader(stream, out var containerType);
if (containerType == FourCharacterCode.ipco)
{
// Parse Item Property Container, which is just an array of preperty boxes.
var endPosition = stream.Position + containerLength;
while (stream.Position < endPosition)
{
var length = ReadBoxHeader(stream, out var boxType);
switch (boxType)
{
case HeicItemPropertyType.ImageSpatialExtents:
// Length should be 12.
Span<byte> buffer = stackalloc new byte[length];
stream.Read(buffer);
// Skip over version (8 bits) and flags (24 bits).
var width = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(4));
var height = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(8));
break;
case HeicItemPropertyType.PixelAspectRatio:
// Length should be 8.
Span<byte> buffer = stackalloc new byte[length];
stream.Read(buffer);
var horizontalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer);
var verticalSpacing = BinaryPrimitives.ReadUInt32BigEndian(buffer.Slice(4));
break;
case HeicItemPropertyType.PixelInformation:
Span<byte> buffer = stackalloc new byte[length];
stream.Read(buffer);
// Skip over version (8 bits) and flags (24 bits).
var channelCount = buffer[4];
int offset = 5;
int bitsPerPixel = 0;
for (int i = 0; i < channelCount; i++)
{
bitsPerPixel += buffer[i];
}
break;
case HeicItemPropertyType.AcessibilityText:
case HeicItemPropertyType.ImageMirror:
case HeicItemPropertyType.ImageRotation:
case HeicItemPropertyType.ImageScaling:
case HeicItemPropertyType.RelativeLocation:
case HeicItemPropertyType.UserDescription;
// TODO: Implement
break;
default:
throw new ImageFormatException($"Unknown item property box type of '{FourCharacterCode.ToString(boxType)}'");
}
}
}
else if (containerType == FourCharacterCode.ipma)
{
// Parse Item Property Association
}
}
private void ParseMediaData(BufferedReadStream stream, uint boxLength)
{
// TODO: Implement
}
/// <summary>
/// Forwards the stream to just past the Start of NAL marker.
/// </summary>
private void FindStartOfNal(BufferedReadStream stream)
{
uint i = stream.Position;
uint length = 0;
var dataLength = stream.Length;
while (i < streamLength)
{
var current = stream.ReadByte();
if (current == 0)
{
length++;
}
else if (length > 1 && current == 1)
{
// Found the marker !
//length++;
break;
}
else
{
// False alarm, resetting...
length = 0;
}
i++;
}
}
}

10
src/ImageSharp/Formats/Heic/HeicImageFormatDetector.cs

@ -10,6 +10,10 @@ namespace SixLabors.ImageSharp.Formats.Heic;
/// </summary>
public sealed class HeicImageFormatDetector : IImageFormatDetector
{
/// <inheritdoc/>
int HeaderSize => 12;
/// <inheritdoc/>
public bool TryDetectFormat(ReadOnlySpan<byte> header, [NotNullWhen(true)] out IImageFormat? format)
{
@ -19,8 +23,8 @@ public sealed class HeicImageFormatDetector : IImageFormatDetector
private static bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{
// TODO: Implement
return false;
return
BinaryPrimitives.ReadUInt32BigEndian(header.Slice(4)) == FourCharacterCode.ftyp &&
BinaryPrimitives.ReadUInt32BigEndian(header.Slice(8)) == FourCharacterCode.heic
}
}

32
src/ImageSharp/Formats/Heic/HeicItem.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
public enum HeicItemType
{
Hvc1,
Grid,
Exif
}
public class HeicItemLink
{
public uint Type;
public HeicItem Source;
public List<HeicItem> Destinations = new List<HeicItem>();
}
/// <summary>
/// Provides definition for a HEIC Item.
/// </summary>
public class HeicItem
{
public uint Id;
public HeicItemType type;
public string Name;
public string ContentType;
public string ContentEncoding;
public uint ExtensionType;
public string UriType;
}

23
src/ImageSharp/Formats/Heic/HeicItemPropertyType.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Provides enumeration of supported Item Property Types for HEIC.
/// </summary>
public enum HeicItemPropertyType : uint
{
Invalid = 0,
AcessibilityText = FourCharacterCode.altt,
Colour = FourCharacterCode.colr,
HvcConfiguration = FourCharacterCode.hvcC,
ImageMirror = FourCharacterCode.imir,
ImageRotation = FourCharacterCode.irot,
ImageScaling = FourCharacterCode.iscl,
ImageSpatialExtents = FourCharacterCode.ispe,
PixelAspectRatio = FourCharacterCode.pasp,
PixelInformation = FourCharacterCode.pixi,
RelativeLocation = FourCharacterCode.rloc,
UserDescription = FourCharacterCode.udes,
}

22
src/ImageSharp/Formats/Heic/HeicMetaSubBoxType.cs

@ -0,0 +1,22 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Provides enumeration of supported sub type boxes within the 'meta' box for HEIC.
/// </summary>
public enum HeicMetaSubBoxType : uint
{
Invalid = 0,
DataInformation = 0, // 'dinf'
GroupsList = 0, // 'grpl'
Handler = 0, // 'hdlr'
ItemData = 0, // 'idat'
ItemInfo = 0, // 'iinf'
ItemLocation = 0, // 'iloc'
ItemProperty = 0, // 'iprp'
ItemProtection = 0, // 'ipro'
ItemReference = 0, // 'iref'
PrimaryItem = 0, // 'pitm'
}

52
src/ImageSharp/Formats/Heic/HeicNalUnitType.cs

@ -6,33 +6,31 @@ namespace SixLabors.ImageSharp.Formats.Heic;
/// <summary>
/// Provides enumeration of supported x265's LAN Unit Types.
/// </summary>
public enum HeicNalUnitType : uint
public enum HeicNalUnitType : byte
{
NAL_UNIT_CODED_SLICE_TRAIL_N = 0,
NAL_UNIT_CODED_SLICE_TRAIL_R,
NAL_UNIT_CODED_SLICE_TSA_N,
NAL_UNIT_CODED_SLICE_TSA_R,
NAL_UNIT_CODED_SLICE_STSA_N,
NAL_UNIT_CODED_SLICE_STSA_R,
NAL_UNIT_CODED_SLICE_RADL_N,
NAL_UNIT_CODED_SLICE_RADL_R,
NAL_UNIT_CODED_SLICE_RASL_N,
NAL_UNIT_CODED_SLICE_RASL_R,
NAL_UNIT_CODED_SLICE_BLA_W_LP = 16,
NAL_UNIT_CODED_SLICE_BLA_W_RADL,
NAL_UNIT_CODED_SLICE_BLA_N_LP,
NAL_UNIT_CODED_SLICE_IDR_W_RADL,
NAL_UNIT_CODED_SLICE_IDR_N_LP,
NAL_UNIT_CODED_SLICE_CRA,
NAL_UNIT_VPS = 32,
NAL_UNIT_SPS,
NAL_UNIT_PPS,
NAL_UNIT_ACCESS_UNIT_DELIMITER,
NAL_UNIT_EOS,
NAL_UNIT_EOB,
NAL_UNIT_FILLER_DATA,
NAL_UNIT_PREFIX_SEI,
NAL_UNIT_SUFFIX_SEI,
Unspecified = 62,
CODED_SLICE_TRAIL_N = 0,
CODED_SLICE_TRAIL_R = 1,
CODED_SLICE_TSA_N = 2,
CODED_SLICE_TSA_R = 3,
CODED_SLICE_STSA_N = 4,
CODED_SLICE_STSA_R = 5,
CODED_SLICE_RADL_N = 6,
CODED_SLICE_RADL_R = 7,
CODED_SLICE_RASL_N = 8,
CODED_SLICE_RASL_R = 9,
VParameterSet = 32,
SequenceParameterSet = 33,
PictureParameterSet = 34,
AccessUnitDelimiter = 35,
EndOfSequence = 36,
IsEndOfStream = 37,
FillerData = 38,
PrefixSei = 39,
SuffixSei = 40,
Invalid = 64,
}

9
src/ImageSharp/Formats/Heic/Readme.md

@ -0,0 +1,9 @@
# Implementation references
[MPEG-4 register authority](https://mp4ra.org/)
[HEIF reference implementation from Nokia](https://github.com/nokiatech/heif)
[Open Source H265 implementation](https://bitbucket.org/multicoreware/x265_git/src)
[Apple's metadata syntax in HEIC images](http://cheeky4n6monkey.blogspot.com/2017/10/monkey-takes-heic.html)
Loading…
Cancel
Save