mss1.c 6.02 KB
Newer Older
Kostya Shishkov's avatar
Kostya Shishkov committed
1 2 3 4
/*
 * Microsoft Screen 1 (aka Windows Media Video V7 Screen) decoder
 * Copyright (c) 2012 Konstantin Shishkov
 *
5
 * This file is part of FFmpeg.
Kostya Shishkov's avatar
Kostya Shishkov committed
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
Kostya Shishkov's avatar
Kostya Shishkov committed
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Kostya Shishkov's avatar
Kostya Shishkov committed
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
Kostya Shishkov's avatar
Kostya Shishkov committed
19 20 21 22 23 24 25 26 27
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * Microsoft Screen 1 (aka Windows Media Video V7 Screen) decoder
 */

#include "avcodec.h"
28
#include "internal.h"
29
#include "mss12.h"
Kostya Shishkov's avatar
Kostya Shishkov committed
30 31

typedef struct MSS1Context {
32
    MSS12Context   ctx;
33
    AVFrame       *pic;
34
    SliceContext   sc;
Kostya Shishkov's avatar
Kostya Shishkov committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
} MSS1Context;

static void arith_normalise(ArithCoder *c)
{
    for (;;) {
        if (c->high >= 0x8000) {
            if (c->low < 0x8000) {
                if (c->low >= 0x4000 && c->high < 0xC000) {
                    c->value -= 0x4000;
                    c->low   -= 0x4000;
                    c->high  -= 0x4000;
                } else {
                    return;
                }
            } else {
                c->value -= 0x8000;
                c->low   -= 0x8000;
                c->high  -= 0x8000;
            }
        }
        c->value <<= 1;
        c->low   <<= 1;
        c->high  <<= 1;
        c->high   |= 1;
Alberto Delmás's avatar
Alberto Delmás committed
59
        c->value  |= get_bits1(c->gbc.gb);
Kostya Shishkov's avatar
Kostya Shishkov committed
60 61 62
    }
}

63
ARITH_GET_BIT(arith)
Kostya Shishkov's avatar
Kostya Shishkov committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

static int arith_get_bits(ArithCoder *c, int bits)
{
    int range = c->high - c->low + 1;
    int val   = (((c->value - c->low + 1) << bits) - 1) / range;
    int prob  = range * val;

    c->high   = ((prob + range) >> bits) + c->low - 1;
    c->low   += prob >> bits;

    arith_normalise(c);

    return val;
}

static int arith_get_number(ArithCoder *c, int mod_val)
{
    int range = c->high - c->low + 1;
    int val   = ((c->value - c->low + 1) * mod_val - 1) / range;
    int prob  = range * val;

    c->high   = (prob + range) / mod_val + c->low - 1;
    c->low   += prob / mod_val;

    arith_normalise(c);

    return val;
}

93
static int arith_get_prob(ArithCoder *c, int16_t *probs)
Kostya Shishkov's avatar
Kostya Shishkov committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107
{
    int range = c->high - c->low + 1;
    int val   = ((c->value - c->low + 1) * probs[0] - 1) / range;
    int sym   = 1;

    while (probs[sym] > val)
        sym++;

    c->high = range * probs[sym - 1] / probs[0] + c->low - 1;
    c->low += range * probs[sym]     / probs[0];

    return sym;
}

108
ARITH_GET_MODEL_SYM(arith)
Kostya Shishkov's avatar
Kostya Shishkov committed
109

110
static void arith_init(ArithCoder *c, GetBitContext *gb)
Kostya Shishkov's avatar
Kostya Shishkov committed
111
{
Alberto Delmás's avatar
Alberto Delmás committed
112 113 114 115
    c->low           = 0;
    c->high          = 0xFFFF;
    c->value         = get_bits(gb, 16);
    c->gbc.gb        = gb;
116 117
    c->get_model_sym = arith_get_model_sym;
    c->get_number    = arith_get_number;
Kostya Shishkov's avatar
Kostya Shishkov committed
118 119
}

Alberto Delmás's avatar
Alberto Delmás committed
120
static int decode_pal(MSS12Context *ctx, ArithCoder *acoder)
Kostya Shishkov's avatar
Kostya Shishkov committed
121 122
{
    int i, ncol, r, g, b;
Alberto Delmás's avatar
Alberto Delmás committed
123
    uint32_t *pal = ctx->pal + 256 - ctx->free_colours;
Kostya Shishkov's avatar
Kostya Shishkov committed
124

Alberto Delmás's avatar
Alberto Delmás committed
125
    if (!ctx->free_colours)
Kostya Shishkov's avatar
Kostya Shishkov committed
126 127
        return 0;

Alberto Delmás's avatar
Alberto Delmás committed
128
    ncol = arith_get_number(acoder, ctx->free_colours + 1);
Kostya Shishkov's avatar
Kostya Shishkov committed
129 130 131 132
    for (i = 0; i < ncol; i++) {
        r = arith_get_bits(acoder, 8);
        g = arith_get_bits(acoder, 8);
        b = arith_get_bits(acoder, 8);
133
        *pal++ = (0xFFU << 24) | (r << 16) | (g << 8) | b;
Kostya Shishkov's avatar
Kostya Shishkov committed
134 135
    }

136
    return !!ncol;
Kostya Shishkov's avatar
Kostya Shishkov committed
137 138
}

139
static int mss1_decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
Kostya Shishkov's avatar
Kostya Shishkov committed
140 141
                             AVPacket *avpkt)
{
Alberto Delmás's avatar
Alberto Delmás committed
142 143
    MSS1Context *ctx = avctx->priv_data;
    MSS12Context *c = &ctx->ctx;
Kostya Shishkov's avatar
Kostya Shishkov committed
144 145 146 147 148
    GetBitContext gb;
    ArithCoder acoder;
    int pal_changed = 0;
    int ret;

149 150 151
    if ((ret = init_get_bits8(&gb, avpkt->data, avpkt->size)) < 0)
        return ret;

Kostya Shishkov's avatar
Kostya Shishkov committed
152 153
    arith_init(&acoder, &gb);

154
    if ((ret = ff_reget_buffer(avctx, ctx->pic)) < 0)
Kostya Shishkov's avatar
Kostya Shishkov committed
155 156
        return ret;

157 158
    c->pal_pic    =  ctx->pic->data[0] + ctx->pic->linesize[0] * (avctx->height - 1);
    c->pal_stride = -ctx->pic->linesize[0];
Alberto Delmás's avatar
Alberto Delmás committed
159 160
    c->keyframe   = !arith_get_bit(&acoder);
    if (c->keyframe) {
161 162
        c->corrupted = 0;
        ff_mss12_slicecontext_reset(&ctx->sc);
Alberto Delmás's avatar
Alberto Delmás committed
163
        pal_changed        = decode_pal(c, &acoder);
164 165
        ctx->pic->key_frame = 1;
        ctx->pic->pict_type = AV_PICTURE_TYPE_I;
Kostya Shishkov's avatar
Kostya Shishkov committed
166
    } else {
Alberto Delmás's avatar
Alberto Delmás committed
167
        if (c->corrupted)
Kostya Shishkov's avatar
Kostya Shishkov committed
168
            return AVERROR_INVALIDDATA;
169 170
        ctx->pic->key_frame = 0;
        ctx->pic->pict_type = AV_PICTURE_TYPE_P;
Kostya Shishkov's avatar
Kostya Shishkov committed
171
    }
172
    c->corrupted = ff_mss12_decode_rect(&ctx->sc, &acoder, 0, 0,
Alberto Delmás's avatar
Alberto Delmás committed
173 174
                                        avctx->width, avctx->height);
    if (c->corrupted)
Kostya Shishkov's avatar
Kostya Shishkov committed
175
        return AVERROR_INVALIDDATA;
176 177
    memcpy(ctx->pic->data[1], c->pal, AVPALETTE_SIZE);
    ctx->pic->palette_has_changed = pal_changed;
Kostya Shishkov's avatar
Kostya Shishkov committed
178

179
    if ((ret = av_frame_ref(data, ctx->pic)) < 0)
180 181
        return ret;

182
    *got_frame      = 1;
Kostya Shishkov's avatar
Kostya Shishkov committed
183 184

    /* always report that the buffer was completely consumed */
185
    return avpkt->size;
Kostya Shishkov's avatar
Kostya Shishkov committed
186 187 188 189 190
}

static av_cold int mss1_decode_init(AVCodecContext *avctx)
{
    MSS1Context * const c = avctx->priv_data;
191
    int ret;
Kostya Shishkov's avatar
Kostya Shishkov committed
192

193
    c->ctx.avctx       = avctx;
194

195 196 197
    c->pic = av_frame_alloc();
    if (!c->pic)
        return AVERROR(ENOMEM);
Kostya Shishkov's avatar
Kostya Shishkov committed
198

199
    ret = ff_mss12_decode_init(&c->ctx, 0, &c->sc, NULL);
200 201
    if (ret < 0)
        av_frame_free(&c->pic);
202

203
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
204 205

    return ret;
Kostya Shishkov's avatar
Kostya Shishkov committed
206 207 208 209
}

static av_cold int mss1_decode_end(AVCodecContext *avctx)
{
Alberto Delmás's avatar
Alberto Delmás committed
210
    MSS1Context * const ctx = avctx->priv_data;
Kostya Shishkov's avatar
Kostya Shishkov committed
211

212
    av_frame_free(&ctx->pic);
Alberto Delmás's avatar
Alberto Delmás committed
213
    ff_mss12_decode_end(&ctx->ctx);
Kostya Shishkov's avatar
Kostya Shishkov committed
214 215 216 217 218 219

    return 0;
}

AVCodec ff_mss1_decoder = {
    .name           = "mss1",
220
    .long_name      = NULL_IF_CONFIG_SMALL("MS Screen 1"),
Kostya Shishkov's avatar
Kostya Shishkov committed
221
    .type           = AVMEDIA_TYPE_VIDEO,
222
    .id             = AV_CODEC_ID_MSS1,
Kostya Shishkov's avatar
Kostya Shishkov committed
223 224 225 226 227 228
    .priv_data_size = sizeof(MSS1Context),
    .init           = mss1_decode_init,
    .close          = mss1_decode_end,
    .decode         = mss1_decode_frame,
    .capabilities   = CODEC_CAP_DR1,
};