[FFmpeg-devel] [PATCH 1/3] avformat/image2: allow muxing gif files.
Clément Bœsch
u at pkh.me
Sat Oct 19 20:47:48 CEST 2013
Fixes Ticket #2936.
---
libavformat/gif.c | 85 ++++++++++++++++++++++++++++++---------------------
libavformat/gif.h | 26 ++++++++++++++++
libavformat/img2enc.c | 24 +++++++++++++++
3 files changed, 101 insertions(+), 34 deletions(-)
create mode 100644 libavformat/gif.h
diff --git a/libavformat/gif.c b/libavformat/gif.c
index f6e7625..cc8ae50 100644
--- a/libavformat/gif.c
+++ b/libavformat/gif.c
@@ -23,6 +23,7 @@
#include "avformat.h"
#include "internal.h"
+#include "gif.h"
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
@@ -76,55 +77,53 @@ typedef struct {
int duration;
} GIFContext;
-static int gif_write_header(AVFormatContext *s)
+int ff_gif_write_header(AVCodecContext *video_enc, AVIOContext *pb, int loop)
{
- GIFContext *gif = s->priv_data;
- AVIOContext *pb = s->pb;
- AVCodecContext *video_enc;
int width, height;
uint32_t palette[AVPALETTE_COUNT];
- if (s->nb_streams != 1 ||
- s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
- s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) {
- av_log(s, AV_LOG_ERROR,
- "GIF muxer supports only a single video GIF stream.\n");
- return AVERROR(EINVAL);
- }
-
- video_enc = s->streams[0]->codec;
width = video_enc->width;
height = video_enc->height;
- avpriv_set_pts_info(s->streams[0], 64, 1, 100);
if (avpriv_set_systematic_pal2(palette, video_enc->pix_fmt) < 0) {
av_assert0(video_enc->pix_fmt == AV_PIX_FMT_PAL8);
- gif_image_write_header(pb, width, height, gif->loop, NULL);
+ gif_image_write_header(pb, width, height, loop, NULL);
} else {
- gif_image_write_header(pb, width, height, gif->loop, palette);
+ gif_image_write_header(pb, width, height, loop, palette);
}
- avio_flush(s->pb);
+ avio_flush(pb);
return 0;
}
-static int flush_packet(AVFormatContext *s, AVPacket *new)
+static int gif_write_header(AVFormatContext *s)
{
GIFContext *gif = s->priv_data;
+
+ if (s->nb_streams != 1 ||
+ s->streams[0]->codec->codec_type != AVMEDIA_TYPE_VIDEO ||
+ s->streams[0]->codec->codec_id != AV_CODEC_ID_GIF) {
+ av_log(s, AV_LOG_ERROR,
+ "GIF muxer supports only a single video GIF stream.\n");
+ return AVERROR(EINVAL);
+ }
+
+ avpriv_set_pts_info(s->streams[0], 64, 1, 100);
+
+ return ff_gif_write_header(s->streams[0]->codec, s->pb, gif->loop);
+}
+
+int ff_gif_write_packet(void *log_ctx, AVIOContext *pb, AVPacket *pkt, int duration)
+{
int size;
- AVIOContext *pb = s->pb;
uint8_t flags = 0x4, transparent_color_index = 0x1f;
const uint32_t *palette;
- AVPacket *pkt = gif->prev_pkt;
-
- if (!pkt)
- return 0;
/* Mark one colour as transparent if the input palette contains at least
* one colour that is more than 50% transparent. */
palette = (uint32_t*)av_packet_get_side_data(pkt, AV_PKT_DATA_PALETTE, &size);
if (palette && size != AVPALETTE_SIZE) {
- av_log(s, AV_LOG_ERROR, "Invalid palette extradata\n");
+ av_log(log_ctx, AV_LOG_ERROR, "Invalid palette extradata\n");
return AVERROR_INVALIDDATA;
}
if (palette) {
@@ -141,22 +140,37 @@ static int flush_packet(AVFormatContext *s, AVPacket *new)
flags |= 0x1; /* Transparent Color Flag */
}
- if (new && new->pts != AV_NOPTS_VALUE)
- gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts);
- else if (!new && gif->last_delay >= 0)
- gif->duration = gif->last_delay;
-
/* graphic control extension block */
avio_w8(pb, 0x21);
avio_w8(pb, 0xf9);
avio_w8(pb, 0x04); /* block size */
avio_w8(pb, flags);
- avio_wl16(pb, gif->duration);
+ avio_wl16(pb, duration);
avio_w8(pb, transparent_color_index);
avio_w8(pb, 0x00);
avio_write(pb, pkt->data, pkt->size);
+ return 0;
+}
+
+static int flush_packet(AVFormatContext *s, AVPacket *new)
+{
+ int ret;
+ GIFContext *gif = s->priv_data;
+
+ if (!gif->prev_pkt)
+ return 0;
+
+ if (new && new->pts != AV_NOPTS_VALUE)
+ gif->duration = av_clip_uint16(new->pts - gif->prev_pkt->pts);
+ else if (!new && gif->last_delay >= 0)
+ gif->duration = gif->last_delay;
+
+ ret = ff_gif_write_packet(s, s->pb, gif->prev_pkt, gif->duration);
+ if (ret < 0)
+ return ret;
+
av_free_packet(gif->prev_pkt);
if (new)
av_copy_packet(gif->prev_pkt, new);
@@ -177,16 +191,19 @@ static int gif_write_packet(AVFormatContext *s, AVPacket *pkt)
return flush_packet(s, pkt);
}
+int ff_gif_write_trailer(AVIOContext *pb)
+{
+ avio_w8(pb, 0x3b);
+ return 0;
+}
+
static int gif_write_trailer(AVFormatContext *s)
{
GIFContext *gif = s->priv_data;
- AVIOContext *pb = s->pb;
flush_packet(s, NULL);
av_freep(&gif->prev_pkt);
- avio_w8(pb, 0x3b);
-
- return 0;
+ return ff_gif_write_trailer(s->pb);
}
#define OFFSET(x) offsetof(GIFContext, x)
diff --git a/libavformat/gif.h b/libavformat/gif.h
new file mode 100644
index 0000000..05f5fd6
--- /dev/null
+++ b/libavformat/gif.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_GIF_H
+#define AVFORMAT_GIF_H
+
+int ff_gif_write_header(AVCodecContext *video_enc, AVIOContext *pb, int loop);
+int ff_gif_write_packet(void *log_ctx, AVIOContext *pb, AVPacket *pkt, int duration);
+int ff_gif_write_trailer(AVIOContext *pb);
+
+#endif /* AVFORMAT_GIF_H */
diff --git a/libavformat/img2enc.c b/libavformat/img2enc.c
index 8adf352..e91d68a 100644
--- a/libavformat/img2enc.c
+++ b/libavformat/img2enc.c
@@ -21,6 +21,7 @@
*/
#include "libavutil/intreadwrite.h"
+#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
@@ -28,6 +29,7 @@
#include "avformat.h"
#include "avio_internal.h"
#include "internal.h"
+#include "gif.h"
typedef struct {
const AVClass *class; /**< Class for private options. */
@@ -54,6 +56,19 @@ static int write_header(AVFormatContext *s)
else
img->is_pipe = 1;
+ if (st->codec->codec_id == AV_CODEC_ID_GIF) {
+ if (img->is_pipe) {
+ av_log(s, AV_LOG_ERROR, "Use the 'gif' format instead of "
+ "'image2pipe' for continuous output GIF stream\n");
+ return AVERROR(EINVAL);
+ }
+ if (!CONFIG_GIF_MUXER) {
+ av_log(s, AV_LOG_ERROR, "'image2' muxer relies on the 'gif' muxer "
+ "for GIF muxing\n");
+ return AVERROR(EINVAL);
+ }
+ }
+
str = strrchr(img->path, '.');
img->split_planes = str
&& !av_strcasecmp(str + 1, "y")
@@ -104,6 +119,12 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt)
break;
filename[strlen(filename) - 1] = ((int[]){'U','V','A','x'})[i];
}
+
+ if (CONFIG_GIF_MUXER && codec->codec_id == AV_CODEC_ID_GIF) {
+ av_assert0(!img->split_planes);
+ ff_gif_write_header(codec, pb[0], -1);
+ }
+
} else {
pb[0] = s->pb;
}
@@ -124,6 +145,9 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt)
avio_write(pb[3], pkt->data + ysize + 2*usize, ysize);
avio_close(pb[3]);
}
+ } else if (CONFIG_GIF_MUXER && codec->codec_id == AV_CODEC_ID_GIF) {
+ ff_gif_write_packet(s, pb[0], pkt, 0); // XXX: we loose timing in the process
+ ff_gif_write_trailer(pb[0]);
} else {
avio_write(pb[0], pkt->data, pkt->size);
}
--
1.8.4.1
More information about the ffmpeg-devel
mailing list