[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