Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * Animated GIF muxer |
||
3 | * Copyright (c) 2000 Fabrice Bellard |
||
4 | * |
||
5 | * first version by Francois Revol |
||
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 "libavutil/avassert.h" |
||
27 | #include "libavutil/imgutils.h" |
||
28 | #include "libavutil/log.h" |
||
29 | #include "libavutil/opt.h" |
||
30 | |||
31 | static int gif_image_write_header(AVIOContext *pb, int width, int height, |
||
32 | int loop_count, uint32_t *palette) |
||
33 | { |
||
34 | int i; |
||
35 | |||
36 | avio_write(pb, "GIF", 3); |
||
37 | avio_write(pb, "89a", 3); |
||
38 | avio_wl16(pb, width); |
||
39 | avio_wl16(pb, height); |
||
40 | |||
41 | if (palette) { |
||
42 | avio_w8(pb, 0xf7); /* flags: global clut, 256 entries */ |
||
43 | avio_w8(pb, 0x1f); /* background color index */ |
||
44 | avio_w8(pb, 0); /* aspect ratio */ |
||
45 | for (i = 0; i < 256; i++) { |
||
46 | const uint32_t v = palette[i] & 0xffffff; |
||
47 | avio_wb24(pb, v); |
||
48 | } |
||
49 | } else { |
||
50 | avio_w8(pb, 0); /* flags */ |
||
51 | avio_w8(pb, 0); /* background color index */ |
||
52 | avio_w8(pb, 0); /* aspect ratio */ |
||
53 | } |
||
54 | |||
55 | |||
56 | if (loop_count >= 0 ) { |
||
57 | /* "NETSCAPE EXTENSION" for looped animation GIF */ |
||
58 | avio_w8(pb, 0x21); /* GIF Extension code */ |
||
59 | avio_w8(pb, 0xff); /* Application Extension Label */ |
||
60 | avio_w8(pb, 0x0b); /* Length of Application Block */ |
||
61 | avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1); |
||
62 | avio_w8(pb, 0x03); /* Length of Data Sub-Block */ |
||
63 | avio_w8(pb, 0x01); |
||
64 | avio_wl16(pb, (uint16_t)loop_count); |
||
65 | avio_w8(pb, 0x00); /* Data Sub-block Terminator */ |
||
66 | } |
||
67 | |||
68 | return 0; |
||
69 | } |
||
70 | |||
71 | typedef struct { |
||
72 | AVClass *class; |
||
73 | int loop; |
||
74 | int last_delay; |
||
75 | AVPacket *prev_pkt; |
||
76 | int duration; |
||
77 | } GIFContext; |
||
78 | |||
79 | static int gif_write_header(AVFormatContext *s) |
||
80 | { |
||
81 | GIFContext *gif = s->priv_data; |
||
82 | AVIOContext *pb = s->pb; |
||
83 | AVCodecContext *video_enc; |
||
84 | int width, height; |
||
85 | uint32_t palette[AVPALETTE_COUNT]; |
||
86 | |||
87 | if (s->nb_streams != 1 || |
||
88 | s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO || |
||
89 | s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) { |
||
90 | av_log(s, AV_LOG_ERROR, |
||
91 | "GIF muxer supports only a single video GIF stream.\n"); |
||
92 | return AVERROR(EINVAL); |
||
93 | } |
||
94 | |||
95 | video_enc = s->streams[0]->codec; |
||
96 | width = video_enc->width; |
||
97 | height = video_enc->height; |
||
98 | |||
99 | avpriv_set_pts_info(s->streams[0], 64, 1, 100); |
||
100 | if (avpriv_set_systematic_pal2(palette, video_enc->pix_fmt) < 0) { |
||
101 | av_assert0(video_enc->pix_fmt == AV_PIX_FMT_PAL8); |
||
102 | gif_image_write_header(pb, width, height, gif->loop, NULL); |
||
103 | } else { |
||
104 | gif_image_write_header(pb, width, height, gif->loop, palette); |
||
105 | } |
||
106 | |||
107 | avio_flush(s->pb); |
||
108 | return 0; |
||
109 | } |
||
110 | |||
111 | static int flush_packet(AVFormatContext *s, AVPacket *new) |
||
112 | { |
||
113 | GIFContext *gif = s->priv_data; |
||
114 | int size; |
||
115 | AVIOContext *pb = s->pb; |
||
116 | uint8_t flags = 0x4, transparent_color_index = 0x1f; |
||
117 | const uint32_t *palette; |
||
118 | AVPacket *pkt = gif->prev_pkt; |
||
119 | |||
120 | if (!pkt) |
||
121 | return 0; |
||
122 | |||
123 | /* Mark one colour as transparent if the input palette contains at least |
||
124 | * one colour that is more than 50% transparent. */ |
||
125 | palette = (uint32_t*)av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size); |
||
126 | if (palette && size != AVPALETTE_SIZE) { |
||
127 | av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n"); |
||
128 | return AVERROR_INVALIDDATA; |
||
129 | } |
||
130 | if (palette) { |
||
131 | unsigned i, smallest_alpha = 0xff; |
||
132 | |||
133 | for (i = 0; i < AVPALETTE_COUNT; i++) { |
||
134 | const uint32_t v = palette[i]; |
||
135 | if (v >> 24 < smallest_alpha) { |
||
136 | smallest_alpha = v >> 24; |
||
137 | transparent_color_index = i; |
||
138 | } |
||
139 | } |
||
140 | if (smallest_alpha < 128) |
||
141 | flags |= 0x1; /* Transparent Color Flag */ |
||
142 | } |
||
143 | |||
144 | if (new && new->pts != AV_NOPTS_VALUE) |
||
145 | gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts); |
||
146 | else if (!new && gif->last_delay >= 0) |
||
147 | gif->duration = gif->last_delay; |
||
148 | |||
149 | /* graphic control extension block */ |
||
150 | avio_w8(pb, 0x21); |
||
151 | avio_w8(pb, 0xf9); |
||
152 | avio_w8(pb, 0x04); /* block size */ |
||
153 | avio_w8(pb, flags); |
||
154 | avio_wl16(pb, gif->duration); |
||
155 | avio_w8(pb, transparent_color_index); |
||
156 | avio_w8(pb, 0x00); |
||
157 | |||
158 | avio_write(pb, pkt->data, pkt->size); |
||
159 | |||
160 | av_free_packet(gif->prev_pkt); |
||
161 | if (new) |
||
162 | av_copy_packet(gif->prev_pkt, new); |
||
163 | |||
164 | return 0; |
||
165 | } |
||
166 | |||
167 | static int gif_write_packet(AVFormatContext *s, AVPacket *pkt) |
||
168 | { |
||
169 | GIFContext *gif = s->priv_data; |
||
170 | |||
171 | if (!gif->prev_pkt) { |
||
172 | gif->prev_pkt = av_malloc(sizeof(*gif->prev_pkt)); |
||
173 | if (!gif->prev_pkt) |
||
174 | return AVERROR(ENOMEM); |
||
175 | return av_copy_packet(gif->prev_pkt, pkt); |
||
176 | } |
||
177 | return flush_packet(s, pkt); |
||
178 | } |
||
179 | |||
180 | static int gif_write_trailer(AVFormatContext *s) |
||
181 | { |
||
182 | GIFContext *gif = s->priv_data; |
||
183 | AVIOContext *pb = s->pb; |
||
184 | |||
185 | flush_packet(s, NULL); |
||
186 | av_freep(&gif->prev_pkt); |
||
187 | avio_w8(pb, 0x3b); |
||
188 | |||
189 | return 0; |
||
190 | } |
||
191 | |||
192 | #define OFFSET(x) offsetof(GIFContext, x) |
||
193 | #define ENC AV_OPT_FLAG_ENCODING_PARAM |
||
194 | static const AVOption options[] = { |
||
195 | { "loop", "Number of times to loop the output: -1 - no loop, 0 - infinite loop", OFFSET(loop), |
||
196 | AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 65535, ENC }, |
||
197 | { "final_delay", "Force delay (in ms) after the last frame", OFFSET(last_delay), |
||
198 | AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC }, |
||
199 | { NULL }, |
||
200 | }; |
||
201 | |||
202 | static const AVClass gif_muxer_class = { |
||
203 | .class_name = "GIF muxer", |
||
204 | .item_name = av_default_item_name, |
||
205 | .version = LIBAVUTIL_VERSION_INT, |
||
206 | .option = options, |
||
207 | }; |
||
208 | |||
209 | AVOutputFormat ff_gif_muxer = { |
||
210 | .name = "gif", |
||
211 | .long_name = NULL_IF_CONFIG_SMALL("GIF Animation"), |
||
212 | .mime_type = "image/gif", |
||
213 | .extensions = "gif", |
||
214 | .priv_data_size = sizeof(GIFContext), |
||
215 | .audio_codec = AV_CODEC_ID_NONE, |
||
216 | .video_codec = AV_CODEC_ID_GIF, |
||
217 | .write_header = gif_write_header, |
||
218 | .write_packet = gif_write_packet, |
||
219 | .write_trailer = gif_write_trailer, |
||
220 | .priv_class = &gif_muxer_class, |
||
221 | .flags = AVFMT_VARIABLE_FPS, |
||
222 | };>>>>> |