Given that the AVCodec.next pointer has now been removed, most of the AVCodecs are not modified at all any more and can therefore be made const (as this patch does); the only exceptions are the very few codecs for external libraries that have a init_static_data callback. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com> Signed-off-by: James Almer <jamrial@gmail.com>
		
			
				
	
	
		
			642 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			642 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * FM Screen Capture Codec decoder
 | |
|  *
 | |
|  * Copyright (c) 2017 Paul B Mahol
 | |
|  *
 | |
|  * 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
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "avcodec.h"
 | |
| #include "bytestream.h"
 | |
| #include "internal.h"
 | |
| 
 | |
| #define BLOCK_HEIGHT 112u
 | |
| #define BLOCK_WIDTH  84u
 | |
| 
 | |
| typedef struct InterBlock {
 | |
|     int      w, h;
 | |
|     int      size;
 | |
|     int      xor;
 | |
| } InterBlock;
 | |
| 
 | |
| typedef struct FMVCContext {
 | |
|     GetByteContext  gb;
 | |
|     PutByteContext  pb;
 | |
|     uint8_t        *buffer;
 | |
|     size_t          buffer_size;
 | |
|     uint8_t        *pbuffer;
 | |
|     size_t          pbuffer_size;
 | |
|     ptrdiff_t       stride;
 | |
|     int             bpp;
 | |
|     int             yb, xb;
 | |
|     InterBlock     *blocks;
 | |
|     unsigned        nb_blocks;
 | |
| } FMVCContext;
 | |
| 
 | |
| static int decode_type2(GetByteContext *gb, PutByteContext *pb)
 | |
| {
 | |
|     unsigned repeat = 0, first = 1, opcode = 0;
 | |
|     int i, len, pos;
 | |
| 
 | |
|     while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|         GetByteContext gbc;
 | |
| 
 | |
|         while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|             if (first) {
 | |
|                 first = 0;
 | |
|                 if (bytestream2_peek_byte(gb) > 17) {
 | |
|                     len = bytestream2_get_byte(gb) - 17;
 | |
|                     if (len < 4) {
 | |
|                         do {
 | |
|                             bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                             --len;
 | |
|                         } while (len);
 | |
|                         opcode = bytestream2_peek_byte(gb);
 | |
|                         continue;
 | |
|                     } else {
 | |
|                         do {
 | |
|                             bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                             --len;
 | |
|                         } while (len);
 | |
|                         opcode = bytestream2_peek_byte(gb);
 | |
|                         if (opcode < 0x10) {
 | |
|                             bytestream2_skip(gb, 1);
 | |
|                             pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049;
 | |
| 
 | |
|                             bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|                             bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
| 
 | |
|                             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                             len = opcode & 3;
 | |
|                             if (!len) {
 | |
|                                 repeat = 1;
 | |
|                             } else {
 | |
|                                 do {
 | |
|                                     bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                                     --len;
 | |
|                                 } while (len);
 | |
|                                 opcode = bytestream2_peek_byte(gb);
 | |
|                             }
 | |
|                             continue;
 | |
|                         }
 | |
|                     }
 | |
|                     repeat = 0;
 | |
|                 }
 | |
|                 repeat = 1;
 | |
|             }
 | |
|             if (repeat) {
 | |
|                 repeat = 0;
 | |
|                 opcode = bytestream2_peek_byte(gb);
 | |
|                 if (opcode < 0x10) {
 | |
|                     bytestream2_skip(gb, 1);
 | |
|                     if (!opcode) {
 | |
|                         if (!bytestream2_peek_byte(gb)) {
 | |
|                             do {
 | |
|                                 bytestream2_skip(gb, 1);
 | |
|                                 opcode += 255;
 | |
|                             } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
 | |
|                         }
 | |
|                         opcode += bytestream2_get_byte(gb) + 15;
 | |
|                     }
 | |
|                     bytestream2_put_le32(pb, bytestream2_get_le32(gb));
 | |
|                     for (i = opcode - 1; i > 0; --i)
 | |
|                         bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                     opcode = bytestream2_peek_byte(gb);
 | |
|                     if (opcode < 0x10) {
 | |
|                         bytestream2_skip(gb, 1);
 | |
|                         pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049;
 | |
| 
 | |
|                         bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|                         bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
| 
 | |
|                         bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                         bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                         bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                         len = opcode & 3;
 | |
|                         if (!len) {
 | |
|                             repeat = 1;
 | |
|                         } else {
 | |
|                             do {
 | |
|                                 bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                                 --len;
 | |
|                             } while (len);
 | |
|                             opcode = bytestream2_peek_byte(gb);
 | |
|                         }
 | |
|                         continue;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (opcode >= 0x40) {
 | |
|                 bytestream2_skip(gb, 1);
 | |
|                 pos = - ((opcode >> 2) & 7) - 1 - 8 * bytestream2_get_byte(gb);
 | |
|                 len =    (opcode >> 5)      - 1;
 | |
| 
 | |
|                 bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|                 bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
| 
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 do {
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                     --len;
 | |
|                 } while (len);
 | |
| 
 | |
|                 len = opcode & 3;
 | |
| 
 | |
|                 if (!len) {
 | |
|                     repeat = 1;
 | |
|                 } else {
 | |
|                     do {
 | |
|                         bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                         --len;
 | |
|                     } while (len);
 | |
|                     opcode = bytestream2_peek_byte(gb);
 | |
|                 }
 | |
|                 continue;
 | |
|             } else if (opcode < 0x20) {
 | |
|                 break;
 | |
|             }
 | |
|             len = opcode & 0x1F;
 | |
|             bytestream2_skip(gb, 1);
 | |
|             if (!len) {
 | |
|                 if (!bytestream2_peek_byte(gb)) {
 | |
|                     do {
 | |
|                         bytestream2_skip(gb, 1);
 | |
|                         len += 255;
 | |
|                     } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
 | |
|                 }
 | |
|                 len += bytestream2_get_byte(gb) + 31;
 | |
|             }
 | |
|             i = bytestream2_get_le16(gb);
 | |
|             pos = - (i >> 2) - 1;
 | |
| 
 | |
|             bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|             bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
| 
 | |
|             if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 do {
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                     --len;
 | |
|                 } while (len);
 | |
|             } else {
 | |
|                 bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
 | |
|                 for (len = len - 2; len; --len)
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             }
 | |
|             len = i & 3;
 | |
|             if (!len) {
 | |
|                 repeat = 1;
 | |
|             } else {
 | |
|                 do {
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                     --len;
 | |
|                 } while (len);
 | |
|                 opcode = bytestream2_peek_byte(gb);
 | |
|             }
 | |
|         }
 | |
|         bytestream2_skip(gb, 1);
 | |
|         if (opcode < 0x10) {
 | |
|             pos = -(opcode >> 2) - 1 - 4 * bytestream2_get_byte(gb);
 | |
| 
 | |
|             bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|             bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
| 
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             len = opcode & 3;
 | |
|             if (!len) {
 | |
|                 repeat = 1;
 | |
|             } else {
 | |
|                 do {
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                     --len;
 | |
|                 } while (len);
 | |
|                 opcode = bytestream2_peek_byte(gb);
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
|         len = opcode & 7;
 | |
|         if (!len) {
 | |
|             if (!bytestream2_peek_byte(gb)) {
 | |
|                 do {
 | |
|                     bytestream2_skip(gb, 1);
 | |
|                     len += 255;
 | |
|                 } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
 | |
|             }
 | |
|             len += bytestream2_get_byte(gb) + 7;
 | |
|         }
 | |
|         i = bytestream2_get_le16(gb);
 | |
|         pos = bytestream2_tell_p(pb) - 2048 * (opcode & 8);
 | |
|         pos = pos - (i >> 2);
 | |
|         if (pos == bytestream2_tell_p(pb))
 | |
|             break;
 | |
| 
 | |
|         pos = pos - 0x4000;
 | |
|         bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|         bytestream2_seek(&gbc, pos, SEEK_SET);
 | |
| 
 | |
|         if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             do {
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 --len;
 | |
|             } while (len);
 | |
|         } else {
 | |
|             bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
 | |
|             for (len = len - 2; len; --len)
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|         }
 | |
| 
 | |
|         len = i & 3;
 | |
|         if (!len) {
 | |
|             repeat = 1;
 | |
|         } else {
 | |
|             do {
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                 --len;
 | |
|             } while (len);
 | |
|             opcode = bytestream2_peek_byte(gb);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int decode_type1(GetByteContext *gb, PutByteContext *pb)
 | |
| {
 | |
|     unsigned opcode = 0, len;
 | |
|     int high = 0;
 | |
|     int i, pos;
 | |
| 
 | |
|     while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|         GetByteContext gbc;
 | |
| 
 | |
|         while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|             while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|                 opcode = bytestream2_get_byte(gb);
 | |
|                 high = opcode >= 0x20;
 | |
|                 if (high)
 | |
|                     break;
 | |
|                 if (opcode)
 | |
|                     break;
 | |
|                 opcode = bytestream2_get_byte(gb);
 | |
|                 if (opcode < 0xF8) {
 | |
|                     opcode += 32;
 | |
|                     break;
 | |
|                 }
 | |
|                 i = opcode - 0xF8;
 | |
|                 if (i) {
 | |
|                     len = 256;
 | |
|                     do {
 | |
|                         len *= 2;
 | |
|                         --i;
 | |
|                     } while (i);
 | |
|                 } else {
 | |
|                     len = 280;
 | |
|                 }
 | |
|                 do {
 | |
|                     bytestream2_put_le32(pb, bytestream2_get_le32(gb));
 | |
|                     bytestream2_put_le32(pb, bytestream2_get_le32(gb));
 | |
|                     len -= 8;
 | |
|                 } while (len && bytestream2_get_bytes_left(gb) > 0);
 | |
|             }
 | |
| 
 | |
|             if (!high) {
 | |
|                 do {
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                     --opcode;
 | |
|                 } while (opcode && bytestream2_get_bytes_left(gb) > 0);
 | |
| 
 | |
|                 while (bytestream2_get_bytes_left(gb) > 0) {
 | |
|                     GetByteContext gbc;
 | |
| 
 | |
|                     opcode = bytestream2_get_byte(gb);
 | |
|                     if (opcode >= 0x20)
 | |
|                         break;
 | |
|                     bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
| 
 | |
|                     pos = -(opcode | 32 * bytestream2_get_byte(gb)) - 1;
 | |
|                     bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                     bytestream2_put_byte(pb, bytestream2_get_byte(gb));
 | |
|                 }
 | |
|             }
 | |
|             high = 0;
 | |
|             if (opcode < 0x40)
 | |
|                 break;
 | |
|             bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|             pos = (-((opcode & 0x1F) | 32 * bytestream2_get_byte(gb)) - 1);
 | |
|             bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             len = (opcode >> 5) - 1;
 | |
|             do {
 | |
|                 bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|                 --len;
 | |
|             } while (len && bytestream2_get_bytes_left(&gbc) > 0);
 | |
|         }
 | |
|         len = opcode & 0x1F;
 | |
|         if (!len) {
 | |
|             if (!bytestream2_peek_byte(gb)) {
 | |
|                 do {
 | |
|                     bytestream2_skip(gb, 1);
 | |
|                     len += 255;
 | |
|                 } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
 | |
|             }
 | |
|             len += bytestream2_get_byte(gb) + 31;
 | |
|         }
 | |
|         pos = -bytestream2_get_byte(gb);
 | |
|         bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
 | |
|         bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos - (bytestream2_get_byte(gb) << 8), SEEK_SET);
 | |
|         if (bytestream2_tell_p(pb) == bytestream2_tell(&gbc))
 | |
|             break;
 | |
|         if (len < 5 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|         } else {
 | |
|             bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
 | |
|             len--;
 | |
|         }
 | |
|         do {
 | |
|             bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
 | |
|             len--;
 | |
|         } while (len && bytestream2_get_bytes_left(&gbc) > 0);
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int decode_frame(AVCodecContext *avctx, void *data,
 | |
|                         int *got_frame, AVPacket *avpkt)
 | |
| {
 | |
|     FMVCContext *s = avctx->priv_data;
 | |
|     GetByteContext *gb = &s->gb;
 | |
|     PutByteContext *pb = &s->pb;
 | |
|     AVFrame *frame = data;
 | |
|     int ret, y, x;
 | |
| 
 | |
|     if (avpkt->size < 8)
 | |
|         return AVERROR_INVALIDDATA;
 | |
| 
 | |
|     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
 | |
|         return ret;
 | |
| 
 | |
|     bytestream2_init(gb, avpkt->data, avpkt->size);
 | |
|     bytestream2_skip(gb, 2);
 | |
| 
 | |
|     frame->key_frame = !!bytestream2_get_le16(gb);
 | |
|     frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
 | |
| 
 | |
|     if (frame->key_frame) {
 | |
|         const uint8_t *src;
 | |
|         unsigned type, size;
 | |
|         uint8_t *dst;
 | |
| 
 | |
|         type = bytestream2_get_le16(gb);
 | |
|         size = bytestream2_get_le16(gb);
 | |
|         if (size > bytestream2_get_bytes_left(gb))
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         bytestream2_init_writer(pb, s->buffer, s->buffer_size);
 | |
|         if (type == 1) {
 | |
|             decode_type1(gb, pb);
 | |
|         } else if (type == 2){
 | |
|             decode_type2(gb, pb);
 | |
|         } else {
 | |
|             avpriv_report_missing_feature(avctx, "Compression type %d", type);
 | |
|             return AVERROR_PATCHWELCOME;
 | |
|         }
 | |
| 
 | |
|         src = s->buffer;
 | |
|         dst = frame->data[0] + (avctx->height - 1) * frame->linesize[0];
 | |
|         for (y = 0; y < avctx->height; y++) {
 | |
|             memcpy(dst, src, avctx->width * s->bpp);
 | |
|             dst -= frame->linesize[0];
 | |
|             src += s->stride * 4;
 | |
|             if (bytestream2_tell_p(pb) < y*s->stride * 4)
 | |
|                 break;
 | |
|         }
 | |
|     } else {
 | |
|         unsigned block, nb_blocks;
 | |
|         int type, k, l;
 | |
|         uint8_t *ssrc, *ddst;
 | |
|         const uint32_t *src;
 | |
|         uint32_t *dst;
 | |
| 
 | |
|         for (block = 0; block < s->nb_blocks; block++)
 | |
|             s->blocks[block].xor = 0;
 | |
| 
 | |
|         nb_blocks = bytestream2_get_le16(gb);
 | |
|         if (nb_blocks > s->nb_blocks)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         bytestream2_init_writer(pb, s->pbuffer, s->pbuffer_size);
 | |
| 
 | |
|         type = bytestream2_get_le16(gb);
 | |
|         for (block = 0; block < nb_blocks; block++) {
 | |
|             unsigned size, offset;
 | |
|             int start = 0;
 | |
| 
 | |
|             offset = bytestream2_get_le16(gb);
 | |
|             if (offset >= s->nb_blocks)
 | |
|                 return AVERROR_INVALIDDATA;
 | |
| 
 | |
|             size = bytestream2_get_le16(gb);
 | |
|             if (size > bytestream2_get_bytes_left(gb))
 | |
|                 return AVERROR_INVALIDDATA;
 | |
| 
 | |
|             start = bytestream2_tell_p(pb);
 | |
|             if (type == 1) {
 | |
|                 decode_type1(gb, pb);
 | |
|             } else if (type == 2){
 | |
|                 decode_type2(gb, pb);
 | |
|             } else {
 | |
|                 avpriv_report_missing_feature(avctx, "Compression type %d", type);
 | |
|                 return AVERROR_PATCHWELCOME;
 | |
|             }
 | |
| 
 | |
|             if (s->blocks[offset].size * 4 != bytestream2_tell_p(pb) - start)
 | |
|                 return AVERROR_INVALIDDATA;
 | |
| 
 | |
|             s->blocks[offset].xor = 1;
 | |
|         }
 | |
| 
 | |
|         src = (const uint32_t *)s->pbuffer;
 | |
|         dst = (uint32_t *)s->buffer;
 | |
| 
 | |
|         for (block = 0, y = 0; y < s->yb; y++) {
 | |
|             int block_h = s->blocks[block].h;
 | |
|             uint32_t *rect = dst;
 | |
| 
 | |
|             for (x = 0; x < s->xb; x++) {
 | |
|                 int block_w = s->blocks[block].w;
 | |
|                 uint32_t *row = dst;
 | |
| 
 | |
|                 block_h = s->blocks[block].h;
 | |
|                 if (s->blocks[block].xor) {
 | |
|                     for (k = 0; k < block_h; k++) {
 | |
|                         uint32_t *column = dst;
 | |
|                         for (l = 0; l < block_w; l++)
 | |
|                             *dst++ ^= *src++;
 | |
|                         dst = &column[s->stride];
 | |
|                     }
 | |
|                 }
 | |
|                 dst = &row[block_w];
 | |
|                 ++block;
 | |
|             }
 | |
|             dst = &rect[block_h * s->stride];
 | |
|         }
 | |
| 
 | |
|         ssrc = s->buffer;
 | |
|         ddst = frame->data[0] + (avctx->height - 1) * frame->linesize[0];
 | |
|         for (y = 0; y < avctx->height; y++) {
 | |
|             memcpy(ddst, ssrc, avctx->width * s->bpp);
 | |
|             ddst -= frame->linesize[0];
 | |
|             ssrc += s->stride * 4;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     *got_frame = 1;
 | |
| 
 | |
|     return avpkt->size;
 | |
| }
 | |
| 
 | |
| static av_cold int decode_init(AVCodecContext *avctx)
 | |
| {
 | |
|     FMVCContext *s = avctx->priv_data;
 | |
|     int i, j, m, block = 0, h = BLOCK_HEIGHT, w = BLOCK_WIDTH;
 | |
| 
 | |
|     switch (avctx->bits_per_coded_sample) {
 | |
|     case 16:
 | |
|         avctx->pix_fmt = AV_PIX_FMT_RGB555LE;
 | |
|         break;
 | |
|     case 24:
 | |
|         avctx->pix_fmt = AV_PIX_FMT_BGR24;
 | |
|         break;
 | |
|     case 32:
 | |
|         avctx->pix_fmt = AV_PIX_FMT_BGRA;
 | |
|         break;
 | |
|     default:
 | |
|         av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n",
 | |
|                avctx->bits_per_coded_sample);
 | |
|         return AVERROR_INVALIDDATA;
 | |
|     }
 | |
| 
 | |
|     s->stride = (avctx->width * avctx->bits_per_coded_sample + 31) / 32;
 | |
|     s->xb     = s->stride / BLOCK_WIDTH;
 | |
|     m         = s->stride % BLOCK_WIDTH;
 | |
|     if (m) {
 | |
|         if (m < 37) {
 | |
|             w = m + BLOCK_WIDTH;
 | |
|         } else {
 | |
|             w = m;
 | |
|             s->xb++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     s->yb = avctx->height / BLOCK_HEIGHT;
 | |
|     m     = avctx->height % BLOCK_HEIGHT;
 | |
|     if (m) {
 | |
|         if (m < 49) {
 | |
|             h = m + BLOCK_HEIGHT;
 | |
|         } else {
 | |
|             h = m;
 | |
|             s->yb++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     s->nb_blocks = s->xb * s->yb;
 | |
|     if (!s->nb_blocks)
 | |
|         return AVERROR_INVALIDDATA;
 | |
|     s->blocks    = av_calloc(s->nb_blocks, sizeof(*s->blocks));
 | |
|     if (!s->blocks)
 | |
|         return AVERROR(ENOMEM);
 | |
| 
 | |
|     for (i = 0; i < s->yb; i++) {
 | |
|         for (j = 0; j < s->xb; j++) {
 | |
|             if (i != (s->yb - 1) || j != (s->xb - 1)) {
 | |
|                 if (i == s->yb - 1) {
 | |
|                     s->blocks[block].w    = BLOCK_WIDTH;
 | |
|                     s->blocks[block].h    = h;
 | |
|                     s->blocks[block].size = BLOCK_WIDTH * h;
 | |
|                 } else if (j == s->xb - 1) {
 | |
|                     s->blocks[block].w    = w;
 | |
|                     s->blocks[block].h    = BLOCK_HEIGHT;
 | |
|                     s->blocks[block].size = BLOCK_HEIGHT * w;
 | |
|                 } else {
 | |
|                     s->blocks[block].w    = BLOCK_WIDTH;
 | |
|                     s->blocks[block].h    = BLOCK_HEIGHT;
 | |
|                     s->blocks[block].size = BLOCK_WIDTH * BLOCK_HEIGHT;
 | |
|                 }
 | |
|             } else {
 | |
|                 s->blocks[block].w    = w;
 | |
|                 s->blocks[block].h    = h;
 | |
|                 s->blocks[block].size = w * h;
 | |
|             }
 | |
|             block++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     s->bpp          = avctx->bits_per_coded_sample >> 3;
 | |
|     s->buffer_size  = avctx->width * avctx->height * 4;
 | |
|     s->pbuffer_size = avctx->width * avctx->height * 4;
 | |
|     s->buffer       = av_mallocz(s->buffer_size);
 | |
|     s->pbuffer      = av_mallocz(s->pbuffer_size);
 | |
|     if (!s->buffer || !s->pbuffer)
 | |
|         return AVERROR(ENOMEM);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static av_cold int decode_close(AVCodecContext *avctx)
 | |
| {
 | |
|     FMVCContext *s = avctx->priv_data;
 | |
| 
 | |
|     av_freep(&s->buffer);
 | |
|     av_freep(&s->pbuffer);
 | |
|     av_freep(&s->blocks);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| const AVCodec ff_fmvc_decoder = {
 | |
|     .name             = "fmvc",
 | |
|     .long_name        = NULL_IF_CONFIG_SMALL("FM Screen Capture Codec"),
 | |
|     .type             = AVMEDIA_TYPE_VIDEO,
 | |
|     .id               = AV_CODEC_ID_FMVC,
 | |
|     .priv_data_size   = sizeof(FMVCContext),
 | |
|     .init             = decode_init,
 | |
|     .close            = decode_close,
 | |
|     .decode           = decode_frame,
 | |
|     .capabilities     = AV_CODEC_CAP_DR1,
 | |
|     .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
 | |
|                         FF_CODEC_CAP_INIT_CLEANUP,
 | |
| };
 |