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