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  }