libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

heif_extractor.c (48697B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2026 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/heif_extractor.c
     22  * @brief plugin to support HEIF/HEIC and AVIF still images
     23  * @author Christian Grothoff
     24  *
     25  * HEIC (the default camera format on recent iPhones) and AVIF (now
     26  * common on the web) are both ISO base media file format containers
     27  * carrying a still image instead of a movie.  We parse the container
     28  * only: no HEVC or AV1 bitstream is decoded, and no external decoder
     29  * library is linked, because this is a fast identification pass.
     30  *
     31  * Structure, per ISO/IEC 14496-12 (box structure), ISO/IEC 23008-12
     32  * (HEIF item structures) and the AV1 Image File Format (AVIF brands):
     33  *
     34  *   ftyp                       major brand + compatible brands
     35  *   meta                       (FullBox)
     36  *     hdlr                     'pict' for a still image collection
     37  *     pitm                     which item is the displayed image
     38  *     iinf / infe              the items and their types
     39  *     iref                     thumbnail / auxiliary relationships
     40  *     iprp
     41  *       ipco                   the properties, addressed by 1-based index
     42  *         ispe pixi irot imir colr av1C hvcC ...
     43  *       ipma                   item -> property index associations
     44  *   mdat                       the coded data (never read here)
     45  *   moov                       only for image sequences (bursts, Live Photos)
     46  *
     47  * Every box walk below is bounded by the extent of its parent box, never
     48  * by the size of the file, and the whole parse shares one box budget and
     49  * one byte budget so that a crafted file cannot make us loop or read the
     50  * whole volume.  There is no recursion: the nesting we care about is
     51  * fixed and spelled out as nested calls.
     52  */
     53 #include "platform.h"
     54 #include "extractor.h"
     55 #include "forensics.h"
     56 
     57 
     58 /**
     59  * Total number of boxes we are willing to look at in one file.
     60  */
     61 #define HEIF_MAX_BOXES 4096
     62 
     63 /**
     64  * Total number of bytes we are willing to read from one file.  Header
     65  * parsing needs a few kilobytes; anything beyond this is a file trying
     66  * to make us do work.
     67  */
     68 #define HEIF_READ_BUDGET (256 * 1024)
     69 
     70 /**
     71  * Maximum number of compatible brands we report.
     72  */
     73 #define HEIF_MAX_BRANDS 32
     74 
     75 /**
     76  * Maximum number of property boxes we index out of 'ipco'.
     77  */
     78 #define HEIF_MAX_PROPS 64
     79 
     80 /**
     81  * Maximum number of properties we follow for the primary item.
     82  */
     83 #define HEIF_MAX_ASSOC 32
     84 
     85 /**
     86  * Maximum number of 'infe' entries we classify.  The reported item
     87  * count still comes from the 'iinf' header, so a truncated walk does
     88  * not falsify #EXTRACTOR_METATYPE_ENTRY_COUNT.
     89  */
     90 #define HEIF_MAX_INFE 512
     91 
     92 /**
     93  * Maximum number of item references we follow.
     94  */
     95 #define HEIF_MAX_IREF 512
     96 
     97 /**
     98  * Largest 'ipma' box we pull into memory.
     99  */
    100 #define HEIF_IPMA_MAX 8192
    101 
    102 /**
    103  * Seconds between 1904-01-01 (the ISO-BMFF epoch) and 1970-01-01.
    104  */
    105 #define HEIF_EPOCH_OFFSET 2082844800LL
    106 
    107 
    108 /**
    109  * One box, as located by #heif_next_box().
    110  */
    111 struct HeifBox
    112 {
    113   /**
    114    * Four character box type.  Not NUL terminated.
    115    */
    116   unsigned char type[4];
    117 
    118   /**
    119    * Absolute offset of the first payload byte.
    120    */
    121   uint64_t payload;
    122 
    123   /**
    124    * Absolute offset one past the last payload byte.
    125    */
    126   uint64_t end;
    127 };
    128 
    129 
    130 /**
    131  * One entry of the 'ipco' property container, addressed by its 1-based
    132  * index from 'ipma'.
    133  */
    134 struct HeifProp
    135 {
    136   /**
    137    * Four character property box type.
    138    */
    139   unsigned char type[4];
    140 
    141   /**
    142    * Absolute offset of the first payload byte.
    143    */
    144   uint64_t payload;
    145 
    146   /**
    147    * Absolute offset one past the last payload byte.
    148    */
    149   uint64_t end;
    150 };
    151 
    152 
    153 /**
    154  * Everything we carry through the parse.
    155  */
    156 struct HeifState
    157 {
    158   /**
    159    * Extraction context we were handed.
    160    */
    161   struct EXTRACTOR_ExtractContext *ec;
    162 
    163   /**
    164    * Properties found in 'ipco', in file order.
    165    */
    166   struct HeifProp props[HEIF_MAX_PROPS];
    167 
    168   /**
    169    * Property indices (1-based) associated with the primary item.
    170    */
    171   unsigned int assoc[HEIF_MAX_ASSOC];
    172 
    173   /**
    174    * Bytes we may still read.
    175    */
    176   uint64_t budget;
    177 
    178   /**
    179    * Boxes we may still visit.
    180    */
    181   unsigned int boxes;
    182 
    183   /**
    184    * Number of entries used in @e props.
    185    */
    186   unsigned int num_props;
    187 
    188   /**
    189    * Number of entries used in @e assoc.
    190    */
    191   unsigned int num_assoc;
    192 
    193   /**
    194    * Item ID of the primary item, from 'pitm'.
    195    */
    196   uint32_t primary_item;
    197 
    198   /**
    199    * Number of items declared by 'iinf'.
    200    */
    201   uint32_t num_items;
    202 
    203   /**
    204    * Number of coded image items seen.
    205    */
    206   unsigned int num_image_items;
    207 
    208   /**
    209    * Number of items that are the source of a 'thmb' reference.
    210    */
    211   unsigned int num_thumbnails;
    212 
    213   /**
    214    * Number of items that are the source of an 'auxl' reference
    215    * (alpha and depth planes).
    216    */
    217   unsigned int num_auxiliary;
    218 
    219   /**
    220    * Number of 'Exif' items.
    221    */
    222   unsigned int num_exif;
    223 
    224   /**
    225    * Number of 'mime' (XMP and friends) items.
    226    */
    227   unsigned int num_mime;
    228 
    229   /**
    230    * Item type of the primary item, if we saw its 'infe'.
    231    */
    232   unsigned char primary_type[4];
    233 
    234   /**
    235    * True if @e primary_item is valid.
    236    */
    237   int have_pitm;
    238 
    239   /**
    240    * True if @e primary_type is valid.
    241    */
    242   int have_primary_type;
    243 
    244   /**
    245    * True if a 'moov' box was found, which makes this a sequence.
    246    */
    247   int have_moov;
    248 
    249   /**
    250    * Set once #EXTRACTOR_ExtractContext::proc asked us to stop.
    251    */
    252   int stop;
    253 };
    254 
    255 
    256 /**
    257  * Read @a len bytes at @a offset, charged against the read budget.
    258  *
    259  * @param hs parser state
    260  * @param offset absolute offset to read from
    261  * @param buf where to put the data
    262  * @param len number of bytes to read
    263  * @return 1 on success, 0 on a short file or an exhausted budget
    264  */
    265 static int
    266 heif_read (struct HeifState *hs,
    267            uint64_t offset,
    268            void *buf,
    269            size_t len)
    270 {
    271   if ( (len > hs->budget) ||
    272        (offset > INT64_MAX) )
    273     return 0;
    274   hs->budget -= len;
    275   return EXTRACTOR_forensic_read_ (hs->ec,
    276                                    (int64_t) offset,
    277                                    buf,
    278                                    len);
    279 }
    280 
    281 
    282 /**
    283  * Test whether @a box has the given four character type.
    284  *
    285  * @param box the box
    286  * @param type four characters to compare against
    287  * @return 1 on a match, 0 otherwise
    288  */
    289 static int
    290 heif_is (const struct HeifBox *box,
    291          const char *type)
    292 {
    293   return 0 == memcmp (box->type,
    294                       type,
    295                       4);
    296 }
    297 
    298 
    299 /**
    300  * Locate the box that starts at @a pos inside a parent that ends at
    301  * @a limit.
    302  *
    303  * Handles all three ISO-BMFF size encodings: a 32-bit size, size 1
    304  * meaning a 64-bit largesize follows the type, and size 0 meaning "up
    305  * to the end of the enclosing box".  A size that does not cover its own
    306  * header, or that runs past the parent, terminates the walk rather than
    307  * being clamped: a box that lies about its size is not one we want to
    308  * keep reading past.
    309  *
    310  * @param hs parser state
    311  * @param pos absolute offset of the box header
    312  * @param limit absolute offset one past the end of the parent
    313  * @param[out] box the box that was found
    314  * @return 1 on success, 0 if there is no further box here
    315  */
    316 static int
    317 heif_next_box (struct HeifState *hs,
    318                uint64_t pos,
    319                uint64_t limit,
    320                struct HeifBox *box)
    321 {
    322   unsigned char hdr[8];
    323   uint64_t size;
    324   unsigned int hlen = 8;
    325 
    326   if (0 == hs->boxes)
    327     return 0;
    328   hs->boxes--;
    329   if ( (pos >= limit) ||
    330        (limit - pos < 8) )
    331     return 0;
    332   if (! heif_read (hs,
    333                    pos,
    334                    hdr,
    335                    sizeof (hdr)))
    336     return 0;
    337   size = EXTRACTOR_forensic_be32_ (hdr);
    338   memcpy (box->type,
    339           &hdr[4],
    340           4);
    341   if (1 == size)
    342   {
    343     unsigned char large[8];
    344 
    345     if (limit - pos < 16)
    346       return 0;
    347     if (! heif_read (hs,
    348                      pos + 8,
    349                      large,
    350                      sizeof (large)))
    351       return 0;
    352     size = EXTRACTOR_forensic_be64_ (large);
    353     hlen = 16;
    354     if (size < 16)
    355       return 0;
    356   }
    357   else if (0 == size)
    358   {
    359     size = limit - pos;   /* extends to the end of the parent */
    360   }
    361   else if (size < 8)
    362   {
    363     return 0;   /* smaller than its own header */
    364   }
    365   if (size > limit - pos)
    366     return 0;
    367   box->payload = pos + hlen;
    368   box->end = pos + size;
    369   if (box->payload > box->end)
    370     return 0;
    371   return 1;
    372 }
    373 
    374 
    375 /**
    376  * Number of payload bytes in @a box.
    377  *
    378  * @param box the box
    379  * @return payload length
    380  */
    381 static uint64_t
    382 heif_payload_len (const struct HeifBox *box)
    383 {
    384   return box->end - box->payload;
    385 }
    386 
    387 
    388 /**
    389  * Read the version byte of a FullBox.
    390  *
    391  * @param hs parser state
    392  * @param box the box, which must be a FullBox
    393  * @param[out] version the version byte
    394  * @param[out] flags the 24-bit flags field
    395  * @return 1 on success, 0 if the box is too short
    396  */
    397 static int
    398 heif_full_header (struct HeifState *hs,
    399                   const struct HeifBox *box,
    400                   unsigned int *version,
    401                   uint32_t *flags)
    402 {
    403   unsigned char vf[4];
    404 
    405   if (heif_payload_len (box) < 4)
    406     return 0;
    407   if (! heif_read (hs,
    408                    box->payload,
    409                    vf,
    410                    sizeof (vf)))
    411     return 0;
    412   *version = vf[0];
    413   *flags = EXTRACTOR_forensic_be32_ (vf) & 0xFFFFFF;
    414   return 1;
    415 }
    416 
    417 
    418 /**
    419  * A brand we recognise, and what it says about the file.
    420  */
    421 struct HeifBrand
    422 {
    423   /**
    424    * The four character brand.
    425    */
    426   const char *brand;
    427 
    428   /**
    429    * MIME type family, without the "-sequence" suffix.
    430    */
    431   const char *family;
    432 
    433   /**
    434    * Coding format the brand implies, NULL if the brand is generic.
    435    */
    436   const char *codec;
    437 
    438   /**
    439    * True if the brand denotes an image sequence rather than a still.
    440    */
    441   int sequence;
    442 };
    443 
    444 
    445 /**
    446  * Brands that make a file ours.  'mif1'/'msf1' are the generic HEIF
    447  * brands and say nothing about the codec, which is why the compatible
    448  * brand list has to be consulted as well: "mif1" as major brand with
    449  * "avif" among the compatible brands is what several AVIF encoders
    450  * produce.
    451  */
    452 static const struct HeifBrand heif_brands[] = {
    453   { "heic", "image/heic", "HEVC", 0 },
    454   { "heix", "image/heic", "HEVC", 0 },
    455   { "heim", "image/heic", "HEVC", 0 },
    456   { "heis", "image/heic", "HEVC", 0 },
    457   { "hevc", "image/heic", "HEVC", 1 },
    458   { "hevx", "image/heic", "HEVC", 1 },
    459   { "hevm", "image/heic", "HEVC", 1 },
    460   { "hevs", "image/heic", "HEVC", 1 },
    461   { "avif", "image/avif", "AV1", 0 },
    462   { "avis", "image/avif", "AV1", 1 },
    463   { "mif1", "image/heif", NULL, 0 },
    464   { "mif2", "image/heif", NULL, 0 },
    465   { "msf1", "image/heif", NULL, 1 },
    466   { NULL, NULL, NULL, 0 }
    467 };
    468 
    469 
    470 /**
    471  * Look @a brand up in #heif_brands.
    472  *
    473  * @param brand four bytes from the file
    474  * @return the entry, or NULL if we do not know the brand
    475  */
    476 static const struct HeifBrand *
    477 heif_lookup_brand (const unsigned char *brand)
    478 {
    479   for (unsigned int i = 0; NULL != heif_brands[i].brand; i++)
    480     if (0 == memcmp (brand,
    481                      heif_brands[i].brand,
    482                      4))
    483       return &heif_brands[i];
    484   return NULL;
    485 }
    486 
    487 
    488 /**
    489  * Human readable name for an item type as it appears in 'infe'.
    490  *
    491  * @param type four bytes from the file
    492  * @return the name, or NULL if we do not know the type
    493  */
    494 static const char *
    495 heif_item_type_name (const unsigned char *type)
    496 {
    497   static const struct
    498   {
    499     const char *type;
    500     const char *name;
    501   } names[] = {
    502     { "hvc1", "HEVC" },
    503     { "hev1", "HEVC" },
    504     { "av01", "AV1" },
    505     { "avc1", "AVC" },
    506     { "vvc1", "VVC" },
    507     { "jpeg", "JPEG" },
    508     { "j2k1", "JPEG 2000" },
    509     { "grid", "tiled grid" },
    510     { "iovl", "overlay" },
    511     { "iden", "identity" },
    512     { "mask", "mask" },
    513     { NULL, NULL }
    514   };
    515 
    516   for (unsigned int i = 0; NULL != names[i].type; i++)
    517     if (0 == memcmp (type,
    518                      names[i].type,
    519                      4))
    520       return names[i].name;
    521   return NULL;
    522 }
    523 
    524 
    525 /**
    526  * True if @a type names a coded or derived image item, as opposed to a
    527  * metadata item such as 'Exif' or 'mime'.
    528  *
    529  * @param type four bytes from the file
    530  * @return 1 if this is an image item
    531  */
    532 static int
    533 heif_is_image_item (const unsigned char *type)
    534 {
    535   return NULL != heif_item_type_name (type);
    536 }
    537 
    538 
    539 /**
    540  * Parse the 'pitm' box, which names the item that a viewer displays.
    541  *
    542  * @param hs parser state
    543  * @param box the 'pitm' box
    544  */
    545 static void
    546 heif_parse_pitm (struct HeifState *hs,
    547                  const struct HeifBox *box)
    548 {
    549   unsigned char buf[4];
    550   unsigned int version;
    551   uint32_t flags;
    552 
    553   if (! heif_full_header (hs,
    554                           box,
    555                           &version,
    556                           &flags))
    557     return;
    558   if (0 == version)
    559   {
    560     if (heif_payload_len (box) < 6)
    561       return;
    562     if (! heif_read (hs,
    563                      box->payload + 4,
    564                      buf,
    565                      2))
    566       return;
    567     hs->primary_item = EXTRACTOR_forensic_be16_ (buf);
    568   }
    569   else
    570   {
    571     if (heif_payload_len (box) < 8)
    572       return;
    573     if (! heif_read (hs,
    574                      box->payload + 4,
    575                      buf,
    576                      4))
    577       return;
    578     hs->primary_item = EXTRACTOR_forensic_be32_ (buf);
    579   }
    580   hs->have_pitm = 1;
    581 }
    582 
    583 
    584 /**
    585  * Parse the 'iinf' box and its 'infe' children: how many items there
    586  * are, what kinds they are, and what the primary item is made of.
    587  *
    588  * @param hs parser state
    589  * @param box the 'iinf' box
    590  */
    591 static void
    592 heif_parse_iinf (struct HeifState *hs,
    593                  const struct HeifBox *box)
    594 {
    595   unsigned char buf[16];
    596   unsigned int version;
    597   uint32_t flags;
    598   uint64_t pos;
    599   unsigned int n;
    600 
    601   if (! heif_full_header (hs,
    602                           box,
    603                           &version,
    604                           &flags))
    605     return;
    606   if (0 == version)
    607   {
    608     if (heif_payload_len (box) < 6)
    609       return;
    610     if (! heif_read (hs,
    611                      box->payload + 4,
    612                      buf,
    613                      2))
    614       return;
    615     hs->num_items = EXTRACTOR_forensic_be16_ (buf);
    616     pos = box->payload + 6;
    617   }
    618   else
    619   {
    620     if (heif_payload_len (box) < 8)
    621       return;
    622     if (! heif_read (hs,
    623                      box->payload + 4,
    624                      buf,
    625                      4))
    626       return;
    627     hs->num_items = EXTRACTOR_forensic_be32_ (buf);
    628     pos = box->payload + 8;
    629   }
    630   n = 0;
    631   while ( (pos < box->end) &&
    632           (n < HEIF_MAX_INFE) )
    633   {
    634     struct HeifBox infe;
    635     unsigned int iversion;
    636     uint32_t item_id;
    637     const unsigned char *item_type;
    638     size_t need;
    639 
    640     if (! heif_next_box (hs,
    641                          pos,
    642                          box->end,
    643                          &infe))
    644       break;
    645     if (infe.end <= pos)
    646       break;   /* no forward progress */
    647     pos = infe.end;
    648     n++;
    649     if (! heif_is (&infe,
    650                    "infe"))
    651       continue;
    652     /* ItemInfoEntry: version 2 uses a 16-bit item_ID, version 3 and up
    653        a 32-bit one; both are followed by a 16-bit protection index and
    654        then the four character item_type.  Versions 0 and 1 predate
    655        item_type and carry a file name instead, which tells us nothing
    656        about the coding format. */
    657     need = 14;
    658     if (heif_payload_len (&infe) < 4)
    659       continue;
    660     if (heif_payload_len (&infe) < need)
    661       need = (size_t) heif_payload_len (&infe);
    662     if (need > sizeof (buf))
    663       need = sizeof (buf);
    664     if (! heif_read (hs,
    665                      infe.payload,
    666                      buf,
    667                      need))
    668       continue;
    669     iversion = buf[0];
    670     if (2 == iversion)
    671     {
    672       if (need < 12)
    673         continue;
    674       item_id = EXTRACTOR_forensic_be16_ (&buf[4]);
    675       item_type = &buf[8];
    676     }
    677     else if (iversion >= 3)
    678     {
    679       if (need < 14)
    680         continue;
    681       item_id = EXTRACTOR_forensic_be32_ (&buf[4]);
    682       item_type = &buf[10];
    683     }
    684     else
    685     {
    686       continue;   /* no item_type in this version */
    687     }
    688     if (heif_is_image_item (item_type))
    689       hs->num_image_items++;
    690     else if (0 == memcmp (item_type,
    691                           "Exif",
    692                           4))
    693       hs->num_exif++;
    694     else if (0 == memcmp (item_type,
    695                           "mime",
    696                           4))
    697       hs->num_mime++;
    698     if ( (hs->have_pitm) &&
    699          (item_id == hs->primary_item) )
    700     {
    701       memcpy (hs->primary_type,
    702               item_type,
    703               4);
    704       hs->have_primary_type = 1;
    705     }
    706   }
    707 }
    708 
    709 
    710 /**
    711  * Parse the 'iref' box: which items are thumbnails of, or auxiliary
    712  * planes for, another item.
    713  *
    714  * @param hs parser state
    715  * @param box the 'iref' box
    716  */
    717 static void
    718 heif_parse_iref (struct HeifState *hs,
    719                  const struct HeifBox *box)
    720 {
    721   unsigned int version;
    722   uint32_t flags;
    723   uint64_t pos;
    724   unsigned int n;
    725 
    726   if (! heif_full_header (hs,
    727                           box,
    728                           &version,
    729                           &flags))
    730     return;
    731   pos = box->payload + 4;
    732   n = 0;
    733   while ( (pos < box->end) &&
    734           (n < HEIF_MAX_IREF) )
    735   {
    736     struct HeifBox ref;
    737 
    738     if (! heif_next_box (hs,
    739                          pos,
    740                          box->end,
    741                          &ref))
    742       break;
    743     if (ref.end <= pos)
    744       break;   /* no forward progress */
    745     pos = ref.end;
    746     n++;
    747     /* Each child is one reference: its type is the relationship and its
    748        from_item_ID is the item that plays the subordinate role. */
    749     if (heif_is (&ref,
    750                  "thmb"))
    751       hs->num_thumbnails++;
    752     else if (heif_is (&ref,
    753                       "auxl"))
    754       hs->num_auxiliary++;
    755   }
    756 }
    757 
    758 
    759 /**
    760  * Parse the 'ipco' box, indexing its children so that 'ipma' can refer
    761  * to them by their 1-based position.
    762  *
    763  * @param hs parser state
    764  * @param box the 'ipco' box
    765  */
    766 static void
    767 heif_parse_ipco (struct HeifState *hs,
    768                  const struct HeifBox *box)
    769 {
    770   uint64_t pos = box->payload;
    771 
    772   while ( (pos < box->end) &&
    773           (hs->num_props < HEIF_MAX_PROPS) )
    774   {
    775     struct HeifBox prop;
    776 
    777     if (! heif_next_box (hs,
    778                          pos,
    779                          box->end,
    780                          &prop))
    781       break;
    782     if (prop.end <= pos)
    783       break;   /* no forward progress */
    784     pos = prop.end;
    785     memcpy (hs->props[hs->num_props].type,
    786             prop.type,
    787             4);
    788     hs->props[hs->num_props].payload = prop.payload;
    789     hs->props[hs->num_props].end = prop.end;
    790     hs->num_props++;
    791   }
    792 }
    793 
    794 
    795 /**
    796  * Parse the 'ipma' box and collect the property indices associated with
    797  * the primary item.
    798  *
    799  * @param hs parser state
    800  * @param box the 'ipma' box
    801  */
    802 static void
    803 heif_parse_ipma (struct HeifState *hs,
    804                  const struct HeifBox *box)
    805 {
    806   unsigned char *buf;
    807   size_t len;
    808   size_t off;
    809   unsigned int version;
    810   uint32_t flags;
    811   uint32_t entries;
    812   int wide_index;
    813 
    814   if (! hs->have_pitm)
    815     return;
    816   if (0 != hs->num_assoc)
    817     return;   /* a previous 'ipma' already answered the question */
    818   len = (size_t) heif_payload_len (box);
    819   if (len < 8)
    820     return;
    821   if (len > HEIF_IPMA_MAX)
    822     len = HEIF_IPMA_MAX;
    823   buf = malloc (len);
    824   if (NULL == buf)
    825     return;
    826   if (! heif_read (hs,
    827                    box->payload,
    828                    buf,
    829                    len))
    830   {
    831     free (buf);
    832     return;
    833   }
    834   version = buf[0];
    835   flags = EXTRACTOR_forensic_be32_ (buf) & 0xFFFFFF;
    836   wide_index = (0 != (flags & 1));
    837   entries = EXTRACTOR_forensic_be32_ (&buf[4]);
    838   off = 8;
    839   /* The entry count is a 32-bit field out of the file, so it is not a
    840      loop bound we can trust; the buffer running out is what actually
    841      ends the walk, and `truncated' carries that out of the inner
    842      loop without ever incrementing `e' past its own maximum. */
    843   for (uint32_t e = 0; e < entries; e++)
    844   {
    845     uint32_t item_id;
    846     unsigned int count;
    847     int mine;
    848     int truncated = 0;
    849 
    850     if (version < 1)
    851     {
    852       if (off + 2 > len)
    853         break;
    854       item_id = EXTRACTOR_forensic_be16_ (&buf[off]);
    855       off += 2;
    856     }
    857     else
    858     {
    859       if (off + 4 > len)
    860         break;
    861       item_id = EXTRACTOR_forensic_be32_ (&buf[off]);
    862       off += 4;
    863     }
    864     if (off >= len)
    865       break;
    866     count = buf[off];
    867     off++;
    868     mine = (item_id == hs->primary_item);
    869     for (unsigned int a = 0; a < count; a++)
    870     {
    871       unsigned int index;
    872 
    873       if (wide_index)
    874       {
    875         if (off + 2 > len)
    876         {
    877           truncated = 1;
    878           break;
    879         }
    880         index = EXTRACTOR_forensic_be16_ (&buf[off]) & 0x7FFF;
    881         off += 2;
    882       }
    883       else
    884       {
    885         if (off + 1 > len)
    886         {
    887           truncated = 1;
    888           break;
    889         }
    890         index = buf[off] & 0x7F;
    891         off++;
    892       }
    893       if ( (mine) &&
    894            (0 != index) &&
    895            (hs->num_assoc < HEIF_MAX_ASSOC) )
    896         hs->assoc[hs->num_assoc++] = index;
    897     }
    898     if (truncated)
    899       break;
    900   }
    901   free (buf);
    902 }
    903 
    904 
    905 /**
    906  * Emit the image dimensions from an 'ispe' property.
    907  *
    908  * @param hs parser state
    909  * @param prop the property
    910  */
    911 static void
    912 heif_emit_ispe (struct HeifState *hs,
    913                 const struct HeifProp *prop)
    914 {
    915   unsigned char buf[12];
    916 
    917   if (prop->end - prop->payload < 12)
    918     return;
    919   if (! heif_read (hs,
    920                    prop->payload,
    921                    buf,
    922                    sizeof (buf)))
    923     return;
    924   if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
    925                                      "heif",
    926                                      EXTRACTOR_METATYPE_IMAGE_DIMENSIONS,
    927                                      "%ux%u",
    928                                      (unsigned int) EXTRACTOR_forensic_be32_ (
    929                                        &buf[4]),
    930                                      (unsigned int) EXTRACTOR_forensic_be32_ (
    931                                        &buf[8])))
    932     hs->stop = 1;
    933 }
    934 
    935 
    936 /**
    937  * Emit the bit depth from a 'pixi' property.  A 10-bit still is the
    938  * usual signal that the file carries HDR content.
    939  *
    940  * @param hs parser state
    941  * @param prop the property
    942  */
    943 static void
    944 heif_emit_pixi (struct HeifState *hs,
    945                 const struct HeifProp *prop)
    946 {
    947   unsigned char buf[8];
    948   unsigned int channels;
    949   uint64_t len = prop->end - prop->payload;
    950 
    951   if (len < 5)
    952     return;
    953   if (! heif_read (hs,
    954                    prop->payload,
    955                    buf,
    956                    5))
    957     return;
    958   channels = buf[4];
    959   if ( (0 == channels) ||
    960        (channels > 4) ||
    961        (len < 5 + channels) )
    962     return;
    963   if (! heif_read (hs,
    964                    prop->payload + 5,
    965                    buf,
    966                    channels))
    967     return;
    968   /* All channels normally share a depth; report the first and say how
    969      many planes there are, which distinguishes monochrome from YUV. */
    970   if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
    971                                      "heif",
    972                                      EXTRACTOR_METATYPE_COLOR_DEPTH,
    973                                      "%u",
    974                                      (unsigned int) buf[0]))
    975     hs->stop = 1;
    976 }
    977 
    978 
    979 /**
    980  * Emit the rotation recorded in an 'irot' property.
    981  *
    982  * @param hs parser state
    983  * @param prop the property
    984  */
    985 static void
    986 heif_emit_irot (struct HeifState *hs,
    987                 const struct HeifProp *prop)
    988 {
    989   unsigned char buf[1];
    990 
    991   if (prop->end - prop->payload < 1)
    992     return;
    993   if (! heif_read (hs,
    994                    prop->payload,
    995                    buf,
    996                    1))
    997     return;
    998   /* The low two bits count anti-clockwise quarter turns to apply when
    999      displaying the image. */
   1000   if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1001                                      "heif",
   1002                                      EXTRACTOR_METATYPE_ORIENTATION,
   1003                                      "rotate %u degrees counter-clockwise",
   1004                                      (unsigned int) (buf[0] & 0x03) * 90))
   1005     hs->stop = 1;
   1006 }
   1007 
   1008 
   1009 /**
   1010  * Emit the mirroring recorded in an 'imir' property.
   1011  *
   1012  * @param hs parser state
   1013  * @param prop the property
   1014  */
   1015 static void
   1016 heif_emit_imir (struct HeifState *hs,
   1017                 const struct HeifProp *prop)
   1018 {
   1019   unsigned char buf[1];
   1020 
   1021   if (prop->end - prop->payload < 1)
   1022     return;
   1023   if (! heif_read (hs,
   1024                    prop->payload,
   1025                    buf,
   1026                    1))
   1027     return;
   1028   /* axis == 0 mirrors about a vertical axis, i.e. left and right are
   1029      swapped; axis == 1 mirrors about a horizontal axis. */
   1030   if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1031                                      "heif",
   1032                                      EXTRACTOR_METATYPE_ORIENTATION,
   1033                                      "mirrored %s",
   1034                                      (0 == (buf[0] & 0x01))
   1035                                      ? "horizontally"
   1036                                      : "vertically"))
   1037     hs->stop = 1;
   1038 }
   1039 
   1040 
   1041 /**
   1042  * Emit the colour information from a 'colr' property.
   1043  *
   1044  * @param hs parser state
   1045  * @param prop the property
   1046  */
   1047 static void
   1048 heif_emit_colr (struct HeifState *hs,
   1049                 const struct HeifProp *prop)
   1050 {
   1051   unsigned char buf[12];
   1052   uint64_t len = prop->end - prop->payload;
   1053 
   1054   if (len < 4)
   1055     return;
   1056   if (! heif_read (hs,
   1057                    prop->payload,
   1058                    buf,
   1059                    (len < sizeof (buf)) ? (size_t) len : sizeof (buf)))
   1060     return;
   1061   if (0 == memcmp (buf,
   1062                    "nclx",
   1063                    4))
   1064   {
   1065     unsigned int primaries;
   1066     unsigned int transfer;
   1067     unsigned int matrix;
   1068     const char *hdr = NULL;
   1069 
   1070     if (len < 11)
   1071       return;
   1072     primaries = EXTRACTOR_forensic_be16_ (&buf[4]);
   1073     transfer = EXTRACTOR_forensic_be16_ (&buf[6]);
   1074     matrix = EXTRACTOR_forensic_be16_ (&buf[8]);
   1075     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1076                                        "heif",
   1077                                        EXTRACTOR_METATYPE_COLOR_PROFILE,
   1078                                        "nclx: primaries %u, transfer %u, "
   1079                                        "matrix %u, %s range",
   1080                                        primaries,
   1081                                        transfer,
   1082                                        matrix,
   1083                                        (0 != (buf[10] & 0x80))
   1084                                        ? "full"
   1085                                        : "limited"))
   1086     {
   1087       hs->stop = 1;
   1088       return;
   1089     }
   1090     /* Transfer characteristics 16 and 18 are the two HDR curves; they
   1091        are worth spelling out because they are what distinguishes an HDR
   1092        capture from an ordinary one. */
   1093     if (16 == transfer)
   1094       hdr = "HDR: PQ transfer function (SMPTE ST 2084)";
   1095     else if (18 == transfer)
   1096       hdr = "HDR: HLG transfer function (ARIB STD-B67)";
   1097     if (NULL != hdr)
   1098     {
   1099       if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1100                                          "heif",
   1101                                          EXTRACTOR_METATYPE_COMMENT,
   1102                                          "%s",
   1103                                          hdr))
   1104         hs->stop = 1;
   1105     }
   1106     return;
   1107   }
   1108   if ( (0 == memcmp (buf,
   1109                      "prof",
   1110                      4)) ||
   1111        (0 == memcmp (buf,
   1112                      "rICC",
   1113                      4)) )
   1114   {
   1115     /* We deliberately do not parse the ICC payload: exiv2 and the
   1116        colour management stack do that far better than we could. */
   1117     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1118                                        "heif",
   1119                                        EXTRACTOR_METATYPE_COLOR_PROFILE,
   1120                                        "ICC profile (%.4s), %llu bytes",
   1121                                        (const char *) buf,
   1122                                        (unsigned long long) (len - 4)))
   1123       hs->stop = 1;
   1124   }
   1125 }
   1126 
   1127 
   1128 /**
   1129  * Emit what an 'av1C' configuration record says about the bit depth,
   1130  * which is the fallback when the file carries no 'pixi'.
   1131  *
   1132  * @param hs parser state
   1133  * @param prop the property
   1134  * @param have_pixi true if a 'pixi' property will report the depth
   1135  */
   1136 static void
   1137 heif_emit_av1c (struct HeifState *hs,
   1138                 const struct HeifProp *prop,
   1139                 int have_pixi)
   1140 {
   1141   unsigned char buf[4];
   1142   unsigned int depth;
   1143 
   1144   if (have_pixi)
   1145     return;
   1146   if (prop->end - prop->payload < 4)
   1147     return;
   1148   if (! heif_read (hs,
   1149                    prop->payload,
   1150                    buf,
   1151                    sizeof (buf)))
   1152     return;
   1153   if (0x81 != buf[0])
   1154     return;   /* marker bit plus version 1 */
   1155   /* byte 2: seq_tier_0(1) high_bitdepth(1) twelve_bit(1) monochrome(1)
   1156      chroma_subsampling_x(1) chroma_subsampling_y(1)
   1157      chroma_sample_position(2) */
   1158   if (0 != (buf[2] & 0x40))
   1159     depth = (0 != (buf[2] & 0x20)) ? 12 : 10;
   1160   else
   1161     depth = 8;
   1162   if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1163                                      "heif",
   1164                                      EXTRACTOR_METATYPE_COLOR_DEPTH,
   1165                                      "%u",
   1166                                      depth))
   1167     hs->stop = 1;
   1168 }
   1169 
   1170 
   1171 /**
   1172  * Walk the properties selected for the primary item and emit what they
   1173  * say.  Only the first property of each kind is reported: a file may
   1174  * legally associate several, but the extra ones describe alternative
   1175  * renderings rather than the image a user would see.
   1176  *
   1177  * @param hs parser state
   1178  */
   1179 static void
   1180 heif_emit_properties (struct HeifState *hs)
   1181 {
   1182   unsigned int order[HEIF_MAX_PROPS];
   1183   unsigned int n = 0;
   1184   int did_ispe = 0;
   1185   int did_pixi = 0;
   1186   int did_irot = 0;
   1187   int did_imir = 0;
   1188   int did_colr = 0;
   1189   int did_av1c = 0;
   1190   int have_pixi = 0;
   1191 
   1192   if (0 != hs->num_assoc)
   1193   {
   1194     for (unsigned int i = 0; i < hs->num_assoc; i++)
   1195       if (hs->assoc[i] <= hs->num_props)
   1196         order[n++] = hs->assoc[i] - 1;
   1197   }
   1198   else
   1199   {
   1200     /* No usable association: fall back to file order.  The first 'ispe'
   1201        in 'ipco' belongs to a thumbnail often enough that this is worth
   1202        flagging, but it is still better than reporting nothing. */
   1203     for (unsigned int i = 0; i < hs->num_props; i++)
   1204       order[n++] = i;
   1205   }
   1206   /* 'pixi' wins over 'av1C' for the bit depth, so find out whether one
   1207      is coming before deciding to use the configuration record. */
   1208   for (unsigned int i = 0; i < n; i++)
   1209     if (0 == memcmp (hs->props[order[i]].type,
   1210                      "pixi",
   1211                      4))
   1212       have_pixi = 1;
   1213   for (unsigned int i = 0; i < n; i++)
   1214   {
   1215     const struct HeifProp *prop = &hs->props[order[i]];
   1216 
   1217     if (hs->stop)
   1218       return;
   1219     if ( (0 == memcmp (prop->type, "ispe", 4)) &&
   1220          (! did_ispe) )
   1221     {
   1222       did_ispe = 1;
   1223       heif_emit_ispe (hs,
   1224                       prop);
   1225     }
   1226     else if ( (0 == memcmp (prop->type, "pixi", 4)) &&
   1227               (! did_pixi) )
   1228     {
   1229       did_pixi = 1;
   1230       heif_emit_pixi (hs,
   1231                       prop);
   1232     }
   1233     else if ( (0 == memcmp (prop->type, "irot", 4)) &&
   1234               (! did_irot) )
   1235     {
   1236       did_irot = 1;
   1237       heif_emit_irot (hs,
   1238                       prop);
   1239     }
   1240     else if ( (0 == memcmp (prop->type, "imir", 4)) &&
   1241               (! did_imir) )
   1242     {
   1243       did_imir = 1;
   1244       heif_emit_imir (hs,
   1245                       prop);
   1246     }
   1247     else if ( (0 == memcmp (prop->type, "colr", 4)) &&
   1248               (! did_colr) )
   1249     {
   1250       did_colr = 1;
   1251       heif_emit_colr (hs,
   1252                       prop);
   1253     }
   1254     else if ( (0 == memcmp (prop->type, "av1C", 4)) &&
   1255               (! did_av1c) )
   1256     {
   1257       did_av1c = 1;
   1258       heif_emit_av1c (hs,
   1259                       prop,
   1260                       have_pixi);
   1261     }
   1262   }
   1263 }
   1264 
   1265 
   1266 /**
   1267  * Parse the 'iprp' box: the property container and the associations.
   1268  *
   1269  * @param hs parser state
   1270  * @param box the 'iprp' box
   1271  */
   1272 static void
   1273 heif_parse_iprp (struct HeifState *hs,
   1274                  const struct HeifBox *box)
   1275 {
   1276   uint64_t pos = box->payload;
   1277 
   1278   /* 'ipco' always precedes 'ipma' in practice, but do not rely on it:
   1279      index the container first in one pass, then read the associations. */
   1280   while (pos < box->end)
   1281   {
   1282     struct HeifBox child;
   1283 
   1284     if (! heif_next_box (hs,
   1285                          pos,
   1286                          box->end,
   1287                          &child))
   1288       break;
   1289     if (child.end <= pos)
   1290       break;   /* no forward progress */
   1291     pos = child.end;
   1292     if (heif_is (&child,
   1293                  "ipco"))
   1294       heif_parse_ipco (hs,
   1295                        &child);
   1296   }
   1297   pos = box->payload;
   1298   while (pos < box->end)
   1299   {
   1300     struct HeifBox child;
   1301 
   1302     if (! heif_next_box (hs,
   1303                          pos,
   1304                          box->end,
   1305                          &child))
   1306       break;
   1307     if (child.end <= pos)
   1308       break;
   1309     pos = child.end;
   1310     if (heif_is (&child,
   1311                  "ipma"))
   1312       heif_parse_ipma (hs,
   1313                        &child);
   1314   }
   1315 }
   1316 
   1317 
   1318 /**
   1319  * Find where the children of a 'meta' box start.
   1320  *
   1321  * In ISO/IEC 14496-12 'meta' is a FullBox, so its children begin four
   1322  * bytes into the payload.  QuickTime writes the same box without the
   1323  * version and flags word, and such files do turn up; probe for a
   1324  * plausible child box at both offsets rather than guessing.
   1325  *
   1326  * @param hs parser state
   1327  * @param box the 'meta' box
   1328  * @param[out] start where the children begin
   1329  * @return 1 if a starting offset was found
   1330  */
   1331 static int
   1332 heif_meta_children (struct HeifState *hs,
   1333                     const struct HeifBox *box,
   1334                     uint64_t *start)
   1335 {
   1336   struct HeifBox probe;
   1337 
   1338   if ( (heif_payload_len (box) > 4) &&
   1339        (heif_next_box (hs,
   1340                        box->payload + 4,
   1341                        box->end,
   1342                        &probe)) )
   1343   {
   1344     *start = box->payload + 4;
   1345     return 1;
   1346   }
   1347   if (heif_next_box (hs,
   1348                      box->payload,
   1349                      box->end,
   1350                      &probe))
   1351   {
   1352     *start = box->payload;
   1353     return 1;
   1354   }
   1355   return 0;
   1356 }
   1357 
   1358 
   1359 /**
   1360  * Parse the 'meta' box: the items, their properties and their
   1361  * relationships.
   1362  *
   1363  * @param hs parser state
   1364  * @param box the 'meta' box
   1365  * @param brand_codec coding format implied by the brand, may be NULL
   1366  */
   1367 static void
   1368 heif_parse_meta (struct HeifState *hs,
   1369                  const struct HeifBox *box,
   1370                  const char *brand_codec)
   1371 {
   1372   struct HeifBox pitm;
   1373   struct HeifBox iinf;
   1374   struct HeifBox iref;
   1375   struct HeifBox iprp;
   1376   int have_pitm_box = 0;
   1377   int have_iinf = 0;
   1378   int have_iref = 0;
   1379   int have_iprp = 0;
   1380   uint64_t pos;
   1381 
   1382   memset (&pitm, 0, sizeof (pitm));
   1383   memset (&iinf, 0, sizeof (iinf));
   1384   memset (&iref, 0, sizeof (iref));
   1385   memset (&iprp, 0, sizeof (iprp));
   1386   if (! heif_meta_children (hs,
   1387                             box,
   1388                             &pos))
   1389     return;
   1390   while (pos < box->end)
   1391   {
   1392     struct HeifBox child;
   1393 
   1394     if (! heif_next_box (hs,
   1395                          pos,
   1396                          box->end,
   1397                          &child))
   1398       break;
   1399     if (child.end <= pos)
   1400       break;   /* no forward progress */
   1401     pos = child.end;
   1402     if ( (heif_is (&child, "pitm")) &&
   1403          (! have_pitm_box) )
   1404     {
   1405       pitm = child;
   1406       have_pitm_box = 1;
   1407     }
   1408     else if ( (heif_is (&child, "iinf")) &&
   1409               (! have_iinf) )
   1410     {
   1411       iinf = child;
   1412       have_iinf = 1;
   1413     }
   1414     else if ( (heif_is (&child, "iref")) &&
   1415               (! have_iref) )
   1416     {
   1417       iref = child;
   1418       have_iref = 1;
   1419     }
   1420     else if ( (heif_is (&child, "iprp")) &&
   1421               (! have_iprp) )
   1422     {
   1423       iprp = child;
   1424       have_iprp = 1;
   1425     }
   1426   }
   1427   /* The order below is ours, not the file's: the primary item ID has to
   1428      be known before 'iinf' and 'ipma' can be interpreted. */
   1429   if (have_pitm_box)
   1430     heif_parse_pitm (hs,
   1431                      &pitm);
   1432   if (have_iinf)
   1433     heif_parse_iinf (hs,
   1434                      &iinf);
   1435   if (have_iref)
   1436     heif_parse_iref (hs,
   1437                      &iref);
   1438 
   1439   /* CODEC: the item type of the primary item is more specific than the
   1440      brand, so prefer it and fall back to the brand. */
   1441   if (hs->have_primary_type)
   1442   {
   1443     const char *name = heif_item_type_name (hs->primary_type);
   1444 
   1445     if (NULL != name)
   1446     {
   1447       if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1448                                          "heif",
   1449                                          EXTRACTOR_METATYPE_CODEC,
   1450                                          "%s (%.4s)",
   1451                                          name,
   1452                                          (const char *) hs->primary_type))
   1453       {
   1454         hs->stop = 1;
   1455         return;
   1456       }
   1457       if ( (0 == memcmp (hs->primary_type, "grid", 4)) ||
   1458            (0 == memcmp (hs->primary_type, "iovl", 4)) ||
   1459            (0 == memcmp (hs->primary_type, "iden", 4)) )
   1460       {
   1461         if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1462                                            "heif",
   1463                                            EXTRACTOR_METATYPE_RESOURCE_TYPE,
   1464                                            "derived image (%.4s)",
   1465                                            (const char *) hs->primary_type))
   1466         {
   1467           hs->stop = 1;
   1468           return;
   1469         }
   1470       }
   1471     }
   1472   }
   1473   else if (NULL != brand_codec)
   1474   {
   1475     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1476                                        "heif",
   1477                                        EXTRACTOR_METATYPE_CODEC,
   1478                                        "%s",
   1479                                        brand_codec))
   1480     {
   1481       hs->stop = 1;
   1482       return;
   1483     }
   1484   }
   1485 
   1486   if (have_iprp)
   1487   {
   1488     heif_parse_iprp (hs,
   1489                      &iprp);
   1490     heif_emit_properties (hs);
   1491     if (hs->stop)
   1492       return;
   1493   }
   1494 
   1495   if (0 != hs->num_items)
   1496   {
   1497     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1498                                        "heif",
   1499                                        EXTRACTOR_METATYPE_ENTRY_COUNT,
   1500                                        "%u",
   1501                                        (unsigned int) hs->num_items))
   1502     {
   1503       hs->stop = 1;
   1504       return;
   1505     }
   1506     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1507                                        "heif",
   1508                                        EXTRACTOR_METATYPE_COMMENT,
   1509                                        "%u image item%s, %u thumbnail%s, "
   1510                                        "%u auxiliary, %u metadata item%s",
   1511                                        hs->num_image_items,
   1512                                        (1 == hs->num_image_items) ? "" : "s",
   1513                                        hs->num_thumbnails,
   1514                                        (1 == hs->num_thumbnails) ? "" : "s",
   1515                                        hs->num_auxiliary,
   1516                                        hs->num_exif + hs->num_mime,
   1517                                        (1 == hs->num_exif + hs->num_mime)
   1518                                        ? "" : "s"))
   1519     {
   1520       hs->stop = 1;
   1521       return;
   1522     }
   1523   }
   1524   /* We do not touch the payload of these items: parsing EXIF and XMP is
   1525      the exiv2 plugin's job.  Saying that they are there is what lets a
   1526      downstream tool decide to run it. */
   1527   if (0 != hs->num_exif)
   1528   {
   1529     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1530                                        "heif",
   1531                                        EXTRACTOR_METATYPE_COMMENT,
   1532                                        "contains an Exif metadata item"))
   1533     {
   1534       hs->stop = 1;
   1535       return;
   1536     }
   1537   }
   1538   if (0 != hs->num_mime)
   1539   {
   1540     if (0 != EXTRACTOR_forensic_emit_ (hs->ec,
   1541                                        "heif",
   1542                                        EXTRACTOR_METATYPE_COMMENT,
   1543                                        "contains an XMP or MIME metadata item"))
   1544     {
   1545       hs->stop = 1;
   1546       return;
   1547     }
   1548   }
   1549 }
   1550 
   1551 
   1552 /**
   1553  * Parse 'moov' and its 'mvhd', which only a sequence file has.
   1554  *
   1555  * @param hs parser state
   1556  * @param box the 'moov' box
   1557  */
   1558 static void
   1559 heif_parse_moov (struct HeifState *hs,
   1560                  const struct HeifBox *box)
   1561 {
   1562   uint64_t pos = box->payload;
   1563 
   1564   while (pos < box->end)
   1565   {
   1566     struct HeifBox child;
   1567     unsigned char buf[28];
   1568     int64_t created;
   1569     int64_t modified;
   1570 
   1571     if (! heif_next_box (hs,
   1572                          pos,
   1573                          box->end,
   1574                          &child))
   1575       break;
   1576     if (child.end <= pos)
   1577       break;   /* no forward progress */
   1578     pos = child.end;
   1579     if (! heif_is (&child,
   1580                    "mvhd"))
   1581       continue;
   1582     if (heif_payload_len (&child) < 5)
   1583       return;
   1584     if (! heif_read (hs,
   1585                      child.payload,
   1586                      buf,
   1587                      5))
   1588       return;
   1589     /* The ISO-BMFF epoch is 1904-01-01; these fields are also famously
   1590        written as local time labelled as UTC by most muxers, so treat
   1591        the result as an approximate wall clock reading, not as UTC. */
   1592     if (0 == buf[0])
   1593     {
   1594       if (heif_payload_len (&child) < 20)
   1595         return;
   1596       if (! heif_read (hs,
   1597                        child.payload + 4,
   1598                        buf,
   1599                        16))
   1600         return;
   1601       created = (int64_t) EXTRACTOR_forensic_be32_ (&buf[0]);
   1602       modified = (int64_t) EXTRACTOR_forensic_be32_ (&buf[4]);
   1603     }
   1604     else
   1605     {
   1606       if (heif_payload_len (&child) < 32)
   1607         return;
   1608       if (! heif_read (hs,
   1609                        child.payload + 4,
   1610                        buf,
   1611                        28))
   1612         return;
   1613       created = (int64_t) (EXTRACTOR_forensic_be64_ (&buf[0])
   1614                            & INT64_MAX);
   1615       modified = (int64_t) (EXTRACTOR_forensic_be64_ (&buf[8])
   1616                             & INT64_MAX);
   1617     }
   1618     if (0 != created)
   1619     {
   1620       if (0 != EXTRACTOR_forensic_emit_unix_time_ (hs->ec,
   1621                                                    "heif",
   1622                                                    EXTRACTOR_METATYPE_CREATION_DATE,
   1623                                                    created - HEIF_EPOCH_OFFSET))
   1624       {
   1625         hs->stop = 1;
   1626         return;
   1627       }
   1628     }
   1629     if (0 != modified)
   1630     {
   1631       if (0 != EXTRACTOR_forensic_emit_unix_time_ (hs->ec,
   1632                                                    "heif",
   1633                                                    EXTRACTOR_METATYPE_MODIFICATION_DATE,
   1634                                                    modified
   1635                                                    - HEIF_EPOCH_OFFSET))
   1636         hs->stop = 1;
   1637     }
   1638     return;
   1639   }
   1640 }
   1641 
   1642 
   1643 /**
   1644  * Main entry method for the HEIF/HEIC/AVIF extraction plugin.
   1645  *
   1646  * @param ec extraction context provided to the plugin
   1647  */
   1648 void
   1649 EXTRACTOR_heif_extract_method (struct EXTRACTOR_ExtractContext *ec);
   1650 
   1651 void
   1652 EXTRACTOR_heif_extract_method (struct EXTRACTOR_ExtractContext *ec)
   1653 {
   1654   struct HeifState hs;
   1655   struct HeifBox meta;
   1656   struct HeifBox moov;
   1657   unsigned char head[16];
   1658   unsigned char brands[HEIF_MAX_BRANDS * 4];
   1659   const struct HeifBrand *major;
   1660   const char *family;
   1661   const char *codec;
   1662   char mime[64];
   1663   char compat[HEIF_MAX_BRANDS * 5 + 1];
   1664   uint64_t fsize;
   1665   uint64_t ftyp_size;
   1666   unsigned int num_brands;
   1667   unsigned int sequence;
   1668   uint64_t pos;
   1669   int have_meta = 0;
   1670 
   1671   fsize = ec->get_size (ec->cls);
   1672   if ( (UINT64_MAX == fsize) ||
   1673        (fsize < 16) )
   1674     return;
   1675   memset (&hs,
   1676           0,
   1677           sizeof (hs));
   1678   memset (&meta, 0, sizeof (meta));
   1679   memset (&moov, 0, sizeof (moov));
   1680   hs.ec = ec;
   1681   hs.boxes = HEIF_MAX_BOXES;
   1682   hs.budget = HEIF_READ_BUDGET;
   1683 
   1684   /* Bail out on the first 16 bytes: almost every file we are handed is
   1685      not ours, and this is the only read those files should cost. */
   1686   if (! heif_read (&hs,
   1687                    0,
   1688                    head,
   1689                    sizeof (head)))
   1690     return;
   1691   if (0 != memcmp (&head[4],
   1692                    "ftyp",
   1693                    4))
   1694     return;
   1695   ftyp_size = EXTRACTOR_forensic_be32_ (head);
   1696   if ( (ftyp_size < 16) ||
   1697        (ftyp_size > fsize) )
   1698     return;   /* 'ftyp' never uses the 64-bit or to-end-of-file forms */
   1699   major = heif_lookup_brand (&head[8]);
   1700 
   1701   num_brands = (unsigned int) ((ftyp_size - 16) / 4);
   1702   if (num_brands > HEIF_MAX_BRANDS)
   1703     num_brands = HEIF_MAX_BRANDS;
   1704   if ( (0 != num_brands) &&
   1705        (! heif_read (&hs,
   1706                      16,
   1707                      brands,
   1708                      num_brands * 4)) )
   1709     num_brands = 0;
   1710 
   1711   family = (NULL != major) ? major->family : NULL;
   1712   codec = (NULL != major) ? major->codec : NULL;
   1713   sequence = (NULL != major) ? (unsigned int) major->sequence : 0;
   1714   if (NULL == codec)
   1715   {
   1716     /* Either the major brand is generic ('mif1'/'msf1') or we do not
   1717        know it at all; the compatible brands decide. */
   1718     for (unsigned int i = 0; i < num_brands; i++)
   1719     {
   1720       const struct HeifBrand *b = heif_lookup_brand (&brands[i * 4]);
   1721 
   1722       if (NULL == b)
   1723         continue;
   1724       if (NULL == family)
   1725       {
   1726         family = b->family;
   1727         sequence = (unsigned int) b->sequence;
   1728       }
   1729       if (NULL != b->codec)
   1730       {
   1731         codec = b->codec;
   1732         family = b->family;
   1733         sequence |= (unsigned int) b->sequence;
   1734         break;
   1735       }
   1736     }
   1737   }
   1738   if (NULL == family)
   1739     return;   /* neither the major nor any compatible brand is ours */
   1740 
   1741   snprintf (mime,
   1742             sizeof (mime),
   1743             "%s%s",
   1744             family,
   1745             (0 != sequence) ? "-sequence" : "");
   1746   if (0 != ec->proc (ec->cls,
   1747                      "heif",
   1748                      EXTRACTOR_METATYPE_MIMETYPE,
   1749                      EXTRACTOR_METAFORMAT_UTF8,
   1750                      "text/plain",
   1751                      mime,
   1752                      strlen (mime) + 1))
   1753     return;
   1754   if (0 != EXTRACTOR_forensic_emit_text_ (ec,
   1755                                           "heif",
   1756                                           EXTRACTOR_METATYPE_FORMAT,
   1757                                           (const char *) &head[8],
   1758                                           4))
   1759     return;
   1760   if (0 != num_brands)
   1761   {
   1762     size_t off = 0;
   1763 
   1764     for (unsigned int i = 0; i < num_brands; i++)
   1765     {
   1766       /* Skip the padding brands some encoders write. */
   1767       if (0 == memcmp (&brands[i * 4],
   1768                        "\0\0\0\0",
   1769                        4))
   1770         continue;
   1771       if (off + 5 >= sizeof (compat))
   1772         break;
   1773       if (0 != off)
   1774         compat[off++] = ',';
   1775       memcpy (&compat[off],
   1776               &brands[i * 4],
   1777               4);
   1778       off += 4;
   1779     }
   1780     if (0 != off)
   1781     {
   1782       if (0 != EXTRACTOR_forensic_emit_text_ (ec,
   1783                                               "heif",
   1784                                               EXTRACTOR_METATYPE_COMPATIBLE_BRANDS,
   1785                                               compat,
   1786                                               off))
   1787         return;
   1788     }
   1789   }
   1790 
   1791   /* Top level walk: we only care about 'meta' and 'moov'.  'mdat' is
   1792      stepped over by its size and never read.  One pass records where
   1793      they are; the emission order below is ours, not the file's. */
   1794   pos = ftyp_size;
   1795   while (pos < fsize)
   1796   {
   1797     struct HeifBox box;
   1798 
   1799     if (! heif_next_box (&hs,
   1800                          pos,
   1801                          fsize,
   1802                          &box))
   1803       break;
   1804     if (box.end <= pos)
   1805       break;   /* no forward progress */
   1806     pos = box.end;
   1807     if ( (heif_is (&box, "meta")) &&
   1808          (! have_meta) )
   1809     {
   1810       meta = box;
   1811       have_meta = 1;
   1812     }
   1813     else if ( (heif_is (&box, "moov")) &&
   1814               (! hs.have_moov) )
   1815     {
   1816       moov = box;
   1817       hs.have_moov = 1;
   1818     }
   1819   }
   1820   if (have_meta)
   1821   {
   1822     heif_parse_meta (&hs,
   1823                      &meta,
   1824                      codec);
   1825     if (hs.stop)
   1826       return;
   1827   }
   1828   if ( (0 != sequence) ||
   1829        (hs.have_moov) )
   1830   {
   1831     /* A burst, a Live Photo or an animated AVIF: several coded images
   1832        that belong together, which is worth calling out because it means
   1833        there is more here than the one frame a viewer shows. */
   1834     if (0 != EXTRACTOR_forensic_emit_ (ec,
   1835                                        "heif",
   1836                                        EXTRACTOR_METATYPE_RESOURCE_TYPE,
   1837                                        "image sequence"))
   1838       return;
   1839     if (0 != EXTRACTOR_forensic_emit_ (ec,
   1840                                        "heif",
   1841                                        EXTRACTOR_METATYPE_COMMENT,
   1842                                        "image sequence (burst, animation or "
   1843                                        "Live Photo)"))
   1844       return;
   1845   }
   1846   if (hs.have_moov)
   1847     heif_parse_moov (&hs,
   1848                      &moov);
   1849 }
   1850 
   1851 
   1852 /* end of heif_extractor.c */