summaryrefslogtreecommitdiff
path: root/libavcodec/gif.c
diff options
context:
space:
mode:
authorPaul B Mahol <onemda@gmail.com>2018-12-11 11:52:17 +0100
committerPaul B Mahol <onemda@gmail.com>2018-12-13 18:58:48 +0100
commitfaca28c264bee8ff93637ae69b62d5e974eee8a8 (patch)
treec70f4484d0acac7b5d647d331e309e452e276d9a /libavcodec/gif.c
parent102c11745e2da4e3ca248c6faed499bb2af19bd5 (diff)
downloadffmpeg-faca28c264bee8ff93637ae69b62d5e974eee8a8.tar.gz
avcodec: rewrite gif muxing and encoding
Now "-c copy" works. Update FATE files. Demuxer only split file into packets, no data is trimmed. Encoder & muxer currently expect completely another format where muxer writes stuff like disposal method which should be really encoder job. With this patch muxer only modifies delay between two packets. Codec copy need to have same behavior between demuxer and muxer to work correctly. Fixes #6640.
Diffstat (limited to 'libavcodec/gif.c')
-rw-r--r--libavcodec/gif.c127
1 files changed, 72 insertions, 55 deletions
diff --git a/libavcodec/gif.c b/libavcodec/gif.c
index d9c99d52cf..3074b7f7db 100644
--- a/libavcodec/gif.c
+++ b/libavcodec/gif.c
@@ -39,17 +39,19 @@
#include "put_bits.h"
+#define DEFAULT_TRANSPARENCY_INDEX 0x1f
+
typedef struct GIFContext {
const AVClass *class;
LZWState *lzw;
uint8_t *buf;
int buf_size;
+ int is_first_frame;
AVFrame *last_frame;
int flags;
uint32_t palette[AVPALETTE_COUNT]; ///< local reference palette for !pal8
int palette_loaded;
int transparent_index;
- uint8_t *pal_exdata;
uint8_t *tmpl; ///< temporary line buffer
} GIFContext;
@@ -58,6 +60,24 @@ enum {
GF_TRANSDIFF = 1<<1,
};
+static int get_palette_transparency_index(const uint32_t *palette)
+{
+ int transparent_color_index = -1;
+ unsigned i, smallest_alpha = 0xff;
+
+ if (!palette)
+ return -1;
+
+ for (i = 0; i < AVPALETTE_COUNT; i++) {
+ const uint32_t v = palette[i];
+ if (v >> 24 < smallest_alpha) {
+ smallest_alpha = v >> 24;
+ transparent_color_index = i;
+ }
+ }
+ return smallest_alpha < 128 ? transparent_color_index : -1;
+}
+
static int pick_palette_entry(const uint8_t *buf, int linesize, int w, int h)
{
int histogram[AVPALETTE_COUNT] = {0};
@@ -83,7 +103,7 @@ static int gif_image_write_image(AVCodecContext *avctx,
GIFContext *s = avctx->priv_data;
int len = 0, height = avctx->height, width = avctx->width, x, y;
int x_start = 0, y_start = 0, trans = s->transparent_index;
- int honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette;
+ int bcid = -1, honor_transparency = (s->flags & GF_TRANSDIFF) && s->last_frame && !palette;
const uint8_t *ptr;
/* Crop image */
@@ -137,6 +157,54 @@ static int gif_image_write_image(AVCodecContext *avctx,
width, height, x_start, y_start, avctx->width, avctx->height);
}
+ if (s->is_first_frame) { /* GIF header */
+ const uint32_t *global_palette = palette ? palette : s->palette;
+ const AVRational sar = avctx->sample_aspect_ratio;
+ int64_t aspect = 0;
+
+ if (sar.num > 0 && sar.den > 0) {
+ aspect = sar.num * 64LL / sar.den - 15;
+ if (aspect < 0 || aspect > 255)
+ aspect = 0;
+ }
+
+ bytestream_put_buffer(bytestream, gif89a_sig, sizeof(gif89a_sig));
+ bytestream_put_le16(bytestream, avctx->width);
+ bytestream_put_le16(bytestream, avctx->height);
+
+ bcid = get_palette_transparency_index(global_palette);
+
+ bytestream_put_byte(bytestream, 0xf7); /* flags: global clut, 256 entries */
+ bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid); /* background color index */
+ bytestream_put_byte(bytestream, aspect);
+ for (int i = 0; i < 256; i++) {
+ const uint32_t v = global_palette[i] & 0xffffff;
+ bytestream_put_be24(bytestream, v);
+ }
+
+ s->is_first_frame = 0;
+ }
+
+ if (honor_transparency && trans < 0) {
+ trans = pick_palette_entry(buf + y_start*linesize + x_start,
+ linesize, width, height);
+ if (trans < 0) // TODO, patch welcome
+ av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n");
+ }
+ if (trans < 0)
+ honor_transparency = 0;
+
+ bcid = (honor_transparency && trans >= 0) ? trans : get_palette_transparency_index(palette);
+
+ /* graphic control extension */
+ bytestream_put_byte(bytestream, GIF_EXTENSION_INTRODUCER);
+ bytestream_put_byte(bytestream, GIF_GCE_EXT_LABEL);
+ bytestream_put_byte(bytestream, 0x04); /* block size */
+ bytestream_put_byte(bytestream, 1<<2 | (bcid >= 0));
+ bytestream_put_le16(bytestream, 5); // default delay
+ bytestream_put_byte(bytestream, bcid < 0 ? DEFAULT_TRANSPARENCY_INDEX : bcid);
+ bytestream_put_byte(bytestream, 0x00);
+
/* image block */
bytestream_put_byte(bytestream, GIF_IMAGE_SEPARATOR);
bytestream_put_le16(bytestream, x_start);
@@ -155,24 +223,6 @@ static int gif_image_write_image(AVCodecContext *avctx,
}
}
- if (honor_transparency && trans < 0) {
- trans = pick_palette_entry(buf + y_start*linesize + x_start,
- linesize, width, height);
- if (trans < 0) { // TODO, patch welcome
- av_log(avctx, AV_LOG_DEBUG, "No available color, can not use transparency\n");
- } else {
- uint8_t *pal_exdata = s->pal_exdata;
- if (!pal_exdata)
- pal_exdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
- if (!pal_exdata)
- return AVERROR(ENOMEM);
- memcpy(pal_exdata, s->palette, AVPALETTE_SIZE);
- pal_exdata[trans*4 + 3*!HAVE_BIGENDIAN] = 0x00;
- }
- }
- if (trans < 0)
- honor_transparency = 0;
-
bytestream_put_byte(bytestream, 0x08);
ff_lzw_encode_init(s->lzw, s->buf, s->buf_size,
@@ -222,14 +272,9 @@ static av_cold int gif_encode_init(AVCodecContext *avctx)
av_log(avctx, AV_LOG_ERROR, "GIF does not support resolutions above 65535x65535\n");
return AVERROR(EINVAL);
}
-#if FF_API_CODED_FRAME
-FF_DISABLE_DEPRECATION_WARNINGS
- avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
- avctx->coded_frame->key_frame = 1;
-FF_ENABLE_DEPRECATION_WARNINGS
-#endif
s->transparent_index = -1;
+ s->is_first_frame = 1;
s->lzw = av_mallocz(ff_lzw_encode_state_size);
s->buf_size = avctx->width*avctx->height*2 + 1000;
@@ -244,25 +289,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
return 0;
}
-/* FIXME: duplicated with lavc */
-static int get_palette_transparency_index(const uint32_t *palette)
-{
- int transparent_color_index = -1;
- unsigned i, smallest_alpha = 0xff;
-
- if (!palette)
- return -1;
-
- for (i = 0; i < AVPALETTE_COUNT; i++) {
- const uint32_t v = palette[i];
- if (v >> 24 < smallest_alpha) {
- smallest_alpha = v >> 24;
- transparent_color_index = i;
- }
- }
- return smallest_alpha < 128 ? transparent_color_index : -1;
-}
-
static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *pict, int *got_packet)
{
@@ -277,22 +303,12 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
end = pkt->data + pkt->size;
if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
- uint8_t *pal_exdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
- if (!pal_exdata)
- return AVERROR(ENOMEM);
- memcpy(pal_exdata, pict->data[1], AVPALETTE_SIZE);
palette = (uint32_t*)pict->data[1];
- s->pal_exdata = pal_exdata;
-
- /* The first palette with PAL8 will be used as generic palette by the
- * muxer so we don't need to write it locally in the packet. We store
- * it as a reference here in case it changes later. */
if (!s->palette_loaded) {
memcpy(s->palette, palette, AVPALETTE_SIZE);
s->transparent_index = get_palette_transparency_index(palette);
s->palette_loaded = 1;
- palette = NULL;
} else if (!memcmp(s->palette, palette, AVPALETTE_SIZE)) {
palette = NULL;
}
@@ -305,6 +321,7 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
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)