Working auth and photo json endpoint
Signed-off-by: Kris Nóva <kris@nivenly.com>
This commit is contained in:
parent
ef275f97f4
commit
e4323b6047
2032 changed files with 821464 additions and 52 deletions
833
vendor/go4.org/media/heif/bmff/bmff.go
generated
vendored
Normal file
833
vendor/go4.org/media/heif/bmff/bmff.go
generated
vendored
Normal file
|
@ -0,0 +1,833 @@
|
|||
/*
|
||||
Copyright 2018 The go4 Authors
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Package bmff reads ISO BMFF boxes, as used by HEIF, etc.
|
||||
//
|
||||
// This is not so much as a generic BMFF reader as it is a BMFF reader
|
||||
// as needed by HEIF, though that may change in time. For now, only
|
||||
// boxes necessary for the go4.org/media/heif package have explicit
|
||||
// parsers.
|
||||
//
|
||||
// This package makes no API compatibility promises; it exists
|
||||
// primarily for use by the go4.org/media/heif package.
|
||||
package bmff
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
br, ok := r.(*bufio.Reader)
|
||||
if !ok {
|
||||
br = bufio.NewReader(r)
|
||||
}
|
||||
return &Reader{br: bufReader{Reader: br}}
|
||||
}
|
||||
|
||||
type Reader struct {
|
||||
br bufReader
|
||||
lastBox Box // or nil
|
||||
noMoreBoxes bool // a box with size 0 (the final box) was seen
|
||||
}
|
||||
|
||||
type BoxType [4]byte
|
||||
|
||||
// Common box types.
|
||||
var (
|
||||
TypeFtyp = BoxType{'f', 't', 'y', 'p'}
|
||||
TypeMeta = BoxType{'m', 'e', 't', 'a'}
|
||||
)
|
||||
|
||||
func (t BoxType) String() string { return string(t[:]) }
|
||||
|
||||
func (t BoxType) EqualString(s string) bool {
|
||||
// Could be cleaner, but see ohttps://github.com/golang/go/issues/24765
|
||||
return len(s) == 4 && s[0] == t[0] && s[1] == t[1] && s[2] == t[2] && s[3] == t[3]
|
||||
}
|
||||
|
||||
type parseFunc func(b box, br *bufio.Reader) (Box, error)
|
||||
|
||||
// Box represents a BMFF box.
|
||||
type Box interface {
|
||||
Size() int64 // 0 means unknown (will read to end of file)
|
||||
Type() BoxType
|
||||
|
||||
// Parses parses the box, populating the fields
|
||||
// in the returned concrete type.
|
||||
//
|
||||
// If Parse has already been called, Parse returns nil.
|
||||
// If the box type is unknown, the returned error is ErrUnknownBox
|
||||
// and it's guaranteed that no bytes have been read from the box.
|
||||
Parse() (Box, error)
|
||||
|
||||
// Body returns the inner bytes of the box, ignoring the header.
|
||||
// The body may start with the 4 byte header of a "Full Box" if the
|
||||
// box's type derives from a full box. Most users will use Parse
|
||||
// instead.
|
||||
// Body will return a new reader at the beginning of the box if the
|
||||
// outer box has already been parsed.
|
||||
Body() io.Reader
|
||||
}
|
||||
|
||||
// ErrUnknownBox is returned by Box.Parse for unrecognized box types.
|
||||
var ErrUnknownBox = errors.New("heif: unknown box")
|
||||
|
||||
type parserFunc func(b *box, br *bufReader) (Box, error)
|
||||
|
||||
func boxType(s string) BoxType {
|
||||
if len(s) != 4 {
|
||||
panic("bogus boxType length")
|
||||
}
|
||||
return BoxType{s[0], s[1], s[2], s[3]}
|
||||
}
|
||||
|
||||
var parsers = map[BoxType]parserFunc{
|
||||
boxType("dinf"): parseDataInformationBox,
|
||||
boxType("dref"): parseDataReferenceBox,
|
||||
boxType("ftyp"): parseFileTypeBox,
|
||||
boxType("hdlr"): parseHandlerBox,
|
||||
boxType("iinf"): parseItemInfoBox,
|
||||
boxType("infe"): parseItemInfoEntry,
|
||||
boxType("iloc"): parseItemLocationBox,
|
||||
boxType("ipco"): parseItemPropertyContainerBox,
|
||||
boxType("ipma"): parseItemPropertyAssociation,
|
||||
boxType("iprp"): parseItemPropertiesBox,
|
||||
boxType("irot"): parseImageRotation,
|
||||
boxType("ispe"): parseImageSpatialExtentsProperty,
|
||||
boxType("meta"): parseMetaBox,
|
||||
boxType("pitm"): parsePrimaryItemBox,
|
||||
}
|
||||
|
||||
type box struct {
|
||||
size int64 // 0 means unknown, will read to end of file (box container)
|
||||
boxType BoxType
|
||||
body io.Reader
|
||||
parsed Box // if non-nil, the Parsed result
|
||||
slurp []byte // if non-nil, the contents slurped to memory
|
||||
}
|
||||
|
||||
func (b *box) Size() int64 { return b.size }
|
||||
func (b *box) Type() BoxType { return b.boxType }
|
||||
|
||||
func (b *box) Body() io.Reader {
|
||||
if b.slurp != nil {
|
||||
return bytes.NewReader(b.slurp)
|
||||
}
|
||||
return b.body
|
||||
}
|
||||
|
||||
func (b *box) Parse() (Box, error) {
|
||||
if b.parsed != nil {
|
||||
return b.parsed, nil
|
||||
}
|
||||
parser, ok := parsers[b.Type()]
|
||||
if !ok {
|
||||
return nil, ErrUnknownBox
|
||||
}
|
||||
v, err := parser(b, &bufReader{Reader: bufio.NewReader(b.Body())})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.parsed = v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
type FullBox struct {
|
||||
*box
|
||||
Version uint8
|
||||
Flags uint32 // 24 bits
|
||||
}
|
||||
|
||||
// ReadBox reads the next box.
|
||||
//
|
||||
// If the previously read box was not read to completion, ReadBox consumes
|
||||
// the rest of its data.
|
||||
//
|
||||
// At the end, the error is io.EOF.
|
||||
func (r *Reader) ReadBox() (Box, error) {
|
||||
if r.noMoreBoxes {
|
||||
return nil, io.EOF
|
||||
}
|
||||
if r.lastBox != nil {
|
||||
if _, err := io.Copy(ioutil.Discard, r.lastBox.Body()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var buf [8]byte
|
||||
|
||||
_, err := io.ReadFull(r.br, buf[:4])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
box := &box{
|
||||
size: int64(binary.BigEndian.Uint32(buf[:4])),
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(r.br, box.boxType[:]) // 4 more bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Special cases for size:
|
||||
var remain int64
|
||||
switch box.size {
|
||||
case 1:
|
||||
// 1 means it's actually a 64-bit size, after the type.
|
||||
_, err = io.ReadFull(r.br, buf[:8])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
box.size = int64(binary.BigEndian.Uint64(buf[:8]))
|
||||
if box.size < 0 {
|
||||
// Go uses int64 for sizes typically, but BMFF uses uint64.
|
||||
// We assume for now that nobody actually uses boxes larger
|
||||
// than int64.
|
||||
return nil, fmt.Errorf("unexpectedly large box %q", box.boxType)
|
||||
}
|
||||
remain = box.size - 2*4 - 8
|
||||
case 0:
|
||||
// 0 means unknown & to read to end of file. No more boxes.
|
||||
r.noMoreBoxes = true
|
||||
default:
|
||||
remain = box.size - 2*4
|
||||
}
|
||||
if remain < 0 {
|
||||
return nil, fmt.Errorf("Box header for %q has size %d, suggesting %d (negative) bytes remain", box.boxType, box.size, remain)
|
||||
}
|
||||
if box.size > 0 {
|
||||
box.body = io.LimitReader(r.br, remain)
|
||||
} else {
|
||||
box.body = r.br
|
||||
}
|
||||
r.lastBox = box
|
||||
return box, nil
|
||||
}
|
||||
|
||||
// ReadAndParseBox wraps the ReadBox method, ensuring that the read box is of type typ
|
||||
// and parses successfully. It returns the parsed box.
|
||||
func (r *Reader) ReadAndParseBox(typ BoxType) (Box, error) {
|
||||
box, err := r.ReadBox()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading %q box: %v", typ, err)
|
||||
}
|
||||
if box.Type() != typ {
|
||||
return nil, fmt.Errorf("error reading %q box: got box type %q instead", typ, box.Type())
|
||||
}
|
||||
pbox, err := box.Parse()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing read %q box: %v", typ, err)
|
||||
}
|
||||
return pbox, nil
|
||||
}
|
||||
|
||||
func readFullBox(outer *box, br *bufReader) (fb FullBox, err error) {
|
||||
fb.box = outer
|
||||
// Parse FullBox header.
|
||||
buf, err := br.Peek(4)
|
||||
if err != nil {
|
||||
return FullBox{}, fmt.Errorf("failed to read 4 bytes of FullBox: %v", err)
|
||||
}
|
||||
fb.Version = buf[0]
|
||||
buf[0] = 0
|
||||
fb.Flags = binary.BigEndian.Uint32(buf[:4])
|
||||
br.Discard(4)
|
||||
return fb, nil
|
||||
}
|
||||
|
||||
type FileTypeBox struct {
|
||||
*box
|
||||
MajorBrand string // 4 bytes
|
||||
MinorVersion string // 4 bytes
|
||||
Compatible []string // all 4 bytes
|
||||
}
|
||||
|
||||
func parseFileTypeBox(outer *box, br *bufReader) (Box, error) {
|
||||
buf, err := br.Peek(8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ft := &FileTypeBox{
|
||||
box: outer,
|
||||
MajorBrand: string(buf[:4]),
|
||||
MinorVersion: string(buf[4:8]),
|
||||
}
|
||||
br.Discard(8)
|
||||
for {
|
||||
buf, err := br.Peek(4)
|
||||
if err == io.EOF {
|
||||
return ft, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ft.Compatible = append(ft.Compatible, string(buf[:4]))
|
||||
br.Discard(4)
|
||||
}
|
||||
}
|
||||
|
||||
type MetaBox struct {
|
||||
FullBox
|
||||
Children []Box
|
||||
}
|
||||
|
||||
func parseMetaBox(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mb := &MetaBox{FullBox: fb}
|
||||
return mb, br.parseAppendBoxes(&mb.Children)
|
||||
}
|
||||
|
||||
func (br *bufReader) parseAppendBoxes(dst *[]Box) error {
|
||||
if br.err != nil {
|
||||
return br.err
|
||||
}
|
||||
boxr := NewReader(br.Reader)
|
||||
for {
|
||||
inner, err := boxr.ReadBox()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return err
|
||||
}
|
||||
slurp, err := ioutil.ReadAll(inner.Body())
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return err
|
||||
}
|
||||
inner.(*box).slurp = slurp
|
||||
*dst = append(*dst, inner)
|
||||
}
|
||||
}
|
||||
|
||||
// ItemInfoEntry represents an "infe" box.
|
||||
//
|
||||
// TODO: currently only parses Version 2 boxes.
|
||||
type ItemInfoEntry struct {
|
||||
FullBox
|
||||
|
||||
ItemID uint16
|
||||
ProtectionIndex uint16
|
||||
ItemType string // always 4 bytes
|
||||
|
||||
Name string
|
||||
|
||||
// If Type == "mime":
|
||||
ContentType string
|
||||
ContentEncoding string
|
||||
|
||||
// If Type == "uri ":
|
||||
ItemURIType string
|
||||
}
|
||||
|
||||
func parseItemInfoEntry(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ie := &ItemInfoEntry{FullBox: fb}
|
||||
if fb.Version != 2 {
|
||||
return nil, fmt.Errorf("TODO: found version %d infe box. Only 2 is supported now.", fb.Version)
|
||||
}
|
||||
|
||||
ie.ItemID, _ = br.readUint16()
|
||||
ie.ProtectionIndex, _ = br.readUint16()
|
||||
if !br.ok() {
|
||||
return nil, br.err
|
||||
}
|
||||
buf, err := br.Peek(4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ie.ItemType = string(buf[:4])
|
||||
ie.Name, _ = br.readString()
|
||||
|
||||
switch ie.ItemType {
|
||||
case "mime":
|
||||
ie.ContentType, _ = br.readString()
|
||||
if br.anyRemain() {
|
||||
ie.ContentEncoding, _ = br.readString()
|
||||
}
|
||||
case "uri ":
|
||||
ie.ItemURIType, _ = br.readString()
|
||||
}
|
||||
if !br.ok() {
|
||||
return nil, br.err
|
||||
}
|
||||
return ie, nil
|
||||
}
|
||||
|
||||
// ItemInfoBox represents an "iinf" box.
|
||||
type ItemInfoBox struct {
|
||||
FullBox
|
||||
Count uint16
|
||||
ItemInfos []*ItemInfoEntry
|
||||
}
|
||||
|
||||
func parseItemInfoBox(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ib := &ItemInfoBox{FullBox: fb}
|
||||
|
||||
ib.Count, _ = br.readUint16()
|
||||
|
||||
var itemInfos []Box
|
||||
br.parseAppendBoxes(&itemInfos)
|
||||
if br.ok() {
|
||||
for _, box := range itemInfos {
|
||||
pb, err := box.Parse()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing ItemInfoEntry in ItemInfoBox: %v", err)
|
||||
}
|
||||
if iie, ok := pb.(*ItemInfoEntry); ok {
|
||||
ib.ItemInfos = append(ib.ItemInfos, iie)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !br.ok() {
|
||||
return FullBox{}, br.err
|
||||
}
|
||||
return ib, nil
|
||||
}
|
||||
|
||||
// bufReader adds some HEIF/BMFF-specific methods around a *bufio.Reader.
|
||||
type bufReader struct {
|
||||
*bufio.Reader
|
||||
err error // sticky error
|
||||
}
|
||||
|
||||
// ok reports whether all previous reads have been error-free.
|
||||
func (br *bufReader) ok() bool { return br.err == nil }
|
||||
|
||||
func (br *bufReader) anyRemain() bool {
|
||||
if br.err != nil {
|
||||
return false
|
||||
}
|
||||
_, err := br.Peek(1)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (br *bufReader) readUintN(bits uint8) (uint64, error) {
|
||||
if br.err != nil {
|
||||
return 0, br.err
|
||||
}
|
||||
if bits == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
nbyte := bits / 8
|
||||
buf, err := br.Peek(int(nbyte))
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return 0, err
|
||||
}
|
||||
defer br.Discard(int(nbyte))
|
||||
switch bits {
|
||||
case 8:
|
||||
return uint64(buf[0]), nil
|
||||
case 16:
|
||||
return uint64(binary.BigEndian.Uint16(buf[:2])), nil
|
||||
case 32:
|
||||
return uint64(binary.BigEndian.Uint32(buf[:4])), nil
|
||||
case 64:
|
||||
return binary.BigEndian.Uint64(buf[:8]), nil
|
||||
default:
|
||||
br.err = fmt.Errorf("invalid uintn read size")
|
||||
return 0, br.err
|
||||
}
|
||||
}
|
||||
|
||||
func (br *bufReader) readUint8() (uint8, error) {
|
||||
if br.err != nil {
|
||||
return 0, br.err
|
||||
}
|
||||
v, err := br.ReadByte()
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return 0, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (br *bufReader) readUint16() (uint16, error) {
|
||||
if br.err != nil {
|
||||
return 0, br.err
|
||||
}
|
||||
buf, err := br.Peek(2)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return 0, err
|
||||
}
|
||||
v := binary.BigEndian.Uint16(buf[:2])
|
||||
br.Discard(2)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (br *bufReader) readUint32() (uint32, error) {
|
||||
if br.err != nil {
|
||||
return 0, br.err
|
||||
}
|
||||
buf, err := br.Peek(4)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return 0, err
|
||||
}
|
||||
v := binary.BigEndian.Uint32(buf[:4])
|
||||
br.Discard(4)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (br *bufReader) readString() (string, error) {
|
||||
if br.err != nil {
|
||||
return "", br.err
|
||||
}
|
||||
s0, err := br.ReadString(0)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
return "", err
|
||||
}
|
||||
s := strings.TrimSuffix(s0, "\x00")
|
||||
if len(s) == len(s0) {
|
||||
err = fmt.Errorf("unexpected non-null terminated string")
|
||||
br.err = err
|
||||
return "", err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// HEIF: ipco
|
||||
type ItemPropertyContainerBox struct {
|
||||
*box
|
||||
Properties []Box // of ItemProperty or ItemFullProperty
|
||||
}
|
||||
|
||||
func parseItemPropertyContainerBox(outer *box, br *bufReader) (Box, error) {
|
||||
ipc := &ItemPropertyContainerBox{box: outer}
|
||||
return ipc, br.parseAppendBoxes(&ipc.Properties)
|
||||
}
|
||||
|
||||
// HEIF: iprp
|
||||
type ItemPropertiesBox struct {
|
||||
*box
|
||||
PropertyContainer *ItemPropertyContainerBox
|
||||
Associations []*ItemPropertyAssociation // at least 1
|
||||
}
|
||||
|
||||
func parseItemPropertiesBox(outer *box, br *bufReader) (Box, error) {
|
||||
ip := &ItemPropertiesBox{
|
||||
box: outer,
|
||||
}
|
||||
|
||||
var boxes []Box
|
||||
err := br.parseAppendBoxes(&boxes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(boxes) < 2 {
|
||||
return nil, fmt.Errorf("expect at least 2 boxes in children; got 0")
|
||||
}
|
||||
|
||||
cb, err := boxes[0].Parse()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse first box, %q: %v", boxes[0].Type(), err)
|
||||
}
|
||||
|
||||
var ok bool
|
||||
ip.PropertyContainer, ok = cb.(*ItemPropertyContainerBox)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type %T for ItemPropertieBox.PropertyContainer", cb)
|
||||
}
|
||||
|
||||
// Association boxes
|
||||
ip.Associations = make([]*ItemPropertyAssociation, 0, len(boxes)-1)
|
||||
for _, box := range boxes[1:] {
|
||||
boxp, err := box.Parse()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse association box: %v", err)
|
||||
}
|
||||
ipa, ok := boxp.(*ItemPropertyAssociation)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected box %q instead of ItemPropertyAssociation", boxp.Type())
|
||||
}
|
||||
ip.Associations = append(ip.Associations, ipa)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
type ItemPropertyAssociation struct {
|
||||
FullBox
|
||||
EntryCount uint32
|
||||
Entries []ItemPropertyAssociationItem
|
||||
}
|
||||
|
||||
// not a box
|
||||
type ItemProperty struct {
|
||||
Essential bool
|
||||
Index uint16
|
||||
}
|
||||
|
||||
// not a box
|
||||
type ItemPropertyAssociationItem struct {
|
||||
ItemID uint32
|
||||
AssociationsCount int // as declared
|
||||
Associations []ItemProperty // as parsed
|
||||
}
|
||||
|
||||
func parseItemPropertyAssociation(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipa := &ItemPropertyAssociation{FullBox: fb}
|
||||
count, _ := br.readUint32()
|
||||
ipa.EntryCount = count
|
||||
|
||||
for i := uint64(0); i < uint64(count) && br.ok(); i++ {
|
||||
var itemID uint32
|
||||
if fb.Version < 1 {
|
||||
itemID16, _ := br.readUint16()
|
||||
itemID = uint32(itemID16)
|
||||
} else {
|
||||
itemID, _ = br.readUint32()
|
||||
}
|
||||
assocCount, _ := br.readUint8()
|
||||
ipai := ItemPropertyAssociationItem{
|
||||
ItemID: itemID,
|
||||
AssociationsCount: int(assocCount),
|
||||
}
|
||||
for j := 0; j < int(assocCount) && br.ok(); j++ {
|
||||
first, _ := br.readUint8()
|
||||
essential := first&(1<<7) != 0
|
||||
first &^= byte(1 << 7)
|
||||
|
||||
var index uint16
|
||||
if fb.Flags&1 != 0 {
|
||||
second, _ := br.readUint8()
|
||||
index = uint16(first)<<8 | uint16(second)
|
||||
} else {
|
||||
index = uint16(first)
|
||||
}
|
||||
ipai.Associations = append(ipai.Associations, ItemProperty{
|
||||
Essential: essential,
|
||||
Index: index,
|
||||
})
|
||||
}
|
||||
ipa.Entries = append(ipa.Entries, ipai)
|
||||
}
|
||||
if !br.ok() {
|
||||
return nil, br.err
|
||||
}
|
||||
return ipa, nil
|
||||
}
|
||||
|
||||
type ImageSpatialExtentsProperty struct {
|
||||
FullBox
|
||||
ImageWidth uint32
|
||||
ImageHeight uint32
|
||||
}
|
||||
|
||||
func parseImageSpatialExtentsProperty(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w, err := br.readUint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, err := br.readUint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ImageSpatialExtentsProperty{
|
||||
FullBox: fb,
|
||||
ImageWidth: w,
|
||||
ImageHeight: h,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type OffsetLength struct {
|
||||
Offset, Length uint64
|
||||
}
|
||||
|
||||
// not a box
|
||||
type ItemLocationBoxEntry struct {
|
||||
ItemID uint16
|
||||
ConstructionMethod uint8 // actually uint4
|
||||
DataReferenceIndex uint16
|
||||
BaseOffset uint64 // uint32 or uint64, depending on encoding
|
||||
ExtentCount uint16
|
||||
Extents []OffsetLength
|
||||
}
|
||||
|
||||
// box "iloc"
|
||||
type ItemLocationBox struct {
|
||||
FullBox
|
||||
|
||||
offsetSize, lengthSize, baseOffsetSize, indexSize uint8 // actually uint4
|
||||
|
||||
ItemCount uint16
|
||||
Items []ItemLocationBoxEntry
|
||||
}
|
||||
|
||||
func parseItemLocationBox(outer *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(outer, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ilb := &ItemLocationBox{
|
||||
FullBox: fb,
|
||||
}
|
||||
buf, err := br.Peek(4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ilb.offsetSize = buf[0] >> 4
|
||||
ilb.lengthSize = buf[0] & 15
|
||||
ilb.baseOffsetSize = buf[1] >> 4
|
||||
if fb.Version > 0 { // version 1
|
||||
ilb.indexSize = buf[1] & 15
|
||||
}
|
||||
|
||||
ilb.ItemCount = binary.BigEndian.Uint16(buf[2:4])
|
||||
br.Discard(4)
|
||||
|
||||
for i := 0; br.ok() && i < int(ilb.ItemCount); i++ {
|
||||
var ent ItemLocationBoxEntry
|
||||
ent.ItemID, _ = br.readUint16()
|
||||
if fb.Version > 0 { // version 1
|
||||
cmeth, _ := br.readUint16()
|
||||
ent.ConstructionMethod = byte(cmeth & 15)
|
||||
}
|
||||
ent.DataReferenceIndex, _ = br.readUint16()
|
||||
if br.ok() && ilb.baseOffsetSize > 0 {
|
||||
br.Discard(int(ilb.baseOffsetSize) / 8)
|
||||
}
|
||||
ent.ExtentCount, _ = br.readUint16()
|
||||
for j := 0; br.ok() && j < int(ent.ExtentCount); j++ {
|
||||
var ol OffsetLength
|
||||
ol.Offset, _ = br.readUintN(ilb.offsetSize * 8)
|
||||
ol.Length, _ = br.readUintN(ilb.lengthSize * 8)
|
||||
if br.err != nil {
|
||||
return nil, br.err
|
||||
}
|
||||
ent.Extents = append(ent.Extents, ol)
|
||||
}
|
||||
ilb.Items = append(ilb.Items, ent)
|
||||
}
|
||||
if !br.ok() {
|
||||
return nil, br.err
|
||||
}
|
||||
return ilb, nil
|
||||
}
|
||||
|
||||
// a "hdlr" box.
|
||||
type HandlerBox struct {
|
||||
FullBox
|
||||
HandlerType string // always 4 bytes; usually "pict" for iOS Camera images
|
||||
Name string
|
||||
}
|
||||
|
||||
func parseHandlerBox(gen *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(gen, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hb := &HandlerBox{
|
||||
FullBox: fb,
|
||||
}
|
||||
buf, err := br.Peek(20)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hb.HandlerType = string(buf[4:8])
|
||||
br.Discard(20)
|
||||
|
||||
hb.Name, _ = br.readString()
|
||||
return hb, br.err
|
||||
}
|
||||
|
||||
// a "dinf" box
|
||||
type DataInformationBox struct {
|
||||
*box
|
||||
Children []Box
|
||||
}
|
||||
|
||||
func parseDataInformationBox(gen *box, br *bufReader) (Box, error) {
|
||||
dib := &DataInformationBox{box: gen}
|
||||
return dib, br.parseAppendBoxes(&dib.Children)
|
||||
}
|
||||
|
||||
// a "dref" box.
|
||||
type DataReferenceBox struct {
|
||||
FullBox
|
||||
EntryCount uint32
|
||||
Children []Box
|
||||
}
|
||||
|
||||
func parseDataReferenceBox(gen *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(gen, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
drb := &DataReferenceBox{FullBox: fb}
|
||||
drb.EntryCount, _ = br.readUint32()
|
||||
return drb, br.parseAppendBoxes(&drb.Children)
|
||||
}
|
||||
|
||||
// "pitm" box
|
||||
type PrimaryItemBox struct {
|
||||
FullBox
|
||||
ItemID uint16
|
||||
}
|
||||
|
||||
func parsePrimaryItemBox(gen *box, br *bufReader) (Box, error) {
|
||||
fb, err := readFullBox(gen, br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pib := &PrimaryItemBox{FullBox: fb}
|
||||
pib.ItemID, _ = br.readUint16()
|
||||
if !br.ok() {
|
||||
return nil, br.err
|
||||
}
|
||||
return pib, nil
|
||||
}
|
||||
|
||||
// ImageRotation is a HEIF "irot" rotation property.
|
||||
type ImageRotation struct {
|
||||
*box
|
||||
Angle uint8 // 1 means 90 degrees counter-clockwise, 2 means 180 counter-clockwise
|
||||
}
|
||||
|
||||
func parseImageRotation(gen *box, br *bufReader) (Box, error) {
|
||||
v, err := br.readUint8()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ImageRotation{box: gen, Angle: v & 3}, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue