static void parse_superframe_index(const uint8_t *data, size_t data_sz, uint32_t sizes[8], int *count, vpx_decrypt_cb decrypt_cb, void *decrypt_state) { uint8_t marker; assert(data_sz); marker = read_marker(decrypt_cb, decrypt_state, data + data_sz - 1); *count = 0; if ((marker & 0xe0) == 0xc0) { const uint32_t frames = (marker & 0x7) + 1; const uint32_t mag = ((marker >> 3) & 0x3) + 1; const size_t index_sz = 2 + mag * frames; uint8_t marker2 = read_marker(decrypt_cb, decrypt_state, data + data_sz - index_sz); if (data_sz >= index_sz && marker2 == marker) { // found a valid superframe index uint32_t i, j; const uint8_t *x = &data[data_sz - index_sz + 1]; // frames has a maximum of 8 and mag has a maximum of 4. uint8_t clear_buffer[32]; assert(sizeof(clear_buffer) >= frames * mag); if (decrypt_cb) { decrypt_cb(decrypt_state, x, clear_buffer, frames * mag); x = clear_buffer; } for (i = 0; i < frames; i++) { uint32_t this_sz = 0; for (j = 0; j < mag; j++) this_sz |= (*x++) << (j * 8); sizes[i] = this_sz; } *count = frames; } } }
static vpx_codec_err_t decode_one_iter(vpx_codec_alg_priv_t *ctx, const uint8_t **data_start_ptr, const uint8_t *data_end, uint32_t frame_size, void *user_priv, long deadline) { const vpx_codec_err_t res = decode_one(ctx, data_start_ptr, frame_size, user_priv, deadline); if (res != VPX_CODEC_OK) return res; // Account for suboptimal termination by the encoder. while (*data_start_ptr < data_end) { const uint8_t marker = read_marker(ctx->decrypt_cb, ctx->decrypt_state, *data_start_ptr); if (marker) break; (*data_start_ptr)++; } return VPX_CODEC_OK; }
static vpx_codec_err_t decoder_decode(vpx_codec_alg_priv_t *ctx, const uint8_t *data, unsigned int data_sz, void *user_priv, long deadline) { const uint8_t *data_start = data; const uint8_t * const data_end = data + data_sz; vpx_codec_err_t res; uint32_t frame_sizes[8]; int frame_count; if (data == NULL && data_sz == 0) { ctx->flushed = 1; return VPX_CODEC_OK; } // Reset flushed when receiving a valid frame. ctx->flushed = 0; // Initialize the decoder workers on the first frame. if (ctx->frame_workers == NULL) { const vpx_codec_err_t res = init_decoder(ctx); if (res != VPX_CODEC_OK) return res; } res = vp9_parse_superframe_index(data, data_sz, frame_sizes, &frame_count, ctx->decrypt_cb, ctx->decrypt_state); if (res != VPX_CODEC_OK) return res; if (ctx->frame_parallel_decode) { // Decode in frame parallel mode. When decoding in this mode, the frame // passed to the decoder must be either a normal frame or a superframe with // superframe index so the decoder could get each frame's start position // in the superframe. if (frame_count > 0) { int i; for (i = 0; i < frame_count; ++i) { const uint8_t *data_start_copy = data_start; const uint32_t frame_size = frame_sizes[i]; if (data_start < data || frame_size > (uint32_t) (data_end - data_start)) { set_error_detail(ctx, "Invalid frame size in index"); return VPX_CODEC_CORRUPT_FRAME; } if (ctx->available_threads == 0) { // No more threads for decoding. Wait until the next output worker // finishes decoding. Then copy the decoded frame into cache. if (ctx->num_cache_frames < FRAME_CACHE_SIZE) { wait_worker_and_cache_frame(ctx); } else { // TODO(hkuang): Add unit test to test this path. set_error_detail(ctx, "Frame output cache is full."); return VPX_CODEC_ERROR; } } res = decode_one(ctx, &data_start_copy, frame_size, user_priv, deadline); if (res != VPX_CODEC_OK) return res; data_start += frame_size; } } else { if (ctx->available_threads == 0) { // No more threads for decoding. Wait until the next output worker // finishes decoding. Then copy the decoded frame into cache. if (ctx->num_cache_frames < FRAME_CACHE_SIZE) { wait_worker_and_cache_frame(ctx); } else { // TODO(hkuang): Add unit test to test this path. set_error_detail(ctx, "Frame output cache is full."); return VPX_CODEC_ERROR; } } res = decode_one(ctx, &data, data_sz, user_priv, deadline); if (res != VPX_CODEC_OK) return res; } } else { // Decode in serial mode. if (frame_count > 0) { int i; for (i = 0; i < frame_count; ++i) { const uint8_t *data_start_copy = data_start; const uint32_t frame_size = frame_sizes[i]; vpx_codec_err_t res; if (data_start < data || frame_size > (uint32_t) (data_end - data_start)) { set_error_detail(ctx, "Invalid frame size in index"); return VPX_CODEC_CORRUPT_FRAME; } res = decode_one(ctx, &data_start_copy, frame_size, user_priv, deadline); if (res != VPX_CODEC_OK) return res; data_start += frame_size; } } else { while (data_start < data_end) { const uint32_t frame_size = (uint32_t) (data_end - data_start); const vpx_codec_err_t res = decode_one(ctx, &data_start, frame_size, user_priv, deadline); if (res != VPX_CODEC_OK) return res; // Account for suboptimal termination by the encoder. while (data_start < data_end) { const uint8_t marker = read_marker(ctx->decrypt_cb, ctx->decrypt_state, data_start); if (marker) break; ++data_start; } } } } return res; }
vpx_codec_err_t vp9_parse_superframe_index(const uint8_t *data, size_t data_sz, uint32_t sizes[8], int *count, vpx_decrypt_cb decrypt_cb, void *decrypt_state) { // A chunk ending with a byte matching 0xc0 is an invalid chunk unless // it is a super frame index. If the last byte of real video compression // data is 0xc0 the encoder must add a 0 byte. If we have the marker but // not the associated matching marker byte at the front of the index we have // an invalid bitstream and need to return an error. uint8_t marker; assert(data_sz); marker = read_marker(decrypt_cb, decrypt_state, data + data_sz - 1); *count = 0; if ((marker & 0xe0) == 0xc0) { const uint32_t frames = (marker & 0x7) + 1; const uint32_t mag = ((marker >> 3) & 0x3) + 1; const size_t index_sz = 2 + mag * frames; // This chunk is marked as having a superframe index but doesn't have // enough data for it, thus it's an invalid superframe index. if (data_sz < index_sz) return VPX_CODEC_CORRUPT_FRAME; { const uint8_t marker2 = read_marker(decrypt_cb, decrypt_state, data + data_sz - index_sz); // This chunk is marked as having a superframe index but doesn't have // the matching marker byte at the front of the index therefore it's an // invalid chunk. if (marker != marker2) return VPX_CODEC_CORRUPT_FRAME; } { // Found a valid superframe index. uint32_t i, j; const uint8_t *x = &data[data_sz - index_sz + 1]; // Frames has a maximum of 8 and mag has a maximum of 4. uint8_t clear_buffer[32]; assert(sizeof(clear_buffer) >= frames * mag); if (decrypt_cb) { decrypt_cb(decrypt_state, x, clear_buffer, frames * mag); x = clear_buffer; } for (i = 0; i < frames; ++i) { uint32_t this_sz = 0; for (j = 0; j < mag; ++j) this_sz |= (*x++) << (j * 8); sizes[i] = this_sz; } *count = frames; } } return VPX_CODEC_OK; }