• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WebPGetDemuxVersion(void)104 int WebPGetDemuxVersion(void) {
105   return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
106 }
107 
108 // -----------------------------------------------------------------------------
109 // MemBuffer
110 
RemapMemBuffer(MemBuffer * const mem,const uint8_t * data,size_t size)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 
InitMemBuffer(MemBuffer * const mem,const uint8_t * data,size_t size)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'.
MemDataSize(const MemBuffer * const 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.
SizeIsInvalid(const MemBuffer * const mem,size_t size)132 static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
133   return (size > mem->riff_end_ - mem->start_);
134 }
135 
Skip(MemBuffer * const mem,size_t size)136 static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
137   mem->start_ += size;
138 }
139 
Rewind(MemBuffer * const mem,size_t size)140 static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
141   mem->start_ -= size;
142 }
143 
GetBuffer(MemBuffer * const mem)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.
ReadByte(MemBuffer * const mem)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 
ReadLE16s(MemBuffer * const mem)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 
ReadLE24s(MemBuffer * const mem)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 
ReadLE32(MemBuffer * const mem)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 
AddChunk(WebPDemuxer * const dmux,Chunk * const chunk)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.
AddFrame(WebPDemuxer * const dmux,Frame * const frame)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.
StoreFrame(int frame_num,uint32_t min_size,MemBuffer * const mem,Frame * const frame,int * const has_vp8l_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.
NewFrame(const MemBuffer * const mem,uint32_t min_size,uint32_t actual_size,Frame ** frame)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.
ParseAnimationFrame(WebPDemuxer * const dmux,uint32_t frame_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.
ParseFragment(WebPDemuxer * const dmux,uint32_t fragment_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.
StoreChunk(WebPDemuxer * const dmux,size_t start_offset,uint32_t size)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 
ReadHeader(MemBuffer * const mem)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 
ParseSingleImage(WebPDemuxer * const dmux)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 
ParseVP8X(WebPDemuxer * const dmux)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 
IsValidSimpleFormat(const WebPDemuxer * const dmux)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 
IsValidExtendedFormat(const WebPDemuxer * const dmux)600 static int IsValidExtendedFormat(const WebPDemuxer* const dmux) {
601   const int has_fragments = !!(dmux->feature_flags_ & FRAGMENTS_FLAG);
602   const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG);
603   const Frame* f;
604 
605   if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
606 
607   if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
608   if (dmux->loop_count_ < 0) return 0;
609   if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0;
610 
611   for (f = dmux->frames_; f != NULL; f = f->next_) {
612     const int cur_frame_set = f->frame_num_;
613     int frame_count = 0, fragment_count = 0;
614 
615     // Check frame properties and if the image is composed of fragments that
616     // each fragment came from a fragment.
617     for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
618       const ChunkData* const image = f->img_components_;
619       const ChunkData* const alpha = f->img_components_ + 1;
620 
621       if (!has_fragments && f->is_fragment_) return 0;
622       if (!has_frames && f->frame_num_ > 1) return 0;
623       if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0;
624       if (f->complete_) {
625         if (alpha->size_ == 0 && image->size_ == 0) return 0;
626         // Ensure alpha precedes image bitstream.
627         if (alpha->size_ > 0 && alpha->offset_ > image->offset_) {
628           return 0;
629         }
630 
631         if (f->width_ <= 0 || f->height_ <= 0) return 0;
632       } else {
633         // There shouldn't be a partial frame in a complete file.
634         if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
635 
636         // Ensure alpha precedes image bitstream.
637         if (alpha->size_ > 0 && image->size_ > 0 &&
638             alpha->offset_ > image->offset_) {
639           return 0;
640         }
641         // There shouldn't be any frames after an incomplete one.
642         if (f->next_ != NULL) return 0;
643       }
644 
645       fragment_count += f->is_fragment_;
646       ++frame_count;
647     }
648     if (!has_fragments && frame_count > 1) return 0;
649     if (fragment_count > 0 && frame_count != fragment_count) return 0;
650     if (f == NULL) break;
651   }
652   return 1;
653 }
654 
655 // -----------------------------------------------------------------------------
656 // WebPDemuxer object
657 
InitDemux(WebPDemuxer * const dmux,const MemBuffer * const mem)658 static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) {
659   dmux->state_ = WEBP_DEMUX_PARSING_HEADER;
660   dmux->loop_count_ = 1;
661   dmux->bgcolor_ = 0xFFFFFFFF;  // White background by default.
662   dmux->canvas_width_ = -1;
663   dmux->canvas_height_ = -1;
664   dmux->frames_tail_ = &dmux->frames_;
665   dmux->mem_ = *mem;
666 }
667 
WebPDemuxInternal(const WebPData * data,int allow_partial,WebPDemuxState * state,int version)668 WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
669                                WebPDemuxState* state, int version) {
670   const ChunkParser* parser;
671   int partial;
672   ParseStatus status = PARSE_ERROR;
673   MemBuffer mem;
674   WebPDemuxer* dmux;
675 
676   if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL;
677   if (data == NULL || data->bytes == NULL || data->size == 0) return NULL;
678 
679   if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
680   if (!ReadHeader(&mem)) return NULL;
681 
682   partial = (mem.buf_size_ < mem.riff_end_);
683   if (!allow_partial && partial) return NULL;
684 
685   dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
686   if (dmux == NULL) return NULL;
687   InitDemux(dmux, &mem);
688 
689   for (parser = kMasterChunks; parser->parse != NULL; ++parser) {
690     if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) {
691       status = parser->parse(dmux);
692       if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE;
693       if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR;
694       if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR;
695       break;
696     }
697   }
698   if (state) *state = dmux->state_;
699 
700   if (status == PARSE_ERROR) {
701     WebPDemuxDelete(dmux);
702     return NULL;
703   }
704   return dmux;
705 }
706 
WebPDemuxDelete(WebPDemuxer * dmux)707 void WebPDemuxDelete(WebPDemuxer* dmux) {
708   Chunk* c;
709   Frame* f;
710   if (dmux == NULL) return;
711 
712   for (f = dmux->frames_; f != NULL;) {
713     Frame* const cur_frame = f;
714     f = f->next_;
715     free(cur_frame);
716   }
717   for (c = dmux->chunks_; c != NULL;) {
718     Chunk* const cur_chunk = c;
719     c = c->next_;
720     free(cur_chunk);
721   }
722   free(dmux);
723 }
724 
725 // -----------------------------------------------------------------------------
726 
WebPDemuxGetI(const WebPDemuxer * dmux,WebPFormatFeature feature)727 uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
728   if (dmux == NULL) return 0;
729 
730   switch (feature) {
731     case WEBP_FF_FORMAT_FLAGS:     return dmux->feature_flags_;
732     case WEBP_FF_CANVAS_WIDTH:     return (uint32_t)dmux->canvas_width_;
733     case WEBP_FF_CANVAS_HEIGHT:    return (uint32_t)dmux->canvas_height_;
734     case WEBP_FF_LOOP_COUNT:       return (uint32_t)dmux->loop_count_;
735     case WEBP_FF_BACKGROUND_COLOR: return dmux->bgcolor_;
736     case WEBP_FF_FRAME_COUNT:      return (uint32_t)dmux->num_frames_;
737   }
738   return 0;
739 }
740 
741 // -----------------------------------------------------------------------------
742 // Frame iteration
743 
744 // Find the first 'frame_num' frame. There may be multiple such frames in a
745 // fragmented frame.
GetFrame(const WebPDemuxer * const dmux,int frame_num)746 static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
747   const Frame* f;
748   for (f = dmux->frames_; f != NULL; f = f->next_) {
749     if (frame_num == f->frame_num_) break;
750   }
751   return f;
752 }
753 
754 // Returns fragment 'fragment_num' and the total count.
GetFragment(const Frame * const frame_set,int fragment_num,int * const count)755 static const Frame* GetFragment(
756     const Frame* const frame_set, int fragment_num, int* const count) {
757   const int this_frame = frame_set->frame_num_;
758   const Frame* f = frame_set;
759   const Frame* fragment = NULL;
760   int total;
761 
762   for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
763     if (++total == fragment_num) fragment = f;
764   }
765   *count = total;
766   return fragment;
767 }
768 
GetFramePayload(const uint8_t * const mem_buf,const Frame * const frame,size_t * const data_size)769 static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
770                                       const Frame* const frame,
771                                       size_t* const data_size) {
772   *data_size = 0;
773   if (frame != NULL) {
774     const ChunkData* const image = frame->img_components_;
775     const ChunkData* const alpha = frame->img_components_ + 1;
776     size_t start_offset = image->offset_;
777     *data_size = image->size_;
778 
779     // if alpha exists it precedes image, update the size allowing for
780     // intervening chunks.
781     if (alpha->size_ > 0) {
782       const size_t inter_size = (image->offset_ > 0)
783                               ? image->offset_ - (alpha->offset_ + alpha->size_)
784                               : 0;
785       start_offset = alpha->offset_;
786       *data_size  += alpha->size_ + inter_size;
787     }
788     return mem_buf + start_offset;
789   }
790   return NULL;
791 }
792 
793 // Create a whole 'frame' from VP8 (+ alpha) or lossless.
SynthesizeFrame(const WebPDemuxer * const dmux,const Frame * const first_frame,int fragment_num,WebPIterator * const iter)794 static int SynthesizeFrame(const WebPDemuxer* const dmux,
795                            const Frame* const first_frame,
796                            int fragment_num, WebPIterator* const iter) {
797   const uint8_t* const mem_buf = dmux->mem_.buf_;
798   int num_fragments;
799   size_t payload_size = 0;
800   const Frame* const fragment =
801       GetFragment(first_frame, fragment_num, &num_fragments);
802   const uint8_t* const payload =
803       GetFramePayload(mem_buf, fragment, &payload_size);
804   if (payload == NULL) return 0;
805   assert(first_frame != NULL);
806 
807   iter->frame_num      = first_frame->frame_num_;
808   iter->num_frames     = dmux->num_frames_;
809   iter->fragment_num   = fragment_num;
810   iter->num_fragments  = num_fragments;
811   iter->x_offset       = fragment->x_offset_;
812   iter->y_offset       = fragment->y_offset_;
813   iter->width          = fragment->width_;
814   iter->height         = fragment->height_;
815   iter->duration       = fragment->duration_;
816   iter->dispose_method = fragment->dispose_method_;
817   iter->complete       = fragment->complete_;
818   iter->fragment.bytes = payload;
819   iter->fragment.size  = payload_size;
820   // TODO(jzern): adjust offsets for 'FRGM's embedded in 'ANMF's
821   return 1;
822 }
823 
SetFrame(int frame_num,WebPIterator * const iter)824 static int SetFrame(int frame_num, WebPIterator* const iter) {
825   const Frame* frame;
826   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
827   if (dmux == NULL || frame_num < 0) return 0;
828   if (frame_num > dmux->num_frames_) return 0;
829   if (frame_num == 0) frame_num = dmux->num_frames_;
830 
831   frame = GetFrame(dmux, frame_num);
832   if (frame == NULL) return 0;
833 
834   return SynthesizeFrame(dmux, frame, 1, iter);
835 }
836 
WebPDemuxGetFrame(const WebPDemuxer * dmux,int frame,WebPIterator * iter)837 int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
838   if (iter == NULL) return 0;
839 
840   memset(iter, 0, sizeof(*iter));
841   iter->private_ = (void*)dmux;
842   return SetFrame(frame, iter);
843 }
844 
WebPDemuxNextFrame(WebPIterator * iter)845 int WebPDemuxNextFrame(WebPIterator* iter) {
846   if (iter == NULL) return 0;
847   return SetFrame(iter->frame_num + 1, iter);
848 }
849 
WebPDemuxPrevFrame(WebPIterator * iter)850 int WebPDemuxPrevFrame(WebPIterator* iter) {
851   if (iter == NULL) return 0;
852   if (iter->frame_num <= 1) return 0;
853   return SetFrame(iter->frame_num - 1, iter);
854 }
855 
WebPDemuxSelectFragment(WebPIterator * iter,int fragment_num)856 int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) {
857   if (iter != NULL && iter->private_ != NULL && fragment_num > 0) {
858     const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
859     const Frame* const frame = GetFrame(dmux, iter->frame_num);
860     if (frame == NULL) return 0;
861 
862     return SynthesizeFrame(dmux, frame, fragment_num, iter);
863   }
864   return 0;
865 }
866 
WebPDemuxReleaseIterator(WebPIterator * iter)867 void WebPDemuxReleaseIterator(WebPIterator* iter) {
868   (void)iter;
869 }
870 
871 // -----------------------------------------------------------------------------
872 // Chunk iteration
873 
ChunkCount(const WebPDemuxer * const dmux,const char fourcc[4])874 static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
875   const uint8_t* const mem_buf = dmux->mem_.buf_;
876   const Chunk* c;
877   int count = 0;
878   for (c = dmux->chunks_; c != NULL; c = c->next_) {
879     const uint8_t* const header = mem_buf + c->data_.offset_;
880     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
881   }
882   return count;
883 }
884 
GetChunk(const WebPDemuxer * const dmux,const char fourcc[4],int chunk_num)885 static const Chunk* GetChunk(const WebPDemuxer* const dmux,
886                              const char fourcc[4], int chunk_num) {
887   const uint8_t* const mem_buf = dmux->mem_.buf_;
888   const Chunk* c;
889   int count = 0;
890   for (c = dmux->chunks_; c != NULL; c = c->next_) {
891     const uint8_t* const header = mem_buf + c->data_.offset_;
892     if (!memcmp(header, fourcc, TAG_SIZE)) ++count;
893     if (count == chunk_num) break;
894   }
895   return c;
896 }
897 
SetChunk(const char fourcc[4],int chunk_num,WebPChunkIterator * const iter)898 static int SetChunk(const char fourcc[4], int chunk_num,
899                     WebPChunkIterator* const iter) {
900   const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
901   int count;
902 
903   if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0;
904   count = ChunkCount(dmux, fourcc);
905   if (count == 0) return 0;
906   if (chunk_num == 0) chunk_num = count;
907 
908   if (chunk_num <= count) {
909     const uint8_t* const mem_buf = dmux->mem_.buf_;
910     const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num);
911     iter->chunk.bytes = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE;
912     iter->chunk.size  = chunk->data_.size_ - CHUNK_HEADER_SIZE;
913     iter->num_chunks  = count;
914     iter->chunk_num   = chunk_num;
915     return 1;
916   }
917   return 0;
918 }
919 
WebPDemuxGetChunk(const WebPDemuxer * dmux,const char fourcc[4],int chunk_num,WebPChunkIterator * iter)920 int WebPDemuxGetChunk(const WebPDemuxer* dmux,
921                       const char fourcc[4], int chunk_num,
922                       WebPChunkIterator* iter) {
923   if (iter == NULL) return 0;
924 
925   memset(iter, 0, sizeof(*iter));
926   iter->private_ = (void*)dmux;
927   return SetChunk(fourcc, chunk_num, iter);
928 }
929 
WebPDemuxNextChunk(WebPChunkIterator * iter)930 int WebPDemuxNextChunk(WebPChunkIterator* iter) {
931   if (iter != NULL) {
932     const char* const fourcc =
933         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
934     return SetChunk(fourcc, iter->chunk_num + 1, iter);
935   }
936   return 0;
937 }
938 
WebPDemuxPrevChunk(WebPChunkIterator * iter)939 int WebPDemuxPrevChunk(WebPChunkIterator* iter) {
940   if (iter != NULL && iter->chunk_num > 1) {
941     const char* const fourcc =
942         (const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
943     return SetChunk(fourcc, iter->chunk_num - 1, iter);
944   }
945   return 0;
946 }
947 
WebPDemuxReleaseChunkIterator(WebPChunkIterator * iter)948 void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
949   (void)iter;
950 }
951 
952 #if defined(__cplusplus) || defined(c_plusplus)
953 }  // extern "C"
954 #endif
955