diff --git a/libavcodec/gif.c b/libavcodec/gif.c index fae1366e01..8c7f72ba80 100644 --- a/libavcodec/gif.c +++ b/libavcodec/gif.c @@ -43,6 +43,7 @@ #include "avcodec.h" #include "bytestream.h" +#include "lzw.h" /* The GIF format uses reversed order for bitstreams... */ /* at least they don't use PDP_ENDIAN :) */ @@ -50,11 +51,10 @@ #include "put_bits.h" -/* bitstream minipacket size */ -#define GIF_CHUNKS 100 - typedef struct { AVFrame picture; + LZWState *lzw; + uint8_t *buf; } GIFContext; /* GIF header */ @@ -82,12 +82,12 @@ static int gif_image_write_header(AVCodecContext *avctx, return 0; } -static int gif_image_write_image(AVCodecContext *avctx, uint8_t **bytestream, +static int gif_image_write_image(AVCodecContext *avctx, + uint8_t **bytestream, uint8_t *end, const uint8_t *buf, int linesize) { - PutBitContext p; - uint8_t buffer[200]; /* 100 * 9 / 8 = 113 */ - int i, left, w; + GIFContext *s = avctx->priv_data; + int len, height; const uint8_t *ptr; /* image block */ @@ -101,39 +101,25 @@ static int gif_image_write_image(AVCodecContext *avctx, uint8_t **bytestream, bytestream_put_byte(bytestream, 0x08); - left= avctx->width * avctx->height; + ff_lzw_encode_init(s->lzw, s->buf, avctx->width*avctx->height, + 12, FF_LZW_GIF, put_bits); - init_put_bits(&p, buffer, 130); - -/* - * the thing here is the bitstream is written as little packets, with a size byte before - * but it's still the same bitstream between packets (no flush !) - */ ptr = buf; - w = avctx->width; - while(left>0) { + for (height = avctx->height; height--;) { + len += ff_lzw_encode(s->lzw, ptr, avctx->width); + ptr += linesize; + } + len += ff_lzw_encode_flush(s->lzw, flush_put_bits); - put_bits(&p, 9, 0x0100); /* clear code */ - - for(i=(leftwidth; - buf += linesize; - ptr = buf; - } - } - - if(left<=GIF_CHUNKS) { - put_bits(&p, 9, 0x101); /* end of stream */ - flush_put_bits(&p); - } - if(put_bits_ptr(&p) - p.buf > 0) { - bytestream_put_byte(bytestream, put_bits_ptr(&p) - p.buf); /* byte count of the packet */ - bytestream_put_buffer(bytestream, p.buf, put_bits_ptr(&p) - p.buf); /* the actual buffer */ - p.buf_ptr = p.buf; /* dequeue the bytes off the bitstream */ - } - left-=GIF_CHUNKS; + ptr = s->buf; + while (len > 0) { + int size = FFMIN(255, len); + bytestream_put_byte(bytestream, size); + if (end - *bytestream < size) + return -1; + bytestream_put_buffer(bytestream, ptr, size); + ptr += size; + len -= size; } bytestream_put_byte(bytestream, 0x00); /* end of image block */ bytestream_put_byte(bytestream, 0x3b); @@ -145,6 +131,12 @@ static av_cold int gif_encode_init(AVCodecContext *avctx) GIFContext *s = avctx->priv_data; avctx->coded_frame = &s->picture; + s->lzw = av_mallocz(ff_lzw_encode_state_size); + if (!s->lzw) + return AVERROR_NOMEM; + s->buf = av_malloc(avctx->width*avctx->height*2); + if (!s->buf) + return AVERROR_NOMEM; return 0; } @@ -155,15 +147,25 @@ static int gif_encode_frame(AVCodecContext *avctx, unsigned char *outbuf, int bu AVFrame *pict = data; AVFrame *const p = (AVFrame *)&s->picture; uint8_t *outbuf_ptr = outbuf; + uint8_t *end = outbuf + buf_size; *p = *pict; p->pict_type = FF_I_TYPE; p->key_frame = 1; gif_image_write_header(avctx, &outbuf_ptr, (uint32_t *)pict->data[1]); - gif_image_write_image(avctx, &outbuf_ptr, pict->data[0], pict->linesize[0]); + gif_image_write_image(avctx, &outbuf_ptr, end, pict->data[0], pict->linesize[0]); return outbuf_ptr - outbuf; } +static int gif_encode_close(AVCodecContext *avctx) +{ + GIFContext *s = avctx->priv_data; + + av_freep(&s->lzw); + av_freep(&s->buf); + return 0; +} + AVCodec gif_encoder = { "gif", CODEC_TYPE_VIDEO, @@ -171,7 +173,7 @@ AVCodec gif_encoder = { sizeof(GIFContext), gif_encode_init, gif_encode_frame, - NULL, //encode_end, + gif_encode_close, .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}, .long_name= NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"), }; diff --git a/libavcodec/lzw.h b/libavcodec/lzw.h index 601d01fd67..bebb335f80 100644 --- a/libavcodec/lzw.h +++ b/libavcodec/lzw.h @@ -32,6 +32,8 @@ #include "get_bits.h" +struct PutBitContext; + enum FF_LZW_MODES{ FF_LZW_GIF, FF_LZW_TIFF @@ -52,8 +54,11 @@ void ff_lzw_decode_tail(LZWState *lzw); struct LZWEncodeState; extern const int ff_lzw_encode_state_size; -void ff_lzw_encode_init(struct LZWEncodeState * s, uint8_t * outbuf, int outsize, int maxbits); +void ff_lzw_encode_init(struct LZWEncodeState *s, uint8_t *outbuf, int outsize, + int maxbits, enum FF_LZW_MODES mode, + void (*lzw_put_bits)(struct PutBitContext *, int, unsigned int)); int ff_lzw_encode(struct LZWEncodeState * s, const uint8_t * inbuf, int insize); -int ff_lzw_encode_flush(struct LZWEncodeState * s); +int ff_lzw_encode_flush(struct LZWEncodeState *s, + void (*lzw_flush_put_bits)(struct PutBitContext *)); #endif /* AVCODEC_LZW_H */ diff --git a/libavcodec/lzwenc.c b/libavcodec/lzwenc.c index f3f66833a1..f8cf4911b6 100644 --- a/libavcodec/lzwenc.c +++ b/libavcodec/lzwenc.c @@ -58,6 +58,8 @@ typedef struct LZWEncodeState { int maxcode; ///< Max value of code int output_bytes; ///< Number of written bytes int last_code; ///< Value of last output code or LZW_PREFIX_EMPTY + enum FF_LZW_MODES mode; ///< TIFF or GIF + void (*put_bits)(PutBitContext *, int, unsigned); ///< GIF is LE while TIFF is BE }LZWEncodeState; @@ -110,7 +112,7 @@ static inline int hashOffset(const int head) static inline void writeCode(LZWEncodeState * s, int c) { assert(0 <= c && c < 1 << s->bits); - put_bits(&s->pb, s->bits, c); + s->put_bits(&s->pb, s->bits, c); } @@ -151,7 +153,7 @@ static inline void addCode(LZWEncodeState * s, uint8_t c, int hash_prefix, int h s->tabsize++; - if (s->tabsize >= 1 << s->bits) + if (s->tabsize >= (1 << s->bits) + (s->mode == FF_LZW_GIF)) s->bits++; } @@ -196,7 +198,9 @@ static int writtenBytes(LZWEncodeState *s){ * @param outsize Size of output buffer * @param maxbits Maximum length of code */ -void ff_lzw_encode_init(LZWEncodeState * s, uint8_t * outbuf, int outsize, int maxbits) +void ff_lzw_encode_init(LZWEncodeState *s, uint8_t *outbuf, int outsize, + int maxbits, enum FF_LZW_MODES mode, + void (*lzw_put_bits)(PutBitContext *, int, unsigned)) { s->clear_code = 256; s->end_code = 257; @@ -208,6 +212,8 @@ void ff_lzw_encode_init(LZWEncodeState * s, uint8_t * outbuf, int outsize, int m s->output_bytes = 0; s->last_code = LZW_PREFIX_EMPTY; s->bits = 9; + s->mode = mode; + s->put_bits = lzw_put_bits; } /** @@ -250,12 +256,13 @@ int ff_lzw_encode(LZWEncodeState * s, const uint8_t * inbuf, int insize) * @param s LZW state * @return Number of bytes written or -1 on error */ -int ff_lzw_encode_flush(LZWEncodeState * s) +int ff_lzw_encode_flush(LZWEncodeState *s, + void (*lzw_flush_put_bits)(PutBitContext *)) { if (s->last_code != -1) writeCode(s, s->last_code); writeCode(s, s->end_code); - flush_put_bits(&s->pb); + lzw_flush_put_bits(&s->pb); s->last_code = -1; return writtenBytes(s); diff --git a/libavcodec/tiffenc.c b/libavcodec/tiffenc.c index 7af39142c9..1d6b5b01f7 100644 --- a/libavcodec/tiffenc.c +++ b/libavcodec/tiffenc.c @@ -32,6 +32,7 @@ #include "tiff.h" #include "rle.h" #include "lzw.h" +#include "put_bits.h" #define TIFF_MAX_ENTRY 32 @@ -352,7 +353,8 @@ static int encode_frame(AVCodecContext * avctx, unsigned char *buf, for (i = 0; i < s->height; i++) { if (strip_sizes[i / s->rps] == 0) { if(s->compr == TIFF_LZW){ - ff_lzw_encode_init(s->lzws, ptr, s->buf_size - (*s->buf - s->buf_start), 12); + ff_lzw_encode_init(s->lzws, ptr, s->buf_size - (*s->buf - s->buf_start), + 12, FF_LZW_TIFF, put_bits); } strip_offsets[i / s->rps] = ptr - buf; } @@ -372,7 +374,7 @@ static int encode_frame(AVCodecContext * avctx, unsigned char *buf, ptr += n; if(s->compr == TIFF_LZW && (i==s->height-1 || i%s->rps == s->rps-1)){ int ret; - ret = ff_lzw_encode_flush(s->lzws); + ret = ff_lzw_encode_flush(s->lzws, flush_put_bits); strip_sizes[(i / s->rps )] += ret ; ptr += ret; }