From 13e50da6f877ea91db104a3e3cefa95e21d1e703 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Thu, 10 Mar 2011 15:05:59 -0800 Subject: [PATCH] make the bitreader preload at least 8bits, instead of post-load them (this makes initialization easier and will be helpful for incremental decoding). Modify ParsePartitions() to accommodate for truncated input. Change-Id: I62f52078d6b7a2314a11880a20d9eac5b4714bd0 --- src/dec/bits.c | 19 ++++++++++--------- src/dec/bits.h | 49 +++++++++++++++++++++++++------------------------ src/dec/vp8.c | 49 ++++++++++++++++++++++++++++++------------------- 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/dec/bits.c b/src/dec/bits.c index 94b8e998..da3b777f 100644 --- a/src/dec/bits.c +++ b/src/dec/bits.c @@ -18,16 +18,17 @@ extern "C" { //----------------------------------------------------------------------------- // VP8BitReader -void VP8Init(VP8BitReader* const br, const uint8_t* buf, uint32_t size) { +void VP8InitBitReader(VP8BitReader* const br, + const uint8_t* const start, const uint8_t* const end) { assert(br); - assert(buf); - br->range_ = 255 - 1; - br->buf_ = buf; - br->buf_end_ = buf + size; - // Need two initial bytes. - br->value_ = (VP8GetByte(br) << 8) | VP8GetByte(br); - br->left_ = -8; - br->eof_ = 0; + assert(start); + assert(start <= end); + br->range_ = 255 - 1; + br->buf_ = start; + br->buf_end_ = end; + br->value_ = 0; + br->missing_ = 8; + br->eof_ = 0; } const uint8_t kVP8Log2Range[128] = { diff --git a/src/dec/bits.h b/src/dec/bits.h index 6bf9296a..82e4c3ae 100644 --- a/src/dec/bits.h +++ b/src/dec/bits.h @@ -30,11 +30,12 @@ typedef struct { // boolean decoder uint32_t range_; // current range minus 1. In [127, 254] interval. uint32_t value_; // current value - int left_; // how many unused bits (negated) + int missing_; // number of missing bits in value_ (8bit) } VP8BitReader; // Initialize the bit reader and the boolean decoder. -void VP8Init(VP8BitReader* const br, const uint8_t* buf, uint32_t size); +void VP8InitBitReader(VP8BitReader* const br, + const uint8_t* const start, const uint8_t* const end); // return the next value made of 'num_bits' bits uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); @@ -55,7 +56,24 @@ static inline uint32_t VP8GetByte(VP8BitReader* const br) { return *br->buf_++; } br->eof_ = 1; - return 0x80; + return 0xff; +} + +static inline uint32_t VP8BitUpdate(VP8BitReader* const br, uint32_t split) { + uint32_t bit; + // Make sure we have a least 8 bits in 'value_' + if (br->missing_ > 0) { + br->value_ |= VP8GetByte(br) << br->missing_; + br->missing_ -= 8; + } + bit = ((br->value_ >> 8) > split); + if (bit) { + br->range_ -= split + 1; + br->value_ -= (split + 1) << 8; + } else { + br->range_ = split; + } + return bit; } static inline void VP8Shift(VP8BitReader* const br) { @@ -63,22 +81,12 @@ static inline void VP8Shift(VP8BitReader* const br) { const int shift = kVP8Log2Range[br->range_]; br->range_ = kVP8NewRange[br->range_]; br->value_ <<= shift; - br->left_ += shift; - if (br->left_ > 0) { - br->value_ |= VP8GetByte(br) << br->left_; - br->left_ -= 8; - } + br->missing_ += shift; } static inline uint32_t VP8GetBit(VP8BitReader* const br, int prob) { const uint32_t split = (br->range_ * prob) >> 8; - const uint32_t bit = ((br->value_ >> 8) > split); - if (bit) { - br->range_ -= split + 1; - br->value_ -= (split + 1) << 8; - } else { - br->range_ = split; - } + const uint32_t bit = VP8BitUpdate(br, split); if (br->range_ < 0x7f) { VP8Shift(br); } @@ -87,16 +95,9 @@ static inline uint32_t VP8GetBit(VP8BitReader* const br, int prob) { static inline int VP8GetSigned(VP8BitReader* const br, int v) { const uint32_t split = br->range_ >> 1; - const uint32_t bit = ((br->value_ >> 8) > split); - if (bit) { - br->range_ -= split + 1; - br->value_ -= (split + 1) << 8; - v = -v; - } else { - br->range_ = split; - } + const uint32_t bit = VP8BitUpdate(br, split); VP8Shift(br); - return v; + return bit ? -v : v; } #if defined(__cplusplus) || defined(c_plusplus) diff --git a/src/dec/vp8.c b/src/dec/vp8.c index 255337c9..b8c9ef25 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -111,33 +111,41 @@ static int ParseSegmentHeader(VP8BitReader* br, } // Paragraph 9.5 -static int ParsePartitions(VP8Decoder* const dec, - const uint8_t* buf, uint32_t size) { +// This function returns VP8_STATUS_SUSPENDED if we don't have all the +// necessary data in 'buf'. +// This case is not necessarily an error (for incremental decoding). +// Still, no bitreader is ever initialized to make it possible to read +// unavailable memory. +// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA +// is returned, and this is an unrecoverable error. +// If the partitions were positioned ok, VP8_STATUS_OK is returned. +static VP8StatusCode ParsePartitions(VP8Decoder* const dec, + const uint8_t* buf, uint32_t size) { VP8BitReader* const br = &dec->br_; const uint8_t* sz = buf; + const uint8_t* buf_end = buf + size; + const uint8_t* part_start; int last_part; - uint32_t offset; int p; dec->num_parts_ = 1 << VP8GetValue(br, 2); last_part = dec->num_parts_ - 1; - offset = last_part * 3; - - if (size <= offset) { - return 0; + part_start = buf + last_part * 3; + if (buf_end < part_start) { + // we can't even read the sizes with sz[]! That's a failure. + return VP8_STATUS_NOT_ENOUGH_DATA; } for (p = 0; p < last_part; ++p) { const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); - if (offset + psize > size) { - return 0; - } - VP8Init(dec->parts_ + p, buf + offset, psize); - offset += psize; + const uint8_t* part_end = part_start + psize; + if (part_end > buf_end) part_end = buf_end; + VP8InitBitReader(dec->parts_ + p, part_start, part_end); + part_start = part_end; sz += 3; } - size -= offset; - VP8Init(dec->parts_ + last_part, buf + offset, size); - return 1; + VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end); + return (part_start < buf_end) ? VP8_STATUS_OK : + VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data } // Paragraph 9.4 @@ -284,14 +292,17 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { dec->segment_ = 0; // default for intra } - br = &dec->br_; - VP8Init(br, buf, buf_size); + // Check if we have all the partition #0 available, and initialize dec->br_ + // to read this partition (and this partition only). if (frm_hdr->partition_length_ > buf_size) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "bad partition length"); - } + } + br = &dec->br_; + VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_); buf += frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_; + if (frm_hdr->key_frame_) { pic_hdr->colorspace_ = VP8Get(br); pic_hdr->clamp_type_ = VP8Get(br); @@ -305,7 +316,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse filter header"); } - if (!ParsePartitions(dec, buf, buf_size)) { + if (ParsePartitions(dec, buf, buf_size) != VP8_STATUS_OK) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "cannot parse partitions"); }