libavcodec/msvideo1enc.c
e4be3485
 /*
  * Microsoft Video-1 Encoder
  * Copyright (c) 2009 Konstantin Shishkov
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
  * 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.
  *
  * FFmpeg is distributed in the hope that it will be useful,
  * 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
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 /**
1912c215
  * @file
e4be3485
  * Microsoft Video-1 encoder
  */
 
 #include "avcodec.h"
6e3aee03
 #include "internal.h"
e4be3485
 #include "bytestream.h"
 #include "libavutil/lfg.h"
 #include "elbg.h"
 #include "libavutil/imgutils.h"
 /**
  * Encoder context
  */
 typedef struct Msvideo1EncContext {
     AVCodecContext *avctx;
     AVLFG rnd;
     uint8_t *prev;
 
     int block[16*3];
     int block2[16*3];
     int codebook[8*3];
     int codebook2[8*3];
     int output[16*3];
     int output2[16*3];
     int avg[3];
     int bestpos;
     int keyint;
 } Msvideo1EncContext;
 
 enum MSV1Mode{
     MODE_SKIP = 0,
     MODE_FILL,
     MODE_2COL,
     MODE_8COL,
 };
 
 #define SKIP_PREFIX 0x8400
fb8f5d05
 #define SKIPS_MAX 0x03FF
9babf137
 #define MKRGB555(in, off) (((in)[off] << 10) | ((in)[(off) + 1] << 5) | ((in)[(off) + 2]))
e4be3485
 
 static const int remap[16] = { 0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15 };
 
73f8f7e8
 static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
                                const AVFrame *pict, int *got_packet)
e4be3485
 {
     Msvideo1EncContext * const c = avctx->priv_data;
c8123465
     const AVFrame *p = pict;
e4be3485
     uint16_t *src;
     uint8_t *prevptr;
73f8f7e8
     uint8_t *dst, *buf;
db3a0aae
     int keyframe = 0;
e4be3485
     int no_skips = 1;
73f8f7e8
     int i, j, k, x, y, ret;
e4be3485
     int skips = 0;
c8123465
     int quality = 24;
e4be3485
 
29d147c9
     if ((ret = ff_alloc_packet2(avctx, pkt, avctx->width*avctx->height*9 + AV_INPUT_BUFFER_MIN_SIZE, 0)) < 0)
73f8f7e8
         return ret;
     dst= buf= pkt->data;
 
e4be3485
     if(!c->prev)
         c->prev = av_malloc(avctx->width * 3 * (avctx->height + 3));
20e5d64a
     prevptr = c->prev + avctx->width * 3 * (FFALIGN(avctx->height, 4) - 1);
     src = (uint16_t*)(p->data[0] + p->linesize[0]*(FFALIGN(avctx->height, 4) - 1));
e4be3485
     if(c->keyint >= avctx->keyint_min)
         keyframe = 1;
 
 
     for(y = 0; y < avctx->height; y += 4){
         for(x = 0; x < avctx->width; x += 4){
             int bestmode = MODE_SKIP;
             int bestscore = INT_MAX;
             int flags = 0;
             int score;
 
             for(j = 0; j < 4; j++){
                 for(i = 0; i < 4; i++){
                     uint16_t val = src[x + i - j*p->linesize[0]/2];
                     for(k = 0; k < 3; k++){
20e5d64a
                         c->block[(i + j*4)*3 + k] =
e4be3485
                         c->block2[remap[i + j*4]*3 + k] = (val >> (10-k*5)) & 0x1F;
                     }
                 }
             }
             if(!keyframe){
                 bestscore = 0;
                 for(j = 0; j < 4; j++){
                     for(i = 0; i < 4*3; i++){
1fe45903
                         int t = prevptr[x*3 + i - j*3*avctx->width] - c->block[i + j*4*3];
e4be3485
                         bestscore += t*t;
                     }
                 }
c8123465
                 bestscore /= quality;
e4be3485
             }
             // try to find optimal value to fill whole 4x4 block
             score = 0;
fe55c319
             avpriv_init_elbg(c->block, 3, 16, c->avg, 1, 1, c->output, &c->rnd);
             avpriv_do_elbg  (c->block, 3, 16, c->avg, 1, 1, c->output, &c->rnd);
e4be3485
             if(c->avg[0] == 1) // red component = 1 will be written as skip code
                 c->avg[0] = 0;
             for(j = 0; j < 4; j++){
                 for(i = 0; i < 4; i++){
                     for(k = 0; k < 3; k++){
                         int t = c->avg[k] - c->block[(i+j*4)*3+k];
                         score += t*t;
                     }
                 }
             }
c8123465
             score /= quality;
e4be3485
             score += 2;
             if(score < bestscore){
                 bestscore = score;
                 bestmode = MODE_FILL;
             }
             // search for optimal filling of 2-color block
             score = 0;
fe55c319
             avpriv_init_elbg(c->block, 3, 16, c->codebook, 2, 1, c->output, &c->rnd);
             avpriv_do_elbg  (c->block, 3, 16, c->codebook, 2, 1, c->output, &c->rnd);
e4be3485
             // last output value should be always 1, swap codebooks if needed
             if(!c->output[15]){
                 for(i = 0; i < 3; i++)
                     FFSWAP(uint8_t, c->codebook[i], c->codebook[i+3]);
                 for(i = 0; i < 16; i++)
                     c->output[i] ^= 1;
             }
             for(j = 0; j < 4; j++){
                 for(i = 0; i < 4; i++){
                     for(k = 0; k < 3; k++){
                         int t = c->codebook[c->output[i+j*4]*3 + k] - c->block[i*3+k+j*4*3];
                         score += t*t;
                     }
                 }
             }
c8123465
             score /= quality;
e4be3485
             score += 6;
             if(score < bestscore){
                 bestscore = score;
                 bestmode = MODE_2COL;
             }
             // search for optimal filling of 2-color 2x2 subblocks
             score = 0;
             for(i = 0; i < 4; i++){
fe55c319
                 avpriv_init_elbg(c->block2 + i*4*3, 3, 4, c->codebook2 + i*2*3, 2, 1, c->output2 + i*4, &c->rnd);
                 avpriv_do_elbg  (c->block2 + i*4*3, 3, 4, c->codebook2 + i*2*3, 2, 1, c->output2 + i*4, &c->rnd);
e4be3485
             }
             // last value should be always 1, swap codebooks if needed
             if(!c->output2[15]){
                 for(i = 0; i < 3; i++)
                     FFSWAP(uint8_t, c->codebook2[i+18], c->codebook2[i+21]);
                 for(i = 12; i < 16; i++)
                     c->output2[i] ^= 1;
             }
             for(j = 0; j < 4; j++){
                 for(i = 0; i < 4; i++){
                     for(k = 0; k < 3; k++){
                         int t = c->codebook2[(c->output2[remap[i+j*4]] + (i&2) + (j&2)*2)*3+k] - c->block[i*3+k + j*4*3];
                         score += t*t;
                     }
                 }
             }
c8123465
             score /= quality;
e4be3485
             score += 18;
             if(score < bestscore){
                 bestscore = score;
                 bestmode = MODE_8COL;
             }
 
             if(bestmode == MODE_SKIP){
                 skips++;
                 no_skips = 0;
             }
             if((bestmode != MODE_SKIP && skips) || skips == SKIPS_MAX){
                 bytestream_put_le16(&dst, skips | SKIP_PREFIX);
                 skips = 0;
             }
 
             switch(bestmode){
             case MODE_FILL:
                 bytestream_put_le16(&dst, MKRGB555(c->avg,0) | 0x8000);
                 for(j = 0; j < 4; j++)
                     for(i = 0; i < 4; i++)
                         for(k = 0; k < 3; k++)
1fe45903
                             prevptr[x*3 + i*3 + k - j*3*avctx->width] = c->avg[k];
e4be3485
                 break;
             case MODE_2COL:
                 for(j = 0; j < 4; j++){
                     for(i = 0; i < 4; i++){
                         flags |= (c->output[i + j*4]^1) << (i + j*4);
                         for(k = 0; k < 3; k++)
1fe45903
                             prevptr[x*3 + i*3 + k - j*3*avctx->width] = c->codebook[c->output[i + j*4]*3 + k];
e4be3485
                     }
                 }
                 bytestream_put_le16(&dst, flags);
                 bytestream_put_le16(&dst, MKRGB555(c->codebook, 0));
                 bytestream_put_le16(&dst, MKRGB555(c->codebook, 3));
                 break;
             case MODE_8COL:
                 for(j = 0; j < 4; j++){
                     for(i = 0; i < 4; i++){
                         flags |= (c->output2[remap[i + j*4]]^1) << (i + j*4);
                         for(k = 0; k < 3; k++)
1fe45903
                             prevptr[x*3 + i*3 + k - j*3*avctx->width] = c->codebook2[(c->output2[remap[i+j*4]] + (i&2) + (j&2)*2)*3 + k];
e4be3485
                     }
                 }
                 bytestream_put_le16(&dst, flags);
                 bytestream_put_le16(&dst, MKRGB555(c->codebook2, 0) | 0x8000);
                 for(i = 3; i < 24; i += 3)
                     bytestream_put_le16(&dst, MKRGB555(c->codebook2, i));
                 break;
             }
         }
         src     -= p->linesize[0] << 1;
         prevptr -= avctx->width * 3 * 4;
     }
     if(skips)
         bytestream_put_le16(&dst, skips | SKIP_PREFIX);
     //EOF
     bytestream_put_byte(&dst, 0);
     bytestream_put_byte(&dst, 0);
 
     if(no_skips)
         keyframe = 1;
     if(keyframe)
         c->keyint = 0;
     else
         c->keyint++;
73f8f7e8
     if (keyframe) pkt->flags |= AV_PKT_FLAG_KEY;
     pkt->size = dst - buf;
     *got_packet = 1;
e4be3485
 
73f8f7e8
     return 0;
e4be3485
 }
 
 
 /**
  * init encoder
  */
 static av_cold int encode_init(AVCodecContext *avctx)
 {
     Msvideo1EncContext * const c = avctx->priv_data;
 
     c->avctx = avctx;
     if (av_image_check_size(avctx->width, avctx->height, 0, avctx) < 0) {
         return -1;
     }
e09ffa45
     if((avctx->width&3) || (avctx->height&3)){
8c1b942a
         av_log(avctx, AV_LOG_ERROR, "width and height must be multiples of 4\n");
e09ffa45
         return -1;
     }
e4be3485
 
cb8f5194
     avctx->bits_per_coded_sample = 16;
e4be3485
 
     c->keyint = avctx->keyint_min;
     av_lfg_init(&c->rnd, 1);
 
     return 0;
 }
 
 
 
 /**
  * Uninit encoder
  */
 static av_cold int encode_end(AVCodecContext *avctx)
 {
     Msvideo1EncContext * const c = avctx->priv_data;
 
     av_freep(&c->prev);
 
     return 0;
 }
 
 AVCodec ff_msvideo1_encoder = {
ba10207b
     .name           = "msvideo1",
b46f1910
     .long_name = NULL_IF_CONFIG_SMALL("Microsoft Video-1"),
ba10207b
     .type           = AVMEDIA_TYPE_VIDEO,
7a72695c
     .id             = AV_CODEC_ID_MSVIDEO1,
ba10207b
     .priv_data_size = sizeof(Msvideo1EncContext),
     .init           = encode_init,
73f8f7e8
     .encode2        = encode_frame,
ba10207b
     .close          = encode_end,
ac627b3d
     .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_RGB555, AV_PIX_FMT_NONE},
e4be3485
 };