[FFmpeg-devel] [PATCH] lavc/gif: crop image when possible.
Clément Bœsch
ubitux at gmail.com
Wed Apr 17 16:23:39 CEST 2013
Increase compression when pictures are similar.
-f lavfi testsrc=300: 61M -> 21M
---
To be applied on top of the previous patch
---
libavcodec/gif.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 77 insertions(+), 10 deletions(-)
diff --git a/libavcodec/gif.c b/libavcodec/gif.c
index 0b8abb1..200d343 100644
--- a/libavcodec/gif.c
+++ b/libavcodec/gif.c
@@ -49,6 +49,7 @@ typedef struct {
AVFrame picture;
LZWState *lzw;
uint8_t *buf;
+ AVFrame *last_frame;
} GIFContext;
static int gif_image_write_image(AVCodecContext *avctx,
@@ -57,15 +58,71 @@ static int gif_image_write_image(AVCodecContext *avctx,
const uint8_t *buf, int linesize)
{
GIFContext *s = avctx->priv_data;
- int len = 0, height;
+ int len = 0, height = avctx->height, width = avctx->width, y;
+ int x_start = 0, y_start = 0;
const uint8_t *ptr;
- /* image block */
+ /* crop image */
+ // TODO support with palette change
+ if (s->last_frame && !palette) {
+ const AVFrame *last = s->last_frame;
+ int x_end = avctx->width - 1,
+ y_end = avctx->height - 1;
+
+ /* skip common lines */
+ while (y_start < height) {
+ if (memcmp(last->data[0] + y_start*last->linesize[0],
+ buf + y_start* linesize,
+ width))
+ break;
+ y_start++;
+ }
+ while (y_end > y_start) {
+ if (memcmp(last->data[0] + y_end*last->linesize[0],
+ buf + y_end* linesize,
+ width))
+ break;
+ y_end--;
+ }
+ height = y_end + 1 - y_start;
+
+ /* skip common columns */
+ while (x_start < width) {
+ int same_column = 1;
+ for (y = y_start; y < y_end; y++) {
+ if (last->data[0][y*last->linesize[0] + x_start] != buf[y*linesize + x_start]) {
+ same_column = 0;
+ break;
+ }
+ }
+ if (!same_column)
+ break;
+ x_start++;
+ }
+ while (x_end > x_start) {
+ int same_column = 1;
+ for (y = y_start; y < y_end; y++) {
+ if (last->data[0][y*last->linesize[0] + x_end] != buf[y*linesize + x_end]) {
+ same_column = 0;
+ break;
+ }
+ }
+ if (!same_column)
+ break;
+ x_end--;
+ }
+ width = x_end + 1 - x_start;
+
+ av_log(avctx, AV_LOG_DEBUG,"%dx%d image at pos (%d;%d) [area:%dx%d]\n",
+ width, height, x_start, y_start, avctx->width, avctx->height);
+ }
+
+ /* image block */
bytestream_put_byte(bytestream, 0x2c);
- bytestream_put_le16(bytestream, 0);
- bytestream_put_le16(bytestream, 0);
- bytestream_put_le16(bytestream, avctx->width);
- bytestream_put_le16(bytestream, avctx->height);
+ bytestream_put_le16(bytestream, x_start);
+ bytestream_put_le16(bytestream, y_start);
+ bytestream_put_le16(bytestream, width);
+ bytestream_put_le16(bytestream, height);
if (!palette) {
bytestream_put_byte(bytestream, 0x00); /* flags */
@@ -82,12 +139,12 @@ static int gif_image_write_image(AVCodecContext *avctx,
bytestream_put_byte(bytestream, 0x08);
- ff_lzw_encode_init(s->lzw, s->buf, avctx->width*avctx->height,
+ ff_lzw_encode_init(s->lzw, s->buf, width * height,
12, FF_LZW_GIF, put_bits);
- ptr = buf;
- for (height = avctx->height; height--;) {
- len += ff_lzw_encode(s->lzw, ptr, avctx->width);
+ ptr = buf + y_start*linesize + x_start;
+ for (y = 0; y < height; y++) {
+ len += ff_lzw_encode(s->lzw, ptr, width);
ptr += linesize;
}
len += ff_lzw_encode_flush(s->lzw, flush_put_bits);
@@ -148,6 +205,15 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
palette = (uint32_t*)p->data[1];
gif_image_write_image(avctx, &outbuf_ptr, end, palette, pict->data[0], pict->linesize[0]);
+ if (!s->last_frame) {
+ s->last_frame = av_frame_alloc();
+ if (!s->last_frame)
+ return AVERROR(ENOMEM);
+ }
+ av_frame_unref(s->last_frame);
+ ret = av_frame_ref(s->last_frame, (AVFrame*)pict);
+ if (ret < 0)
+ return ret;
pkt->size = outbuf_ptr - pkt->data;
pkt->flags |= AV_PKT_FLAG_KEY;
@@ -162,6 +228,7 @@ static int gif_encode_close(AVCodecContext *avctx)
av_freep(&s->lzw);
av_freep(&s->buf);
+ av_frame_free(&s->last_frame);
return 0;
}
--
1.8.2.1
More information about the ffmpeg-devel
mailing list