Mercurial > libavcodec.hg
comparison gif.c @ 10634:e15eb76d9e47 libavcodec
use lzw compression in gif encoder
author | bcoudurier |
---|---|
date | Thu, 03 Dec 2009 19:17:39 +0000 |
parents | f164bb900d0b |
children | 8a4984c5cacc |
comparison
equal
deleted
inserted
replaced
10633:66242b8fbd32 | 10634:e15eb76d9e47 |
---|---|
41 * some sites mentions an RLE type compression also. | 41 * some sites mentions an RLE type compression also. |
42 */ | 42 */ |
43 | 43 |
44 #include "avcodec.h" | 44 #include "avcodec.h" |
45 #include "bytestream.h" | 45 #include "bytestream.h" |
46 #include "lzw.h" | |
46 | 47 |
47 /* The GIF format uses reversed order for bitstreams... */ | 48 /* The GIF format uses reversed order for bitstreams... */ |
48 /* at least they don't use PDP_ENDIAN :) */ | 49 /* at least they don't use PDP_ENDIAN :) */ |
49 #define BITSTREAM_WRITER_LE | 50 #define BITSTREAM_WRITER_LE |
50 | 51 |
51 #include "put_bits.h" | 52 #include "put_bits.h" |
52 | 53 |
53 /* bitstream minipacket size */ | |
54 #define GIF_CHUNKS 100 | |
55 | |
56 typedef struct { | 54 typedef struct { |
57 AVFrame picture; | 55 AVFrame picture; |
56 LZWState *lzw; | |
57 uint8_t *buf; | |
58 } GIFContext; | 58 } GIFContext; |
59 | 59 |
60 /* GIF header */ | 60 /* GIF header */ |
61 static int gif_image_write_header(AVCodecContext *avctx, | 61 static int gif_image_write_header(AVCodecContext *avctx, |
62 uint8_t **bytestream, uint32_t *palette) | 62 uint8_t **bytestream, uint32_t *palette) |
80 } | 80 } |
81 | 81 |
82 return 0; | 82 return 0; |
83 } | 83 } |
84 | 84 |
85 static int gif_image_write_image(AVCodecContext *avctx, uint8_t **bytestream, | 85 static int gif_image_write_image(AVCodecContext *avctx, |
86 uint8_t **bytestream, uint8_t *end, | |
86 const uint8_t *buf, int linesize) | 87 const uint8_t *buf, int linesize) |
87 { | 88 { |
88 PutBitContext p; | 89 GIFContext *s = avctx->priv_data; |
89 uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */ | 90 int len, height; |
90 int i, left, w; | |
91 const uint8_t *ptr; | 91 const uint8_t *ptr; |
92 /* image block */ | 92 /* image block */ |
93 | 93 |
94 bytestream_put_byte(bytestream, 0x2c); | 94 bytestream_put_byte(bytestream, 0x2c); |
95 bytestream_put_le16(bytestream, 0); | 95 bytestream_put_le16(bytestream, 0); |
99 bytestream_put_byte(bytestream, 0x00); /* flags */ | 99 bytestream_put_byte(bytestream, 0x00); /* flags */ |
100 /* no local clut */ | 100 /* no local clut */ |
101 | 101 |
102 bytestream_put_byte(bytestream, 0x08); | 102 bytestream_put_byte(bytestream, 0x08); |
103 | 103 |
104 left= avctx->width * avctx->height; | 104 ff_lzw_encode_init(s->lzw, s->buf, avctx->width*avctx->height, |
105 12, FF_LZW_GIF, put_bits); | |
105 | 106 |
106 init_put_bits(&p, buffer, 130); | 107 ptr = buf; |
108 for (height = avctx->height; height--;) { | |
109 len += ff_lzw_encode(s->lzw, ptr, avctx->width); | |
110 ptr += linesize; | |
111 } | |
112 len += ff_lzw_encode_flush(s->lzw, flush_put_bits); | |
107 | 113 |
108 /* | 114 ptr = s->buf; |
109 * the thing here is the bitstream is written as little packets, with a size byte before | 115 while (len > 0) { |
110 * but it's still the same bitstream between packets (no flush !) | 116 int size = FFMIN(255, len); |
111 */ | 117 bytestream_put_byte(bytestream, size); |
112 ptr = buf; | 118 if (end - *bytestream < size) |
113 w = avctx->width; | 119 return -1; |
114 while(left>0) { | 120 bytestream_put_buffer(bytestream, ptr, size); |
115 | 121 ptr += size; |
116 put_bits(&p, 9, 0x0100); /* clear code */ | 122 len -= size; |
117 | |
118 for(i=(left<GIF_CHUNKS)?left:GIF_CHUNKS;i;i--) { | |
119 put_bits(&p, 9, *ptr++); | |
120 if (--w == 0) { | |
121 w = avctx->width; | |
122 buf += linesize; | |
123 ptr = buf; | |
124 } | |
125 } | |
126 | |
127 if(left<=GIF_CHUNKS) { | |
128 put_bits(&p, 9, 0x101); /* end of stream */ | |
129 flush_put_bits(&p); | |
130 } | |
131 if(put_bits_ptr(&p) - p.buf > 0) { | |
132 bytestream_put_byte(bytestream, put_bits_ptr(&p) - p.buf); /* byte count of the packet */ | |
133 bytestream_put_buffer(bytestream, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */ | |
134 p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */ | |
135 } | |
136 left-=GIF_CHUNKS; | |
137 } | 123 } |
138 bytestream_put_byte(bytestream, 0x00); /* end of image block */ | 124 bytestream_put_byte(bytestream, 0x00); /* end of image block */ |
139 bytestream_put_byte(bytestream, 0x3b); | 125 bytestream_put_byte(bytestream, 0x3b); |
140 return 0; | 126 return 0; |
141 } | 127 } |
143 static av_cold int gif_encode_init(AVCodecContext *avctx) | 129 static av_cold int gif_encode_init(AVCodecContext *avctx) |
144 { | 130 { |
145 GIFContext *s = avctx->priv_data; | 131 GIFContext *s = avctx->priv_data; |
146 | 132 |
147 avctx->coded_frame = &s->picture; | 133 avctx->coded_frame = &s->picture; |
134 s->lzw = av_mallocz(ff_lzw_encode_state_size); | |
135 if (!s->lzw) | |
136 return AVERROR_NOMEM; | |
137 s->buf = av_malloc(avctx->width*avctx->height*2); | |
138 if (!s->buf) | |
139 return AVERROR_NOMEM; | |
148 return 0; | 140 return 0; |
149 } | 141 } |
150 | 142 |
151 /* better than nothing gif encoder */ | 143 /* better than nothing gif encoder */ |
152 static int gif_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, int buf_size, void *data) | 144 static int gif_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, int buf_size, void *data) |
153 { | 145 { |
154 GIFContext *s = avctx->priv_data; | 146 GIFContext *s = avctx->priv_data; |
155 AVFrame *pict = data; | 147 AVFrame *pict = data; |
156 AVFrame *const p = (AVFrame *)&s->picture; | 148 AVFrame *const p = (AVFrame *)&s->picture; |
157 uint8_t *outbuf_ptr = outbuf; | 149 uint8_t *outbuf_ptr = outbuf; |
150 uint8_t *end = outbuf + buf_size; | |
158 | 151 |
159 *p = *pict; | 152 *p = *pict; |
160 p->pict_type = FF_I_TYPE; | 153 p->pict_type = FF_I_TYPE; |
161 p->key_frame = 1; | 154 p->key_frame = 1; |
162 gif_image_write_header(avctx, &outbuf_ptr, (uint32_t *)pict->data[1]); | 155 gif_image_write_header(avctx, &outbuf_ptr, (uint32_t *)pict->data[1]); |
163 gif_image_write_image(avctx, &outbuf_ptr, pict->data[0], pict->linesize[0]); | 156 gif_image_write_image(avctx, &outbuf_ptr, end, pict->data[0], pict->linesize[0]); |
164 return outbuf_ptr - outbuf; | 157 return outbuf_ptr - outbuf; |
158 } | |
159 | |
160 static int gif_encode_close(AVCodecContext *avctx) | |
161 { | |
162 GIFContext *s = avctx->priv_data; | |
163 | |
164 av_freep(&s->lzw); | |
165 av_freep(&s->buf); | |
166 return 0; | |
165 } | 167 } |
166 | 168 |
167 AVCodec gif_encoder = { | 169 AVCodec gif_encoder = { |
168 "gif", | 170 "gif", |
169 CODEC_TYPE_VIDEO, | 171 CODEC_TYPE_VIDEO, |
170 CODEC_ID_GIF, | 172 CODEC_ID_GIF, |
171 sizeof(GIFContext), | 173 sizeof(GIFContext), |
172 gif_encode_init, | 174 gif_encode_init, |
173 gif_encode_frame, | 175 gif_encode_frame, |
174 NULL, //encode_end, | 176 gif_encode_close, |
175 .pix_fmts= (const enum PixelFormat[]){PIX_FMT_RGB8, PIX_FMT_BGR8, PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE, PIX_FMT_GRAY8, PIX_FMT_PAL8, PIX_FMT_NONE}, | 177 .pix_fmts= (const enum PixelFormat[]){PIX_FMT_RGB8, PIX_FMT_BGR8, PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE, PIX_FMT_GRAY8, PIX_FMT_PAL8, PIX_FMT_NONE}, |
176 .long_name= NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"), | 178 .long_name= NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"), |
177 }; | 179 }; |