From 076c1c956c55887ca63a797baf0143f56dd30879 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 22 Mar 2013 11:38:46 +0100 Subject: [PATCH] vp8: WebP decoding support VP8L, padding and bounds checks by Pascal Massimino Reviewed-by: Pascal Massimino Signed-off-by: Michael Niedermayer --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 +++++ libavcodec/vp8.c | 61 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 0d73067410..bbb7083452 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -461,6 +461,7 @@ OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8dsp.o vp56rac.o OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o ass.o OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o +OBJS-$(CONFIG_WEBP_DECODER) += vp8.o vp8dsp.o vp56rac.o OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 1eaf2d3397..4ff63ef098 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -283,6 +283,7 @@ void avcodec_register_all(void) REGISTER_DECODER(VP6F, vp6f); REGISTER_DECODER(VP8, vp8); REGISTER_DECODER(VQA, vqa); + REGISTER_DECODER(WEBP, webp); REGISTER_ENCDEC (WMV1, wmv1); REGISTER_ENCDEC (WMV2, wmv2); REGISTER_DECODER(WMV3, wmv3); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index c3bd534ed7..1b5f9d4999 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -292,6 +292,7 @@ enum AVCodecID { AV_CODEC_ID_MVC1 = MKBETAG('M','V','C','1'), AV_CODEC_ID_MVC2 = MKBETAG('M','V','C','2'), AV_CODEC_ID_SNOW = MKBETAG('S','N','O','W'), + AV_CODEC_ID_WEBP = MKBETAG('W','E','B','P'), /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index ded09c93e8..2b3eed7760 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -1037,6 +1037,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Google VP9"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_WEBP, + .type = AVMEDIA_TYPE_VIDEO, + .name = "webp", + .long_name = NULL_IF_CONFIG_SMALL("WebP"), + .props = AV_CODEC_PROP_LOSSY, + }, { .id = AV_CODEC_ID_PICTOR, .type = AVMEDIA_TYPE_VIDEO, diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index ac929d03b4..1ab76b5278 100644 --- a/libavcodec/vp8.c +++ b/libavcodec/vp8.c @@ -2094,6 +2094,52 @@ static int vp8_decode_update_thread_context(AVCodecContext *dst, const AVCodecCo return 0; } +static unsigned apply_padding(unsigned size) { return size + (size & 1); } + +static int webp_decode_frame(AVCodecContext *avctx, void *data, int *data_size, + AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + AVPacket pkt = *avpkt; + + if (buf_size >= 16 + && AV_RL32(buf ) == AV_RL32("RIFF") + && AV_RL32(buf+ 8) == AV_RL32("WEBP")) { + unsigned riff_size = apply_padding(AV_RL32(buf+4)) + 8; + buf += 12; // Skip over main header + buf_size -= 12; + if (buf_size < 8 || riff_size < 8) { + av_log(avctx, AV_LOG_ERROR, "Incomplete header.\n"); + return AVERROR_INVALIDDATA; + } + if (AV_RL32(buf) == AV_RL32("VP8L")) { + av_log(avctx, AV_LOG_ERROR, "Unsupported WebP lossless format.\n"); + return AVERROR_PATCHWELCOME; + } + if (AV_RL32(buf) == AV_RL32("VP8X") && AV_RL32(buf+4) < (unsigned)buf_size) { + unsigned size = apply_padding(AV_RL32(buf+4) + 8); + buf += size; + buf_size -= size; + } + if (buf_size >= 8 + && AV_RL32(buf) == AV_RL32("ALPH") && AV_RL32(buf+4) < (unsigned)buf_size) { + unsigned size = apply_padding(AV_RL32(buf+4) + 8); + buf += size; + buf_size -= size; + av_log(avctx, AV_LOG_WARNING, "Skipping alpha plane\n"); + } + if (buf_size >= 8 && AV_RL32(buf) == AV_RL32("VP8 ")) { + buf += 8; + buf_size -= 8; + } + } + pkt.data = buf; + pkt.size = buf_size; + + return vp8_decode_frame(avctx, data, data_size, &pkt); +} + AVCodec ff_vp8_decoder = { .name = "vp8", .type = AVMEDIA_TYPE_VIDEO, @@ -2108,3 +2154,18 @@ AVCodec ff_vp8_decoder = { .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp8_decode_init_thread_copy), .update_thread_context = ONLY_IF_THREADS_ENABLED(vp8_decode_update_thread_context), }; + +AVCodec ff_webp_decoder = { + .name = "webp", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_WEBP, + .priv_data_size = sizeof(VP8Context), + .init = vp8_decode_init, + .close = vp8_decode_free, + .decode = webp_decode_frame, + .capabilities = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS | CODEC_CAP_SLICE_THREADS, + .flush = vp8_decode_flush, + .long_name = NULL_IF_CONFIG_SMALL("WebP"), + .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp8_decode_init_thread_copy), + .update_thread_context = ONLY_IF_THREADS_ENABLED(vp8_decode_update_thread_context), +};