1
// Copyright 2012 Google Inc. All Rights Reserved.
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
// -----------------------------------------------------------------------------
10
// WebP container demux.
21
#include "../utils/utils.h"
22
#include "../webp/decode.h" // WebPGetFeatures
23
#include "../webp/demux.h"
24
#include "../webp/format_constants.h"
26
#if defined(__cplusplus) || defined(c_plusplus)
30
#define DMUX_MAJ_VERSION 0
31
#define DMUX_MIN_VERSION 1
32
#define DMUX_REV_VERSION 1
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
47
typedef struct Frame {
48
int x_offset_, y_offset_;
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
59
typedef struct Chunk {
66
WebPDemuxState state_;
68
uint32_t feature_flags_;
69
int canvas_width_, canvas_height_;
75
Chunk* chunks_; // non-image chunks
84
typedef struct ChunkParser {
86
ParseStatus (*parse)(WebPDemuxer* const dmux);
87
int (*valid)(const WebPDemuxer* const dmux);
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);
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 },
102
//------------------------------------------------------------------------------
104
int WebPGetDemuxVersion(void) {
105
return (DMUX_MAJ_VERSION << 16) | (DMUX_MIN_VERSION << 8) | DMUX_REV_VERSION;
108
// -----------------------------------------------------------------------------
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!
116
mem->end_ = mem->buf_size_ = 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);
126
// Return the remaining data size available in 'mem'.
127
static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) {
128
return (mem->end_ - mem->start_);
131
// Return true if 'size' exceeds the end of the RIFF chunk.
132
static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) {
133
return (size > mem->riff_end_ - mem->start_);
136
static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) {
140
static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) {
144
static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) {
145
return mem->buf_ + mem->start_;
148
// Read from 'mem' and skip the read bytes.
149
static WEBP_INLINE uint8_t ReadByte(MemBuffer* const mem) {
150
const uint8_t byte = mem->buf_[mem->start_];
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);
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);
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);
176
// -----------------------------------------------------------------------------
177
// Secondary chunk parsing
179
static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) {
180
Chunk** c = &dmux->chunks_;
181
while (*c != NULL) c = &(*c)->next_;
186
// Add a frame to the end of the list, ensuring the last frame is complete.
187
// Returns true on success, false otherwise.
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;
192
*dmux->frames_tail_ = frame;
194
dmux->frames_tail_ = &frame->next_;
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.
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;
209
if (has_vp8l_alpha != NULL) *has_vp8l_alpha = 0; // Default.
211
if (done) return PARSE_NEED_MORE_DATA;
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;
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;
227
case MKFOURCC('A', 'L', 'P', 'H'):
228
if (alpha_chunks == 0) {
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);
238
case MKFOURCC('V', 'P', '8', 'L'):
239
if (alpha_chunks > 0) return PARSE_ERROR; // VP8L has its own alpha
241
case MKFOURCC('V', 'P', '8', ' '):
242
if (image_chunks == 0) {
243
// Extract the bitstream features, tolerating failures when the data
245
WebPBitstreamFeatures features;
246
const VP8StatusCode vp8_status =
247
WebPGetFeatures(mem->buf_ + chunk_start_offset, chunk_size,
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.
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);
271
// Restore fourcc/size when moving up one level in parsing.
272
Rewind(mem, CHUNK_HEADER_SIZE);
277
if (mem->start_ == mem->riff_end_) {
279
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
280
status = PARSE_NEED_MORE_DATA;
282
} while (!done && status == PARSE_OK);
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.
291
static ParseStatus NewFrame(const MemBuffer* const mem,
292
uint32_t min_size, uint32_t actual_size,
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;
298
*frame = (Frame*)calloc(1, sizeof(**frame));
299
return (*frame == NULL) ? PARSE_ERROR : PARSE_OK;
302
// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow.
303
// 'frame_chunk_size' is the previously validated, padded 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;
309
MemBuffer* const mem = &dmux->mem_;
312
NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame);
313
if (status != PARSE_OK) return status;
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) {
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,
330
if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) {
331
added_frame = AddFrame(dmux, frame);
335
status = PARSE_ERROR;
339
if (!added_frame) free(frame);
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.
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_;
355
NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame);
356
if (status != PARSE_OK) return status;
358
frame->is_fragment_ = 1;
359
frame->x_offset_ = 2 * ReadLE24s(mem);
360
frame->y_offset_ = 2 * ReadLE24s(mem);
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;
370
dmux->num_frames_ = 1;
374
if (!added_fragment) free(frame);
377
#endif // WEBP_EXPERIMENTAL_FEATURES
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.
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;
388
chunk->data_.offset_ = start_offset;
389
chunk->data_.size_ = size;
390
AddChunk(dmux, chunk);
394
// -----------------------------------------------------------------------------
395
// Primary chunk parsing
397
static int ReadHeader(MemBuffer* const mem) {
398
const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
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)) {
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;
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_;
418
Skip(mem, RIFF_HEADER_SIZE);
422
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) {
423
const size_t min_size = CHUNK_HEADER_SIZE;
424
MemBuffer* const mem = &dmux->mem_;
427
int has_vp8l_alpha = 0; // Frame contains a lossless image with alpha.
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;
433
frame = (Frame*)calloc(1, sizeof(*frame));
434
if (frame == NULL) return PARSE_ERROR;
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,
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;
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;
456
AddFrame(dmux, frame);
457
dmux->num_frames_ = 1;
465
static ParseStatus ParseVP8X(WebPDemuxer* const dmux) {
466
MemBuffer* const mem = &dmux->mem_;
469
ParseStatus status = PARSE_OK;
471
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
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;
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
489
Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data.
490
dmux->state_ = WEBP_DEMUX_PARSED_HEADER;
492
if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR;
493
if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA;
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);
502
if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR;
503
if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR;
506
case MKFOURCC('V', 'P', '8', 'X'): {
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;
515
Rewind(mem, CHUNK_HEADER_SIZE);
516
status = ParseSingleImage(dmux);
519
case MKFOURCC('A', 'N', 'I', 'M'): {
520
if (chunk_size_padded < ANIM_CHUNK_SIZE) return PARSE_ERROR;
522
if (MemDataSize(mem) < chunk_size_padded) {
523
status = PARSE_NEED_MORE_DATA;
524
} else if (anim_chunks == 0) {
526
dmux->bgcolor_ = ReadLE32(mem);
527
dmux->loop_count_ = ReadLE16s(mem);
528
Skip(mem, chunk_size_padded - ANIM_CHUNK_SIZE);
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);
540
#ifdef WEBP_EXPERIMENTAL_FEATURES
541
case MKFOURCC('F', 'R', 'G', 'M'): {
542
status = ParseFragment(dmux, chunk_size_padded);
546
case MKFOURCC('I', 'C', 'C', 'P'): {
547
store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG);
550
case MKFOURCC('X', 'M', 'P', ' '): {
551
store_chunk = !!(dmux->feature_flags_ & XMP_FLAG);
554
case MKFOURCC('E', 'X', 'I', 'F'): {
555
store_chunk = !!(dmux->feature_flags_ & EXIF_FLAG);
560
if (chunk_size_padded <= MemDataSize(mem)) {
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)) {
569
Skip(mem, chunk_size_padded);
571
status = PARSE_NEED_MORE_DATA;
576
if (mem->start_ == mem->riff_end_) {
578
} else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
579
status = PARSE_NEED_MORE_DATA;
581
} while (status == PARSE_OK);
586
// -----------------------------------------------------------------------------
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;
593
if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0;
594
if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0;
596
if (frame->width_ <= 0 || frame->height_ <= 0) return 0;
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);
605
if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1;
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;
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;
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;
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;
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_) {
631
if (f->width_ <= 0 || f->height_ <= 0) return 0;
633
// There shouldn't be a partial frame in a complete file.
634
if (dmux->state_ == WEBP_DEMUX_DONE) return 0;
636
// Ensure alpha precedes image bitstream.
637
if (alpha->size_ > 0 && image->size_ > 0 &&
638
alpha->offset_ > image->offset_) {
641
// There shouldn't be any frames after an incomplete one.
642
if (f->next_ != NULL) return 0;
645
fragment_count += f->is_fragment_;
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;
655
// -----------------------------------------------------------------------------
656
// WebPDemuxer object
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_;
668
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial,
669
WebPDemuxState* state, int version) {
670
const ChunkParser* parser;
672
ParseStatus status = PARSE_ERROR;
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;
679
if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL;
680
if (!ReadHeader(&mem)) return NULL;
682
partial = (mem.buf_size_ < mem.riff_end_);
683
if (!allow_partial && partial) return NULL;
685
dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux));
686
if (dmux == NULL) return NULL;
687
InitDemux(dmux, &mem);
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;
698
if (state) *state = dmux->state_;
700
if (status == PARSE_ERROR) {
701
WebPDemuxDelete(dmux);
707
void WebPDemuxDelete(WebPDemuxer* dmux) {
710
if (dmux == NULL) return;
712
for (f = dmux->frames_; f != NULL;) {
713
Frame* const cur_frame = f;
717
for (c = dmux->chunks_; c != NULL;) {
718
Chunk* const cur_chunk = c;
725
// -----------------------------------------------------------------------------
727
uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) {
728
if (dmux == NULL) return 0;
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_;
741
// -----------------------------------------------------------------------------
744
// Find the first 'frame_num' frame. There may be multiple such frames in a
746
static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) {
748
for (f = dmux->frames_; f != NULL; f = f->next_) {
749
if (frame_num == f->frame_num_) break;
754
// Returns fragment 'fragment_num' and the total 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;
762
for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) {
763
if (++total == fragment_num) fragment = f;
769
static const uint8_t* GetFramePayload(const uint8_t* const mem_buf,
770
const Frame* const frame,
771
size_t* const data_size) {
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_;
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_)
785
start_offset = alpha->offset_;
786
*data_size += alpha->size_ + inter_size;
788
return mem_buf + start_offset;
793
// Create a whole 'frame' from VP8 (+ alpha) or lossless.
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_;
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);
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
824
static int SetFrame(int frame_num, WebPIterator* const iter) {
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_;
831
frame = GetFrame(dmux, frame_num);
832
if (frame == NULL) return 0;
834
return SynthesizeFrame(dmux, frame, 1, iter);
837
int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) {
838
if (iter == NULL) return 0;
840
memset(iter, 0, sizeof(*iter));
841
iter->private_ = (void*)dmux;
842
return SetFrame(frame, iter);
845
int WebPDemuxNextFrame(WebPIterator* iter) {
846
if (iter == NULL) return 0;
847
return SetFrame(iter->frame_num + 1, 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);
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;
862
return SynthesizeFrame(dmux, frame, fragment_num, iter);
867
void WebPDemuxReleaseIterator(WebPIterator* iter) {
871
// -----------------------------------------------------------------------------
874
static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) {
875
const uint8_t* const mem_buf = dmux->mem_.buf_;
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;
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_;
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;
898
static int SetChunk(const char fourcc[4], int chunk_num,
899
WebPChunkIterator* const iter) {
900
const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_;
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;
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;
920
int WebPDemuxGetChunk(const WebPDemuxer* dmux,
921
const char fourcc[4], int chunk_num,
922
WebPChunkIterator* iter) {
923
if (iter == NULL) return 0;
925
memset(iter, 0, sizeof(*iter));
926
iter->private_ = (void*)dmux;
927
return SetChunk(fourcc, chunk_num, iter);
930
int WebPDemuxNextChunk(WebPChunkIterator* iter) {
932
const char* const fourcc =
933
(const char*)iter->chunk.bytes - CHUNK_HEADER_SIZE;
934
return SetChunk(fourcc, iter->chunk_num + 1, 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);
948
void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) {
952
#if defined(__cplusplus) || defined(c_plusplus)