Home | History | Annotate | Download | only in demux
      1 // Copyright 2012 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 //  WebP container demux.
     11 //
     12 
     13 #ifdef HAVE_CONFIG_H
     14 #include "config.h"
     15 #endif
     16 
     17 #include <assert.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 
     21 #include "../utils/utils.h"
     22 #include "../webp/decode.h"     // WebPGetFeatures
     23 #include "../webp/demux.h"
     24 #include "../webp/format_constants.h"
     25 
     26 #if defined(__cplusplus) || defined(c_plusplus)
     27 extern "C" {
     28 #endif
     29 
     30 #define DMUX_MAJ_VERSION 0
     31 #define DMUX_MIN_VERSION 1
     32 #define DMUX_REV_VERSION 1
     33 
     34 typedef struct {
     35   size_t start_;        // start location of the data
     36   size_t end_;          // end location
     37   size_t riff_end_;     // riff chunk end location, can be > end_.
     38   size_t buf_size_;     // size of the buffer
     39   const uint8_t* buf_;
     40 } MemBuffer;
     41 
     42 typedef struct {
     43   size_t offset_;
     44   size_t size_;
     45 } ChunkData;
     46 
     47 typedef struct Frame {
     48   int x_offset_, y_offset_;
     49   int width_, height_;
     50   int duration_;
     51   WebPMuxAnimDispose dispose_method_;
     52   int is_fragment_;  // this is a frame fragment (and not a full frame).
     53   int frame_num_;  // the referent frame number for use in assembling fragments.
     54   int complete_;   // img_components_ contains a full image.
     55   ChunkData img_components_[2];  // 0=VP8{,L} 1=ALPH
     56   struct Frame* next_;
     57 } Frame;
     58 
     59 typedef struct Chunk {
     60   ChunkData data_;
     61   struct Chunk* next_;
     62 } Chunk;
     63 
     64 struct WebPDemuxer {
     65   MemBuffer mem_;
     66   WebPDemuxState state_;
     67   int is_ext_format_;
     68   uint32_t feature_flags_;
     69   int canvas_width_, canvas_height_;
     70   int loop_count_;
     71   uint32_t bgcolor_;
     72   int num_frames_;
     73   Frame* frames_;
     74   Frame** frames_tail_;
     75   Chunk* chunks_;  // non-image chunks
     76 };
     77 
     78 typedef enum {
     79   PARSE_OK,
     80   PARSE_NEED_MORE_DATA,
     81   PARSE_ERROR
     82 } ParseStatus;
     83 
     84 typedef struct ChunkParser {
     85   uint8_t id[4];
     86   ParseStatus (*parse)(WebPDemuxer* const dmux);
     87   int (*valid)(const WebPDemuxer* const dmux);
     88 } ChunkParser;
     89 
     90 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux);
     91 static ParseStatus ParseVP8X(WebPDemuxer* const dmux);
     92 static int IsValidSimpleFormat(const WebPDemuxer* const dmux);
     93 static int IsValidExtendedFormat(const WebPDemuxer* const dmux);
     94 
     95 static const ChunkParser kMasterChunks[] = {
     96   { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat },
     97   { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat },
     98   { { 'V', 'P', '8', 'X' }, ParseVP8X,        IsValidExtendedFormat },
     99   { { '0', '0', '0', '0' }, NULL,             NULL },
    100 };
    101 
    102 //------------------------------------------------------------------------------
    103 
    104 int WebPGetDemuxVersion(void) {
    105   return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
    106 }
    107 
    108 // -----------------------------------------------------------------------------
    109 // MemBuffer
    110 
    111 static int RemapMemBuffer(MemBuffer* const mem,
    112                           const uint8_t* data, size_t size) {
    113   if (size < mem->buf_size_) return 0;  // can't remap to a shorter buffer!
    114 
    115   mem->buf_ = data;
    116   mem->end_ = mem->buf_size_ = size;
    117   return 1;
    118 }
    119 
    120 static int InitMemBuffer(MemBuffer* const mem,
    121                          const uint8_t* data, size_t size) {
    122   memset(mem, 0, sizeof(*mem));
    123   return RemapMemBuffer(mem, data, size);
    124 }
    125 
    126 // Return the remaining data size available in 'mem'.
    127 static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
    128   return (mem->end_ - mem->start_);
    129 }
    130 
    131 // Return true if 'size' exceeds the end of the RIFF chunk.
    132 static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
    133   return (size > mem->riff_end_ - mem->start_);
    134 }
    135 
    136 static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
    137   mem->start_ += size;
    138 }
    139 
    140 static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
    141   mem->start_ -= size;
    142 }
    143 
    144 static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
    145   return mem->buf_ + mem->start_;
    146 }
    147 
    148 // Read from 'mem' and skip the read bytes.
    149 static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
    150   const uint8_t byte = mem->buf_[mem->start_];
    151   Skip(mem, 1);
    152   return byte;
    153 }
    154 
    155 static WEBP_INLINE int ReadLE16s(MemBuffer* const mem) {
    156   const uint8_t* const data = mem->buf_ + mem->start_;
    157   const int val = GetLE16(data);
    158   Skip(mem, 2);
    159   return val;
    160 }
    161 
    162 static WEBP_INLINE int ReadLE24s(MemBuffer* const mem) {
    163   const uint8_t* const data = mem->buf_ + mem->start_;
    164   const int val = GetLE24(data);
    165   Skip(mem, 3);
    166   return val;
    167 }
    168 
    169 static WEBP_INLINE uint32_t ReadLE32(MemBuffer* const mem) {
    170   const uint8_t* const data = mem->buf_ + mem->start_;
    171   const uint32_t val = GetLE32(data);
    172   Skip(mem, 4);
    173   return val;
    174 }
    175 
    176 // -----------------------------------------------------------------------------
    177 // Secondary chunk parsing
    178 
    179 static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
    180   Chunk** c = &dmux->chunks_;
    181   while (*c != NULL) c = &(*c)->next_;
    182   *c = chunk;
    183   chunk->next_ = NULL;
    184 }
    185 
    186 // Add a frame to the end of the list, ensuring the last frame is complete.
    187 // Returns true on success, false otherwise.
    188 static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) {
    189   const Frame* const last_frame = *dmux->frames_tail_;
    190   if (last_frame != NULL && !last_frame->complete_) return 0;
    191 
    192   *dmux->frames_tail_ = frame;
    193   frame->next_ = NULL;
    194   dmux->frames_tail_ = &frame->next_;
    195   return 1;
    196 }
    197 
    198 // Store image bearing chunks to 'frame'.
    199 // If 'has_vp8l_alpha' is not NULL, it will be set to true if the frame is a
    200 // lossless image with alpha.
    201 static ParseStatus StoreFrame(int frame_num, uint32_t min_size,
    202                               MemBuffer* const mem, Frame* const frame,
    203                               int* const has_vp8l_alpha) {
    204   int alpha_chunks = 0;
    205   int image_chunks = 0;
    206   int done = (MemDataSize(mem) < min_size);
    207   ParseStatus status = PARSE_OK;
    208 
    209   if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0;  // Default.
    210 
    211   if (done) return PARSE_NEED_MORE_DATA;
    212 
    213   do {
    214     const size_t chunk_start_offset = mem->start_;
    215     const uint32_t fourcc = ReadLE32(mem);
    216     const uint32_t payload_size = ReadLE32(mem);
    217     const uint32_t payload_size_padded = payload_size + (payload_size & 1);
    218     const size_t payload_available = (payload_size_padded > MemDataSize(mem))
    219                                    ? MemDataSize(mem) : payload_size_padded;
    220     const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available;
    221 
    222     if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
    223     if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR;
    224     if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA;
    225 
    226     switch (fourcc) {
    227       case MKFOURCC('A', 'L', 'P', 'H'):
    228         if (alpha_chunks == 0) {
    229           ++alpha_chunks;
    230           frame->img_components_[1].offset_ = chunk_start_offset;
    231           frame->img_components_[1].size_ = chunk_size;
    232           frame->frame_num_ = frame_num;
    233           Skip(mem, payload_available);
    234         } else {
    235           goto Done;
    236         }
    237         break;
    238       case MKFOURCC('V', 'P', '8', 'L'):
    239         if (alpha_chunks > 0) return PARSE_ERROR;  // VP8L has its own alpha
    240         // fall through
    241       case MKFOURCC('V', 'P', '8', ' '):
    242         if (image_chunks == 0) {
    243           // Extract the bitstream features, tolerating failures when the data
    244           // is incomplete.
    245           WebPBitstreamFeatures features;
    246           const VP8StatusCode vp8_status =
    247               WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
    248                               &features);
    249           if (status == PARSE_NEED_MORE_DATA &&
    250               vp8_status == VP8_STATUS_NOT_ENOUGH_DATA) {
    251             return PARSE_NEED_MORE_DATA;
    252           } else if (vp8_status != VP8_STATUS_OK) {
    253             // We have enough data, and yet WebPGetFeatures() failed.
    254             return PARSE_ERROR;
    255           }
    256           ++image_chunks;
    257           frame->img_components_[0].offset_ = chunk_start_offset;
    258           frame->img_components_[0].size_ = chunk_size;
    259           frame->width_ = features.width;
    260           frame->height_ = features.height;
    261           if (has_vp8l_alpha != NULL) *has_vp8l_alpha = features.has_alpha;
    262           frame->frame_num_ = frame_num;
    263           frame->complete_ = (status == PARSE_OK);
    264           Skip(mem, payload_available);
    265         } else {
    266           goto Done;
    267         }
    268         break;
    269  Done:
    270       default:
    271         // Restore fourcc/size when moving up one level in parsing.
    272         Rewind(mem, CHUNK_HEADER_SIZE);
    273         done = 1;
    274         break;
    275     }
    276 
    277     if (mem->start_ == mem->riff_end_) {
    278       done = 1;
    279     } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
    280       status = PARSE_NEED_MORE_DATA;
    281     }
    282   } while (!done && status == PARSE_OK);
    283 
    284   return status;
    285 }
    286 
    287 // Creates a new Frame if 'actual_size' is within bounds and 'mem' contains
    288 // enough data ('min_size') to parse the payload.
    289 // Returns PARSE_OK on success with *frame pointing to the new Frame.
    290 // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise.
    291 static ParseStatus NewFrame(const MemBuffer* const mem,
    292                             uint32_t min_size, uint32_t actual_size,
    293                             Frame** frame) {
    294   if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
    295   if (actual_size < min_size) return PARSE_ERROR;
    296   if (MemDataSize(mem) < min_size)  return PARSE_NEED_MORE_DATA;
    297 
    298   *frame = (Frame*)calloc(1, sizeof(**frame));
    299   return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
    300 }
    301 
    302 // Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
    303 // 'frame_chunk_size' is the previously validated, padded chunk size.
    304 static ParseStatus ParseAnimationFrame(
    305     WebPDemuxer* const dmux, uint32_t frame_chunk_size) {
    306   const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
    307   const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE;
    308   int added_frame = 0;
    309   MemBuffer* const mem = &dmux->mem_;
    310   Frame* frame;
    311   ParseStatus status =
    312       NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
    313   if (status != PARSE_OK) return status;
    314 
    315   frame->x_offset_       = 2 * ReadLE24s(mem);
    316   frame->y_offset_       = 2 * ReadLE24s(mem);
    317   frame->width_          = 1 + ReadLE24s(mem);
    318   frame->height_         = 1 + ReadLE24s(mem);
    319   frame->duration_       = ReadLE24s(mem);
    320   frame->dispose_method_ = (WebPMuxAnimDispose)(ReadByte(mem) & 1);
    321   if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) {
    322     free(frame);
    323     return PARSE_ERROR;
    324   }
    325 
    326   // Store a frame only if the animation flag is set there is some data for
    327   // this frame is available.
    328   status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame,
    329                       NULL);
    330   if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
    331     added_frame = AddFrame(dmux, frame);
    332     if (added_frame) {
    333       ++dmux->num_frames_;
    334     } else {
    335       status = PARSE_ERROR;
    336     }
    337   }
    338 
    339   if (!added_frame) free(frame);
    340   return status;
    341 }
    342 
    343 #ifdef WEBP_EXPERIMENTAL_FEATURES
    344 // Parse a 'FRGM' chunk and any image bearing chunks that immediately follow.
    345 // 'fragment_chunk_size' is the previously validated, padded chunk size.
    346 static ParseStatus ParseFragment(WebPDemuxer* const dmux,
    347                                  uint32_t fragment_chunk_size) {
    348   const int frame_num = 1;  // All fragments belong to the 1st (and only) frame.
    349   const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
    350   const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE;
    351   int added_fragment = 0;
    352   MemBuffer* const mem = &dmux->mem_;
    353   Frame* frame;
    354   ParseStatus status =
    355       NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
    356   if (status != PARSE_OK) return status;
    357 
    358   frame->is_fragment_ = 1;
    359   frame->x_offset_ = 2 * ReadLE24s(mem);
    360   frame->y_offset_ = 2 * ReadLE24s(mem);
    361 
    362   // Store a fragment only if the fragments flag is set there is some data for
    363   // this fragment is available.
    364   status = StoreFrame(frame_num, frgm_payload_size, mem, frame, NULL);
    365   if (status != PARSE_ERROR && has_fragments && frame->frame_num_ > 0) {
    366     added_fragment = AddFrame(dmux, frame);
    367     if (!added_fragment) {
    368       status = PARSE_ERROR;
    369     } else {
    370       dmux->num_frames_ = 1;
    371     }
    372   }
    373 
    374   if (!added_fragment) free(frame);
    375   return status;
    376 }
    377 #endif  // WEBP_EXPERIMENTAL_FEATURES
    378 
    379 // General chunk storage, starting with the header at 'start_offset', allowing
    380 // the user to request the payload via a fourcc string. 'size' includes the
    381 // header and the unpadded payload size.
    382 // Returns true on success, false otherwise.
    383 static int StoreChunk(WebPDemuxer* const dmux,
    384                       size_t start_offset, uint32_t size) {
    385   Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk));
    386   if (chunk == NULL) return 0;
    387 
    388   chunk->data_.offset_ = start_offset;
    389   chunk->data_.size_ = size;
    390   AddChunk(dmux, chunk);
    391   return 1;
    392 }
    393 
    394 // -----------------------------------------------------------------------------
    395 // Primary chunk parsing
    396 
    397 static int ReadHeader(MemBuffer* const mem) {
    398   const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
    399   uint32_t riff_size;
    400 
    401   // Basic file level validation.
    402   if (MemDataSize(mem) < min_size) return 0;
    403   if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
    404       memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
    405     return 0;
    406   }
    407 
    408   riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
    409   if (riff_size < CHUNK_HEADER_SIZE) return 0;
    410   if (riff_size > MAX_CHUNK_PAYLOAD) return 0;
    411 
    412   // There's no point in reading past the end of the RIFF chunk
    413   mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE;
    414   if (mem->buf_size_ > mem->riff_end_) {
    415     mem->buf_size_ = mem->end_ = mem->riff_end_;
    416   }
    417 
    418   Skip(mem, RIFF_HEADER_SIZE);
    419   return 1;
    420 }
    421 
    422 static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
    423   const size_t min_size = CHUNK_HEADER_SIZE;
    424   MemBuffer* const mem = &dmux->mem_;
    425   Frame* frame;
    426   ParseStatus status;
    427   int has_vp8l_alpha = 0;  // Frame contains a lossless image with alpha.
    428 
    429   if (dmux->frames_ != NULL) return PARSE_ERROR;
    430   if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR;
    431   if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA;
    432 
    433   frame = (Frame*)calloc(1, sizeof(*frame));
    434   if (frame == NULL) return PARSE_ERROR;
    435 
    436   // For the single image case we allow parsing of a partial frame, but we need
    437   // at least CHUNK_HEADER_SIZE for parsing.
    438   status = StoreFrame(1, CHUNK_HEADER_SIZE, &dmux->mem_, frame,
    439                       &has_vp8l_alpha);
    440   if (status != PARSE_ERROR) {
    441     const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG);
    442     // Clear any alpha when the alpha flag is missing.
    443     if (!has_alpha && frame->img_components_[1].size_ > 0) {
    444       frame->img_components_[1].offset_ = 0;
    445       frame->img_components_[1].size_ = 0;
    446     }
    447 
    448     // Use the frame width/height as the canvas values for non-vp8x files.
    449     // Also, set ALPHA_FLAG if this is a lossless image with alpha.
    450     if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) {
    451       dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
    452       dmux->canvas_width_ = frame->width_;
    453       dmux->canvas_height_ = frame->height_;
    454       dmux->feature_flags_ |= has_vp8l_alpha ? ALPHA_FLAG : 0;
    455     }
    456     AddFrame(dmux, frame);
    457     dmux->num_frames_ = 1;
    458   } else {
    459     free(frame);
    460   }
    461 
    462   return status;
    463 }
    464 
    465 static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
    466   MemBuffer* const mem = &dmux->mem_;
    467   int anim_chunks = 0;
    468   uint32_t vp8x_size;
    469   ParseStatus status = PARSE_OK;
    470 
    471   if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
    472 
    473   dmux->is_ext_format_ = 1;
    474   Skip(mem, TAG_SIZE);  // VP8X
    475   vp8x_size = ReadLE32(mem);
    476   if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
    477   if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR;
    478   vp8x_size += vp8x_size & 1;
    479   if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR;
    480   if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA;
    481 
    482   dmux->feature_flags_ = ReadByte(mem);
    483   Skip(mem, 3);  // Reserved.
    484   dmux->canvas_width_  = 1 + ReadLE24s(mem);
    485   dmux->canvas_height_ = 1 + ReadLE24s(mem);
    486   if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) {
    487     return PARSE_ERROR;  // image final dimension is too large
    488   }
    489   Skip(mem, vp8x_size - VP8X_CHUNK_SIZE);  // skip any trailing data.
    490   dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
    491 
    492   if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
    493   if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
    494 
    495   do {
    496     int store_chunk = 1;
    497     const size_t chunk_start_offset = mem->start_;
    498     const uint32_t fourcc = ReadLE32(mem);
    499     const uint32_t chunk_size = ReadLE32(mem);
    500     const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1);
    501 
    502     if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
    503     if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
    504 
    505     switch (fourcc) {
    506       case MKFOURCC('V', 'P', '8', 'X'): {
    507         return PARSE_ERROR;
    508       }
    509       case MKFOURCC('A', 'L', 'P', 'H'):
    510       case MKFOURCC('V', 'P', '8', ' '):
    511       case MKFOURCC('V', 'P', '8', 'L'): {
    512         // check that this isn't an animation (all frames should be in an ANMF).
    513         if (anim_chunks > 0) return PARSE_ERROR;
    514 
    515         Rewind(mem, CHUNK_HEADER_SIZE);
    516         status = ParseSingleImage(dmux);
    517         break;
    518       }
    519       case MKFOURCC('A', 'N', 'I', 'M'): {
    520         if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
    521 
    522         if (MemDataSize(mem) < chunk_size_padded) {
    523           status = PARSE_NEED_MORE_DATA;
    524         } else if (anim_chunks == 0) {
    525           ++anim_chunks;
    526           dmux->bgcolor_ = ReadLE32(mem);
    527           dmux->loop_count_ = ReadLE16s(mem);
    528           Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
    529         } else {
    530           store_chunk = 0;
    531           goto Skip;
    532         }
    533         break;
    534       }
    535       case MKFOURCC('A', 'N', 'M', 'F'): {
    536         if (anim_chunks == 0) return PARSE_ERROR;  // 'ANIM' precedes frames.
    537         status = ParseAnimationFrame(dmux, chunk_size_padded);
    538         break;
    539       }
    540 #ifdef WEBP_EXPERIMENTAL_FEATURES
    541       case MKFOURCC('F', 'R', 'G', 'M'): {
    542         status = ParseFragment(dmux, chunk_size_padded);
    543         break;
    544       }
    545 #endif
    546       case MKFOURCC('I', 'C', 'C', 'P'): {
    547         store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
    548         goto Skip;
    549       }
    550       case MKFOURCC('X', 'M', 'P', ' '): {
    551         store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
    552         goto Skip;
    553       }
    554       case MKFOURCC('E', 'X', 'I', 'F'): {
    555         store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
    556         goto Skip;
    557       }
    558  Skip:
    559       default: {
    560         if (chunk_size_padded <= MemDataSize(mem)) {
    561           if (store_chunk) {
    562             // Store only the chunk header and unpadded size as only the payload
    563             // will be returned to the user.
    564             if (!StoreChunk(dmux, chunk_start_offset,
    565                             CHUNK_HEADER_SIZE + chunk_size)) {
    566               return PARSE_ERROR;
    567             }
    568           }
    569           Skip(mem, chunk_size_padded);
    570         } else {
    571           status = PARSE_NEED_MORE_DATA;
    572         }
    573       }
    574     }
    575 
    576     if (mem->start_ == mem->riff_end_) {
    577       break;
    578     } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
    579       status = PARSE_NEED_MORE_DATA;
    580     }
    581   } while (status == PARSE_OK);
    582 
    583   return status;
    584 }
    585 
    586 // -----------------------------------------------------------------------------
    587 // Format validation
    588 
    589 static int IsValidSimpleFormat(const WebPDemuxer* const dmux) {
    590   const Frame* const frame = dmux->frames_;
    591   if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
    592 
    593   if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
    594   if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
    595 
    596   if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
    597   return 1;
    598 }
    599 
    600 // If 'exact' is true, check that the image resolution matches the canvas.
    601 // If 'exact' is false, check that the x/y offsets do not exceed the canvas.
    602 static int CheckFrameBounds(const Frame* const frame, int exact,
    603                             int canvas_width, int canvas_height) {
    604   if (exact) {
    605     if (frame->x_offset_ != 0 || frame->y_offset_ != 0) {
    606       return 0;
    607     }
    608     if (frame->width_ != canvas_width || frame->height_ != canvas_height) {
    609       return 0;
    610     }
    611   } else {
    612     if (frame->x_offset_ < 0 || frame->y_offset_ < 0) return 0;
    613     if (frame->width_ + frame->x_offset_ > canvas_width) return 0;
    614     if (frame->height_ + frame->y_offset_ > canvas_height) return 0;
    615   }
    616   return 1;
    617 }
    618 
    619 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
    620   const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
    621   const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
    622   const Frame* f;
    623 
    624   if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
    625 
    626   if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
    627   if (dmux->loop_count_ < 0) return 0;
    628   if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
    629 
    630   for (f = dmux->frames_; f != NULL; f = f->next_) {
    631     const int cur_frame_set = f->frame_num_;
    632     int frame_count = 0, fragment_count = 0;
    633 
    634     // Check frame properties and if the image is composed of fragments that
    635     // each fragment came from a fragment.
    636     for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
    637       const ChunkData* const image = f->img_components_;
    638       const ChunkData* const alpha = f->img_components_ + 1;
    639 
    640       if (!has_fragments && f->is_fragment_) return 0;
    641       if (!has_frames && f->frame_num_ > 1) return 0;
    642       if (f->complete_) {
    643         if (alpha->size_ == 0 && image->size_ == 0) return 0;
    644         // Ensure alpha precedes image bitstream.
    645         if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
    646           return 0;
    647         }
    648 
    649         if (f->width_ <= 0 || f->height_ <= 0) return 0;
    650       } else {
    651         // There shouldn't be a partial frame in a complete file.
    652         if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
    653 
    654         // Ensure alpha precedes image bitstream.
    655         if (alpha->size_ > 0 && image->size_ > 0 &&
    656             alpha->offset_ > image->offset_) {
    657           return 0;
    658         }
    659         // There shouldn't be any frames after an incomplete one.
    660         if (f->next_ != NULL) return 0;
    661       }
    662 
    663       if (f->width_ > 0 && f->height_ > 0 &&
    664           !CheckFrameBounds(f, !(has_frames || has_fragments),
    665                             dmux->canvas_width_, dmux->canvas_height_)) {
    666         return 0;
    667       }
    668 
    669       fragment_count += f->is_fragment_;
    670       ++frame_count;
    671     }
    672     if (!has_fragments && frame_count > 1) return 0;
    673     if (fragment_count > 0 && frame_count != fragment_count) return 0;
    674     if (f == NULL) break;
    675   }
    676   return 1;
    677 }
    678 
    679 // -----------------------------------------------------------------------------
    680 // WebPDemuxer object
    681 
    682 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
    683   dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
    684   dmux->loop_count_ = 1;
    685   dmux->bgcolor_ = 0xFFFFFFFF;  // White background by default.
    686   dmux->canvas_width_ = -1;
    687   dmux->canvas_height_ = -1;
    688   dmux->frames_tail_ = &dmux->frames_;
    689   dmux->mem_ = *mem;
    690 }
    691 
    692 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
    693                                WebPDemuxState* state, int version) {
    694   const ChunkParser* parser;
    695   int partial;
    696   ParseStatus status = PARSE_ERROR;
    697   MemBuffer mem;
    698   WebPDemuxer* dmux;
    699 
    700   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
    701   if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
    702 
    703   if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
    704   if (!ReadHeader(&mem)) return NULL;
    705 
    706   partial = (mem.buf_size_ < mem.riff_end_);
    707   if (!allow_partial && partial) return NULL;
    708 
    709   dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
    710   if (dmux == NULL) return NULL;
    711   InitDemux(dmux, &mem);
    712 
    713   for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
    714     if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
    715       status = parser->parse(dmux);
    716       if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
    717       if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
    718       if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
    719       break;
    720     }
    721   }
    722   if (state) *state = dmux->state_;
    723 
    724   if (status == PARSE_ERROR) {
    725     WebPDemuxDelete(dmux);
    726     return NULL;
    727   }
    728   return dmux;
    729 }
    730 
    731 void WebPDemuxDelete(WebPDemuxer* dmux) {
    732   Chunk* c;
    733   Frame* f;
    734   if (dmux == NULL) return;
    735 
    736   for (f = dmux->frames_; f != NULL;) {
    737     Frame* const cur_frame = f;
    738     f = f->next_;
    739     free(cur_frame);
    740   }
    741   for (c = dmux->chunks_; c != NULL;) {
    742     Chunk* const cur_chunk = c;
    743     c = c->next_;
    744     free(cur_chunk);
    745   }
    746   free(dmux);
    747 }
    748 
    749 // -----------------------------------------------------------------------------
    750 
    751 uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
    752   if (dmux == NULL) return 0;
    753 
    754   switch (feature) {
    755     case WEBP_FF_FORMAT_FLAGS:     return dmux->feature_flags_;
    756     case WEBP_FF_CANVAS_WIDTH:     return (uint32_t)dmux->canvas_width_;
    757     case WEBP_FF_CANVAS_HEIGHT:    return (uint32_t)dmux->canvas_height_;
    758     case WEBP_FF_LOOP_COUNT:       return (uint32_t)dmux->loop_count_;
    759     case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
    760     case WEBP_FF_FRAME_COUNT:      return (uint32_t)dmux->num_frames_;
    761   }
    762   return 0;
    763 }
    764 
    765 // -----------------------------------------------------------------------------
    766 // Frame iteration
    767 
    768 // Find the first 'frame_num' frame. There may be multiple such frames in a
    769 // fragmented frame.
    770 static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
    771   const Frame* f;
    772   for (f = dmux->frames_; f != NULL; f = f->next_) {
    773     if (frame_num == f->frame_num_) break;
    774   }
    775   return f;
    776 }
    777 
    778 // Returns fragment 'fragment_num' and the total count.
    779 static const Frame* GetFragment(
    780     const Frame* const frame_set, int fragment_num, int* const count) {
    781   const int this_frame = frame_set->frame_num_;
    782   const Frame* f = frame_set;
    783   const Frame* fragment = NULL;
    784   int total;
    785 
    786   for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
    787     if (++total == fragment_num) fragment = f;
    788   }
    789   *count = total;
    790   return fragment;
    791 }
    792 
    793 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
    794                                       const Frame* const frame,
    795                                       size_t* const data_size) {
    796   *data_size = 0;
    797   if (frame != NULL) {
    798     const ChunkData* const image = frame->img_components_;
    799     const ChunkData* const alpha = frame->img_components_ + 1;
    800     size_t start_offset = image->offset_;
    801     *data_size = image->size_;
    802 
    803     // if alpha exists it precedes image, update the size allowing for
    804     // intervening chunks.
    805     if (alpha->size_ > 0) {
    806       const size_t inter_size = (image->offset_ > 0)
    807                               ? image->offset_ - (alpha->offset_ + alpha->size_)
    808                               : 0;
    809       start_offset = alpha->offset_;
    810       *data_size  += alpha->size_ + inter_size;
    811     }
    812     return mem_buf + start_offset;
    813   }
    814   return NULL;
    815 }
    816 
    817 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
    818 static int SynthesizeFrame(const WebPDemuxer* const dmux,
    819                            const Frame* const first_frame,
    820                            int fragment_num, WebPIterator* const iter) {
    821   const uint8_t* const mem_buf = dmux->mem_.buf_;
    822   int num_fragments;
    823   size_t payload_size = 0;
    824   const Frame* const fragment =
    825       GetFragment(first_frame, fragment_num, &num_fragments);
    826   const uint8_t* const payload =
    827       GetFramePayload(mem_buf, fragment, &payload_size);
    828   if (payload == NULL) return 0;
    829   assert(first_frame != NULL);
    830 
    831   iter->frame_num      = first_frame->frame_num_;
    832   iter->num_frames     = dmux->num_frames_;
    833   iter->fragment_num   = fragment_num;
    834   iter->num_fragments  = num_fragments;
    835   iter->x_offset       = fragment->x_offset_;
    836   iter->y_offset       = fragment->y_offset_;
    837   iter->width          = fragment->width_;
    838   iter->height         = fragment->height_;
    839   iter->duration       = fragment->duration_;
    840   iter->dispose_method = fragment->dispose_method_;
    841   iter->complete       = fragment->complete_;
    842   iter->fragment.bytes = payload;
    843   iter->fragment.size  = payload_size;
    844   // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
    845   return 1;
    846 }
    847 
    848 static int SetFrame(int frame_num, WebPIterator* const iter) {
    849   const Frame* frame;
    850   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
    851   if (dmux == NULL || frame_num < 0) return 0;
    852   if (frame_num > dmux->num_frames_) return 0;
    853   if (frame_num == 0) frame_num = dmux->num_frames_;
    854 
    855   frame = GetFrame(dmux, frame_num);
    856   if (frame == NULL) return 0;
    857 
    858   return SynthesizeFrame(dmux, frame, 1, iter);
    859 }
    860 
    861 int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
    862   if (iter == NULL) return 0;
    863 
    864   memset(iter, 0, sizeof(*iter));
    865   iter->private_ = (void*)dmux;
    866   return SetFrame(frame, iter);
    867 }
    868 
    869 int WebPDemuxNextFrame(WebPIterator* iter) {
    870   if (iter == NULL) return 0;
    871   return SetFrame(iter->frame_num + 1, iter);
    872 }
    873 
    874 int WebPDemuxPrevFrame(WebPIterator* iter) {
    875   if (iter == NULL) return 0;
    876   if (iter->frame_num <= 1) return 0;
    877   return SetFrame(iter->frame_num - 1, iter);
    878 }
    879 
    880 int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
    881   if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
    882     const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
    883     const Frame* const frame = GetFrame(dmux, iter->frame_num);
    884     if (frame == NULL) return 0;
    885 
    886     return SynthesizeFrame(dmux, frame, fragment_num, iter);
    887   }
    888   return 0;
    889 }
    890 
    891 void WebPDemuxReleaseIterator(WebPIterator* iter) {
    892   (void)iter;
    893 }
    894 
    895 // -----------------------------------------------------------------------------
    896 // Chunk iteration
    897 
    898 static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
    899   const uint8_t* const mem_buf = dmux->mem_.buf_;
    900   const Chunk* c;
    901   int count = 0;
    902   for (c = dmux->chunks_; c != NULL; c = c->next_) {
    903     const uint8_t* const header = mem_buf + c->data_.offset_;
    904     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
    905   }
    906   return count;
    907 }
    908 
    909 static const Chunk* GetChunk(const WebPDemuxer* const dmux,
    910                              const char fourcc[4], int chunk_num) {
    911   const uint8_t* const mem_buf = dmux->mem_.buf_;
    912   const Chunk* c;
    913   int count = 0;
    914   for (c = dmux->chunks_; c != NULL; c = c->next_) {
    915     const uint8_t* const header = mem_buf + c->data_.offset_;
    916     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
    917     if (count == chunk_num) break;
    918   }
    919   return c;
    920 }
    921 
    922 static int SetChunk(const char fourcc[4], int chunk_num,
    923                     WebPChunkIterator* const iter) {
    924   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
    925   int count;
    926 
    927   if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
    928   count = ChunkCount(dmux, fourcc);
    929   if (count == 0) return 0;
    930   if (chunk_num == 0) chunk_num = count;
    931 
    932   if (chunk_num <= count) {
    933     const uint8_t* const mem_buf = dmux->mem_.buf_;
    934     const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
    935     iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
    936     iter->chunk.size  = chunk->data_.size_ - CHUNK_HEADER_SIZE;
    937     iter->num_chunks  = count;
    938     iter->chunk_num   = chunk_num;
    939     return 1;
    940   }
    941   return 0;
    942 }
    943 
    944 int WebPDemuxGetChunk(const WebPDemuxer* dmux,
    945                       const char fourcc[4], int chunk_num,
    946                       WebPChunkIterator* iter) {
    947   if (iter == NULL) return 0;
    948 
    949   memset(iter, 0, sizeof(*iter));
    950   iter->private_ = (void*)dmux;
    951   return SetChunk(fourcc, chunk_num, iter);
    952 }
    953 
    954 int WebPDemuxNextChunk(WebPChunkIterator* iter) {
    955   if (iter != NULL) {
    956     const char* const fourcc =
    957         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
    958     return SetChunk(fourcc, iter->chunk_num + 1, iter);
    959   }
    960   return 0;
    961 }
    962 
    963 int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
    964   if (iter != NULL && iter->chunk_num > 1) {
    965     const char* const fourcc =
    966         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
    967     return SetChunk(fourcc, iter->chunk_num - 1, iter);
    968   }
    969   return 0;
    970 }
    971 
    972 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
    973   (void)iter;
    974 }
    975 
    976 #if defined(__cplusplus) || defined(c_plusplus)
    977 }  // extern "C"
    978 #endif
    979