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