1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Flash Screen Video encoder 3cabdff1aSopenharmony_ci * Copyright (C) 2004 Alex Beregszaszi 4cabdff1aSopenharmony_ci * Copyright (C) 2006 Benjamin Larsson 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * This file is part of FFmpeg. 7cabdff1aSopenharmony_ci * 8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 12cabdff1aSopenharmony_ci * 13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16cabdff1aSopenharmony_ci * Lesser General Public License for more details. 17cabdff1aSopenharmony_ci * 18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21cabdff1aSopenharmony_ci */ 22cabdff1aSopenharmony_ci 23cabdff1aSopenharmony_ci/* Encoding development sponsored by http://fh-campuswien.ac.at */ 24cabdff1aSopenharmony_ci 25cabdff1aSopenharmony_ci/** 26cabdff1aSopenharmony_ci * @file 27cabdff1aSopenharmony_ci * Flash Screen Video encoder 28cabdff1aSopenharmony_ci * @author Alex Beregszaszi 29cabdff1aSopenharmony_ci * @author Benjamin Larsson 30cabdff1aSopenharmony_ci * 31cabdff1aSopenharmony_ci * A description of the bitstream format for Flash Screen Video version 1/2 32cabdff1aSopenharmony_ci * is part of the SWF File Format Specification (version 10), which can be 33cabdff1aSopenharmony_ci * downloaded from http://www.adobe.com/devnet/swf.html. 34cabdff1aSopenharmony_ci */ 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_ci/* 37cabdff1aSopenharmony_ci * Encoding ideas: A basic encoder would just use a fixed block size. 38cabdff1aSopenharmony_ci * Block sizes can be multiples of 16, from 16 to 256. The blocks don't 39cabdff1aSopenharmony_ci * have to be quadratic. A brute force search with a set of different 40cabdff1aSopenharmony_ci * block sizes should give a better result than to just use a fixed size. 41cabdff1aSopenharmony_ci * 42cabdff1aSopenharmony_ci * TODO: 43cabdff1aSopenharmony_ci * Don't reencode the frame in brute force mode if the frame is a dupe. 44cabdff1aSopenharmony_ci * Speed up. Make the difference check faster. 45cabdff1aSopenharmony_ci */ 46cabdff1aSopenharmony_ci 47cabdff1aSopenharmony_ci#include <stdio.h> 48cabdff1aSopenharmony_ci#include <stdlib.h> 49cabdff1aSopenharmony_ci#include <zlib.h> 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_ci#include "avcodec.h" 52cabdff1aSopenharmony_ci#include "codec_internal.h" 53cabdff1aSopenharmony_ci#include "encode.h" 54cabdff1aSopenharmony_ci#include "put_bits.h" 55cabdff1aSopenharmony_ci#include "bytestream.h" 56cabdff1aSopenharmony_ci 57cabdff1aSopenharmony_ci 58cabdff1aSopenharmony_citypedef struct FlashSVContext { 59cabdff1aSopenharmony_ci AVCodecContext *avctx; 60cabdff1aSopenharmony_ci uint8_t *previous_frame; 61cabdff1aSopenharmony_ci int image_width, image_height; 62cabdff1aSopenharmony_ci int block_width, block_height; 63cabdff1aSopenharmony_ci uint8_t *encbuffer; 64cabdff1aSopenharmony_ci int block_size; 65cabdff1aSopenharmony_ci int last_key_frame; 66cabdff1aSopenharmony_ci uint8_t tmpblock[3 * 256 * 256]; 67cabdff1aSopenharmony_ci} FlashSVContext; 68cabdff1aSopenharmony_ci 69cabdff1aSopenharmony_cistatic int copy_region_enc(uint8_t *sptr, uint8_t *dptr, int dx, int dy, 70cabdff1aSopenharmony_ci int h, int w, int stride, uint8_t *pfptr) 71cabdff1aSopenharmony_ci{ 72cabdff1aSopenharmony_ci int i, j; 73cabdff1aSopenharmony_ci uint8_t *nsptr; 74cabdff1aSopenharmony_ci uint8_t *npfptr; 75cabdff1aSopenharmony_ci int diff = 0; 76cabdff1aSopenharmony_ci 77cabdff1aSopenharmony_ci for (i = dx + h; i > dx; i--) { 78cabdff1aSopenharmony_ci nsptr = sptr + i * stride + dy * 3; 79cabdff1aSopenharmony_ci npfptr = pfptr + i * stride + dy * 3; 80cabdff1aSopenharmony_ci for (j = 0; j < w * 3; j++) { 81cabdff1aSopenharmony_ci diff |= npfptr[j] ^ nsptr[j]; 82cabdff1aSopenharmony_ci dptr[j] = nsptr[j]; 83cabdff1aSopenharmony_ci } 84cabdff1aSopenharmony_ci dptr += w * 3; 85cabdff1aSopenharmony_ci } 86cabdff1aSopenharmony_ci if (diff) 87cabdff1aSopenharmony_ci return 1; 88cabdff1aSopenharmony_ci return 0; 89cabdff1aSopenharmony_ci} 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_cistatic av_cold int flashsv_encode_end(AVCodecContext *avctx) 92cabdff1aSopenharmony_ci{ 93cabdff1aSopenharmony_ci FlashSVContext *s = avctx->priv_data; 94cabdff1aSopenharmony_ci 95cabdff1aSopenharmony_ci av_freep(&s->encbuffer); 96cabdff1aSopenharmony_ci av_freep(&s->previous_frame); 97cabdff1aSopenharmony_ci 98cabdff1aSopenharmony_ci return 0; 99cabdff1aSopenharmony_ci} 100cabdff1aSopenharmony_ci 101cabdff1aSopenharmony_cistatic av_cold int flashsv_encode_init(AVCodecContext *avctx) 102cabdff1aSopenharmony_ci{ 103cabdff1aSopenharmony_ci FlashSVContext *s = avctx->priv_data; 104cabdff1aSopenharmony_ci 105cabdff1aSopenharmony_ci s->avctx = avctx; 106cabdff1aSopenharmony_ci 107cabdff1aSopenharmony_ci if (avctx->width > 4095 || avctx->height > 4095) { 108cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 109cabdff1aSopenharmony_ci "Input dimensions too large, input must be max 4095x4095 !\n"); 110cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 111cabdff1aSopenharmony_ci } 112cabdff1aSopenharmony_ci 113cabdff1aSopenharmony_ci s->last_key_frame = 0; 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci s->image_width = avctx->width; 116cabdff1aSopenharmony_ci s->image_height = avctx->height; 117cabdff1aSopenharmony_ci 118cabdff1aSopenharmony_ci s->encbuffer = av_mallocz(s->image_width * s->image_height * 3); 119cabdff1aSopenharmony_ci 120cabdff1aSopenharmony_ci if (!s->encbuffer) { 121cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n"); 122cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 123cabdff1aSopenharmony_ci } 124cabdff1aSopenharmony_ci 125cabdff1aSopenharmony_ci return 0; 126cabdff1aSopenharmony_ci} 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci 129cabdff1aSopenharmony_cistatic int encode_bitstream(FlashSVContext *s, const AVFrame *p, uint8_t *buf, 130cabdff1aSopenharmony_ci int buf_size, int block_width, int block_height, 131cabdff1aSopenharmony_ci uint8_t *previous_frame, int *I_frame) 132cabdff1aSopenharmony_ci{ 133cabdff1aSopenharmony_ci 134cabdff1aSopenharmony_ci PutBitContext pb; 135cabdff1aSopenharmony_ci int h_blocks, v_blocks, h_part, v_part, i, j; 136cabdff1aSopenharmony_ci int buf_pos, res; 137cabdff1aSopenharmony_ci int pred_blocks = 0; 138cabdff1aSopenharmony_ci 139cabdff1aSopenharmony_ci init_put_bits(&pb, buf, buf_size); 140cabdff1aSopenharmony_ci 141cabdff1aSopenharmony_ci put_bits(&pb, 4, block_width / 16 - 1); 142cabdff1aSopenharmony_ci put_bits(&pb, 12, s->image_width); 143cabdff1aSopenharmony_ci put_bits(&pb, 4, block_height / 16 - 1); 144cabdff1aSopenharmony_ci put_bits(&pb, 12, s->image_height); 145cabdff1aSopenharmony_ci flush_put_bits(&pb); 146cabdff1aSopenharmony_ci buf_pos = 4; 147cabdff1aSopenharmony_ci 148cabdff1aSopenharmony_ci h_blocks = s->image_width / block_width; 149cabdff1aSopenharmony_ci h_part = s->image_width % block_width; 150cabdff1aSopenharmony_ci v_blocks = s->image_height / block_height; 151cabdff1aSopenharmony_ci v_part = s->image_height % block_height; 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci /* loop over all block columns */ 154cabdff1aSopenharmony_ci for (j = 0; j < v_blocks + (v_part ? 1 : 0); j++) { 155cabdff1aSopenharmony_ci 156cabdff1aSopenharmony_ci int y_pos = j * block_height; // vertical position in frame 157cabdff1aSopenharmony_ci int cur_blk_height = (j < v_blocks) ? block_height : v_part; 158cabdff1aSopenharmony_ci 159cabdff1aSopenharmony_ci /* loop over all block rows */ 160cabdff1aSopenharmony_ci for (i = 0; i < h_blocks + (h_part ? 1 : 0); i++) { 161cabdff1aSopenharmony_ci int x_pos = i * block_width; // horizontal position in frame 162cabdff1aSopenharmony_ci int cur_blk_width = (i < h_blocks) ? block_width : h_part; 163cabdff1aSopenharmony_ci int ret = Z_OK; 164cabdff1aSopenharmony_ci uint8_t *ptr = buf + buf_pos; 165cabdff1aSopenharmony_ci 166cabdff1aSopenharmony_ci /* copy the block to the temp buffer before compression 167cabdff1aSopenharmony_ci * (if it differs from the previous frame's block) */ 168cabdff1aSopenharmony_ci res = copy_region_enc(p->data[0], s->tmpblock, 169cabdff1aSopenharmony_ci s->image_height - (y_pos + cur_blk_height + 1), 170cabdff1aSopenharmony_ci x_pos, cur_blk_height, cur_blk_width, 171cabdff1aSopenharmony_ci p->linesize[0], previous_frame); 172cabdff1aSopenharmony_ci 173cabdff1aSopenharmony_ci if (res || *I_frame) { 174cabdff1aSopenharmony_ci unsigned long zsize = 3 * block_width * block_height; 175cabdff1aSopenharmony_ci ret = compress2(ptr + 2, &zsize, s->tmpblock, 176cabdff1aSopenharmony_ci 3 * cur_blk_width * cur_blk_height, 9); 177cabdff1aSopenharmony_ci 178cabdff1aSopenharmony_ci if (ret != Z_OK) 179cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_ERROR, 180cabdff1aSopenharmony_ci "error while compressing block %dx%d\n", i, j); 181cabdff1aSopenharmony_ci 182cabdff1aSopenharmony_ci bytestream_put_be16(&ptr, zsize); 183cabdff1aSopenharmony_ci buf_pos += zsize + 2; 184cabdff1aSopenharmony_ci ff_dlog(s->avctx, "buf_pos = %d\n", buf_pos); 185cabdff1aSopenharmony_ci } else { 186cabdff1aSopenharmony_ci pred_blocks++; 187cabdff1aSopenharmony_ci bytestream_put_be16(&ptr, 0); 188cabdff1aSopenharmony_ci buf_pos += 2; 189cabdff1aSopenharmony_ci } 190cabdff1aSopenharmony_ci } 191cabdff1aSopenharmony_ci } 192cabdff1aSopenharmony_ci 193cabdff1aSopenharmony_ci if (pred_blocks) 194cabdff1aSopenharmony_ci *I_frame = 0; 195cabdff1aSopenharmony_ci else 196cabdff1aSopenharmony_ci *I_frame = 1; 197cabdff1aSopenharmony_ci 198cabdff1aSopenharmony_ci return buf_pos; 199cabdff1aSopenharmony_ci} 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_cistatic int flashsv_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 203cabdff1aSopenharmony_ci const AVFrame *pict, int *got_packet) 204cabdff1aSopenharmony_ci{ 205cabdff1aSopenharmony_ci FlashSVContext * const s = avctx->priv_data; 206cabdff1aSopenharmony_ci const AVFrame * const p = pict; 207cabdff1aSopenharmony_ci uint8_t *pfptr; 208cabdff1aSopenharmony_ci int res; 209cabdff1aSopenharmony_ci int I_frame = 0; 210cabdff1aSopenharmony_ci int opt_w = 4, opt_h = 4; 211cabdff1aSopenharmony_ci 212cabdff1aSopenharmony_ci /* First frame needs to be a keyframe */ 213cabdff1aSopenharmony_ci if (avctx->frame_number == 0) { 214cabdff1aSopenharmony_ci s->previous_frame = av_mallocz(FFABS(p->linesize[0]) * s->image_height); 215cabdff1aSopenharmony_ci if (!s->previous_frame) { 216cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n"); 217cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 218cabdff1aSopenharmony_ci } 219cabdff1aSopenharmony_ci I_frame = 1; 220cabdff1aSopenharmony_ci } 221cabdff1aSopenharmony_ci 222cabdff1aSopenharmony_ci if (p->linesize[0] < 0) 223cabdff1aSopenharmony_ci pfptr = s->previous_frame - (s->image_height - 1) * p->linesize[0]; 224cabdff1aSopenharmony_ci else 225cabdff1aSopenharmony_ci pfptr = s->previous_frame; 226cabdff1aSopenharmony_ci 227cabdff1aSopenharmony_ci /* Check the placement of keyframes */ 228cabdff1aSopenharmony_ci if (avctx->gop_size > 0 && 229cabdff1aSopenharmony_ci avctx->frame_number >= s->last_key_frame + avctx->gop_size) { 230cabdff1aSopenharmony_ci I_frame = 1; 231cabdff1aSopenharmony_ci } 232cabdff1aSopenharmony_ci 233cabdff1aSopenharmony_ci if ((res = ff_alloc_packet(avctx, pkt, s->image_width * s->image_height * 3)) < 0) 234cabdff1aSopenharmony_ci return res; 235cabdff1aSopenharmony_ci 236cabdff1aSopenharmony_ci pkt->size = encode_bitstream(s, p, pkt->data, pkt->size, opt_w * 16, opt_h * 16, 237cabdff1aSopenharmony_ci pfptr, &I_frame); 238cabdff1aSopenharmony_ci 239cabdff1aSopenharmony_ci //save the current frame 240cabdff1aSopenharmony_ci if (p->linesize[0] > 0) 241cabdff1aSopenharmony_ci memcpy(s->previous_frame, p->data[0], s->image_height * p->linesize[0]); 242cabdff1aSopenharmony_ci else 243cabdff1aSopenharmony_ci memcpy(s->previous_frame, 244cabdff1aSopenharmony_ci p->data[0] + p->linesize[0] * (s->image_height - 1), 245cabdff1aSopenharmony_ci s->image_height * FFABS(p->linesize[0])); 246cabdff1aSopenharmony_ci 247cabdff1aSopenharmony_ci //mark the frame type so the muxer can mux it correctly 248cabdff1aSopenharmony_ci if (I_frame) { 249cabdff1aSopenharmony_ci s->last_key_frame = avctx->frame_number; 250cabdff1aSopenharmony_ci ff_dlog(avctx, "Inserting keyframe at frame %d\n", avctx->frame_number); 251cabdff1aSopenharmony_ci } 252cabdff1aSopenharmony_ci 253cabdff1aSopenharmony_ci if (I_frame) 254cabdff1aSopenharmony_ci pkt->flags |= AV_PKT_FLAG_KEY; 255cabdff1aSopenharmony_ci *got_packet = 1; 256cabdff1aSopenharmony_ci 257cabdff1aSopenharmony_ci return 0; 258cabdff1aSopenharmony_ci} 259cabdff1aSopenharmony_ci 260cabdff1aSopenharmony_ciconst FFCodec ff_flashsv_encoder = { 261cabdff1aSopenharmony_ci .p.name = "flashsv", 262cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("Flash Screen Video"), 263cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 264cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_FLASHSV, 265cabdff1aSopenharmony_ci .priv_data_size = sizeof(FlashSVContext), 266cabdff1aSopenharmony_ci .init = flashsv_encode_init, 267cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(flashsv_encode_frame), 268cabdff1aSopenharmony_ci .close = flashsv_encode_end, 269cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE }, 270cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 271cabdff1aSopenharmony_ci}; 272