3 * Copyright (c) 2000 Fabrice Bellard
5 * first version by Francois Revol <revol@free.fr>
7 * This file is part of FFmpeg.
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.
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.
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
27 #include "libavutil/imgutils.h"
28 #include "libavutil/log.h"
29 #include "libavutil/opt.h"
30 #include "libavcodec/bytestream.h"
31 #include "libavcodec/gif.h"
33 typedef struct GIFContext
{
43 static av_cold
int gif_init(AVFormatContext
*s
)
45 avpriv_set_pts_info(s
->streams
[0], 64, 1, 100);
50 static int gif_parse_packet(AVFormatContext
*s
, const uint8_t *data
, int size
)
55 bytestream2_init(&gb
, data
, size
);
57 while (bytestream2_get_bytes_left(&gb
) > 0) {
58 x
= bytestream2_get_byte(&gb
);
59 if (x
!= GIF_EXTENSION_INTRODUCER
)
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
);
67 bytestream2_skip(&gb
, block_size
);
70 if (x
== GIF_GCE_EXT_LABEL
)
71 return bytestream2_tell(&gb
) + 2;
77 static int gif_get_delay(GIFContext
*gif
, AVPacket
*prev
, AVPacket
*new)
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
;
89 static int gif_write_packet(AVFormatContext
*s
, AVPacket
*new_pkt
)
91 GIFContext
*gif
= s
->priv_data
;
92 AVIOContext
*pb
= s
->pb
;
93 AVPacket
*pkt
= gif
->prev_pkt
;
96 gif
->prev_pkt
= av_packet_alloc();
98 return AVERROR(ENOMEM
);
99 return av_packet_ref(gif
->prev_pkt
, new_pkt
);
102 gif
->last_pos
= avio_tell(pb
);
104 gif
->have_end
= pkt
->data
[pkt
->size
- 1] == GIF_TRAILER
;
106 if (!gif
->last_pos
) {
111 return AVERROR(EINVAL
);
113 if (pkt
->data
[10] & 0x80)
114 off
+= 3 * (1 << ((pkt
->data
[10] & 0x07) + 1));
116 if (pkt
->size
< off
+ 2)
117 return AVERROR(EINVAL
);
119 avio_write(pb
, pkt
->data
, off
);
121 if (pkt
->data
[off
] == GIF_EXTENSION_INTRODUCER
&& pkt
->data
[off
+ 1] == 0xff)
124 if (pkt
->size
<= off
)
125 return AVERROR(EINVAL
);
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 */
135 avio_wl16(pb
, (uint16_t)gif
->loop
);
136 avio_w8(pb
, 0x00); /* Data Sub-block Terminator */
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);
145 avio_write(pb
, pkt
->data
+ off
, pkt
->size
- off
);
148 int delay_pos
= gif_parse_packet(s
, pkt
->data
, pkt
->size
);
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);
155 avio_write(pb
, pkt
->data
, pkt
->size
);
159 av_packet_unref(gif
->prev_pkt
);
161 return av_packet_ref(gif
->prev_pkt
, new_pkt
);
166 static int gif_write_trailer(AVFormatContext
*s
)
168 GIFContext
*gif
= s
->priv_data
;
169 AVIOContext
*pb
= s
->pb
;
172 return AVERROR(EINVAL
);
174 gif_write_packet(s
, NULL
);
177 avio_w8(pb
, GIF_TRAILER
);
178 av_packet_free(&gif
->prev_pkt
);
183 #define OFFSET(x) offsetof(GIFContext, x)
184 #define ENC AV_OPT_FLAG_ENCODING_PARAM
185 static 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
},
193 static const AVClass gif_muxer_class
= {
194 .class_name
= "GIF muxer",
195 .item_name
= av_default_item_name
,
196 .version
= LIBAVUTIL_VERSION_INT
,
200 const FFOutputFormat ff_gif_muxer
= {
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
|
210 FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
,
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
,