github.com/camlistore/go4@v0.0.0-20200104003542-c7e774b10ea0/media/heif/bmff/bmff.go (about)

     1  /*
     2  Copyright 2018 The go4 Authors
     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  
    17  // Package bmff reads ISO BMFF boxes, as used by HEIF, etc.
    18  //
    19  // This is not so much as a generic BMFF reader as it is a BMFF reader
    20  // as needed by HEIF, though that may change in time. For now, only
    21  // boxes necessary for the go4.org/media/heif package have explicit
    22  // parsers.
    23  //
    24  // This package makes no API compatibility promises; it exists
    25  // primarily for use by the go4.org/media/heif package.
    26  package bmff
    27  
    28  import (
    29  	"bufio"
    30  	"bytes"
    31  	"encoding/binary"
    32  	"errors"
    33  	"fmt"
    34  	"io"
    35  	"io/ioutil"
    36  	"strings"
    37  )
    38  
    39  func NewReader(r io.Reader) *Reader {
    40  	br, ok := r.(*bufio.Reader)
    41  	if !ok {
    42  		br = bufio.NewReader(r)
    43  	}
    44  	return &Reader{br: bufReader{Reader: br}}
    45  }
    46  
    47  type Reader struct {
    48  	br          bufReader
    49  	lastBox     Box  // or nil
    50  	noMoreBoxes bool // a box with size 0 (the final box) was seen
    51  }
    52  
    53  type BoxType [4]byte
    54  
    55  // Common box types.
    56  var (
    57  	TypeFtyp = BoxType{'f', 't', 'y', 'p'}
    58  	TypeMeta = BoxType{'m', 'e', 't', 'a'}
    59  )
    60  
    61  func (t BoxType) String() string { return string(t[:]) }
    62  
    63  func (t BoxType) EqualString(s string) bool {
    64  	// Could be cleaner, but see ohttps://github.com/golang/go/issues/24765
    65  	return len(s) == 4 && s[0] == t[0] && s[1] == t[1] && s[2] == t[2] && s[3] == t[3]
    66  }
    67  
    68  type parseFunc func(b box, br *bufio.Reader) (Box, error)
    69  
    70  // Box represents a BMFF box.
    71  type Box interface {
    72  	Size() int64 // 0 means unknown (will read to end of file)
    73  	Type() BoxType
    74  
    75  	// Parses parses the box, populating the fields
    76  	// in the returned concrete type.
    77  	//
    78  	// If Parse has already been called, Parse returns nil.
    79  	// If the box type is unknown, the returned error is ErrUnknownBox
    80  	// and it's guaranteed that no bytes have been read from the box.
    81  	Parse() (Box, error)
    82  
    83  	// Body returns the inner bytes of the box, ignoring the header.
    84  	// The body may start with the 4 byte header of a "Full Box" if the
    85  	// box's type derives from a full box. Most users will use Parse
    86  	// instead.
    87  	// Body will return a new reader at the beginning of the box if the
    88  	// outer box has already been parsed.
    89  	Body() io.Reader
    90  }
    91  
    92  // ErrUnknownBox is returned by Box.Parse for unrecognized box types.
    93  var ErrUnknownBox = errors.New("heif: unknown box")
    94  
    95  type parserFunc func(b *box, br *bufReader) (Box, error)
    96  
    97  func boxType(s string) BoxType {
    98  	if len(s) != 4 {
    99  		panic("bogus boxType length")
   100  	}
   101  	return BoxType{s[0], s[1], s[2], s[3]}
   102  }
   103  
   104  var parsers = map[BoxType]parserFunc{
   105  	boxType("dinf"): parseDataInformationBox,
   106  	boxType("dref"): parseDataReferenceBox,
   107  	boxType("ftyp"): parseFileTypeBox,
   108  	boxType("hdlr"): parseHandlerBox,
   109  	boxType("iinf"): parseItemInfoBox,
   110  	boxType("infe"): parseItemInfoEntry,
   111  	boxType("iloc"): parseItemLocationBox,
   112  	boxType("ipco"): parseItemPropertyContainerBox,
   113  	boxType("ipma"): parseItemPropertyAssociation,
   114  	boxType("iprp"): parseItemPropertiesBox,
   115  	boxType("irot"): parseImageRotation,
   116  	boxType("ispe"): parseImageSpatialExtentsProperty,
   117  	boxType("meta"): parseMetaBox,
   118  	boxType("pitm"): parsePrimaryItemBox,
   119  }
   120  
   121  type box struct {
   122  	size    int64 // 0 means unknown, will read to end of file (box container)
   123  	boxType BoxType
   124  	body    io.Reader
   125  	parsed  Box    // if non-nil, the Parsed result
   126  	slurp   []byte // if non-nil, the contents slurped to memory
   127  }
   128  
   129  func (b *box) Size() int64   { return b.size }
   130  func (b *box) Type() BoxType { return b.boxType }
   131  
   132  func (b *box) Body() io.Reader {
   133  	if b.slurp != nil {
   134  		return bytes.NewReader(b.slurp)
   135  	}
   136  	return b.body
   137  }
   138  
   139  func (b *box) Parse() (Box, error) {
   140  	if b.parsed != nil {
   141  		return b.parsed, nil
   142  	}
   143  	parser, ok := parsers[b.Type()]
   144  	if !ok {
   145  		return nil, ErrUnknownBox
   146  	}
   147  	v, err := parser(b, &bufReader{Reader: bufio.NewReader(b.Body())})
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	b.parsed = v
   152  	return v, nil
   153  }
   154  
   155  type FullBox struct {
   156  	*box
   157  	Version uint8
   158  	Flags   uint32 // 24 bits
   159  }
   160  
   161  // ReadBox reads the next box.
   162  //
   163  // If the previously read box was not read to completion, ReadBox consumes
   164  // the rest of its data.
   165  //
   166  // At the end, the error is io.EOF.
   167  func (r *Reader) ReadBox() (Box, error) {
   168  	if r.noMoreBoxes {
   169  		return nil, io.EOF
   170  	}
   171  	if r.lastBox != nil {
   172  		if _, err := io.Copy(ioutil.Discard, r.lastBox.Body()); err != nil {
   173  			return nil, err
   174  		}
   175  	}
   176  	var buf [8]byte
   177  
   178  	_, err := io.ReadFull(r.br, buf[:4])
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	box := &box{
   183  		size: int64(binary.BigEndian.Uint32(buf[:4])),
   184  	}
   185  
   186  	_, err = io.ReadFull(r.br, box.boxType[:]) // 4 more bytes
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// Special cases for size:
   192  	var remain int64
   193  	switch box.size {
   194  	case 1:
   195  		// 1 means it's actually a 64-bit size, after the type.
   196  		_, err = io.ReadFull(r.br, buf[:8])
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		box.size = int64(binary.BigEndian.Uint64(buf[:8]))
   201  		if box.size < 0 {
   202  			// Go uses int64 for sizes typically, but BMFF uses uint64.
   203  			// We assume for now that nobody actually uses boxes larger
   204  			// than int64.
   205  			return nil, fmt.Errorf("unexpectedly large box %q", box.boxType)
   206  		}
   207  		remain = box.size - 2*4 - 8
   208  	case 0:
   209  		// 0 means unknown & to read to end of file. No more boxes.
   210  		r.noMoreBoxes = true
   211  	default:
   212  		remain = box.size - 2*4
   213  	}
   214  	if remain < 0 {
   215  		return nil, fmt.Errorf("Box header for %q has size %d, suggesting %d (negative) bytes remain", box.boxType, box.size, remain)
   216  	}
   217  	if box.size > 0 {
   218  		box.body = io.LimitReader(r.br, remain)
   219  	} else {
   220  		box.body = r.br
   221  	}
   222  	r.lastBox = box
   223  	return box, nil
   224  }
   225  
   226  // ReadAndParseBox wraps the ReadBox method, ensuring that the read box is of type typ
   227  // and parses successfully. It returns the parsed box.
   228  func (r *Reader) ReadAndParseBox(typ BoxType) (Box, error) {
   229  	box, err := r.ReadBox()
   230  	if err != nil {
   231  		return nil, fmt.Errorf("error reading %q box: %v", typ, err)
   232  	}
   233  	if box.Type() != typ {
   234  		return nil, fmt.Errorf("error reading %q box: got box type %q instead", typ, box.Type())
   235  	}
   236  	pbox, err := box.Parse()
   237  	if err != nil {
   238  		return nil, fmt.Errorf("error parsing read %q box: %v", typ, err)
   239  	}
   240  	return pbox, nil
   241  }
   242  
   243  func readFullBox(outer *box, br *bufReader) (fb FullBox, err error) {
   244  	fb.box = outer
   245  	// Parse FullBox header.
   246  	buf, err := br.Peek(4)
   247  	if err != nil {
   248  		return FullBox{}, fmt.Errorf("failed to read 4 bytes of FullBox: %v", err)
   249  	}
   250  	fb.Version = buf[0]
   251  	buf[0] = 0
   252  	fb.Flags = binary.BigEndian.Uint32(buf[:4])
   253  	br.Discard(4)
   254  	return fb, nil
   255  }
   256  
   257  type FileTypeBox struct {
   258  	*box
   259  	MajorBrand   string   // 4 bytes
   260  	MinorVersion string   // 4 bytes
   261  	Compatible   []string // all 4 bytes
   262  }
   263  
   264  func parseFileTypeBox(outer *box, br *bufReader) (Box, error) {
   265  	buf, err := br.Peek(8)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	ft := &FileTypeBox{
   270  		box:          outer,
   271  		MajorBrand:   string(buf[:4]),
   272  		MinorVersion: string(buf[4:8]),
   273  	}
   274  	br.Discard(8)
   275  	for {
   276  		buf, err := br.Peek(4)
   277  		if err == io.EOF {
   278  			return ft, nil
   279  		}
   280  		if err != nil {
   281  			return nil, err
   282  		}
   283  		ft.Compatible = append(ft.Compatible, string(buf[:4]))
   284  		br.Discard(4)
   285  	}
   286  }
   287  
   288  type MetaBox struct {
   289  	FullBox
   290  	Children []Box
   291  }
   292  
   293  func parseMetaBox(outer *box, br *bufReader) (Box, error) {
   294  	fb, err := readFullBox(outer, br)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	mb := &MetaBox{FullBox: fb}
   299  	return mb, br.parseAppendBoxes(&mb.Children)
   300  }
   301  
   302  func (br *bufReader) parseAppendBoxes(dst *[]Box) error {
   303  	if br.err != nil {
   304  		return br.err
   305  	}
   306  	boxr := NewReader(br.Reader)
   307  	for {
   308  		inner, err := boxr.ReadBox()
   309  		if err == io.EOF {
   310  			return nil
   311  		}
   312  		if err != nil {
   313  			br.err = err
   314  			return err
   315  		}
   316  		slurp, err := ioutil.ReadAll(inner.Body())
   317  		if err != nil {
   318  			br.err = err
   319  			return err
   320  		}
   321  		inner.(*box).slurp = slurp
   322  		*dst = append(*dst, inner)
   323  	}
   324  }
   325  
   326  // ItemInfoEntry represents an "infe" box.
   327  //
   328  // TODO: currently only parses Version 2 boxes.
   329  type ItemInfoEntry struct {
   330  	FullBox
   331  
   332  	ItemID          uint16
   333  	ProtectionIndex uint16
   334  	ItemType        string // always 4 bytes
   335  
   336  	Name string
   337  
   338  	// If Type == "mime":
   339  	ContentType     string
   340  	ContentEncoding string
   341  
   342  	// If Type == "uri ":
   343  	ItemURIType string
   344  }
   345  
   346  func parseItemInfoEntry(outer *box, br *bufReader) (Box, error) {
   347  	fb, err := readFullBox(outer, br)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  	ie := &ItemInfoEntry{FullBox: fb}
   352  	if fb.Version != 2 {
   353  		return nil, fmt.Errorf("TODO: found version %d infe box. Only 2 is supported now.", fb.Version)
   354  	}
   355  
   356  	ie.ItemID, _ = br.readUint16()
   357  	ie.ProtectionIndex, _ = br.readUint16()
   358  	if !br.ok() {
   359  		return nil, br.err
   360  	}
   361  	buf, err := br.Peek(4)
   362  	if err != nil {
   363  		return nil, err
   364  	}
   365  	ie.ItemType = string(buf[:4])
   366  	ie.Name, _ = br.readString()
   367  
   368  	switch ie.ItemType {
   369  	case "mime":
   370  		ie.ContentType, _ = br.readString()
   371  		if br.anyRemain() {
   372  			ie.ContentEncoding, _ = br.readString()
   373  		}
   374  	case "uri ":
   375  		ie.ItemURIType, _ = br.readString()
   376  	}
   377  	if !br.ok() {
   378  		return nil, br.err
   379  	}
   380  	return ie, nil
   381  }
   382  
   383  // ItemInfoBox represents an "iinf" box.
   384  type ItemInfoBox struct {
   385  	FullBox
   386  	Count     uint16
   387  	ItemInfos []*ItemInfoEntry
   388  }
   389  
   390  func parseItemInfoBox(outer *box, br *bufReader) (Box, error) {
   391  	fb, err := readFullBox(outer, br)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	ib := &ItemInfoBox{FullBox: fb}
   396  
   397  	ib.Count, _ = br.readUint16()
   398  
   399  	var itemInfos []Box
   400  	br.parseAppendBoxes(&itemInfos)
   401  	if br.ok() {
   402  		for _, box := range itemInfos {
   403  			pb, err := box.Parse()
   404  			if err != nil {
   405  				return nil, fmt.Errorf("error parsing ItemInfoEntry in ItemInfoBox: %v", err)
   406  			}
   407  			if iie, ok := pb.(*ItemInfoEntry); ok {
   408  				ib.ItemInfos = append(ib.ItemInfos, iie)
   409  			}
   410  		}
   411  	}
   412  	if !br.ok() {
   413  		return FullBox{}, br.err
   414  	}
   415  	return ib, nil
   416  }
   417  
   418  // bufReader adds some HEIF/BMFF-specific methods around a *bufio.Reader.
   419  type bufReader struct {
   420  	*bufio.Reader
   421  	err error // sticky error
   422  }
   423  
   424  // ok reports whether all previous reads have been error-free.
   425  func (br *bufReader) ok() bool { return br.err == nil }
   426  
   427  func (br *bufReader) anyRemain() bool {
   428  	if br.err != nil {
   429  		return false
   430  	}
   431  	_, err := br.Peek(1)
   432  	return err == nil
   433  }
   434  
   435  func (br *bufReader) readUintN(bits uint8) (uint64, error) {
   436  	if br.err != nil {
   437  		return 0, br.err
   438  	}
   439  	if bits == 0 {
   440  		return 0, nil
   441  	}
   442  	nbyte := bits / 8
   443  	buf, err := br.Peek(int(nbyte))
   444  	if err != nil {
   445  		br.err = err
   446  		return 0, err
   447  	}
   448  	defer br.Discard(int(nbyte))
   449  	switch bits {
   450  	case 8:
   451  		return uint64(buf[0]), nil
   452  	case 16:
   453  		return uint64(binary.BigEndian.Uint16(buf[:2])), nil
   454  	case 32:
   455  		return uint64(binary.BigEndian.Uint32(buf[:4])), nil
   456  	case 64:
   457  		return binary.BigEndian.Uint64(buf[:8]), nil
   458  	default:
   459  		br.err = fmt.Errorf("invalid uintn read size")
   460  		return 0, br.err
   461  	}
   462  }
   463  
   464  func (br *bufReader) readUint8() (uint8, error) {
   465  	if br.err != nil {
   466  		return 0, br.err
   467  	}
   468  	v, err := br.ReadByte()
   469  	if err != nil {
   470  		br.err = err
   471  		return 0, err
   472  	}
   473  	return v, nil
   474  }
   475  
   476  func (br *bufReader) readUint16() (uint16, error) {
   477  	if br.err != nil {
   478  		return 0, br.err
   479  	}
   480  	buf, err := br.Peek(2)
   481  	if err != nil {
   482  		br.err = err
   483  		return 0, err
   484  	}
   485  	v := binary.BigEndian.Uint16(buf[:2])
   486  	br.Discard(2)
   487  	return v, nil
   488  }
   489  
   490  func (br *bufReader) readUint32() (uint32, error) {
   491  	if br.err != nil {
   492  		return 0, br.err
   493  	}
   494  	buf, err := br.Peek(4)
   495  	if err != nil {
   496  		br.err = err
   497  		return 0, err
   498  	}
   499  	v := binary.BigEndian.Uint32(buf[:4])
   500  	br.Discard(4)
   501  	return v, nil
   502  }
   503  
   504  func (br *bufReader) readString() (string, error) {
   505  	if br.err != nil {
   506  		return "", br.err
   507  	}
   508  	s0, err := br.ReadString(0)
   509  	if err != nil {
   510  		br.err = err
   511  		return "", err
   512  	}
   513  	s := strings.TrimSuffix(s0, "\x00")
   514  	if len(s) == len(s0) {
   515  		err = fmt.Errorf("unexpected non-null terminated string")
   516  		br.err = err
   517  		return "", err
   518  	}
   519  	return s, nil
   520  }
   521  
   522  // HEIF: ipco
   523  type ItemPropertyContainerBox struct {
   524  	*box
   525  	Properties []Box // of ItemProperty or ItemFullProperty
   526  }
   527  
   528  func parseItemPropertyContainerBox(outer *box, br *bufReader) (Box, error) {
   529  	ipc := &ItemPropertyContainerBox{box: outer}
   530  	return ipc, br.parseAppendBoxes(&ipc.Properties)
   531  }
   532  
   533  // HEIF: iprp
   534  type ItemPropertiesBox struct {
   535  	*box
   536  	PropertyContainer *ItemPropertyContainerBox
   537  	Associations      []*ItemPropertyAssociation // at least 1
   538  }
   539  
   540  func parseItemPropertiesBox(outer *box, br *bufReader) (Box, error) {
   541  	ip := &ItemPropertiesBox{
   542  		box: outer,
   543  	}
   544  
   545  	var boxes []Box
   546  	err := br.parseAppendBoxes(&boxes)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	if len(boxes) < 2 {
   551  		return nil, fmt.Errorf("expect at least 2 boxes in children; got 0")
   552  	}
   553  
   554  	cb, err := boxes[0].Parse()
   555  	if err != nil {
   556  		return nil, fmt.Errorf("failed to parse first box, %q: %v", boxes[0].Type(), err)
   557  	}
   558  
   559  	var ok bool
   560  	ip.PropertyContainer, ok = cb.(*ItemPropertyContainerBox)
   561  	if !ok {
   562  		return nil, fmt.Errorf("unexpected type %T for ItemPropertieBox.PropertyContainer", cb)
   563  	}
   564  
   565  	// Association boxes
   566  	ip.Associations = make([]*ItemPropertyAssociation, 0, len(boxes)-1)
   567  	for _, box := range boxes[1:] {
   568  		boxp, err := box.Parse()
   569  		if err != nil {
   570  			return nil, fmt.Errorf("failed to parse association box: %v", err)
   571  		}
   572  		ipa, ok := boxp.(*ItemPropertyAssociation)
   573  		if !ok {
   574  			return nil, fmt.Errorf("unexpected box %q instead of ItemPropertyAssociation", boxp.Type())
   575  		}
   576  		ip.Associations = append(ip.Associations, ipa)
   577  	}
   578  	return ip, nil
   579  }
   580  
   581  type ItemPropertyAssociation struct {
   582  	FullBox
   583  	EntryCount uint32
   584  	Entries    []ItemPropertyAssociationItem
   585  }
   586  
   587  // not a box
   588  type ItemProperty struct {
   589  	Essential bool
   590  	Index     uint16
   591  }
   592  
   593  // not a box
   594  type ItemPropertyAssociationItem struct {
   595  	ItemID            uint32
   596  	AssociationsCount int            // as declared
   597  	Associations      []ItemProperty // as parsed
   598  }
   599  
   600  func parseItemPropertyAssociation(outer *box, br *bufReader) (Box, error) {
   601  	fb, err := readFullBox(outer, br)
   602  	if err != nil {
   603  		return nil, err
   604  	}
   605  	ipa := &ItemPropertyAssociation{FullBox: fb}
   606  	count, _ := br.readUint32()
   607  	ipa.EntryCount = count
   608  
   609  	for i := uint64(0); i < uint64(count) && br.ok(); i++ {
   610  		var itemID uint32
   611  		if fb.Version < 1 {
   612  			itemID16, _ := br.readUint16()
   613  			itemID = uint32(itemID16)
   614  		} else {
   615  			itemID, _ = br.readUint32()
   616  		}
   617  		assocCount, _ := br.readUint8()
   618  		ipai := ItemPropertyAssociationItem{
   619  			ItemID:            itemID,
   620  			AssociationsCount: int(assocCount),
   621  		}
   622  		for j := 0; j < int(assocCount) && br.ok(); j++ {
   623  			first, _ := br.readUint8()
   624  			essential := first&(1<<7) != 0
   625  			first &^= byte(1 << 7)
   626  
   627  			var index uint16
   628  			if fb.Flags&1 != 0 {
   629  				second, _ := br.readUint8()
   630  				index = uint16(first)<<8 | uint16(second)
   631  			} else {
   632  				index = uint16(first)
   633  			}
   634  			ipai.Associations = append(ipai.Associations, ItemProperty{
   635  				Essential: essential,
   636  				Index:     index,
   637  			})
   638  		}
   639  		ipa.Entries = append(ipa.Entries, ipai)
   640  	}
   641  	if !br.ok() {
   642  		return nil, br.err
   643  	}
   644  	return ipa, nil
   645  }
   646  
   647  type ImageSpatialExtentsProperty struct {
   648  	FullBox
   649  	ImageWidth  uint32
   650  	ImageHeight uint32
   651  }
   652  
   653  func parseImageSpatialExtentsProperty(outer *box, br *bufReader) (Box, error) {
   654  	fb, err := readFullBox(outer, br)
   655  	if err != nil {
   656  		return nil, err
   657  	}
   658  	w, err := br.readUint32()
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  	h, err := br.readUint32()
   663  	if err != nil {
   664  		return nil, err
   665  	}
   666  	return &ImageSpatialExtentsProperty{
   667  		FullBox:     fb,
   668  		ImageWidth:  w,
   669  		ImageHeight: h,
   670  	}, nil
   671  }
   672  
   673  type OffsetLength struct {
   674  	Offset, Length uint64
   675  }
   676  
   677  // not a box
   678  type ItemLocationBoxEntry struct {
   679  	ItemID             uint16
   680  	ConstructionMethod uint8 // actually uint4
   681  	DataReferenceIndex uint16
   682  	BaseOffset         uint64 // uint32 or uint64, depending on encoding
   683  	ExtentCount        uint16
   684  	Extents            []OffsetLength
   685  }
   686  
   687  // box "iloc"
   688  type ItemLocationBox struct {
   689  	FullBox
   690  
   691  	offsetSize, lengthSize, baseOffsetSize, indexSize uint8 // actually uint4
   692  
   693  	ItemCount uint16
   694  	Items     []ItemLocationBoxEntry
   695  }
   696  
   697  func parseItemLocationBox(outer *box, br *bufReader) (Box, error) {
   698  	fb, err := readFullBox(outer, br)
   699  	if err != nil {
   700  		return nil, err
   701  	}
   702  	ilb := &ItemLocationBox{
   703  		FullBox: fb,
   704  	}
   705  	buf, err := br.Peek(4)
   706  	if err != nil {
   707  		return nil, err
   708  	}
   709  	ilb.offsetSize = buf[0] >> 4
   710  	ilb.lengthSize = buf[0] & 15
   711  	ilb.baseOffsetSize = buf[1] >> 4
   712  	if fb.Version > 0 { // version 1
   713  		ilb.indexSize = buf[1] & 15
   714  	}
   715  
   716  	ilb.ItemCount = binary.BigEndian.Uint16(buf[2:4])
   717  	br.Discard(4)
   718  
   719  	for i := 0; br.ok() && i < int(ilb.ItemCount); i++ {
   720  		var ent ItemLocationBoxEntry
   721  		ent.ItemID, _ = br.readUint16()
   722  		if fb.Version > 0 { // version 1
   723  			cmeth, _ := br.readUint16()
   724  			ent.ConstructionMethod = byte(cmeth & 15)
   725  		}
   726  		ent.DataReferenceIndex, _ = br.readUint16()
   727  		if br.ok() && ilb.baseOffsetSize > 0 {
   728  			br.Discard(int(ilb.baseOffsetSize) / 8)
   729  		}
   730  		ent.ExtentCount, _ = br.readUint16()
   731  		for j := 0; br.ok() && j < int(ent.ExtentCount); j++ {
   732  			var ol OffsetLength
   733  			ol.Offset, _ = br.readUintN(ilb.offsetSize * 8)
   734  			ol.Length, _ = br.readUintN(ilb.lengthSize * 8)
   735  			if br.err != nil {
   736  				return nil, br.err
   737  			}
   738  			ent.Extents = append(ent.Extents, ol)
   739  		}
   740  		ilb.Items = append(ilb.Items, ent)
   741  	}
   742  	if !br.ok() {
   743  		return nil, br.err
   744  	}
   745  	return ilb, nil
   746  }
   747  
   748  // a "hdlr" box.
   749  type HandlerBox struct {
   750  	FullBox
   751  	HandlerType string // always 4 bytes; usually "pict" for iOS Camera images
   752  	Name        string
   753  }
   754  
   755  func parseHandlerBox(gen *box, br *bufReader) (Box, error) {
   756  	fb, err := readFullBox(gen, br)
   757  	if err != nil {
   758  		return nil, err
   759  	}
   760  	hb := &HandlerBox{
   761  		FullBox: fb,
   762  	}
   763  	buf, err := br.Peek(20)
   764  	if err != nil {
   765  		return nil, err
   766  	}
   767  	hb.HandlerType = string(buf[4:8])
   768  	br.Discard(20)
   769  
   770  	hb.Name, _ = br.readString()
   771  	return hb, br.err
   772  }
   773  
   774  // a "dinf" box
   775  type DataInformationBox struct {
   776  	*box
   777  	Children []Box
   778  }
   779  
   780  func parseDataInformationBox(gen *box, br *bufReader) (Box, error) {
   781  	dib := &DataInformationBox{box: gen}
   782  	return dib, br.parseAppendBoxes(&dib.Children)
   783  }
   784  
   785  // a "dref" box.
   786  type DataReferenceBox struct {
   787  	FullBox
   788  	EntryCount uint32
   789  	Children   []Box
   790  }
   791  
   792  func parseDataReferenceBox(gen *box, br *bufReader) (Box, error) {
   793  	fb, err := readFullBox(gen, br)
   794  	if err != nil {
   795  		return nil, err
   796  	}
   797  	drb := &DataReferenceBox{FullBox: fb}
   798  	drb.EntryCount, _ = br.readUint32()
   799  	return drb, br.parseAppendBoxes(&drb.Children)
   800  }
   801  
   802  // "pitm" box
   803  type PrimaryItemBox struct {
   804  	FullBox
   805  	ItemID uint16
   806  }
   807  
   808  func parsePrimaryItemBox(gen *box, br *bufReader) (Box, error) {
   809  	fb, err := readFullBox(gen, br)
   810  	if err != nil {
   811  		return nil, err
   812  	}
   813  	pib := &PrimaryItemBox{FullBox: fb}
   814  	pib.ItemID, _ = br.readUint16()
   815  	if !br.ok() {
   816  		return nil, br.err
   817  	}
   818  	return pib, nil
   819  }
   820  
   821  // ImageRotation is a HEIF "irot" rotation property.
   822  type ImageRotation struct {
   823  	*box
   824  	Angle uint8 // 1 means 90 degrees counter-clockwise, 2 means 180 counter-clockwise
   825  }
   826  
   827  func parseImageRotation(gen *box, br *bufReader) (Box, error) {
   828  	v, err := br.readUint8()
   829  	if err != nil {
   830  		return nil, err
   831  	}
   832  	return &ImageRotation{box: gen, Angle: v & 3}, nil
   833  }