github.com/camlistore/go4@v0.0.0-20200104003542-c7e774b10ea0/media/heif/dumpheif/dumpheif.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 // The dumpheif program dumps the structure and metadata of a HEIF file. 18 // 19 // It exists purely for debugging the go4.org/media/heif and 20 // go4.org/media/heif/bmff packages; it makes no backwards 21 // compatibility promises. 22 package main 23 24 import ( 25 "bytes" 26 "flag" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "log" 31 "os" 32 "strings" 33 34 "github.com/rwcarlsen/goexif/exif" 35 "github.com/rwcarlsen/goexif/tiff" 36 37 "go4.org/media/heif" 38 "go4.org/media/heif/bmff" 39 ) 40 41 var ( 42 exifItemID uint16 43 exifLoc bmff.ItemLocationBoxEntry 44 ) 45 46 func main() { 47 flag.Parse() 48 if flag.NArg() != 1 { 49 fmt.Fprintf(os.Stderr, "usage: dumpheif <file>\n") 50 os.Exit(1) 51 } 52 f, err := os.Open(flag.Arg(0)) 53 if err != nil { 54 log.Fatal(err) 55 } 56 defer f.Close() 57 58 hf := heif.Open(f) 59 60 it, err := hf.PrimaryItem() 61 if err != nil { 62 log.Fatalf("PrimaryItem: %v", err) 63 } 64 fmt.Printf("primary item: %v\n", it.ID) 65 66 width, height, ok := it.SpatialExtents() 67 if ok { 68 fmt.Printf("spatial extents: %d x %d\n", width, height) 69 } 70 fmt.Printf("properties:\n") 71 for _, prop := range it.Properties { 72 fmt.Printf("\t%q: %#v\n", prop.Type(), prop) 73 } 74 if len(it.Properties) == 0 { 75 fmt.Printf("\t(no properties)\n") 76 } 77 78 if ex, err := hf.EXIF(); err == nil { 79 fmt.Printf("EXIF dump:\n") 80 ex, err := exif.Decode(bytes.NewReader(ex)) 81 if err != nil { 82 log.Fatalf("EXIF decode: %v", err) 83 } 84 ex.Walk(exifWalkFunc(func(name exif.FieldName, tag *tiff.Tag) error { 85 fmt.Printf("\t%v = %v\n", name, tag) 86 return nil 87 })) 88 fmt.Printf("\n") 89 } 90 91 fmt.Printf("BMFF boxes:\n") 92 r := bmff.NewReader(f) 93 for { 94 box, err := r.ReadBox() 95 if err == io.EOF { 96 break 97 } 98 if err != nil { 99 log.Fatalf("ReadBox: %v", err) 100 } 101 dumpBox(box, 0) 102 } 103 104 } 105 106 type exifWalkFunc func(exif.FieldName, *tiff.Tag) error 107 108 func (f exifWalkFunc) Walk(name exif.FieldName, tag *tiff.Tag) error { 109 return f(name, tag) 110 } 111 112 func dumpBox(box bmff.Box, depth int) { 113 indent := strings.Repeat(" ", depth) 114 fmt.Printf("%sBox: type %q, size %v\n", indent, box.Type(), box.Size()) 115 116 box2, err := box.Parse() 117 if err == bmff.ErrUnknownBox { 118 slurp, err := ioutil.ReadAll(box.Body()) 119 if err != nil { 120 log.Fatalf("%sreading body: %v", indent, err) 121 } 122 if len(slurp) < 5000 { 123 fmt.Printf("%s- contents: %q\n", indent, slurp) 124 } else { 125 fmt.Printf("%s- contents: (... %d bytes, starting with %q ...)\n", indent, len(slurp), slurp[:100]) 126 } 127 return 128 } 129 if err != nil { 130 slurp, _ := ioutil.ReadAll(box.Body()) 131 log.Fatalf("Parse box type %q: %v; slurp: %q", box.Type(), err, slurp) 132 } 133 134 switch v := box2.(type) { 135 case *bmff.FileTypeBox, *bmff.HandlerBox, *bmff.PrimaryItemBox: 136 fmt.Printf("%s- %T: %+v\n", indent, v, v) 137 case *bmff.MetaBox: 138 fmt.Printf("%s- %T, %d children:\n", indent, v, len(v.Children)) 139 for _, child := range v.Children { 140 dumpBox(child, depth+1) 141 } 142 case *bmff.ItemInfoBox: 143 //slurp, _ := ioutil.ReadAll(box.Body()) 144 //fmt.Printf("%s- %T raw: %q\n", indent, v, slurp) 145 fmt.Printf("%s- %T, %d children (%d in slice):\n", indent, v, v.Count, len(v.ItemInfos)) 146 for _, child := range v.ItemInfos { 147 dumpBox(child, depth+1) 148 } 149 case *bmff.ItemInfoEntry: 150 fmt.Printf("%s- %T, %+v\n", indent, v, v) 151 if v.ItemType == "Exif" { 152 exifItemID = v.ItemID 153 } 154 case *bmff.ItemPropertiesBox: 155 fmt.Printf("%s- %T\n", indent, v) 156 if v.PropertyContainer != nil { 157 dumpBox(v.PropertyContainer, depth+1) 158 } 159 for _, child := range v.Associations { 160 dumpBox(child, depth+1) 161 } 162 case *bmff.ItemPropertyAssociation: 163 fmt.Printf("%s- %T: %d declared entries, %d parsed:\n", indent, v, v.EntryCount, len(v.Entries)) 164 for _, ai := range v.Entries { 165 fmt.Printf("%s for Item ID %d, %d associations declared, %d parsed:\n", indent, ai.ItemID, ai.AssociationsCount, len(ai.Associations)) 166 for _, ass := range ai.Associations { 167 fmt.Printf("%s index: %d, essential: %v\n", indent, ass.Index, ass.Essential) 168 } 169 } 170 case *bmff.DataInformationBox: 171 fmt.Printf("%s- %T\n", indent, v) 172 for _, child := range v.Children { 173 dumpBox(child, depth+1) 174 } 175 case *bmff.DataReferenceBox: 176 fmt.Printf("%s- %T\n", indent, v) 177 for _, child := range v.Children { 178 dumpBox(child, depth+1) 179 } 180 case *bmff.ItemPropertyContainerBox: 181 fmt.Printf("%s- %T\n", indent, v) 182 for _, child := range v.Properties { 183 dumpBox(child, depth+1) 184 } 185 case *bmff.ItemLocationBox: 186 fmt.Printf("%s- %T: %d items declared, %d parsed:\n", indent, v, v.ItemCount, len(v.Items)) 187 for _, lbe := range v.Items { 188 fmt.Printf("%s %+v\n", indent, lbe) 189 if exifItemID != 0 && lbe.ItemID == exifItemID { 190 exifLoc = lbe 191 } 192 } 193 194 case *bmff.ImageSpatialExtentsProperty: 195 fmt.Printf("%s- %T dimensions: %d x %d\n", indent, v, v.ImageWidth, v.ImageHeight) 196 default: 197 fmt.Printf("%s- gotype: %T\n", indent, box2) 198 } 199 200 }