Design change in ANMF and FRGM chunks:

- Make ANMF and FRGM chunks hierarchical so that they encompass all chunks of
  that frame.
- Use this in demuxer: stop parsing a frame if all image data for it isn't
  available yet. Thus, we have a frame-level incremental support; that is,
  all frames that are fully available can be parsed.
- Note: We still keep incremental support for single images - so that they can
  be decoded with incremental decoding.

Change-Id: Id1585b16b06caee1d84009c42a25d2de29fa6135
This commit is contained in:
Urvang Joshi
2012-10-31 17:19:31 -07:00
parent f903cbab9a
commit 81b8a741ed
3 changed files with 141 additions and 53 deletions

View File

@ -51,10 +51,9 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
// Fill the chunk with the given data (includes chunk header bytes), after some
// verifications.
static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
const uint8_t* data,
size_t data_size, size_t riff_size,
int copy_data) {
static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
const uint8_t* data, size_t data_size,
size_t riff_size, int copy_data) {
uint32_t chunk_size;
WebPData chunk_data;
@ -74,6 +73,66 @@ static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
}
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
WebPMuxImage* const wpi) {
const uint8_t* bytes = chunk->data_.bytes;
size_t size = chunk->data_.size;
const uint8_t* const last = bytes + size;
WebPChunk subchunk;
size_t subchunk_size;
ChunkInit(&subchunk);
assert(chunk->tag_ == kChunks[IDX_ANMF].tag ||
chunk->tag_ == kChunks[IDX_FRGM].tag);
assert(!wpi->is_partial_);
// ANMF/FRGM.
{
const size_t hdr_size = (chunk->tag_ == kChunks[IDX_ANMF].tag) ?
ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE;
const WebPData temp = { bytes, hdr_size };
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
}
ChunkSetNth(&subchunk, &wpi->header_, 1);
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
// Rest of the chunks.
subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
bytes += subchunk_size;
size -= subchunk_size;
while (bytes != last) {
ChunkInit(&subchunk);
if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
copy_data) != WEBP_MUX_OK) {
goto Fail;
}
switch (ChunkGetIdFromTag(subchunk.tag_)) {
case WEBP_CHUNK_ALPHA:
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
break;
case WEBP_CHUNK_IMAGE:
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
wpi->is_partial_ = 0; // wpi is completely filled.
break;
default:
goto Fail;
break;
}
subchunk_size = ChunkDiskSize(&subchunk);
bytes += subchunk_size;
size -= subchunk_size;
}
if (wpi->is_partial_) goto Fail;
return 1;
Fail:
ChunkRelease(&subchunk);
return 0;
}
//------------------------------------------------------------------------------
// Create a mux object from WebP-RIFF data.
@ -136,42 +195,46 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
// Loop over chunks.
while (data != end) {
size_t data_size;
WebPChunkId id;
WebPMuxError err;
err = ChunkVerifyAndAssignData(&chunk, data, size, riff_size, copy_data);
if (err != WEBP_MUX_OK) goto Err;
WebPChunk** chunk_list;
if (ChunkVerifyAndAssign(&chunk, data, size, riff_size,
copy_data) != WEBP_MUX_OK) {
goto Err;
}
data_size = ChunkDiskSize(&chunk);
id = ChunkGetIdFromTag(chunk.tag_);
if (IsWPI(id)) { // An image chunk (frame/tile/alpha/vp8).
WebPChunk** wpi_chunk_ptr =
MuxImageGetListFromId(wpi, id); // Image chunk to set.
assert(wpi_chunk_ptr != NULL);
if (*wpi_chunk_ptr != NULL) goto Err; // Consecutive alpha chunks or
// consecutive frame/tile chunks.
if (ChunkSetNth(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err;
if (id == WEBP_CHUNK_IMAGE) {
switch (id) {
case WEBP_CHUNK_ALPHA:
if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks.
if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err;
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
break;
case WEBP_CHUNK_IMAGE:
if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err;
wpi->is_partial_ = 0; // wpi is completely filled.
PushImage:
// Add this to mux->images_ list.
if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err;
MuxImageInit(wpi); // Reset for reading next image.
} else {
wpi->is_partial_ = 1; // wpi is only partially filled.
}
} else { // A non-image chunk.
WebPChunk** chunk_list;
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
}
{
const size_t data_size = ChunkDiskSize(&chunk);
data += data_size;
size -= data_size;
break;
case WEBP_CHUNK_ANMF:
case WEBP_CHUNK_FRGM:
if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete.
if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err;
ChunkRelease(&chunk);
goto PushImage;
break;
default: // A non-image chunk.
if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before
// getting all chunks of an image.
chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk.
if (chunk_list == NULL) chunk_list = &mux->unknown_;
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
break;
}
data += data_size;
size -= data_size;
ChunkInit(&chunk);
}