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