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