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