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