FFmpeg
Loading...
Searching...
No Matches
gif.c
Go to the documentation of this file.
1/*
2 * Animated GIF muxer
3 * Copyright (c) 2000 Fabrice Bellard
4 *
5 * first version by Francois Revol <revol@free.fr>
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include "avformat.h"
25#include "internal.h"
26#include "mux.h"
27#include "libavutil/imgutils.h"
28#include "libavutil/log.h"
29#include "libavutil/opt.h"
31#include "libavcodec/gif.h"
32
33typedef struct GIFContext {
34 AVClass *class;
35 int loop;
42
44{
45 avpriv_set_pts_info(s->streams[0], 64, 1, 100);
46
47 return 0;
48}
49
50static int gif_parse_packet(AVFormatContext *s, const uint8_t *data, int size)
51{
53 int x;
54
56
57 while (bytestream2_get_bytes_left(&gb) > 0) {
58 x = bytestream2_get_byte(&gb);
60 return 0;
61
62 x = bytestream2_get_byte(&gb);
63 while (x != GIF_GCE_EXT_LABEL && bytestream2_get_bytes_left(&gb) > 0) {
64 int block_size = bytestream2_get_byte(&gb);
65 if (!block_size)
66 break;
67 bytestream2_skip(&gb, block_size);
68 }
69
70 if (x == GIF_GCE_EXT_LABEL)
71 return bytestream2_tell(&gb) + 2;
72 }
73
74 return 0;
75}
76
77static int gif_get_delay(GIFContext *gif, AVPacket *prev, AVPacket *new)
78{
79 if (new && new->pts != AV_NOPTS_VALUE)
80 gif->duration = av_clip_uint16(new->pts - prev->pts);
81 else if (!new && gif->last_delay >= 0)
82 gif->duration = gif->last_delay;
83 else if (prev->duration)
84 gif->duration = prev->duration;
85
86 return gif->duration;
87}
88
90{
91 GIFContext *gif = s->priv_data;
92 AVIOContext *pb = s->pb;
93 AVPacket *pkt = gif->prev_pkt;
94
95 if (!gif->prev_pkt) {
97 if (!gif->prev_pkt)
98 return AVERROR(ENOMEM);
99 return av_packet_ref(gif->prev_pkt, new_pkt);
100 }
101
102 gif->last_pos = avio_tell(pb);
103 if (pkt->size > 0)
104 gif->have_end = pkt->data[pkt->size - 1] == GIF_TRAILER;
105
106 if (!gif->last_pos) {
107 int delay_pos;
108 int off = 13;
109
110 if (pkt->size < 13)
111 return AVERROR(EINVAL);
112
113 if (pkt->data[10] & 0x80)
114 off += 3 * (1 << ((pkt->data[10] & 0x07) + 1));
115
116 if (pkt->size < off + 2)
117 return AVERROR(EINVAL);
118
119 avio_write(pb, pkt->data, off);
120
121 if (pkt->data[off] == GIF_EXTENSION_INTRODUCER && pkt->data[off + 1] == 0xff)
122 off += 19;
123
124 if (pkt->size <= off)
125 return AVERROR(EINVAL);
126
127 /* "NETSCAPE EXTENSION" for looped animation GIF */
128 if (gif->loop >= 0) {
129 avio_w8(pb, GIF_EXTENSION_INTRODUCER); /* GIF Extension code */
130 avio_w8(pb, GIF_APP_EXT_LABEL); /* Application Extension Label */
131 avio_w8(pb, 0x0b); /* Length of Application Block */
132 avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);
133 avio_w8(pb, 0x03); /* Length of Data Sub-Block */
134 avio_w8(pb, 0x01);
135 avio_wl16(pb, (uint16_t)gif->loop);
136 avio_w8(pb, 0x00); /* Data Sub-block Terminator */
137 }
138
139 delay_pos = gif_parse_packet(s, pkt->data + off, pkt->size - off);
140 if (delay_pos > 0 && delay_pos < pkt->size - off - 2) {
141 avio_write(pb, pkt->data + off, delay_pos);
142 avio_wl16(pb, gif_get_delay(gif, pkt, new_pkt));
143 avio_write(pb, pkt->data + off + delay_pos + 2, pkt->size - off - delay_pos - 2);
144 } else {
145 avio_write(pb, pkt->data + off, pkt->size - off);
146 }
147 } else {
148 int delay_pos = gif_parse_packet(s, pkt->data, pkt->size);
149
150 if (delay_pos > 0 && delay_pos < pkt->size - 2) {
151 avio_write(pb, pkt->data, delay_pos);
152 avio_wl16(pb, gif_get_delay(gif, pkt, new_pkt));
153 avio_write(pb, pkt->data + delay_pos + 2, pkt->size - delay_pos - 2);
154 } else {
155 avio_write(pb, pkt->data, pkt->size);
156 }
157 }
158
160 if (new_pkt)
161 return av_packet_ref(gif->prev_pkt, new_pkt);
162
163 return 0;
164}
165
167{
168 GIFContext *gif = s->priv_data;
169 AVIOContext *pb = s->pb;
170
171 if (!gif->prev_pkt)
172 return AVERROR(EINVAL);
173
175
176 if (!gif->have_end)
177 avio_w8(pb, GIF_TRAILER);
179
180 return 0;
181}
182
183#define OFFSET(x) offsetof(GIFContext, x)
184#define ENC AV_OPT_FLAG_ENCODING_PARAM
185static const AVOption options[] = {
186 { "loop", "Number of times to loop the output: -1 - no loop, 0 - infinite loop", OFFSET(loop),
187 AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 65535, ENC },
188 { "final_delay", "Force delay (in centiseconds) after the last frame", OFFSET(last_delay),
189 AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC },
190 { NULL },
191};
192
193static const AVClass gif_muxer_class = {
194 .class_name = "GIF muxer",
195 .item_name = av_default_item_name,
196 .version = LIBAVUTIL_VERSION_INT,
197 .option = options,
198};
199
201 .p.name = "gif",
202 .p.long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"),
203 .p.mime_type = "image/gif",
204 .p.extensions = "gif",
205 .priv_data_size = sizeof(GIFContext),
206 .p.audio_codec = AV_CODEC_ID_NONE,
207 .p.video_codec = AV_CODEC_ID_GIF,
208 .p.subtitle_codec = AV_CODEC_ID_NONE,
209 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
211 .init = gif_init,
212 .write_packet = gif_write_packet,
213 .write_trailer = gif_write_trailer,
214 .p.priv_class = &gif_muxer_class,
215 .p.flags = AVFMT_VARIABLE_FPS,
216};
const FFOutputFormat ff_gif_muxer
Definition gif.c:200
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVFMT_VARIABLE_FPS
Format allows variable fps.
Definition avformat.h:501
void avio_wl16(AVIOContext *s, unsigned int val)
Definition aviobuf.c:440
void avio_w8(AVIOContext *s, int b)
Definition aviobuf.c:184
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
static av_always_inline int bytestream2_get_bytes_left(const GetByteContext *g)
Definition bytestream.h:158
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition bytestream.h:137
static av_always_inline void bytestream2_skip(GetByteContext *g, unsigned int size)
Definition bytestream.h:168
static av_always_inline int bytestream2_tell(const GetByteContext *g)
Definition bytestream.h:192
#define ENC
Definition caca.c:198
#define s(width, name)
Definition cbs_vp9.c:198
#define av_clip_uint16
Definition common.h:112
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
static int loop
Definition ffplay.c:338
GIF format definitions.
#define GIF_APP_EXT_LABEL
Definition gif.h:49
#define GIF_TRAILER
Definition gif.h:44
#define GIF_EXTENSION_INTRODUCER
Definition gif.h:45
#define GIF_GCE_EXT_LABEL
Definition gif.h:47
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_CODEC_ID_GIF
Definition codec_id.h:147
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
void av_packet_free(AVPacket **pkt)
Free the packet, if the packet is reference counted, it will be unreferenced first.
Definition packet.c:74
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:434
AVPacket * av_packet_alloc(void)
Allocate an AVPacket and set its fields to default values.
Definition packet.c:63
int av_packet_ref(AVPacket *dst, const AVPacket *src)
Setup a new reference to the data described by a given packet.
Definition packet.c:442
#define AVERROR(e)
Definition error.h:45
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition avutil.h:247
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
misc image utilities
#define OFFSET(x)
Definition gif.c:537
static int gif_write_packet(AVFormatContext *s, AVPacket *new_pkt)
Definition gif.c:89
static int gif_parse_packet(AVFormatContext *s, const uint8_t *data, int size)
Definition gif.c:50
static const AVClass gif_muxer_class
Definition gif.c:193
static av_cold int gif_init(AVFormatContext *s)
Definition gif.c:43
static int gif_get_delay(GIFContext *gif, AVPacket *prev, AVPacket *new)
Definition gif.c:77
static int gif_write_trailer(AVFormatContext *s)
Definition gif.c:166
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition mux.h:59
const char data[16]
Definition mxf.c:149
AVOptions.
Describe the class of an AVClass context structure.
Definition log.h:76
Format I/O context.
Definition avformat.h:1333
Bytestream IO Context.
Definition avio.h:160
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
int64_t duration
Duration of this packet in AVStream->time_base units, 0 if unknown.
Definition packet.h:621
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition packet.h:596
int have_end
Definition gif.c:39
int last_delay
Definition gif.c:36
AVPacket * prev_pkt
Definition gif.c:40
int duration
Definition gif.c:37
int loop
Definition gif.c:35
int64_t last_pos
Definition gif.c:38
int size