Index: libavcodec/qtrleenc.c =================================================================== --- libavcodec/qtrleenc.c (revision 0) +++ libavcodec/qtrleenc.c (revision 0) @@ -0,0 +1,325 @@ +/* + * Quicktime Animation (RLE) Video Encoder + * Copyright (C) 2007 Clemens Fruhwirth + * + * This file is part of FFmpeg. + * + * This file is based on flashsvenc.c, + * Copyright (C) 2004 Alex Beregszaszi + * Copyright (C) 2006 Benjamin Larsson + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1, as published by the Free Software Foundation + * + * 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 +#include +#include +#include + +#include "common.h" +#include "avcodec.h" +#include "bytestream.h" + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +/* Maximum RLE codes for bulk copy and repeat */ +#define MAX_RLE_BULK 127 +#define MAX_RLE_REPEAT 128 + +typedef struct QtrleEncContext { + AVCodecContext *avctx; + uint8_t *previous_frame; + AVFrame frame; + int image_width, image_height; + int pixel_size; + uint32_t frame_number; + int max_buf_size; +} QtrleEncContext; + +static int qtrle_encode_init(AVCodecContext *avctx) +{ + QtrleEncContext *s = (QtrleEncContext *)avctx->priv_data; + + if (avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) { + return -1; + } + s->avctx=avctx; + s->frame_number = 0; + s->image_width = avctx->width; + s->image_height = avctx->height; + + switch (avctx->pix_fmt) { + case PIX_FMT_RGB555: + s->pixel_size = 2; + break; + case PIX_FMT_RGB24: + s->pixel_size = 3; + break; + default: + av_log (avctx, AV_LOG_ERROR, "Unsupported colorspace.\n"); + break; + } + avctx->bits_per_sample = s->pixel_size*8; + + s->previous_frame = av_mallocz(s->image_width*s->pixel_size*s->image_height); + if (!s->previous_frame) { + av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n"); + return -1; + } + s->max_buf_size=(s->image_width*s->image_height*s->pixel_size /* image base material */ + + 15 /* header + footer */ + + s->image_height*2 /* skip code+rle end */ + + s->image_width%MAX_RLE_BULK /* rle codes */); + return 0; +} + +static void put_pixel(QtrleEncContext *s, uint8_t *pixpointer, uint8_t **buf) +{ + switch (s->pixel_size) { + case 2: + bytestream_put_be16(buf,*((uint16_t *)pixpointer)); + break; + case 3: + bytestream_put_buffer(buf, pixpointer, s->pixel_size); + break; + default: + /* Internal Error. Die? */ + av_log(s->avctx, AV_LOG_ERROR, "Internal Error. Unknown pixel size in qtrleenc.c:put_pixel.\n"); + break; + } +} + +/* Scans two memory stripes in junks for equality */ +static int scan_equal(uint8_t *a, uint8_t *b, int limit, int junksize) +{ + int orig_limit=limit; + while(limit && (memcmp(a,b,junksize) == 0)) { + a+=junksize; + b+=junksize; + limit--; + } + return orig_limit-limit; +} + +/* Scans two memory stripes in junks for !equlity */ +static int scan_unequal(uint8_t *a, uint8_t *b, int limit, int junksize) +{ + int orig_limit=limit; + while(limit && (memcmp(a,b,junksize) != 0)) { + a+=junksize; + b+=junksize; + limit--; + } + return orig_limit-limit; +} + +/* This is the RLE encoder with bulk copy instruction */ +static void encode_rle(QtrleEncContext *s, uint8_t *pix, uint8_t **buf, int rlepix) +{ + while(rlepix) { + /* Number of subsequent non-unique pixels (at least 1) */ + int equals=1+scan_equal(pix, + pix+s->pixel_size, + min(rlepix-1,MAX_RLE_REPEAT-1), + s->pixel_size); + if(equals == 1) { + /* Output bulk copy */ + /* Scan how much unique pixels to include in this bulk copy */ + signed char rlecode=scan_unequal(pix, + pix+s->pixel_size, + min(rlepix-1,MAX_RLE_BULK), + s->pixel_size); + rlepix-=rlecode; + /* Ugly corner case */ + if(rlepix==1 && rlecode < MAX_RLE_BULK) { + rlecode++; rlepix--; + } + bytestream_put_byte(buf, rlecode); + while(rlecode--) { + put_pixel(s,pix,buf); + pix+=s->pixel_size; + } + } else { + /* Output repeat code */ + signed char rlecode=-equals; + bytestream_put_byte(buf, rlecode); + put_pixel(s,pix,buf); + pix+=equals*s->pixel_size; + rlepix-=equals; + } + } +} + +static void generate_skipcodes(uint8_t **buf, int skippix) +{ + while(skippix) { + int skipped=skippix<254?skippix:254; + bytestream_put_byte(buf, skipped+1); // skip code + bytestream_put_byte(buf, 0); // RLE code 0: another skip code is coming. + skippix-=skipped; + } + (*buf)--; // Remove last RLE code announcing a skip code +} + +/* Dump line with skip codes */ +static void dump_line_incremental(QtrleEncContext *s, AVFrame *p, int line, uint8_t **buf) +{ + uint8_t *this_line = p->data[0]+(line*p->linesize[0]); + uint8_t *prev_line = s->previous_frame+(line*p->linesize[0]); + int i=s->image_width; + + while(i) { + int skip_pix; + int encode_pix; + + skip_pix=scan_equal(this_line, prev_line, i, s->pixel_size); + i-=skip_pix; + + this_line+=skip_pix*s->pixel_size; + prev_line+=skip_pix*s->pixel_size; + + encode_pix=scan_unequal(this_line, prev_line, i, s->pixel_size); + i-=encode_pix; + + /* Output skip byte */ + /* special case optimization: nothing left to encode, hence + choose minimal skip code as does not matter. */ + if(i==0 && encode_pix == 0) + generate_skipcodes(buf, 1); + else + generate_skipcodes(buf, skip_pix); + + /* Output RLE lines */ + encode_rle(s, this_line, buf, encode_pix); + + this_line+=encode_pix*s->pixel_size; + prev_line+=encode_pix*s->pixel_size; + + bytestream_put_byte(buf, 0); // RLE code 0: another skip code is coming. + } + (*buf)--; // Remove last incorrect RLE code, announcing a skip code + bytestream_put_byte(buf, (signed char)-1); // end RLE line +} + +static void dump_line_complete(QtrleEncContext *s, AVFrame *p, int line, uint8_t **buf) +{ + bytestream_put_byte(buf, 1); // nothing to skip + encode_rle(s, p->data[0]+(line*p->linesize[0]), buf, s->image_width); + bytestream_put_byte(buf, (signed char)-1); // end RLE line +} + +/* Dumps frame including header */ +static int dump_frame(QtrleEncContext *s, AVFrame *p, uint8_t *buf, int incremental) +{ + int i; + int start_line=0; + int end_line=s->image_height; + uint8_t *orig_buf=buf; + + if(incremental) { + for(start_line=0; start_line < s->image_height; start_line++) + if(memcmp(p->data[0]+(start_line*p->linesize[0]), + s->previous_frame+(start_line*p->linesize[0]), + p->linesize[0])) + break; + + for(end_line=s->image_height; end_line > start_line; end_line--) + if(memcmp(p->data[0]+(end_line-1)*p->linesize[0], + s->previous_frame+(end_line-1)*p->linesize[0], + p->linesize[0])) + break; + } + + bytestream_put_be32(&buf, 0); // CHUNK SIZE, patched later + + if((start_line==0 && end_line == s->image_height) || start_line==s->image_height) + bytestream_put_be16(&buf, 0); // header + else { + bytestream_put_be16(&buf, 8); // header + bytestream_put_be16(&buf, start_line); // starting line + bytestream_put_be16(&buf, 0); // unknown + bytestream_put_be16(&buf, end_line-start_line); // lines to update + bytestream_put_be16(&buf, 0); // unknown + } + for(i=start_line; i < end_line; i++) { + if(incremental) + dump_line_incremental(s, p, i, &buf); + else { + dump_line_complete(s, p, i, &buf); + } + } + bytestream_put_byte(&buf, 0); // zero skip code = frame finished + bytestream_put_be32(&orig_buf,buf-orig_buf); // patch the chunk size + return (buf-orig_buf)+4; +} + +static int qtrle_encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data) +{ + QtrleEncContext * const s = (QtrleEncContext *)avctx->priv_data; + AVFrame *pict = data; + AVFrame * const p = &s->frame; + int chunksize; + + *p = *pict; + + if (buf_size < s->max_buf_size) { + /* Upper bound check for compressed data */ + av_log(avctx, AV_LOG_ERROR, "buf_size %d < %d\n", buf_size, s->max_buf_size); + return -1; + } + + if (avctx->gop_size == 0 || (s->frame_number % avctx->gop_size) == 0) { + /* I-Frame */ + p->pict_type = FF_I_TYPE; + p->key_frame = 1; + chunksize = dump_frame(s, pict, buf, 0); + } else { + /* P-Frame */ + p->pict_type = FF_P_TYPE; + p->key_frame = 0; + chunksize = dump_frame(s, pict, buf, 1); + } + + /* save the current frame */ + memcpy(s->previous_frame, p->data[0], s->image_height*p->linesize[0]); + + avctx->coded_frame = p; + + // av_log(s->avctx, AV_LOG_INFO, "chunksize=%d\n",chunksize); + s->frame_number++; + return chunksize; +} + +static int qtrle_encode_end(AVCodecContext *avctx) +{ + QtrleEncContext *s = (QtrleEncContext *)avctx->priv_data; + + av_free(s->previous_frame); + return 0; +} +AVCodec qtrle_encoder = { + "qtrle", + CODEC_TYPE_VIDEO, + CODEC_ID_QTRLE, + sizeof(QtrleEncContext), + qtrle_encode_init, + qtrle_encode_frame, + qtrle_encode_end, + .pix_fmts= (enum PixelFormat[]){PIX_FMT_RGB24, PIX_FMT_RGB555, -1}, +}; + +/* Local Variables: */ +/* c-basic-offset:4 */ +/* indent-tabs-mode:nil */ +/* End: */ Index: libavcodec/qtrle.c =================================================================== --- libavcodec/qtrle.c (revision 7881) +++ libavcodec/qtrle.c (working copy) @@ -40,6 +40,8 @@ #include "common.h" #include "avcodec.h" #include "dsputil.h" +#include "bitstream.h" +#include "bytestream.h" typedef struct QtrleContext { @@ -49,9 +51,11 @@ unsigned char *buf; int size; + uint8_t *previous_frame; } QtrleContext; + #define CHECK_STREAM_PTR(n) \ if ((stream_ptr + n) > s->size) { \ av_log (s->avctx, AV_LOG_INFO, "Problem: stream_ptr out of bounds (%d >= %d)\n", \ @@ -627,4 +631,3 @@ qtrle_decode_frame, CODEC_CAP_DR1, }; - Index: libavcodec/allcodecs.c =================================================================== --- libavcodec/allcodecs.c (revision 7881) +++ libavcodec/allcodecs.c (working copy) @@ -109,7 +109,7 @@ REGISTER_ENCODER(PPM, ppm); REGISTER_DECODER(QDRAW, qdraw); REGISTER_DECODER(QPEG, qpeg); - REGISTER_DECODER(QTRLE, qtrle); + REGISTER_ENCDEC(QTRLE, qtrle); REGISTER_ENCDEC (RAWVIDEO, rawvideo); REGISTER_DECODER(ROQ, roq); REGISTER_DECODER(RPZA, rpza); Index: libavcodec/Makefile =================================================================== --- libavcodec/Makefile (revision 7881) +++ libavcodec/Makefile (working copy) @@ -114,6 +114,7 @@ OBJS-$(CONFIG_QDRAW_DECODER) += qdrw.o OBJS-$(CONFIG_QPEG_DECODER) += qpeg.o OBJS-$(CONFIG_QTRLE_DECODER) += qtrle.o +OBJS-$(CONFIG_QTRLE_ENCODER) += qtrleenc.o OBJS-$(CONFIG_RA_144_DECODER) += ra144.o OBJS-$(CONFIG_RA_288_DECODER) += ra288.o OBJS-$(CONFIG_ROQ_DECODER) += roqvideo.o Index: libavcodec/avcodec.h =================================================================== --- libavcodec/avcodec.h (revision 7881) +++ libavcodec/avcodec.h (working copy) @@ -2273,6 +2273,7 @@ extern AVCodec qdraw_decoder; extern AVCodec qpeg_decoder; extern AVCodec qtrle_decoder; +extern AVCodec qtrle_encoder; extern AVCodec ra_144_decoder; extern AVCodec ra_288_decoder; extern AVCodec roq_decoder; Index: libavformat/movenc.c =================================================================== --- libavformat/movenc.c (revision 7881) +++ libavformat/movenc.c (working copy) @@ -533,6 +533,7 @@ { CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') }, + { CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* special handling in mov_find_video_codec_tag */ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */