diff --git a/src/dec/frame.c b/src/dec/frame.c index d9344125..beae6fc6 100644 --- a/src/dec/frame.c +++ b/src/dec/frame.c @@ -44,7 +44,8 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) { dec->mem_size_ = 0; dec->mem_ = (uint8_t*)malloc(needed); if (dec->mem_ == NULL) { - return VP8SetError(dec, 1, "no memory during frame initialization."); + return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "no memory during frame initialization."); } dec->mem_size_ = needed; } @@ -210,7 +211,7 @@ void VP8StoreBlock(VP8Decoder* const dec) { } } -void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { +int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { const int extra_y_rows = kFilterExtraRows[dec->filter_type_]; const int ysize = extra_y_rows * dec->cache_y_stride_; const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_; @@ -246,7 +247,9 @@ void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { } io->mb_y = y_start; io->mb_h = y_end - y_start; - io->put(io); + if (!io->put(io)) { + return 0; + } } // rotate top samples if (!last_row) { @@ -254,6 +257,7 @@ void VP8FinishRow(VP8Decoder* const dec, VP8Io* io) { memcpy(udst, udst + 8 * dec->cache_uv_stride_, uvsize); memcpy(vdst, vdst + 8 * dec->cache_uv_stride_, uvsize); } + return 1; } //----------------------------------------------------------------------------- diff --git a/src/dec/vp8.c b/src/dec/vp8.c index caae7d9e..2176ee12 100644 --- a/src/dec/vp8.c +++ b/src/dec/vp8.c @@ -16,7 +16,7 @@ // VP8Decoder static void SetOk(VP8Decoder* const dec) { - dec->status_ = 0; + dec->status_ = VP8_STATUS_OK; dec->error_msg_ = "OK"; } @@ -35,8 +35,8 @@ VP8Decoder* VP8New() { return dec; } -int VP8Status(VP8Decoder* const dec) { - if (!dec) return 2; +VP8StatusCode VP8Status(VP8Decoder* const dec) { + if (!dec) return VP8_STATUS_INVALID_PARAM; return dec->status_; } @@ -53,7 +53,8 @@ void VP8Delete(VP8Decoder* const dec) { } } -int VP8SetError(VP8Decoder* const dec, int error, const char *msg) { +int VP8SetError(VP8Decoder* const dec, + VP8StatusCode error, const char * const msg) { dec->status_ = error; dec->error_msg_ = msg; dec->ready_ = 0; @@ -189,13 +190,15 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { } SetOk(dec); if (io == NULL) { - return VP8SetError(dec, 2, "null VP8Io passed to VP8GetHeaders()"); + return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, + "null VP8Io passed to VP8GetHeaders()"); } buf = (uint8_t *)io->data; buf_size = io->data_size; if (buf == NULL || buf_size <= 4) { - return VP8SetError(dec, 2, "Not enough data to parse frame header"); + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Not enough data to parse frame header"); } // Skip over valid RIFF headers @@ -203,18 +206,22 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { uint32_t riff_size; uint32_t chunk_size; if (buf_size < 20 + 4) { - return VP8SetError(dec, 2, "RIFF: Truncated header."); + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "RIFF: Truncated header."); } if (memcmp(buf + 8, "WEBP", 4)) { // wrong image file signature - return VP8SetError(dec, 2, "RIFF: WEBP signature not found."); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "RIFF: WEBP signature not found."); } riff_size = get_le32(buf + 4); if (memcmp(buf + 12, "VP8 ", 4)) { - return VP8SetError(dec, 2, "RIFF: Invalid compression format."); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "RIFF: Invalid compression format."); } chunk_size = get_le32(buf + 16); if ((chunk_size > riff_size + 8) || (chunk_size & 1)) { - return VP8SetError(dec, 2, "RIFF: Inconsistent size information."); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "RIFF: Inconsistent size information."); } buf += 20; buf_size -= 20; @@ -236,10 +243,12 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { if (frm_hdr->key_frame_) { // Paragraph 9.2 if (buf_size < 7) { - return VP8SetError(dec, 2, "cannot parse picture header"); + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "cannot parse picture header"); } if (buf[0] != 0x9d || buf[1] != 0x01 || buf[2] != 0x2a) { - return VP8SetError(dec, 2, "Bad code word"); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Bad code word"); } pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff; pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2 @@ -261,7 +270,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { br = &dec->br_; VP8Init(br, buf, buf_size); if (frm_hdr->partition_length_ > buf_size) { - return VP8SetError(dec, 2, "bad partition length"); + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "bad partition length"); } buf += frm_hdr->partition_length_; buf_size -= frm_hdr->partition_length_; @@ -270,14 +280,17 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { pic_hdr->clamp_type_ = VP8Get(br); } if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) { - return VP8SetError(dec, 2, "cannot parse segment header"); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "cannot parse segment header"); } // Filter specs if (!ParseFilterHeader(br, dec)) { - return VP8SetError(dec, 2, "cannot parse filter header"); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "cannot parse filter header"); } if (!ParsePartitions(dec, buf, buf_size)) { - return VP8SetError(dec, 2, "cannot parse partitions"); + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "cannot parse partitions"); } // quantizer change @@ -298,7 +311,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref #else - return VP8SetError(dec, 2, "Not a key frame."); + return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE, + "Not a key frame."); #endif } else { dec->buffer_flags_ = 0x003 | 0x100; @@ -500,7 +514,6 @@ static int ParseResiduals(VP8Decoder* const dec, // Main loop static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { - int ok = 1; VP8BitReader* const br = &dec->br_; VP8BitReader* token_br; @@ -530,8 +543,8 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { if (!info->skip_) { if (!ParseResiduals(dec, info, token_br)) { - ok = 0; - break; + return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, + "Residual parsing failed."); } } else { left->nz_ = info->nz_ = 0; @@ -546,13 +559,13 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { // Store data and save block's filtering params VP8StoreBlock(dec); } - if (!ok) { - break; + if (!VP8FinishRow(dec, io)) { + return VP8SetError(dec, VP8_STATUS_USER_ABORT, + "Output aborted."); } - VP8FinishRow(dec, io); if (dec->br_.eof_ || token_br->eof_) { - ok = 0; - break; + return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, + "Premature end-of-file encountered."); } } @@ -563,7 +576,7 @@ static int ParseFrame(VP8Decoder* const dec, VP8Io* io) { } #endif - return ok; + return 1; } // Main entry point @@ -572,7 +585,8 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { return 0; } if (io == NULL) { - return VP8SetError(dec, 2, "NULL VP8Io parameter in VP8Decode()."); + return VP8SetError(dec, VP8_STATUS_INVALID_PARAM, + "NULL VP8Io parameter in VP8Decode()."); } if (!dec->ready_) { @@ -585,13 +599,15 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { // will allocate memory and prepare everything. if (!VP8InitFrame(dec, io)) { VP8Clear(dec); - return VP8SetError(dec, 3, "Allocation failed"); + return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY, + "Allocation failed"); } if (io->setup && !io->setup(io)) { VP8Clear(dec); - return VP8SetError(dec, 3, "Frame setup failed"); + return VP8SetError(dec, VP8_STATUS_USER_ABORT, + "Frame setup failed"); } // Main decoding loop @@ -602,7 +618,7 @@ int VP8Decode(VP8Decoder* const dec, VP8Io* const io) { } if (!ret) { VP8Clear(dec); - return VP8SetError(dec, 3, "Frame decoding failed"); + return 0; } } diff --git a/src/dec/vp8i.h b/src/dec/vp8i.h index cf1b436d..3e5ee889 100644 --- a/src/dec/vp8i.h +++ b/src/dec/vp8i.h @@ -162,7 +162,7 @@ typedef struct { // VP8Decoder: the main opaque structure handed over to user struct VP8Decoder { - int status_; // 0 = OK + VP8StatusCode status_; int ready_; // true if ready to decode a picture with VP8Decode() const char* error_msg_; // set when status_ is not OK. @@ -246,7 +246,8 @@ struct VP8Decoder { // internal functions. Not public. // in vp8.c -int VP8SetError(VP8Decoder* const dec, int error, const char *msg); +int VP8SetError(VP8Decoder* const dec, + VP8StatusCode error, const char * const msg); // in tree.c void VP8ResetProba(VP8Proba* const proba); @@ -262,8 +263,8 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io); void VP8ReconstructBlock(VP8Decoder* const dec); // Store a block, along with filtering params void VP8StoreBlock(VP8Decoder* const dec); -// Finalize and transmit a complete row -void VP8FinishRow(VP8Decoder* const dec, VP8Io* io); +// Finalize and transmit a complete row. Return false in case of user-abort. +int VP8FinishRow(VP8Decoder* const dec, VP8Io* io); // in dsp.c typedef void (*VP8Idct)(const int16_t* coeffs, uint8_t* dst); diff --git a/src/dec/webp.c b/src/dec/webp.c index e88267ac..c6ecd32f 100644 --- a/src/dec/webp.c +++ b/src/dec/webp.c @@ -188,14 +188,16 @@ typedef struct { CSP_MODE mode; } Params; -static void CustomPut(const VP8Io* io) { +static int CustomPut(const VP8Io* io) { Params *p = (Params*)io->opaque; const int w = io->width; const int mb_h = io->mb_h; const int uv_w = (w + 1) / 2; assert(!(io->mb_y & 1)); - if (w <= 0 || mb_h <= 0) return; + if (w <= 0 || mb_h <= 0) { + return 0; + } if (p->mode == MODE_YUV) { uint8_t* const y_dst = p->output + io->mb_y * p->stride; @@ -282,6 +284,7 @@ static void CustomPut(const VP8Io* io) { } } } + return 1; } //----------------------------------------------------------------------------- diff --git a/src/webp/decode.h b/src/webp/decode.h index e7198564..7099fb20 100644 --- a/src/webp/decode.h +++ b/src/webp/decode.h @@ -96,4 +96,4 @@ uint8_t* WebPDecodeYUVInto(const uint8_t* data, uint32_t data_size, } // extern "C" #endif -#endif // WEBP_WEBP_DECODE_H_ +#endif /* WEBP_WEBP_DECODE_H_ */ diff --git a/src/webp/decode_vp8.h b/src/webp/decode_vp8.h index 3709b61f..e7044edf 100644 --- a/src/webp/decode_vp8.h +++ b/src/webp/decode_vp8.h @@ -53,8 +53,9 @@ struct VP8Io { // called when fresh samples are available. Currently, samples are in // YUV420 format, and can be up to width x 24 in size (depending on the - // in-loop filtering level, e.g.). - void (*put)(const VP8Io* io); + // in-loop filtering level, e.g.). Should return false in case of error + // or abort request. + int (*put)(const VP8Io* io); // called just before starting to decode the blocks. // Should returns 0 in case of error. @@ -86,15 +87,23 @@ void VP8InitIo(VP8Io* const io); int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io); // Decode a picture. Will call VP8GetHeaders() if it wasn't done already. +// Returns false in case of error. int VP8Decode(VP8Decoder* const dec, VP8Io* const io); +// Enumeration of the codes returned by VP8Status() +typedef enum { + VP8_STATUS_OK = 0, + VP8_STATUS_OUT_OF_MEMORY, + VP8_STATUS_INVALID_PARAM, + VP8_STATUS_BITSTREAM_ERROR, + VP8_STATUS_UNSUPPORTED_FEATURE, + VP8_STATUS_SUSPENDED, + VP8_STATUS_USER_ABORT, + VP8_STATUS_NOT_ENOUGH_DATA, +} VP8StatusCode; + // Return current status of the decoder: -// 0 = OK -// 1 = OUT_OF_MEMORY -// 2 = INVALID_PARAM -// 3 = BITSTREAM_ERROR -// 4 = UNSUPPORTED_FEATURE -int VP8Status(VP8Decoder* const dec); +VP8StatusCode VP8Status(VP8Decoder* const dec); // return readable string corresponding to the last status. const char* VP8StatusMessage(VP8Decoder* const dec); @@ -112,4 +121,4 @@ void VP8Delete(VP8Decoder* const dec); } // extern "C" #endif -#endif // WEBP_WEBP_DECODE_VP8_H_ +#endif /* WEBP_WEBP_DECODE_VP8_H_ */ diff --git a/src/webp/types.h b/src/webp/types.h index 82fa7bbb..a6d2cd4c 100644 --- a/src/webp/types.h +++ b/src/webp/types.h @@ -22,6 +22,7 @@ typedef unsigned short uint16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef unsigned long long int uint64_t; +typedef long long int int64_t; #define inline __forceinline #endif /* _MSC_VER */