1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Animated GIF muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2000 Fabrice Bellard
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * first version by Francois Revol <revol@free.fr>
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * This file is part of FFmpeg.
8cabdff1aSopenharmony_ci *
9cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
10cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
11cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
12cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
13cabdff1aSopenharmony_ci *
14cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
15cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
16cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17cabdff1aSopenharmony_ci * Lesser General Public License for more details.
18cabdff1aSopenharmony_ci *
19cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
20cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
21cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22cabdff1aSopenharmony_ci */
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include "avformat.h"
25cabdff1aSopenharmony_ci#include "internal.h"
26cabdff1aSopenharmony_ci#include "libavutil/imgutils.h"
27cabdff1aSopenharmony_ci#include "libavutil/log.h"
28cabdff1aSopenharmony_ci#include "libavutil/opt.h"
29cabdff1aSopenharmony_ci#include "libavcodec/bytestream.h"
30cabdff1aSopenharmony_ci#include "libavcodec/gif.h"
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_citypedef struct GIFContext {
33cabdff1aSopenharmony_ci    AVClass *class;
34cabdff1aSopenharmony_ci    int loop;
35cabdff1aSopenharmony_ci    int last_delay;
36cabdff1aSopenharmony_ci    int duration;
37cabdff1aSopenharmony_ci    int64_t last_pos;
38cabdff1aSopenharmony_ci    int have_end;
39cabdff1aSopenharmony_ci    AVPacket *prev_pkt;
40cabdff1aSopenharmony_ci} GIFContext;
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_cistatic int gif_write_header(AVFormatContext *s)
43cabdff1aSopenharmony_ci{
44cabdff1aSopenharmony_ci    if (s->nb_streams != 1 ||
45cabdff1aSopenharmony_ci        s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO ||
46cabdff1aSopenharmony_ci        s->streams[0]->codecpar->codec_id   != AV_CODEC_ID_GIF) {
47cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
48cabdff1aSopenharmony_ci               "GIF muxer supports only a single video GIF stream.\n");
49cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
50cabdff1aSopenharmony_ci    }
51cabdff1aSopenharmony_ci
52cabdff1aSopenharmony_ci    avpriv_set_pts_info(s->streams[0], 64, 1, 100);
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci    return 0;
55cabdff1aSopenharmony_ci}
56cabdff1aSopenharmony_ci
57cabdff1aSopenharmony_cistatic int gif_parse_packet(AVFormatContext *s, const uint8_t *data, int size)
58cabdff1aSopenharmony_ci{
59cabdff1aSopenharmony_ci    GetByteContext gb;
60cabdff1aSopenharmony_ci    int x;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_ci    bytestream2_init(&gb, data, size);
63cabdff1aSopenharmony_ci
64cabdff1aSopenharmony_ci    while (bytestream2_get_bytes_left(&gb) > 0) {
65cabdff1aSopenharmony_ci        x = bytestream2_get_byte(&gb);
66cabdff1aSopenharmony_ci        if (x != GIF_EXTENSION_INTRODUCER)
67cabdff1aSopenharmony_ci            return 0;
68cabdff1aSopenharmony_ci
69cabdff1aSopenharmony_ci        x = bytestream2_get_byte(&gb);
70cabdff1aSopenharmony_ci        while (x != GIF_GCE_EXT_LABEL && bytestream2_get_bytes_left(&gb) > 0) {
71cabdff1aSopenharmony_ci            int block_size = bytestream2_get_byte(&gb);
72cabdff1aSopenharmony_ci            if (!block_size)
73cabdff1aSopenharmony_ci                break;
74cabdff1aSopenharmony_ci            bytestream2_skip(&gb, block_size);
75cabdff1aSopenharmony_ci        }
76cabdff1aSopenharmony_ci
77cabdff1aSopenharmony_ci        if (x == GIF_GCE_EXT_LABEL)
78cabdff1aSopenharmony_ci            return bytestream2_tell(&gb) + 2;
79cabdff1aSopenharmony_ci    }
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci    return 0;
82cabdff1aSopenharmony_ci}
83cabdff1aSopenharmony_ci
84cabdff1aSopenharmony_cistatic int gif_get_delay(GIFContext *gif, AVPacket *prev, AVPacket *new)
85cabdff1aSopenharmony_ci{
86cabdff1aSopenharmony_ci    if (new && new->pts != AV_NOPTS_VALUE)
87cabdff1aSopenharmony_ci        gif->duration = av_clip_uint16(new->pts - prev->pts);
88cabdff1aSopenharmony_ci    else if (!new && gif->last_delay >= 0)
89cabdff1aSopenharmony_ci        gif->duration = gif->last_delay;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci    return gif->duration;
92cabdff1aSopenharmony_ci}
93cabdff1aSopenharmony_ci
94cabdff1aSopenharmony_cistatic int gif_write_packet(AVFormatContext *s, AVPacket *new_pkt)
95cabdff1aSopenharmony_ci{
96cabdff1aSopenharmony_ci    GIFContext *gif = s->priv_data;
97cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
98cabdff1aSopenharmony_ci    AVPacket *pkt = gif->prev_pkt;
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci    if (!gif->prev_pkt) {
101cabdff1aSopenharmony_ci        gif->prev_pkt = av_packet_alloc();
102cabdff1aSopenharmony_ci        if (!gif->prev_pkt)
103cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
104cabdff1aSopenharmony_ci        return av_packet_ref(gif->prev_pkt, new_pkt);
105cabdff1aSopenharmony_ci    }
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_ci    gif->last_pos = avio_tell(pb);
108cabdff1aSopenharmony_ci    if (pkt->size > 0)
109cabdff1aSopenharmony_ci        gif->have_end = pkt->data[pkt->size - 1] == GIF_TRAILER;
110cabdff1aSopenharmony_ci
111cabdff1aSopenharmony_ci    if (!gif->last_pos) {
112cabdff1aSopenharmony_ci        int delay_pos;
113cabdff1aSopenharmony_ci        int off = 13;
114cabdff1aSopenharmony_ci
115cabdff1aSopenharmony_ci        if (pkt->size < 13)
116cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
117cabdff1aSopenharmony_ci
118cabdff1aSopenharmony_ci        if (pkt->data[10] & 0x80)
119cabdff1aSopenharmony_ci            off += 3 * (1 << ((pkt->data[10] & 0x07) + 1));
120cabdff1aSopenharmony_ci
121cabdff1aSopenharmony_ci        if (pkt->size < off + 2)
122cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci        avio_write(pb, pkt->data, off);
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci        if (pkt->data[off] == GIF_EXTENSION_INTRODUCER && pkt->data[off + 1] == 0xff)
127cabdff1aSopenharmony_ci            off += 19;
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci        if (pkt->size <= off)
130cabdff1aSopenharmony_ci            return AVERROR(EINVAL);
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci        /* "NETSCAPE EXTENSION" for looped animation GIF */
133cabdff1aSopenharmony_ci        if (gif->loop >= 0) {
134cabdff1aSopenharmony_ci            avio_w8(pb, GIF_EXTENSION_INTRODUCER); /* GIF Extension code */
135cabdff1aSopenharmony_ci            avio_w8(pb, GIF_APP_EXT_LABEL); /* Application Extension Label */
136cabdff1aSopenharmony_ci            avio_w8(pb, 0x0b); /* Length of Application Block */
137cabdff1aSopenharmony_ci            avio_write(pb, "NETSCAPE2.0", sizeof("NETSCAPE2.0") - 1);
138cabdff1aSopenharmony_ci            avio_w8(pb, 0x03); /* Length of Data Sub-Block */
139cabdff1aSopenharmony_ci            avio_w8(pb, 0x01);
140cabdff1aSopenharmony_ci            avio_wl16(pb, (uint16_t)gif->loop);
141cabdff1aSopenharmony_ci            avio_w8(pb, 0x00); /* Data Sub-block Terminator */
142cabdff1aSopenharmony_ci        }
143cabdff1aSopenharmony_ci
144cabdff1aSopenharmony_ci        delay_pos = gif_parse_packet(s, pkt->data + off, pkt->size - off);
145cabdff1aSopenharmony_ci        if (delay_pos > 0 && delay_pos < pkt->size - off - 2) {
146cabdff1aSopenharmony_ci            avio_write(pb, pkt->data + off, delay_pos);
147cabdff1aSopenharmony_ci            avio_wl16(pb, gif_get_delay(gif, pkt, new_pkt));
148cabdff1aSopenharmony_ci            avio_write(pb, pkt->data + off + delay_pos + 2, pkt->size - off - delay_pos - 2);
149cabdff1aSopenharmony_ci        } else {
150cabdff1aSopenharmony_ci            avio_write(pb, pkt->data + off, pkt->size - off);
151cabdff1aSopenharmony_ci        }
152cabdff1aSopenharmony_ci    } else {
153cabdff1aSopenharmony_ci        int delay_pos = gif_parse_packet(s, pkt->data, pkt->size);
154cabdff1aSopenharmony_ci
155cabdff1aSopenharmony_ci        if (delay_pos > 0 && delay_pos < pkt->size - 2) {
156cabdff1aSopenharmony_ci            avio_write(pb, pkt->data, delay_pos);
157cabdff1aSopenharmony_ci            avio_wl16(pb, gif_get_delay(gif, pkt, new_pkt));
158cabdff1aSopenharmony_ci            avio_write(pb, pkt->data + delay_pos + 2, pkt->size - delay_pos - 2);
159cabdff1aSopenharmony_ci        } else {
160cabdff1aSopenharmony_ci            avio_write(pb, pkt->data, pkt->size);
161cabdff1aSopenharmony_ci        }
162cabdff1aSopenharmony_ci    }
163cabdff1aSopenharmony_ci
164cabdff1aSopenharmony_ci    av_packet_unref(gif->prev_pkt);
165cabdff1aSopenharmony_ci    if (new_pkt)
166cabdff1aSopenharmony_ci        return av_packet_ref(gif->prev_pkt, new_pkt);
167cabdff1aSopenharmony_ci
168cabdff1aSopenharmony_ci    return 0;
169cabdff1aSopenharmony_ci}
170cabdff1aSopenharmony_ci
171cabdff1aSopenharmony_cistatic int gif_write_trailer(AVFormatContext *s)
172cabdff1aSopenharmony_ci{
173cabdff1aSopenharmony_ci    GIFContext *gif = s->priv_data;
174cabdff1aSopenharmony_ci    AVIOContext *pb = s->pb;
175cabdff1aSopenharmony_ci
176cabdff1aSopenharmony_ci    if (!gif->prev_pkt)
177cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
178cabdff1aSopenharmony_ci
179cabdff1aSopenharmony_ci    gif_write_packet(s, NULL);
180cabdff1aSopenharmony_ci
181cabdff1aSopenharmony_ci    if (!gif->have_end)
182cabdff1aSopenharmony_ci        avio_w8(pb, GIF_TRAILER);
183cabdff1aSopenharmony_ci    av_packet_free(&gif->prev_pkt);
184cabdff1aSopenharmony_ci
185cabdff1aSopenharmony_ci    return 0;
186cabdff1aSopenharmony_ci}
187cabdff1aSopenharmony_ci
188cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(GIFContext, x)
189cabdff1aSopenharmony_ci#define ENC AV_OPT_FLAG_ENCODING_PARAM
190cabdff1aSopenharmony_cistatic const AVOption options[] = {
191cabdff1aSopenharmony_ci    { "loop", "Number of times to loop the output: -1 - no loop, 0 - infinite loop", OFFSET(loop),
192cabdff1aSopenharmony_ci      AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 65535, ENC },
193cabdff1aSopenharmony_ci    { "final_delay", "Force delay (in centiseconds) after the last frame", OFFSET(last_delay),
194cabdff1aSopenharmony_ci      AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 65535, ENC },
195cabdff1aSopenharmony_ci    { NULL },
196cabdff1aSopenharmony_ci};
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_cistatic const AVClass gif_muxer_class = {
199cabdff1aSopenharmony_ci    .class_name = "GIF muxer",
200cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
201cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
202cabdff1aSopenharmony_ci    .option     = options,
203cabdff1aSopenharmony_ci};
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_ciconst AVOutputFormat ff_gif_muxer = {
206cabdff1aSopenharmony_ci    .name           = "gif",
207cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"),
208cabdff1aSopenharmony_ci    .mime_type      = "image/gif",
209cabdff1aSopenharmony_ci    .extensions     = "gif",
210cabdff1aSopenharmony_ci    .priv_data_size = sizeof(GIFContext),
211cabdff1aSopenharmony_ci    .audio_codec    = AV_CODEC_ID_NONE,
212cabdff1aSopenharmony_ci    .video_codec    = AV_CODEC_ID_GIF,
213cabdff1aSopenharmony_ci    .write_header   = gif_write_header,
214cabdff1aSopenharmony_ci    .write_packet   = gif_write_packet,
215cabdff1aSopenharmony_ci    .write_trailer  = gif_write_trailer,
216cabdff1aSopenharmony_ci    .priv_class     = &gif_muxer_class,
217cabdff1aSopenharmony_ci    .flags          = AVFMT_VARIABLE_FPS,
218cabdff1aSopenharmony_ci};
219