ext/ffmpeg/: Add GIF (animations and single images) decoding and encoding support.
authorSebastian Dröge <slomo@circular-chaos.org>
Mon, 17 Dec 2007 20:31:35 +0000 (20:31 +0000)
committerSebastian Dröge <slomo@circular-chaos.org>
Mon, 17 Dec 2007 20:31:35 +0000 (20:31 +0000)
Original commit message from CVS:
* ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_codecid_to_caps),
(gst_ffmpeg_formatid_get_codecids),
(gst_ffmpeg_get_codecid_longname):
* ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_loop),
(gst_ffmpegdemux_register):
* ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_collected),
(gst_ffmpegmux_register):
Add GIF (animations and single images) decoding and encoding support.
Fixes #503249.

ChangeLog
common
ext/ffmpeg/gstffmpegcodecmap.c
ext/ffmpeg/gstffmpegdemux.c
ext/ffmpeg/gstffmpegmux.c

index 82e20f3..0823514 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-12-17  Sebastian Dröge  <slomo@circular-chaos.org>
+
+       * ext/ffmpeg/gstffmpegcodecmap.c: (gst_ffmpeg_codecid_to_caps),
+       (gst_ffmpeg_formatid_get_codecids),
+       (gst_ffmpeg_get_codecid_longname):
+       * ext/ffmpeg/gstffmpegdemux.c: (gst_ffmpegdemux_loop),
+       (gst_ffmpegdemux_register):
+       * ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_collected),
+       (gst_ffmpegmux_register):
+       Add GIF (animations and single images) decoding and encoding support.
+       Fixes #503249.
+
 2007-12-17  Edward Hervey  <edward.hervey@collabora.co.uk>
 
        * configure.ac:
diff --git a/common b/common
index a00d4c1..208ef72 160000 (submodule)
--- a/common
+++ b/common
@@ -1 +1 @@
-Subproject commit a00d4c1966aab517c2694c61d580489ebcbce448
+Subproject commit 208ef72f86e944e6ba6941c68e57ffcea8d2a8f4
index abb7832..1a79acb 100644 (file)
@@ -636,8 +636,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
     case CODEC_ID_VC1:
       caps = gst_ff_vid_caps_new (context, codec_id, "video/x-wmv",
-                                 "wmvversion", G_TYPE_INT, 3, "fourcc", GST_TYPE_FOURCC, 
-                                 GST_MAKE_FOURCC('W', 'V', 'C', '1'), NULL);
+          "wmvversion", G_TYPE_INT, 3, "fourcc", GST_TYPE_FOURCC,
+          GST_MAKE_FOURCC ('W', 'V', 'C', '1'), NULL);
       break;
     case CODEC_ID_QDM2:
       caps = gst_ff_aud_caps_new (context, codec_id, "audio/x-qdm2", NULL);
@@ -683,6 +683,10 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       caps = gst_ff_vid_caps_new (context, codec_id, "video/x-nuv", NULL);
       break;
 
+    case CODEC_ID_GIF:
+      caps = gst_ff_vid_caps_new (context, codec_id, "image/gif", NULL);
+      break;
+
     case CODEC_ID_PNG:
       caps = gst_ff_vid_caps_new (context, codec_id, "image/png", NULL);
       break;
@@ -1914,6 +1918,12 @@ gst_ffmpeg_formatid_get_codecids (const gchar * format_name,
     };
     *video_codec_list = NULL;
     *audio_codec_list = amr_audio_list;
+  } else if (!strcmp (format_name, "gif")) {
+    static enum CodecID gif_image_list[] = {
+      CODEC_ID_RAWVIDEO, CODEC_ID_NONE
+    };
+    *video_codec_list = gif_image_list;
+    *audio_codec_list = NULL;
   } else {
     GST_LOG ("Format %s not found", format_name);
     return FALSE;
@@ -2055,14 +2065,15 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
           id = CODEC_ID_WMV2;
           break;
         case 3:
-         {
-           guint32 fourcc;
-           if (gst_structure_get_fourcc (structure, "fourcc", &fourcc)) {
-             if (fourcc == GST_MAKE_FOURCC ('W', 'V', 'C', '1'))
-               id = CODEC_ID_VC1;
-           } else
-             id = CODEC_ID_WMV3;
-         }
+        {
+          guint32 fourcc;
+
+          if (gst_structure_get_fourcc (structure, "fourcc", &fourcc)) {
+            if (fourcc == GST_MAKE_FOURCC ('W', 'V', 'C', '1'))
+              id = CODEC_ID_VC1;
+          } else
+            id = CODEC_ID_WMV3;
+        }
           break;
       }
     }
@@ -2726,6 +2737,9 @@ gst_ffmpeg_get_codecid_longname (enum CodecID codec_id)
     case CODEC_ID_XVID:
       name = "XviD video";
       break;
+    case CODEC_ID_GIF:
+      name = "GIF image";
+      break;
     case CODEC_ID_PNG:
       name = "PNG image";
       break;
index 30872b7..2bdac03 100644 (file)
@@ -1162,6 +1162,8 @@ gst_ffmpegdemux_loop (GstPad * pad)
   AVStream *avstream;
   GstBuffer *outbuf;
   GstClockTime timestamp, duration;
+  gint outsize;
+  gboolean rawvideo;
 
   demux = (GstFFMpegDemux *) (GST_PAD_PARENT (pad));
 
@@ -1222,16 +1224,52 @@ gst_ffmpegdemux_loop (GstPad * pad)
   /* prepare to push packet to peer */
   srcpad = stream->pad;
 
+  rawvideo = (avstream->codec->codec_type == CODEC_TYPE_VIDEO &&
+      avstream->codec->codec_id == CODEC_ID_RAWVIDEO);
+
+  if (rawvideo)
+    outsize = gst_ffmpeg_avpicture_get_size (avstream->codec->pix_fmt,
+        avstream->codec->width, avstream->codec->height);
+  else
+    outsize = pkt.size;
+
   ret = gst_pad_alloc_buffer_and_set_caps (srcpad,
-      GST_CLOCK_TIME_NONE, pkt.size, GST_PAD_CAPS (srcpad), &outbuf);
+      GST_CLOCK_TIME_NONE, outsize, GST_PAD_CAPS (srcpad), &outbuf);
   /* we can ignore not linked */
   if (ret == GST_FLOW_NOT_LINKED)
     goto done;
   if (ret != GST_FLOW_OK)
     goto no_buffer;
 
-  /* copy the data from packet into the target buffer */
-  memcpy (GST_BUFFER_DATA (outbuf), pkt.data, pkt.size);
+  /* copy the data from packet into the target buffer
+   * and do conversions for raw video packets */
+  if (rawvideo) {
+    AVPicture src, dst;
+    const gchar *plugin_name =
+        ((GstFFMpegDemuxClass *) (G_OBJECT_GET_CLASS (demux)))->in_plugin->name;
+
+    if (strcmp (plugin_name, "gif") == 0) {
+      src.data[0] = pkt.data;
+      src.data[1] = NULL;
+      src.data[2] = NULL;
+      src.linesize[0] = avstream->codec->width * 3;;
+    } else {
+      GST_WARNING ("Unknown demuxer %s, no idea what to do", plugin_name);
+      gst_ffmpeg_avpicture_fill (&src, pkt.data,
+          avstream->codec->pix_fmt, avstream->codec->width,
+          avstream->codec->height);
+    }
+
+    gst_ffmpeg_avpicture_fill (&dst, GST_BUFFER_DATA (outbuf),
+        avstream->codec->pix_fmt, avstream->codec->width,
+        avstream->codec->height);
+
+    gst_ffmpeg_img_convert (&dst, avstream->codec->pix_fmt,
+        &src, avstream->codec->pix_fmt, avstream->codec->width,
+        avstream->codec->height);
+  } else {
+    memcpy (GST_BUFFER_DATA (outbuf), pkt.data, outsize);
+  }
 
   GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
   GST_BUFFER_DURATION (outbuf) = duration;
@@ -1498,7 +1536,8 @@ gst_ffmpegdemux_register (GstPlugin * plugin)
         !strcmp (in_plugin->name, "wav") ||
         !strcmp (in_plugin->name, "au") ||
         !strcmp (in_plugin->name, "tta") ||
-        !strcmp (in_plugin->name, "rm") || !strcmp (in_plugin->name, "amr"))
+        !strcmp (in_plugin->name, "rm") ||
+        !strcmp (in_plugin->name, "amr") || !strcmp (in_plugin->name, "gif"))
       register_typefind_func = FALSE;
 
     /* Set the rank of demuxers know to work to MARGINAL.
@@ -1533,7 +1572,8 @@ gst_ffmpegdemux_register (GstPlugin * plugin)
         !strcmp (in_plugin->name, "avs") ||
         !strcmp (in_plugin->name, "aiff") ||
         !strcmp (in_plugin->name, "4xm") ||
-        !strcmp (in_plugin->name, "yuv4mpegpipe"))
+        !strcmp (in_plugin->name, "yuv4mpegpipe") ||
+        !strcmp (in_plugin->name, "gif"))
       rank = GST_RANK_MARGINAL;
     else
       rank = GST_RANK_NONE;
index 3962d0a..3c0efdc 100644 (file)
@@ -525,6 +525,7 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
   if (best_pad != NULL) {
     GstBuffer *buf;
     AVPacket pkt;
+    gboolean need_free = FALSE;
 
     /* push out current buffer */
     buf = gst_collect_pads_pop (ffmpegmux->collect,
@@ -536,8 +537,30 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
     pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
         ffmpegmux->context->streams[best_pad->padnum]->time_base);
     pkt.dts = pkt.pts;
-    pkt.data = GST_BUFFER_DATA (buf);
-    pkt.size = GST_BUFFER_SIZE (buf);
+
+    if (strcmp (ffmpegmux->context->oformat->name, "gif") == 0) {
+      AVStream *st = ffmpegmux->context->streams[best_pad->padnum];
+      AVPicture src, dst;
+
+      need_free = TRUE;
+      pkt.size = st->codec->width * st->codec->height * 3;
+      pkt.data = g_malloc (pkt.size);
+
+      dst.data[0] = pkt.data;
+      dst.data[1] = NULL;
+      dst.data[2] = NULL;
+      dst.linesize[0] = st->codec->width * 3;
+
+      gst_ffmpeg_avpicture_fill (&src, GST_BUFFER_DATA (buf),
+          PIX_FMT_RGB24, st->codec->width, st->codec->height);
+
+      gst_ffmpeg_img_convert (&dst, PIX_FMT_RGB24,
+          &src, PIX_FMT_RGB24, st->codec->width, st->codec->height);
+    } else {
+      pkt.data = GST_BUFFER_DATA (buf);
+      pkt.size = GST_BUFFER_SIZE (buf);
+    }
+
     pkt.stream_index = best_pad->padnum;
     pkt.flags = 0;
 
@@ -552,6 +575,8 @@ gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
       pkt.duration = 0;
     av_write_frame (ffmpegmux->context, &pkt);
     gst_buffer_unref (buf);
+    if (need_free)
+      g_free (pkt.data);
   } else {
     /* close down */
     av_write_trailer (ffmpegmux->context);
@@ -725,6 +750,12 @@ gst_ffmpegmux_register (GstPlugin * plugin)
       const gint rates[] = { 44100, 22050, 11025 };
 
       gst_ffmpeg_mux_simple_caps_set_int_list (audiosinkcaps, "rate", 3, rates);
+    } else if (strcmp (in_plugin->name, "gif") == 0) {
+      if (videosinkcaps)
+        gst_caps_unref (videosinkcaps);
+
+      videosinkcaps =
+          gst_caps_from_string ("video/x-raw-rgb, bpp=(int)24, depth=(int)24");
     }
 
     /* create a cache for these properties */