1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Microsoft Video-1 Encoder 3cabdff1aSopenharmony_ci * Copyright (c) 2009 Konstantin Shishkov 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * Microsoft Video-1 encoder 25cabdff1aSopenharmony_ci */ 26cabdff1aSopenharmony_ci 27cabdff1aSopenharmony_ci#include "avcodec.h" 28cabdff1aSopenharmony_ci#include "codec_internal.h" 29cabdff1aSopenharmony_ci#include "encode.h" 30cabdff1aSopenharmony_ci#include "bytestream.h" 31cabdff1aSopenharmony_ci#include "libavutil/lfg.h" 32cabdff1aSopenharmony_ci#include "elbg.h" 33cabdff1aSopenharmony_ci#include "libavutil/imgutils.h" 34cabdff1aSopenharmony_ci/** 35cabdff1aSopenharmony_ci * Encoder context 36cabdff1aSopenharmony_ci */ 37cabdff1aSopenharmony_citypedef struct Msvideo1EncContext { 38cabdff1aSopenharmony_ci AVCodecContext *avctx; 39cabdff1aSopenharmony_ci struct ELBGContext *elbg; 40cabdff1aSopenharmony_ci AVLFG rnd; 41cabdff1aSopenharmony_ci uint8_t *prev; 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_ci int block[16*3]; 44cabdff1aSopenharmony_ci int block2[16*3]; 45cabdff1aSopenharmony_ci int codebook[8*3]; 46cabdff1aSopenharmony_ci int codebook2[8*3]; 47cabdff1aSopenharmony_ci int output[16*3]; 48cabdff1aSopenharmony_ci int output2[16*3]; 49cabdff1aSopenharmony_ci int avg[3]; 50cabdff1aSopenharmony_ci int bestpos; 51cabdff1aSopenharmony_ci int keyint; 52cabdff1aSopenharmony_ci} Msvideo1EncContext; 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_cienum MSV1Mode{ 55cabdff1aSopenharmony_ci MODE_SKIP = 0, 56cabdff1aSopenharmony_ci MODE_FILL, 57cabdff1aSopenharmony_ci MODE_2COL, 58cabdff1aSopenharmony_ci MODE_8COL, 59cabdff1aSopenharmony_ci}; 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ci#define SKIP_PREFIX 0x8400 62cabdff1aSopenharmony_ci#define SKIPS_MAX 0x03FF 63cabdff1aSopenharmony_ci#define MKRGB555(in, off) (((in)[off] << 10) | ((in)[(off) + 1] << 5) | ((in)[(off) + 2])) 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_cistatic const int remap[16] = { 0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15 }; 66cabdff1aSopenharmony_ci 67cabdff1aSopenharmony_cistatic int encode_frame(AVCodecContext *avctx, AVPacket *pkt, 68cabdff1aSopenharmony_ci const AVFrame *pict, int *got_packet) 69cabdff1aSopenharmony_ci{ 70cabdff1aSopenharmony_ci Msvideo1EncContext * const c = avctx->priv_data; 71cabdff1aSopenharmony_ci const AVFrame *p = pict; 72cabdff1aSopenharmony_ci uint16_t *src; 73cabdff1aSopenharmony_ci uint8_t *prevptr; 74cabdff1aSopenharmony_ci uint8_t *dst, *buf; 75cabdff1aSopenharmony_ci int keyframe = 0; 76cabdff1aSopenharmony_ci int no_skips = 1; 77cabdff1aSopenharmony_ci int i, j, k, x, y, ret; 78cabdff1aSopenharmony_ci int skips = 0; 79cabdff1aSopenharmony_ci int quality = 24; 80cabdff1aSopenharmony_ci 81cabdff1aSopenharmony_ci if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) 82cabdff1aSopenharmony_ci return ret; 83cabdff1aSopenharmony_ci dst= buf= pkt->data; 84cabdff1aSopenharmony_ci 85cabdff1aSopenharmony_ci if(!c->prev) 86cabdff1aSopenharmony_ci c->prev = av_malloc(avctx->width * 3 * (avctx->height + 3)); 87cabdff1aSopenharmony_ci prevptr = c->prev + avctx->width * 3 * (FFALIGN(avctx->height, 4) - 1); 88cabdff1aSopenharmony_ci src = (uint16_t*)(p->data[0] + p->linesize[0]*(FFALIGN(avctx->height, 4) - 1)); 89cabdff1aSopenharmony_ci if(c->keyint >= avctx->keyint_min) 90cabdff1aSopenharmony_ci keyframe = 1; 91cabdff1aSopenharmony_ci 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci for(y = 0; y < avctx->height; y += 4){ 94cabdff1aSopenharmony_ci for(x = 0; x < avctx->width; x += 4){ 95cabdff1aSopenharmony_ci int bestmode = MODE_SKIP; 96cabdff1aSopenharmony_ci int bestscore = INT_MAX; 97cabdff1aSopenharmony_ci int flags = 0; 98cabdff1aSopenharmony_ci int score; 99cabdff1aSopenharmony_ci 100cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 101cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 102cabdff1aSopenharmony_ci uint16_t val = src[x + i - j*p->linesize[0]/2]; 103cabdff1aSopenharmony_ci for(k = 0; k < 3; k++){ 104cabdff1aSopenharmony_ci c->block[(i + j*4)*3 + k] = 105cabdff1aSopenharmony_ci c->block2[remap[i + j*4]*3 + k] = (val >> (10-k*5)) & 0x1F; 106cabdff1aSopenharmony_ci } 107cabdff1aSopenharmony_ci } 108cabdff1aSopenharmony_ci } 109cabdff1aSopenharmony_ci if(!keyframe){ 110cabdff1aSopenharmony_ci bestscore = 0; 111cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 112cabdff1aSopenharmony_ci for(i = 0; i < 4*3; i++){ 113cabdff1aSopenharmony_ci int t = prevptr[x*3 + i - j*3*avctx->width] - c->block[i + j*4*3]; 114cabdff1aSopenharmony_ci bestscore += t*t; 115cabdff1aSopenharmony_ci } 116cabdff1aSopenharmony_ci } 117cabdff1aSopenharmony_ci bestscore /= quality; 118cabdff1aSopenharmony_ci } 119cabdff1aSopenharmony_ci // try to find optimal value to fill whole 4x4 block 120cabdff1aSopenharmony_ci score = 0; 121cabdff1aSopenharmony_ci ret = avpriv_elbg_do(&c->elbg, c->block, 3, 16, c->avg, 122cabdff1aSopenharmony_ci 1, 1, c->output, &c->rnd, 0); 123cabdff1aSopenharmony_ci if (ret < 0) 124cabdff1aSopenharmony_ci return ret; 125cabdff1aSopenharmony_ci if(c->avg[0] == 1) // red component = 1 will be written as skip code 126cabdff1aSopenharmony_ci c->avg[0] = 0; 127cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 128cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 129cabdff1aSopenharmony_ci for(k = 0; k < 3; k++){ 130cabdff1aSopenharmony_ci int t = c->avg[k] - c->block[(i+j*4)*3+k]; 131cabdff1aSopenharmony_ci score += t*t; 132cabdff1aSopenharmony_ci } 133cabdff1aSopenharmony_ci } 134cabdff1aSopenharmony_ci } 135cabdff1aSopenharmony_ci score /= quality; 136cabdff1aSopenharmony_ci score += 2; 137cabdff1aSopenharmony_ci if(score < bestscore){ 138cabdff1aSopenharmony_ci bestscore = score; 139cabdff1aSopenharmony_ci bestmode = MODE_FILL; 140cabdff1aSopenharmony_ci } 141cabdff1aSopenharmony_ci // search for optimal filling of 2-color block 142cabdff1aSopenharmony_ci score = 0; 143cabdff1aSopenharmony_ci ret = avpriv_elbg_do(&c->elbg, c->block, 3, 16, c->codebook, 144cabdff1aSopenharmony_ci 2, 1, c->output, &c->rnd, 0); 145cabdff1aSopenharmony_ci if (ret < 0) 146cabdff1aSopenharmony_ci return ret; 147cabdff1aSopenharmony_ci // last output value should be always 1, swap codebooks if needed 148cabdff1aSopenharmony_ci if(!c->output[15]){ 149cabdff1aSopenharmony_ci for(i = 0; i < 3; i++) 150cabdff1aSopenharmony_ci FFSWAP(uint8_t, c->codebook[i], c->codebook[i+3]); 151cabdff1aSopenharmony_ci for(i = 0; i < 16; i++) 152cabdff1aSopenharmony_ci c->output[i] ^= 1; 153cabdff1aSopenharmony_ci } 154cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 155cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 156cabdff1aSopenharmony_ci for(k = 0; k < 3; k++){ 157cabdff1aSopenharmony_ci int t = c->codebook[c->output[i+j*4]*3 + k] - c->block[i*3+k+j*4*3]; 158cabdff1aSopenharmony_ci score += t*t; 159cabdff1aSopenharmony_ci } 160cabdff1aSopenharmony_ci } 161cabdff1aSopenharmony_ci } 162cabdff1aSopenharmony_ci score /= quality; 163cabdff1aSopenharmony_ci score += 6; 164cabdff1aSopenharmony_ci if(score < bestscore){ 165cabdff1aSopenharmony_ci bestscore = score; 166cabdff1aSopenharmony_ci bestmode = MODE_2COL; 167cabdff1aSopenharmony_ci } 168cabdff1aSopenharmony_ci // search for optimal filling of 2-color 2x2 subblocks 169cabdff1aSopenharmony_ci score = 0; 170cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 171cabdff1aSopenharmony_ci ret = avpriv_elbg_do(&c->elbg, c->block2 + i * 4 * 3, 3, 4, 172cabdff1aSopenharmony_ci c->codebook2 + i * 2 * 3, 2, 1, 173cabdff1aSopenharmony_ci c->output2 + i * 4, &c->rnd, 0); 174cabdff1aSopenharmony_ci if (ret < 0) 175cabdff1aSopenharmony_ci return ret; 176cabdff1aSopenharmony_ci } 177cabdff1aSopenharmony_ci // last value should be always 1, swap codebooks if needed 178cabdff1aSopenharmony_ci if(!c->output2[15]){ 179cabdff1aSopenharmony_ci for(i = 0; i < 3; i++) 180cabdff1aSopenharmony_ci FFSWAP(uint8_t, c->codebook2[i+18], c->codebook2[i+21]); 181cabdff1aSopenharmony_ci for(i = 12; i < 16; i++) 182cabdff1aSopenharmony_ci c->output2[i] ^= 1; 183cabdff1aSopenharmony_ci } 184cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 185cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 186cabdff1aSopenharmony_ci for(k = 0; k < 3; k++){ 187cabdff1aSopenharmony_ci 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]; 188cabdff1aSopenharmony_ci score += t*t; 189cabdff1aSopenharmony_ci } 190cabdff1aSopenharmony_ci } 191cabdff1aSopenharmony_ci } 192cabdff1aSopenharmony_ci score /= quality; 193cabdff1aSopenharmony_ci score += 18; 194cabdff1aSopenharmony_ci if(score < bestscore){ 195cabdff1aSopenharmony_ci bestscore = score; 196cabdff1aSopenharmony_ci bestmode = MODE_8COL; 197cabdff1aSopenharmony_ci } 198cabdff1aSopenharmony_ci 199cabdff1aSopenharmony_ci if(bestmode == MODE_SKIP){ 200cabdff1aSopenharmony_ci skips++; 201cabdff1aSopenharmony_ci no_skips = 0; 202cabdff1aSopenharmony_ci } 203cabdff1aSopenharmony_ci if((bestmode != MODE_SKIP && skips) || skips == SKIPS_MAX){ 204cabdff1aSopenharmony_ci bytestream_put_le16(&dst, skips | SKIP_PREFIX); 205cabdff1aSopenharmony_ci skips = 0; 206cabdff1aSopenharmony_ci } 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_ci switch(bestmode){ 209cabdff1aSopenharmony_ci case MODE_FILL: 210cabdff1aSopenharmony_ci bytestream_put_le16(&dst, MKRGB555(c->avg,0) | 0x8000); 211cabdff1aSopenharmony_ci for(j = 0; j < 4; j++) 212cabdff1aSopenharmony_ci for(i = 0; i < 4; i++) 213cabdff1aSopenharmony_ci for(k = 0; k < 3; k++) 214cabdff1aSopenharmony_ci prevptr[x*3 + i*3 + k - j*3*avctx->width] = c->avg[k]; 215cabdff1aSopenharmony_ci break; 216cabdff1aSopenharmony_ci case MODE_2COL: 217cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 218cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 219cabdff1aSopenharmony_ci flags |= (c->output[i + j*4]^1) << (i + j*4); 220cabdff1aSopenharmony_ci for(k = 0; k < 3; k++) 221cabdff1aSopenharmony_ci prevptr[x*3 + i*3 + k - j*3*avctx->width] = c->codebook[c->output[i + j*4]*3 + k]; 222cabdff1aSopenharmony_ci } 223cabdff1aSopenharmony_ci } 224cabdff1aSopenharmony_ci bytestream_put_le16(&dst, flags); 225cabdff1aSopenharmony_ci bytestream_put_le16(&dst, MKRGB555(c->codebook, 0)); 226cabdff1aSopenharmony_ci bytestream_put_le16(&dst, MKRGB555(c->codebook, 3)); 227cabdff1aSopenharmony_ci break; 228cabdff1aSopenharmony_ci case MODE_8COL: 229cabdff1aSopenharmony_ci for(j = 0; j < 4; j++){ 230cabdff1aSopenharmony_ci for(i = 0; i < 4; i++){ 231cabdff1aSopenharmony_ci flags |= (c->output2[remap[i + j*4]]^1) << (i + j*4); 232cabdff1aSopenharmony_ci for(k = 0; k < 3; k++) 233cabdff1aSopenharmony_ci 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]; 234cabdff1aSopenharmony_ci } 235cabdff1aSopenharmony_ci } 236cabdff1aSopenharmony_ci bytestream_put_le16(&dst, flags); 237cabdff1aSopenharmony_ci bytestream_put_le16(&dst, MKRGB555(c->codebook2, 0) | 0x8000); 238cabdff1aSopenharmony_ci for(i = 3; i < 24; i += 3) 239cabdff1aSopenharmony_ci bytestream_put_le16(&dst, MKRGB555(c->codebook2, i)); 240cabdff1aSopenharmony_ci break; 241cabdff1aSopenharmony_ci } 242cabdff1aSopenharmony_ci } 243cabdff1aSopenharmony_ci src -= p->linesize[0] << 1; 244cabdff1aSopenharmony_ci prevptr -= avctx->width * 3 * 4; 245cabdff1aSopenharmony_ci } 246cabdff1aSopenharmony_ci if(skips) 247cabdff1aSopenharmony_ci bytestream_put_le16(&dst, skips | SKIP_PREFIX); 248cabdff1aSopenharmony_ci //EOF 249cabdff1aSopenharmony_ci bytestream_put_byte(&dst, 0); 250cabdff1aSopenharmony_ci bytestream_put_byte(&dst, 0); 251cabdff1aSopenharmony_ci 252cabdff1aSopenharmony_ci if(no_skips) 253cabdff1aSopenharmony_ci keyframe = 1; 254cabdff1aSopenharmony_ci if(keyframe) 255cabdff1aSopenharmony_ci c->keyint = 0; 256cabdff1aSopenharmony_ci else 257cabdff1aSopenharmony_ci c->keyint++; 258cabdff1aSopenharmony_ci if (keyframe) pkt->flags |= AV_PKT_FLAG_KEY; 259cabdff1aSopenharmony_ci pkt->size = dst - buf; 260cabdff1aSopenharmony_ci *got_packet = 1; 261cabdff1aSopenharmony_ci 262cabdff1aSopenharmony_ci return 0; 263cabdff1aSopenharmony_ci} 264cabdff1aSopenharmony_ci 265cabdff1aSopenharmony_ci 266cabdff1aSopenharmony_ci/** 267cabdff1aSopenharmony_ci * init encoder 268cabdff1aSopenharmony_ci */ 269cabdff1aSopenharmony_cistatic av_cold int encode_init(AVCodecContext *avctx) 270cabdff1aSopenharmony_ci{ 271cabdff1aSopenharmony_ci Msvideo1EncContext * const c = avctx->priv_data; 272cabdff1aSopenharmony_ci 273cabdff1aSopenharmony_ci c->avctx = avctx; 274cabdff1aSopenharmony_ci if (av_image_check_size(avctx->width, avctx->height, 0, avctx) < 0) { 275cabdff1aSopenharmony_ci return -1; 276cabdff1aSopenharmony_ci } 277cabdff1aSopenharmony_ci if((avctx->width&3) || (avctx->height&3)){ 278cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "width and height must be multiples of 4\n"); 279cabdff1aSopenharmony_ci return -1; 280cabdff1aSopenharmony_ci } 281cabdff1aSopenharmony_ci 282cabdff1aSopenharmony_ci avctx->bits_per_coded_sample = 16; 283cabdff1aSopenharmony_ci 284cabdff1aSopenharmony_ci c->keyint = avctx->keyint_min; 285cabdff1aSopenharmony_ci av_lfg_init(&c->rnd, 1); 286cabdff1aSopenharmony_ci 287cabdff1aSopenharmony_ci return 0; 288cabdff1aSopenharmony_ci} 289cabdff1aSopenharmony_ci 290cabdff1aSopenharmony_ci 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci/** 293cabdff1aSopenharmony_ci * Uninit encoder 294cabdff1aSopenharmony_ci */ 295cabdff1aSopenharmony_cistatic av_cold int encode_end(AVCodecContext *avctx) 296cabdff1aSopenharmony_ci{ 297cabdff1aSopenharmony_ci Msvideo1EncContext * const c = avctx->priv_data; 298cabdff1aSopenharmony_ci 299cabdff1aSopenharmony_ci av_freep(&c->prev); 300cabdff1aSopenharmony_ci avpriv_elbg_free(&c->elbg); 301cabdff1aSopenharmony_ci 302cabdff1aSopenharmony_ci return 0; 303cabdff1aSopenharmony_ci} 304cabdff1aSopenharmony_ci 305cabdff1aSopenharmony_ciconst FFCodec ff_msvideo1_encoder = { 306cabdff1aSopenharmony_ci .p.name = "msvideo1", 307cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("Microsoft Video-1"), 308cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 309cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_MSVIDEO1, 310cabdff1aSopenharmony_ci .priv_data_size = sizeof(Msvideo1EncContext), 311cabdff1aSopenharmony_ci .init = encode_init, 312cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(encode_frame), 313cabdff1aSopenharmony_ci .close = encode_end, 314cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_RGB555, AV_PIX_FMT_NONE}, 315cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 316cabdff1aSopenharmony_ci}; 317