>From 1a04903b69eaac956d2b7f1d2abf17f9b2e01843 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alberto=20Delm=C3=A1s?= <adel...@gmail.com>
Date: Mon, 15 Aug 2011 20:42:26 +0200
Subject: [PATCH] Windows Media Image decoder (WMVP/WVP2)

---
 Changelog              |    1 +
 configure              |    1 +
 doc/general.texi       |    1 +
 libavcodec/allcodecs.c |    1 +
 libavcodec/avcodec.h   |    1 +
 libavcodec/h263dec.c   |    1 +
 libavcodec/mpegvideo.c |   14 ++-
 libavcodec/vc1.c       |    3 -
 libavcodec/vc1.h       |    3 +
 libavcodec/vc1dec.c    |  326 ++++++++++++++++++++++++++++++++++++++++--------
 libavcodec/vc1dsp.c    |   69 ++++++++++
 libavcodec/vc1dsp.h    |   10 ++
 libavcodec/version.h   |    4 +-
 libavformat/riff.c     |    4 +-
 14 files changed, 379 insertions(+), 60 deletions(-)

diff --git a/Changelog b/Changelog
index 363d9a6..f5f9ffa 100644
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,7 @@ version <next>:
 - BWF muxer
 - Flash Screen Video 2 decoder
 - ffplay/ffprobe/ffserver renamed to avplay/avprobe/avserver
+- Windows Media Image decoder
 
 
 version 0.7:
diff --git a/configure b/configure
index 9c6ce0a..306b84b 100755
--- a/configure
+++ b/configure
@@ -1366,6 +1366,7 @@ wmav1_encoder_select="mdct sinewin"
 wmav2_decoder_select="mdct sinewin"
 wmav2_encoder_select="mdct sinewin"
 wmavoice_decoder_select="lsp rdft dct mdct sinewin"
+wmimage_decoder_select="vc1_decoder"
 wmv1_decoder_select="h263_decoder"
 wmv1_encoder_select="h263_encoder"
 wmv2_decoder_select="h263_decoder"
diff --git a/doc/general.texi b/doc/general.texi
index be5b570..49ee367 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -516,6 +516,7 @@ following image formats are supported:
 @item VMware Screen Codec / VMware Video  @tab     @tab  X
     @tab Codec used in videos captured by VMware.
 @item Westwood Studios VQA (Vector Quantized Animation) video  @tab     @tab  X
+@item Windows Media Image    @tab     @tab  X
 @item Windows Media Video 7  @tab  X  @tab  X
 @item Windows Media Video 8  @tab  X  @tab  X
 @item Windows Media Video 9  @tab     @tab  X
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index dcef0d6..58f6cd7 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -213,6 +213,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER (VP6F, vp6f);
     REGISTER_DECODER (VP8, vp8);
     REGISTER_DECODER (VQA, vqa);
+    REGISTER_DECODER (WMIMAGE, wmimage);
     REGISTER_ENCDEC  (WMV1, wmv1);
     REGISTER_ENCDEC  (WMV2, wmv2);
     REGISTER_DECODER (WMV3, wmv3);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 5bc1878..091ac4a 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -208,6 +208,7 @@ enum CodecID {
     CODEC_ID_PRORES,
     CODEC_ID_JV,
     CODEC_ID_DFA,
+    CODEC_ID_WMIMAGE,
 
     /* various PCM "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
diff --git a/libavcodec/h263dec.c b/libavcodec/h263dec.c
index cba0c5a..4502da3 100644
--- a/libavcodec/h263dec.c
+++ b/libavcodec/h263dec.c
@@ -91,6 +91,7 @@ av_cold int ff_h263_decode_init(AVCodecContext *avctx)
         break;
     case CODEC_ID_VC1:
     case CODEC_ID_WMV3:
+    case CODEC_ID_WMIMAGE:
         s->h263_pred = 1;
         s->msmpeg4_version=6;
         avctx->chroma_sample_location = AVCHROMA_LOC_LEFT;
diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c
index 365a0d4..7e8359d 100644
--- a/libavcodec/mpegvideo.c
+++ b/libavcodec/mpegvideo.c
@@ -208,7 +208,12 @@ void ff_copy_picture(Picture *dst, Picture *src){
  */
 static void free_frame_buffer(MpegEncContext *s, Picture *pic)
 {
-    ff_thread_release_buffer(s->avctx, (AVFrame*)pic);
+    /* WMImage codecs allocate internal buffers with different dimensions;
+       ignore user defined callbacks for these */
+    if (!s->codec_id == CODEC_ID_WMIMAGE)
+        ff_thread_release_buffer(s->avctx, (AVFrame*)pic);
+    else
+        avcodec_default_release_buffer(s->avctx, (AVFrame*)pic);
     av_freep(&pic->f.hwaccel_picture_private);
 }
 
@@ -230,7 +235,10 @@ static int alloc_frame_buffer(MpegEncContext *s, Picture 
*pic)
         }
     }
 
-    r = ff_thread_get_buffer(s->avctx, (AVFrame*)pic);
+    if (!s->codec_id == CODEC_ID_WMIMAGE)
+        r = ff_thread_get_buffer(s->avctx, (AVFrame*)pic);
+    else
+        r = avcodec_default_get_buffer(s->avctx, (AVFrame*)pic);
 
     if (r < 0 || !pic->f.age || !pic->f.type || !pic->f.data[0]) {
         av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed (%d %d %d %p)\n",
@@ -2317,7 +2325,7 @@ void ff_draw_horiz_band(MpegEncContext *s, int y, int h){
 
     if(field_pic && s->first_field && 
!(s->avctx->slice_flags&SLICE_FLAG_ALLOW_FIELD)) return;
 
-    if (s->avctx->draw_horiz_band) {
+    if (s->avctx->draw_horiz_band && s->codec_id != CODEC_ID_WMIMAGE) {
         AVFrame *src;
         int offset[4];
 
diff --git a/libavcodec/vc1.c b/libavcodec/vc1.c
index 243bef5..fe9781b 100644
--- a/libavcodec/vc1.c
+++ b/libavcodec/vc1.c
@@ -314,9 +314,6 @@ int vc1_decode_sequence_header(AVCodecContext *avctx, 
VC1Context *v, GetBitConte
                    "Old interlaced mode is not supported\n");
             return -1;
         }
-        if (v->res_sprite) {
-            av_log(avctx, AV_LOG_ERROR, "WMVP is not fully supported\n");
-        }
     }
 
     // (fps-2)/4 (->30)
diff --git a/libavcodec/vc1.h b/libavcodec/vc1.h
index 96e5744..e1365ab 100644
--- a/libavcodec/vc1.h
+++ b/libavcodec/vc1.h
@@ -311,6 +311,9 @@ typedef struct VC1Context{
     //@{
     int new_sprite;
     int two_sprites;
+    AVFrame sprite_output_frame;
+    int output_width, output_height, sprite_width, sprite_height;
+    uint8_t* sr_rows[2][2];      ///< Sprite resizer line cache
     //@}
 
     int p_frame_skipped;
diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c
index ae7906d..d3a0569 100644
--- a/libavcodec/vc1dec.c
+++ b/libavcodec/vc1dec.c
@@ -3278,12 +3278,20 @@ static void vc1_decode_blocks(VC1Context *v)
     }
 }
 
+#if CONFIG_WMIMAGE_DECODER
+
+typedef struct {
+    float coefs[2][7];
+    int effect_type, effect_flag, effect_pcount1, effect_pcount2;
+    float effect_params1[14], effect_params2[10];
+} SpriteData;
+
 static inline float get_float_val(GetBitContext* gb)
 {
-    return (float)get_bits_long(gb, 30) / (1<<15) - (1<<14);
+    return (float)(get_bits_long(gb, 30) - (1<<29)) / (1<<15) ;
 }
 
-static void vc1_sprite_parse_transform(VC1Context *v, GetBitContext* gb, float 
c[7])
+static void vc1_sprite_parse_transform(GetBitContext* gb, float c[7])
 {
     c[1] = c[3] = 0.0f;
 
@@ -3303,7 +3311,6 @@ static void vc1_sprite_parse_transform(VC1Context *v, 
GetBitContext* gb, float c
         c[4] = get_float_val(gb);
         break;
     case 3:
-        av_log_ask_for_sample(v->s.avctx, NULL);
         c[0] = get_float_val(gb);
         c[1] = get_float_val(gb);
         c[2] = get_float_val(gb);
@@ -3318,76 +3325,226 @@ static void vc1_sprite_parse_transform(VC1Context *v, 
GetBitContext* gb, float c
         c[6] = 1.0f;
 }
 
-static void vc1_parse_sprites(VC1Context *v, GetBitContext* gb)
+static void vc1_parse_sprites(VC1Context *v, GetBitContext* gb, SpriteData* sd)
 {
-    int effect_type, effect_flag, effect_pcount1, effect_pcount2, i;
-    float effect_params1[14], effect_params2[10];
+    AVCodecContext *avctx = v->s.avctx;
+    int i;
 
-    float coefs[2][7];
-    vc1_sprite_parse_transform(v, gb, coefs[0]);
-    av_log(v->s.avctx, AV_LOG_DEBUG, "S1:");
+    vc1_sprite_parse_transform(gb, sd->coefs[0]);
+    if (sd->coefs[0][1] != 0.0f || sd->coefs[0][3] != 0.0f)
+        av_log_ask_for_sample(avctx, NULL);
+    av_log(avctx, AV_LOG_DEBUG, "S1:");
     for (i = 0; i < 7; i++)
-        av_log(v->s.avctx, AV_LOG_DEBUG, " %.3f", coefs[0][i]);
-    av_log(v->s.avctx, AV_LOG_DEBUG, "\n");
+        av_log(avctx, AV_LOG_DEBUG, " %.3f", sd->coefs[0][i]);
+    av_log(avctx, AV_LOG_DEBUG, "\n");
 
     if (v->two_sprites) {
-        vc1_sprite_parse_transform(v, gb, coefs[1]);
-        av_log(v->s.avctx, AV_LOG_DEBUG, "S2:");
+        vc1_sprite_parse_transform(gb, sd->coefs[1]);
+        if (sd->coefs[0][1] != 0.0f || sd->coefs[0][3] != 0.0f)
+            av_log_ask_for_sample(avctx, NULL);
+        av_log(avctx, AV_LOG_DEBUG, "S2:");
         for (i = 0; i < 7; i++)
-            av_log(v->s.avctx, AV_LOG_DEBUG, " %.3f", coefs[1][i]);
-        av_log(v->s.avctx, AV_LOG_DEBUG, "\n");
+            av_log(avctx, AV_LOG_DEBUG, " %.3f", sd->coefs[1][i]);
+        av_log(avctx, AV_LOG_DEBUG, "\n");
     }
     skip_bits(gb, 2);
-    if (effect_type = get_bits_long(gb, 30)){
-        switch (effect_pcount1 = get_bits(gb, 4)) {
+    if (sd->effect_type = get_bits_long(gb, 30)){
+        switch (sd->effect_pcount1 = get_bits(gb, 4)) {
         case 2:
-            effect_params1[0] = get_float_val(gb);
-            effect_params1[1] = get_float_val(gb);
+            sd->effect_params1[0] = get_float_val(gb);
+            sd->effect_params1[1] = get_float_val(gb);
             break;
         case 7:
-            vc1_sprite_parse_transform(v, gb, effect_params1);
+            vc1_sprite_parse_transform(gb, sd->effect_params1);
             break;
         case 14:
-            vc1_sprite_parse_transform(v, gb, effect_params1);
-            vc1_sprite_parse_transform(v, gb, &effect_params1[7]);
+            vc1_sprite_parse_transform(gb, sd->effect_params1);
+            vc1_sprite_parse_transform(gb, &sd->effect_params1[7]);
             break;
         default:
-            av_log_ask_for_sample(v->s.avctx, NULL);
+            av_log_ask_for_sample(avctx, NULL);
             return;
         }
-        if (effect_type != 13 || effect_params1[0] != coefs[0][6]) {
+        if (sd->effect_type != 13 || sd->effect_params1[0] != sd->coefs[0][6]) 
{
             // effect 13 is simple alpha blending and matches the opacity above
-            av_log(v->s.avctx, AV_LOG_DEBUG, "Effect: %d; params: ", 
effect_type);
-            for (i = 0; i < effect_pcount1; i++)
-                av_log(v->s.avctx, AV_LOG_DEBUG, " %.3f", effect_params1[i]);
-            av_log(v->s.avctx, AV_LOG_DEBUG, "\n");
+            av_log(avctx, AV_LOG_DEBUG, "Effect: %d; params: ", 
sd->effect_type);
+            for (i = 0; i < sd->effect_pcount1; i++)
+                av_log(avctx, AV_LOG_DEBUG, " %.3f", sd->effect_params1[i]);
+            av_log(avctx, AV_LOG_DEBUG, "\n");
         }
 
-        effect_pcount2 = get_bits(gb, 16);
-        if (effect_pcount2 > 10) {
-            av_log(v->s.avctx, AV_LOG_ERROR, "Too many effect parameters\n");
+        sd->effect_pcount2 = get_bits(gb, 16);
+        if (sd->effect_pcount2 > 10) {
+            av_log(avctx, AV_LOG_ERROR, "Too many effect parameters\n");
             return;
-        } else if (effect_pcount2) {
+        } else if (sd->effect_pcount2) {
             i = 0;
-            av_log(v->s.avctx, AV_LOG_DEBUG, "Effect params 2: ");
-            while (i < effect_pcount2){
-                effect_params2[i] = get_float_val(gb);
-                av_log(v->s.avctx, AV_LOG_DEBUG, " %.3f", effect_params2[i]);
+            av_log(avctx, AV_LOG_DEBUG, "Effect params 2: ");
+            while (i < sd->effect_pcount2){
+                sd->effect_params2[i] = get_float_val(gb);
+                av_log(avctx, AV_LOG_DEBUG, " %.3f", sd->effect_params2[i]);
                 i++;
             }
-            av_log(v->s.avctx, AV_LOG_DEBUG, "\n");
+            av_log(avctx, AV_LOG_DEBUG, "\n");
         }
     }
-    if (effect_flag = get_bits1(gb))
-        av_log(v->s.avctx, AV_LOG_DEBUG, "Effect flag set\n");
+    if (sd->effect_flag = get_bits1(gb))
+        av_log(avctx, AV_LOG_DEBUG, "Effect flag set\n");
 
     if (get_bits_count(gb) >= gb->size_in_bits +
-       (v->s.avctx->codec_id == CODEC_ID_WMV3 ? 64 : 0))
-        av_log(v->s.avctx, AV_LOG_ERROR, "Buffer overrun\n");
+       (avctx->codec_tag == MKTAG('W', 'M', 'V', 'P') ? 64 : 0))
+        av_log(avctx, AV_LOG_ERROR, "Buffer overrun\n");
     if (get_bits_count(gb) < gb->size_in_bits - 8)
-        av_log(v->s.avctx, AV_LOG_WARNING, "Buffer not fully read\n");
+        av_log(avctx, AV_LOG_WARNING, "Buffer not fully read\n");
+}
+
+static void vc1_draw_sprites(VC1Context *v, SpriteData* sd)
+{
+    int i, plane, row, sprite;
+    int sr_cache[2][2] = { { -1, -1 }, { -1, -1 } };
+    uint8_t* src_h[2][2];
+    int xoff[2], xadv[2], yoff[2], yadv[2], alpha; // 16.16
+    int ysub[2];
+    MpegEncContext *s = &v->s;
+
+    for (i = 0; i < 2; i++) {
+        xoff[i] = av_clip(sd->coefs[i][2]*(1<<16), 0, v->sprite_width-1<<16);
+        xadv[i] = sd->coefs[i][0]*(1<<16);
+        if (v->sprite_width != v->output_width || xadv[i] != 1<<16 || xoff[i]) 
{
+            int maxscale = ((v->sprite_width<<16) - xoff[i] - 1) / 
v->output_width;
+            xadv[i] = av_clip(xadv[i], 0, maxscale);
+        }
+
+        yoff[i] = av_clip(sd->coefs[i][5]*(1<<16), 0, v->sprite_height-1<<16);
+        yadv[i] = FFMAX(0, sd->coefs[i][4]*(1<<16));
+        if (v->sprite_height != v->output_height || yadv[i] != 1<<16 || 
yoff[i]) {
+            int maxscale = ((v->sprite_height<<16) - yoff[i] - 1) / 
v->output_height;
+            yadv[i] = av_clip(yadv[i], 0, maxscale);
+        }
+    }
+    alpha = av_clip(sd->coefs[1][6]*(1<<16), 0, (1<<16)-1);
+
+    for (plane = 0; plane < (s->flags&CODEC_FLAG_GRAY ? 1 : 3); plane++) {
+        uint8_t *oplane = v->sprite_output_frame.data[plane];
+        int      oline  = v->sprite_output_frame.linesize[plane];
+        int      width  = v->output_width>>!!plane;
+        for (row = 0; row < v->output_height>>!!plane; row++) {
+            for (sprite = 0; sprite <= v->two_sprites; sprite++) {
+                uint8_t *iplane = s->current_picture.f.data[plane];
+                int      iline  = s->current_picture.f.linesize[plane];
+                int      ycoord = yoff[sprite] + yadv[sprite]*row;
+                int      yline  = ycoord>>16;
+                ysub[sprite] = ycoord&0xFFFF;
+                if (sprite) {
+                    iplane = s->last_picture.f.data[plane];
+                    iline  = s->last_picture.f.linesize[plane];
+                }
+                if (!(xoff[sprite]&0xFFFF) && xadv[sprite] == 1<<16) {
+                        src_h[sprite][0] = iplane+(xoff[sprite]>>16)+ yline   
*iline;
+                    if (ysub[sprite])
+                        src_h[sprite][1] = 
iplane+(xoff[sprite]>>16)+(yline+1)*iline;
+                } else {
+                    if (sr_cache[sprite][0] != yline) {
+                        if (sr_cache[sprite][1] == yline) {
+                            FFSWAP(uint8_t*, v->sr_rows[sprite][0], 
v->sr_rows[sprite][1]);
+                            FFSWAP(int,        sr_cache[sprite][0],   
sr_cache[sprite][1]);
+                        } else {
+                            v->vc1dsp.sprite_h(v->sr_rows[sprite][0], 
iplane+yline*iline, xoff[sprite], xadv[sprite], width);
+                            sr_cache[sprite][0] = yline;
+                        }
+                    }
+                    if (ysub[sprite] && sr_cache[sprite][1] != yline + 1) {
+                        v->vc1dsp.sprite_h(v->sr_rows[sprite][1], 
iplane+(yline+1)*iline, xoff[sprite], xadv[sprite], width);
+                        sr_cache[sprite][1] = yline + 1;
+                    }
+                    src_h[sprite][0] = v->sr_rows[sprite][0];
+                    src_h[sprite][1] = v->sr_rows[sprite][1];
+                }
+            }
+            if (!v->two_sprites) {
+                if (ysub[0]) {
+                    v->vc1dsp.sprite_v_single(oplane+row*oline, src_h[0][0], 
src_h[0][1], ysub[0], width);
+                } else {
+                    memcpy(oplane+row*oline, src_h[0][0], width);
+                }
+            } else {
+                if (ysub[0] && ysub[1]) {
+                    v->vc1dsp.sprite_v_double_twoscale(oplane+row*oline, 
src_h[0][0], src_h[0][1], ysub[0],
+                                                       src_h[1][0], 
src_h[1][1], ysub[1], alpha, width);
+                } else if (ysub[0]) {
+                    v->vc1dsp.sprite_v_double_onescale(oplane+row*oline, 
src_h[0][0], src_h[0][1], ysub[0],
+                                                       src_h[1][0], alpha, 
width);
+                } else if (ysub[1]) {
+                    v->vc1dsp.sprite_v_double_onescale(oplane+row*oline, 
src_h[1][0], src_h[1][1], ysub[1],
+                                                       src_h[0][0], 
(1<<16)-1-alpha, width);
+                } else {
+                    v->vc1dsp.sprite_v_double_noscale(oplane+row*oline, 
src_h[0][0], src_h[1][0], alpha, width);
+                }
+            }
+        }
+        if (!plane) {
+            for (i = 0; i < 2; i++) {
+                xoff[i] >>= 1;
+                yoff[i] >>= 1;
+            }
+        }
+    }
+}
+
+
+static int vc1_decode_sprites(VC1Context *v, GetBitContext* gb)
+{
+    MpegEncContext *s = &v->s;
+    AVCodecContext *avctx = s->avctx;
+    SpriteData sd;
+
+    vc1_parse_sprites(v, gb, &sd);
+
+    if (!s->current_picture.f.data[0]) {
+        av_log(avctx, AV_LOG_ERROR, "Got no sprites\n");
+        return -1;
+    }
+
+    if (v->two_sprites && (!s->last_picture_ptr || 
!s->last_picture.f.data[0])) {
+        av_log(avctx, AV_LOG_WARNING, "Need two sprites, only got one\n");
+        v->two_sprites = 0;
+    }
+
+    if (v->sprite_output_frame.data[0])
+        avctx->release_buffer(avctx, &v->sprite_output_frame);
+
+    v->sprite_output_frame.buffer_hints = FF_BUFFER_HINTS_VALID;
+    v->sprite_output_frame.reference = 0;
+    if (avctx->get_buffer(avctx, &v->sprite_output_frame) < 0) {
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
+        return -1;
+    }
+
+    vc1_draw_sprites(v, &sd);
+
+    return 0;
+}
+
+static void vc1_sprite_flush(AVCodecContext *avctx)
+{
+    VC1Context *v = avctx->priv_data;
+    MpegEncContext *s = &v->s;
+    AVFrame *f = &s->current_picture.f;
+    int plane, i;
+
+    /* WMImage has a convergence interval of two keyframes. Since we can't
+       enforce it, clear to black the missing sprite. This is wrong but it
+       looks better than doing nothing. */
+
+    if (f->data[0])
+        for (plane = 0; plane < (s->flags&CODEC_FLAG_GRAY ? 1 : 3); plane++)
+            for (i = 0; i < v->sprite_height>>!!plane; i++)
+                memset(f->data[plane]+i*f->linesize[plane],
+                       plane ? 128 : 0, f->linesize[plane]);
 }
 
+#endif
+
 /** Initialize a VC1/WMV3 decoder
  * @todo TODO: Handle VC-1 IDUs (Transport level?)
  * @todo TODO: Decypher remaining bits in extra_data
@@ -3399,6 +3556,10 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx)
     GetBitContext gb;
     int i, cur_width, cur_height;
 
+    /* save the container output size for WMImage */
+    v->output_width  = avctx->width;
+    v->output_height = avctx->height;
+
     if (!avctx->extradata_size || !avctx->extradata) return -1;
     if (!(avctx->flags & CODEC_FLAG_GRAY))
         avctx->pix_fmt = avctx->get_format(avctx, avctx->codec->pix_fmts);
@@ -3420,7 +3581,7 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx)
 
     cur_width = avctx->coded_width = avctx->width;
     cur_height = avctx->coded_height = avctx->height;
-    if (avctx->codec_id == CODEC_ID_WMV3)
+    if (avctx->codec_id == CODEC_ID_WMV3 || avctx->codec_tag == MKTAG('W', 
'M', 'V', 'P'))
     {
         int count = 0;
 
@@ -3562,6 +3723,25 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx)
     }
 
     ff_intrax8_common_init(&v->x8,s);
+
+    if (avctx->codec_id == CODEC_ID_WMIMAGE) {
+        for (i = 0; i < 4; i++)
+            if (!(v->sr_rows[i>>1][i%2] = av_malloc(v->output_width))) return 
-1;
+
+        s->low_delay = 1;
+
+        v->sprite_width  = avctx->coded_width;
+        v->sprite_height = avctx->coded_height;
+
+        avctx->coded_width  = avctx->width  = v->output_width;
+        avctx->coded_height = avctx->height = v->output_height;
+
+        // prevent 16.16 overflows
+        if (v->sprite_width  > 1<<14 ||
+            v->sprite_height > 1<<14 ||
+            v->output_width  > 1<<14 ||
+            v->output_height > 1<<14) return -1;
+    }
     return 0;
 }
 
@@ -3614,7 +3794,7 @@ static int vc1_decode_frame(AVCodecContext *avctx,
     }
 
     //for advanced profile we may need to parse and unescape data
-    if (avctx->codec_id == CODEC_ID_VC1) {
+    if (avctx->codec_id == CODEC_ID_VC1 || avctx->codec_tag == MKTAG('W', 'V', 
'P', '2')) {
         int buf_size2 = 0;
         buf2 = av_mallocz(buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
 
@@ -3679,8 +3859,19 @@ static int vc1_decode_frame(AVCodecContext *avctx,
     if (v->res_sprite) {
         v->new_sprite = !get_bits1(&s->gb);
         v->two_sprites = get_bits1(&s->gb);
-        if (!v->new_sprite)
-            goto end;
+        /* res_sprite means a Windows Media Image stream, CODEC_ID_WMIMAGE 
means
+           we're using the WMImage decoder. These are intentionally kept 
separate
+           so you can get the raw sprites by using the wmv3 decoder for WMVP or
+           the vc1 one for WVP2 */
+        if (avctx->codec_id == CODEC_ID_WMIMAGE) {
+            if (v->new_sprite) {
+                // switch AVCodecContext parameters to those of the sprites
+                avctx->width  = avctx->coded_width  = v->sprite_width;
+                avctx->height = avctx->coded_height = v->sprite_height;
+            } else {
+                goto sprite;
+            }
+        }
     }
 
     // do parse frame header
@@ -3694,8 +3885,9 @@ static int vc1_decode_frame(AVCodecContext *avctx,
         }
     }
 
-    if (v->res_sprite && s->pict_type!=AV_PICTURE_TYPE_I) {
-        av_log(v->s.avctx, AV_LOG_WARNING, "Sprite decoder: expected 
I-frame\n");
+    if (avctx->codec_id == CODEC_ID_WMIMAGE && 
s->pict_type!=AV_PICTURE_TYPE_I) {
+        av_log(v->s.avctx, AV_LOG_ERROR, "Sprite decoder: expected I-frame\n");
+        goto err;
     }
 
     // for skipping the frame
@@ -3758,6 +3950,19 @@ static int vc1_decode_frame(AVCodecContext *avctx,
 
 assert(s->current_picture.f.pict_type == s->current_picture_ptr->f.pict_type);
 assert(s->current_picture.f.pict_type == s->pict_type);
+
+    if (avctx->codec_id == CODEC_ID_WMIMAGE) {
+sprite:
+        avctx->width  = avctx->coded_width  = v->output_width;
+        avctx->height = avctx->coded_height = v->output_height;
+        if (avctx->skip_frame >= AVDISCARD_NONREF) goto end;
+#if CONFIG_WMIMAGE_DECODER
+        if (vc1_decode_sprites(v, &s->gb)) goto err;
+#endif
+        *pict = v->sprite_output_frame;
+        *data_size = sizeof(AVFrame);
+    } else {
+
     if (s->pict_type == AV_PICTURE_TYPE_B || s->low_delay) {
         *pict= *(AVFrame*)s->current_picture_ptr;
     } else if (s->last_picture_ptr != NULL) {
@@ -3769,9 +3974,9 @@ assert(s->current_picture.f.pict_type == s->pict_type);
         ff_print_debug_info(s, pict);
     }
 
+    }
+
 end:
-    if (v->res_sprite)
-        vc1_parse_sprites(v, &s->gb);
     av_free(buf2);
     for (i = 0; i < n_slices; i++)
         av_free(slices[i].buf);
@@ -3793,7 +3998,12 @@ err:
 static av_cold int vc1_decode_end(AVCodecContext *avctx)
 {
     VC1Context *v = avctx->priv_data;
+    int i;
 
+    if (avctx->codec_id == CODEC_ID_WMIMAGE && v->sprite_output_frame.data[0])
+        avctx->release_buffer(avctx, &v->sprite_output_frame);
+    for (i = 0; i < 4; i++)
+        av_freep(&v->sr_rows[i>>1][i%2]);
     av_freep(&v->hrd_rate);
     av_freep(&v->hrd_buffer);
     MPV_common_end(&v->s);
@@ -3880,3 +4090,19 @@ AVCodec ff_vc1_vdpau_decoder = {
     .profiles = NULL_IF_CONFIG_SMALL(profiles)
 };
 #endif
+
+#if CONFIG_WMIMAGE_DECODER
+AVCodec ff_wmimage_decoder = {
+    .name           = "wmimage",
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = CODEC_ID_WMIMAGE,
+    .priv_data_size = sizeof(VC1Context),
+    .init           = vc1_decode_init,
+    .close          = vc1_decode_end,
+    .decode         = vc1_decode_frame,
+    .capabilities   = CODEC_CAP_DR1,
+    .flush          = vc1_sprite_flush,
+    .long_name = NULL_IF_CONFIG_SMALL("Windows Media Video 9 Image"),
+    .pix_fmts = ff_pixfmt_list_420
+};
+#endif
diff --git a/libavcodec/vc1dsp.c b/libavcodec/vc1dsp.c
index 7d0e406..f89b16e 100644
--- a/libavcodec/vc1dsp.c
+++ b/libavcodec/vc1dsp.c
@@ -713,6 +713,67 @@ static void avg_no_rnd_vc1_chroma_mc8_c(uint8_t 
*dst/*align 8*/, uint8_t *src/*a
     }
 }
 
+#if CONFIG_WMIMAGE_DECODER
+
+static void sprite_h_c(uint8_t *dst, const uint8_t *src, int offset, int 
advance, int count)
+{
+    while (count--) {
+        int a = src[(offset >> 16)    ];
+        int b = src[(offset >> 16) + 1];
+        *dst++ = a + ((b - a) * (offset&0xFFFF) >> 16);
+        offset += advance;
+    }
+}
+
+static av_always_inline void sprite_v_template(uint8_t *dst, const uint8_t 
*src1a, const uint8_t *src1b, int offset1,
+                                            int two_sprites, const uint8_t 
*src2a, const uint8_t *src2b, int offset2,
+                                            int alpha, int scaled, int width)
+{
+    int a1, b1, a2, b2;
+    while (width) {
+        a1 = *src1a++;
+        if (scaled) {
+            b1 = *src1b++;
+            a1 = a1 + ((b1 - a1) * offset1 >> 16);
+        }
+        if (two_sprites) {
+            a2 = *src2a++;
+            if (scaled > 1) {
+                b2 = *src2b++;
+                a2 = a2 + ((b2 - a2) * offset2 >> 16);
+            }
+            a1 = a1 + ((a2 - a1) * alpha >> 16);
+        }
+        *dst++ = a1;
+        width--;
+    }
+}
+
+static void sprite_v_single_c(uint8_t *dst, const uint8_t *src1a, const 
uint8_t *src1b, int offset, int width)
+{
+    sprite_v_template(dst, src1a, src1b, offset, 0, NULL, NULL, 0, 0, 1, 
width);
+}
+
+static void sprite_v_double_noscale_c(uint8_t *dst, const uint8_t *src1a, 
const uint8_t *src2a, int alpha, int width)
+{
+    sprite_v_template(dst, src1a, NULL, 0, 1, src2a, NULL, 0, alpha, 0, width);
+}
+
+static void sprite_v_double_onescale_c(uint8_t *dst, const uint8_t *src1a, 
const uint8_t *src1b, int offset1,
+                                                     const uint8_t *src2a, int 
alpha, int width)
+{
+    sprite_v_template(dst, src1a, src1b, offset1, 1, src2a, NULL, 0, alpha, 1, 
width);
+}
+
+static void sprite_v_double_twoscale_c(uint8_t *dst, const uint8_t *src1a, 
const uint8_t *src1b, int offset1,
+                                                     const uint8_t *src2a, 
const uint8_t *src2b, int offset2,
+                                       int alpha, int width)
+{
+    sprite_v_template(dst, src1a, src1b, offset1, 1, src2a, src2b, offset2, 
alpha, 2, width);
+}
+
+#endif
+
 av_cold void ff_vc1dsp_init(VC1DSPContext* dsp) {
     dsp->vc1_inv_trans_8x8 = vc1_inv_trans_8x8_c;
     dsp->vc1_inv_trans_4x8 = vc1_inv_trans_4x8_c;
@@ -770,6 +831,14 @@ av_cold void ff_vc1dsp_init(VC1DSPContext* dsp) {
     dsp->put_no_rnd_vc1_chroma_pixels_tab[0]= put_no_rnd_vc1_chroma_mc8_c;
     dsp->avg_no_rnd_vc1_chroma_pixels_tab[0]= avg_no_rnd_vc1_chroma_mc8_c;
 
+#if CONFIG_WMIMAGE_DECODER
+    dsp->sprite_h = sprite_h_c;
+    dsp->sprite_v_single = sprite_v_single_c;
+    dsp->sprite_v_double_noscale = sprite_v_double_noscale_c;
+    dsp->sprite_v_double_onescale = sprite_v_double_onescale_c;
+    dsp->sprite_v_double_twoscale = sprite_v_double_twoscale_c;
+#endif
+
     if (HAVE_ALTIVEC)
         ff_vc1dsp_init_altivec(dsp);
     if (HAVE_MMX)
diff --git a/libavcodec/vc1dsp.h b/libavcodec/vc1dsp.h
index e1b6ba0..6d51084 100644
--- a/libavcodec/vc1dsp.h
+++ b/libavcodec/vc1dsp.h
@@ -60,6 +60,16 @@ typedef struct VC1DSPContext {
     /* This is really one func used in VC-1 decoding */
     h264_chroma_mc_func put_no_rnd_vc1_chroma_pixels_tab[3];
     h264_chroma_mc_func avg_no_rnd_vc1_chroma_pixels_tab[3];
+
+    /* WMImage functions */
+    void (*sprite_h)(uint8_t *dst, const uint8_t *src, int offset, int 
advance, int count);
+    void (*sprite_v_single)(uint8_t *dst, const uint8_t *src1a, const uint8_t 
*src1b, int offset, int width);
+    void (*sprite_v_double_noscale)(uint8_t *dst, const uint8_t *src1a, const 
uint8_t *src2a, int alpha, int width);
+    void (*sprite_v_double_onescale)(uint8_t *dst, const uint8_t *src1a, const 
uint8_t *src1b, int offset1,
+                                                   const uint8_t *src2a, int 
alpha, int width);
+    void (*sprite_v_double_twoscale)(uint8_t *dst, const uint8_t *src1a, const 
uint8_t *src1b, int offset1,
+                                                   const uint8_t *src2a, const 
uint8_t *src2b, int offset2,
+                                     int alpha, int width);
 } VC1DSPContext;
 
 void ff_vc1dsp_init(VC1DSPContext* c);
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 7eb5ce0..24a33d7 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -21,8 +21,8 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 53
-#define LIBAVCODEC_VERSION_MINOR  7
-#define LIBAVCODEC_VERSION_MICRO  1
+#define LIBAVCODEC_VERSION_MINOR  8
+#define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index c426ae5..dc6e956 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -237,10 +237,10 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { CODEC_ID_QPEG,         MKTAG('Q', '1', '.', '0') },
     { CODEC_ID_QPEG,         MKTAG('Q', '1', '.', '1') },
     { CODEC_ID_WMV3,         MKTAG('W', 'M', 'V', '3') },
-    { CODEC_ID_WMV3,         MKTAG('W', 'M', 'V', 'P') },
+    { CODEC_ID_WMIMAGE,      MKTAG('W', 'M', 'V', 'P') },
     { CODEC_ID_VC1,          MKTAG('W', 'V', 'C', '1') },
     { CODEC_ID_VC1,          MKTAG('W', 'M', 'V', 'A') },
-    { CODEC_ID_VC1,          MKTAG('W', 'V', 'P', '2') },
+    { CODEC_ID_WMIMAGE,      MKTAG('W', 'V', 'P', '2') },
     { CODEC_ID_LOCO,         MKTAG('L', 'O', 'C', 'O') },
     { CODEC_ID_WNV1,         MKTAG('W', 'N', 'V', '1') },
     { CODEC_ID_AASC,         MKTAG('A', 'A', 'S', 'C') },
-- 
1.7.5.1

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to