Skip to content

Commit c08e7c9

Browse files
rpuneetclaude
andauthored
feat: add AVIF format support (#31)
Extend the HEIC parser to also detect and parse AVIF image files. Both formats use the same ISOBMFF container structure, differing only in the image codec (HEVC vs AV1). The parser now recognizes AVIF brand codes: avif, avis, av01. Closes #1 (work-001) Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent a6cda1a commit c08e7c9

File tree

3 files changed

+38
-12
lines changed

3 files changed

+38
-12
lines changed

internal/parser/heic/constants.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ const (
3131
)
3232

3333
// Valid HEIC/HEIF major brands
34-
var validBrands = []string{
34+
var heicBrands = []string{
3535
"heic", "heif", "heix", "hevc", "heim", "heis",
3636
"mif1", "msf1", "heiv", "hevx",
3737
}
3838

39+
// Valid AVIF major brands
40+
var avifBrands = []string{
41+
"avif", "avis", "av01",
42+
}
43+
3944
// Box header sizes
4045
const (
4146
boxHeaderSize = 8 // Standard box header (size + type)

internal/parser/heic/heic.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
// Package heic implements a parser for HEIC/HEIF image files.
1+
// Package heic implements a parser for HEIC/HEIF and AVIF image files.
22
//
3-
// HEIC (High Efficiency Image Container) is based on the ISO Base Media File
4-
// Format (ISOBMFF). This parser extracts EXIF, XMP, and ICC metadata from
5-
// HEIC/HEIF files by parsing the box structure and building an item index.
3+
// HEIC (High Efficiency Image Container) and AVIF (AV1 Image File Format)
4+
// are both based on the ISO Base Media File Format (ISOBMFF). They share the
5+
// same container structure, differing only in the image codec used (HEVC vs AV1).
6+
// This parser extracts EXIF, XMP, and ICC metadata from both formats by parsing
7+
// the box structure and building an item index.
68
package heic
79

810
import (
@@ -18,7 +20,8 @@ import (
1820
// maxBoxScan is the maximum number of bytes to scan when searching for boxes.
1921
const maxBoxScan = 100 * 1024 * 1024 // 100MB
2022

21-
// Parser parses HEIC/HEIF image files.
23+
// Parser parses HEIC/HEIF and AVIF image files.
24+
// Both formats use the ISO Base Media File Format (ISOBMFF) container.
2225
type Parser struct {
2326
tiff *tiff.Parser
2427
xmp *xmp.Parser
@@ -35,11 +38,13 @@ func New() *Parser {
3538
}
3639

3740
// Name returns the parser name.
41+
// This parser handles both HEIC/HEIF and AVIF formats.
3842
func (p *Parser) Name() string {
3943
return "HEIC"
4044
}
4145

42-
// Detect checks if the data is a HEIC/HEIF file.
46+
// Detect checks if the data is a HEIC/HEIF or AVIF file.
47+
// Both formats use the same ISOBMFF container structure.
4348
func (p *Parser) Detect(r io.ReaderAt) bool {
4449
buf := make([]byte, 12)
4550
if _, err := r.ReadAt(buf[:12], 0); err != nil {
@@ -51,9 +56,14 @@ func (p *Parser) Detect(r io.ReaderAt) bool {
5156
return false
5257
}
5358

54-
// Check major brand
59+
// Check major brand (HEIC or AVIF)
5560
brand := string(buf[8:12])
56-
for _, valid := range validBrands {
61+
for _, valid := range heicBrands {
62+
if brand == valid {
63+
return true
64+
}
65+
}
66+
for _, valid := range avifBrands {
5767
if brand == valid {
5868
return true
5969
}

internal/parser/heic/heic_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,23 @@ func TestParser_Detect(t *testing.T) {
7373

7474
func TestParser_Detect_AllBrands(t *testing.T) {
7575
p := New()
76-
for _, brand := range validBrands {
77-
t.Run(brand, func(t *testing.T) {
76+
// Test HEIC brands
77+
for _, brand := range heicBrands {
78+
t.Run("HEIC_"+brand, func(t *testing.T) {
7879
data := []byte{0, 0, 0, 24, 'f', 't', 'y', 'p', brand[0], brand[1], brand[2], brand[3]}
7980
r := bytes.NewReader(data)
8081
if !p.Detect(r) {
81-
t.Errorf("Detect() should recognize brand %s", brand)
82+
t.Errorf("Detect() should recognize HEIC brand %s", brand)
83+
}
84+
})
85+
}
86+
// Test AVIF brands
87+
for _, brand := range avifBrands {
88+
t.Run("AVIF_"+brand, func(t *testing.T) {
89+
data := []byte{0, 0, 0, 24, 'f', 't', 'y', 'p', brand[0], brand[1], brand[2], brand[3]}
90+
r := bytes.NewReader(data)
91+
if !p.Detect(r) {
92+
t.Errorf("Detect() should recognize AVIF brand %s", brand)
8293
}
8394
})
8495
}

0 commit comments

Comments
 (0)