ext/ffmpeg/gstffmpeg.c: Enable muxers (only mp4 muxer for now).
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>
Mon, 14 Mar 2005 15:27:43 +0000 (15:27 +0000)
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>
Mon, 14 Mar 2005 15:27:43 +0000 (15:27 +0000)
Original commit message from CVS:
* ext/ffmpeg/gstffmpeg.c: (plugin_init):
Enable muxers (only mp4 muxer for now).
* ext/ffmpeg/gstffmpegcodecmap.c:
(gst_ffmpeg_formatid_get_codecids), (gst_ffmpeg_caps_to_codecid):
* ext/ffmpeg/gstffmpegcodecmap.h:
Fix a bunch of typos in codec-id lookup (false/true return value
mixup), add a codec-id list retrieval function (because ffmpeg
does not provide one). With that, we can make valid pad templates.
* ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_init),
(gst_ffmpegmux_connect), (gst_ffmpegmux_loop),
(gst_ffmpegmux_change_state), (gst_ffmpegmux_get_id_caps),
(gst_ffmpegmux_register):
Fix for whatever changed since I last tried this. Works for MP4
muxing.
* ext/ffmpeg/gstffmpegprotocol.c: (gst_ffmpegdata_open),
(gst_ffmpegdata_peek), (gst_ffmpegdata_write),
(gst_ffmpegdata_seek), (gst_ffmpegdata_close):
Update obviously-untested write code...

ChangeLog
ext/ffmpeg/gstffmpeg.c
ext/ffmpeg/gstffmpegcodecmap.c
ext/ffmpeg/gstffmpegcodecmap.h
ext/ffmpeg/gstffmpegmux.c
ext/ffmpeg/gstffmpegprotocol.c

index 0008844..9190419 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2005-03-14  Ronald S. Bultje  <rbultje@ronald.bitfreak.net>
+
+       * ext/ffmpeg/gstffmpeg.c: (plugin_init):
+         Enable muxers (only mp4 muxer for now).
+       * ext/ffmpeg/gstffmpegcodecmap.c:
+       (gst_ffmpeg_formatid_get_codecids), (gst_ffmpeg_caps_to_codecid):
+       * ext/ffmpeg/gstffmpegcodecmap.h:
+         Fix a bunch of typos in codec-id lookup (false/true return value
+         mixup), add a codec-id list retrieval function (because ffmpeg
+         does not provide one). With that, we can make valid pad templates.
+       * ext/ffmpeg/gstffmpegmux.c: (gst_ffmpegmux_init),
+       (gst_ffmpegmux_connect), (gst_ffmpegmux_loop),
+       (gst_ffmpegmux_change_state), (gst_ffmpegmux_get_id_caps),
+       (gst_ffmpegmux_register):
+         Fix for whatever changed since I last tried this. Works for MP4
+         muxing.
+       * ext/ffmpeg/gstffmpegprotocol.c: (gst_ffmpegdata_open),
+       (gst_ffmpegdata_peek), (gst_ffmpegdata_write),
+       (gst_ffmpegdata_seek), (gst_ffmpegdata_close):
+         Update obviously-untested write code...
+
 2005-03-13  Ronald S. Bultje  <rbultje@ronald.bitfreak.net>
 
        * ext/ffmpeg/gstffmpegenc.c: (gst_ffmpegenc_chain_video):
index eb3b429..16b18ff 100644 (file)
@@ -80,7 +80,7 @@ plugin_init (GstPlugin * plugin)
   gst_ffmpegenc_register (plugin);
   gst_ffmpegdec_register (plugin);
   gst_ffmpegdemux_register (plugin);
-  /*gst_ffmpegmux_register (plugin); */
+  gst_ffmpegmux_register (plugin);
   gst_ffmpegcsp_register (plugin);
 
   register_protocol (&gstreamer_protocol);
index 4c7e758..4be5dd4 100644 (file)
@@ -107,6 +107,8 @@ gst_ffmpeg_set_palette (GstCaps *caps, AVCodecContext *context)
        __VA_ARGS__, NULL)                                      \
     :                                                          \
     gst_caps_new_simple (mimetype,                             \
+        "rate", GST_TYPE_INT_RANGE, 8000, 96000,               \
+       "channels", GST_TYPE_INT_RANGE, 1, 2,                   \
        __VA_ARGS__, NULL)
 
 /* Convert a FFMPEG codec ID and optional AVCodecContext
@@ -1325,6 +1327,24 @@ gst_ffmpeg_formatid_to_caps (const gchar * format_name)
   return caps;
 }
 
+gboolean
+gst_ffmpeg_formatid_get_codecids (const gchar *format_name,
+    enum CodecID ** video_codec_list, enum CodecID ** audio_codec_list)
+{
+  if (!strcmp (format_name, "mp4")) {
+    static enum CodecID mp4_video_list[] = { CODEC_ID_MPEG4, CODEC_ID_NONE };
+    static enum CodecID mp4_audio_list[] = { CODEC_ID_AAC, CODEC_ID_NONE };
+
+    *video_codec_list = mp4_video_list;
+    *audio_codec_list = mp4_audio_list;
+  } else {
+    GST_WARNING ("Format %s not found", format_name);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 /* Convert a GstCaps to a FFMPEG codec ID. Size et all
  * are omitted, that can be queried by the user itself,
  * we're not eating the GstCaps or anything
@@ -1398,7 +1418,7 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
   } else if (!strcmp (mimetype, "video/x-dv")) {
     gboolean sys_strm;
 
-    if (!gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
+    if (gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
         !sys_strm) {
       id = CODEC_ID_DVVIDEO;
       video = TRUE;
@@ -1419,8 +1439,8 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
     gboolean sys_strm;
     gint mpegversion;
 
-    if (!gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
-        !gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
+    if (gst_structure_get_boolean (structure, "systemstream", &sys_strm) &&
+        gst_structure_get_int (structure, "mpegversion", &mpegversion) &&
         !sys_strm) {
       switch (mpegversion) {
         case 1:
@@ -1468,7 +1488,7 @@ gst_ffmpeg_caps_to_codecid (const GstCaps * caps, AVCodecContext * context)
       switch (mpegversion) {
         case 2:                /* ffmpeg uses faad for both... */
         case 4:
-          id = CODEC_ID_MPEG4AAC;
+          id = CODEC_ID_AAC;
           break;
         case 1:
           if (gst_structure_get_int (structure, "layer", &layer)) {
index 7226bda..4ba8287 100644 (file)
@@ -88,6 +88,17 @@ GstCaps *
 gst_ffmpeg_formatid_to_caps (const gchar *format_name);
 
 /*
+ * _formatid_get_codecids () can be used to get the codecIDs
+ * (CODEC_ID_NONE-terminated list) that fit that specific
+ * output format.
+ */
+
+gboolean
+gst_ffmpeg_formatid_get_codecids (const gchar *format_name,
+                                 enum CodecID ** video_codec_list,
+                                 enum CodecID ** audio_codec_list);
+
+/*
  * Since FFMpeg has such really cool and useful descriptions
  * of its codecs, we use our own...
  */
@@ -118,7 +129,8 @@ gst_ffmpeg_avpicture_fill (AVPicture * picture,
  */
 int
 gst_ffmpeg_img_convert (AVPicture * dst, int dst_pix_fmt,
-    const AVPicture * src, int src_pix_fmt, int src_width, int src_height);
+                       const AVPicture * src, int src_pix_fmt,
+                       int src_width, int src_height);
 
 #endif /* __GST_FFMPEG_CODECMAP_H__ */
 
index ae858f2..e8320cf 100644 (file)
@@ -45,6 +45,8 @@ struct _GstFFMpegMux
   AVFormatContext *context;
   gboolean opened;
 
+  GstTagList *tags;
+
   GstPad *sinkpads[MAX_STREAMS];
   gint videopads, audiopads;
   GstBuffer *bufferqueue[MAX_STREAMS];
@@ -189,6 +191,8 @@ gst_ffmpegmux_init (GstFFMpegMux * ffmpegmux)
 
   ffmpegmux->videopads = 0;
   ffmpegmux->audiopads = 0;
+
+  ffmpegmux->tags = NULL;
 }
 
 static void
@@ -269,7 +273,6 @@ gst_ffmpegmux_connect (GstPad * pad, const GstCaps * caps)
 
   /*g_return_val_if_fail (ffmpegmux->opened == FALSE,
      GST_PAD_LINK_REFUSED); */
-
   for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
     if (pad == ffmpegmux->sinkpads[i]) {
       break;
@@ -318,6 +321,16 @@ gst_ffmpegmux_loop (GstElement * element)
             ffmpegmux->eos[i] = TRUE;
             gst_event_unref (event);
             break;
+          case GST_EVENT_TAG:
+            if (ffmpegmux->tags) {
+              gst_tag_list_insert (ffmpegmux->tags,
+                  gst_event_tag_get_list (event), GST_TAG_MERGE_PREPEND);
+            } else {
+              ffmpegmux->tags =
+                  gst_tag_list_copy (gst_event_tag_get_list (event));
+            }
+            gst_event_unref (event);
+            break;
           default:
             gst_pad_event_default (pad, event);
             break;
@@ -330,6 +343,8 @@ gst_ffmpegmux_loop (GstElement * element)
 
   /* open "file" (gstreamer protocol to next element) */
   if (!ffmpegmux->opened) {
+    const GstTagList *iface_tags;
+
     /* we do need all streams to have started capsnego,
      * or things will go horribly wrong */
     for (i = 0; i < ffmpegmux->context->nb_streams; i++) {
@@ -343,6 +358,58 @@ gst_ffmpegmux_loop (GstElement * element)
                 "video" : "audio"));
         return;
       }
+      if (st->codec.codec_type == CODEC_TYPE_AUDIO) {
+        st->codec.frame_size =
+            st->codec.sample_rate *
+            GST_BUFFER_DURATION (ffmpegmux->bufferqueue[i]) / GST_SECOND;
+      }
+    }
+
+    /* tags */
+    iface_tags = gst_tag_setter_get_list (GST_TAG_SETTER (ffmpegmux));
+    if (ffmpegmux->tags || iface_tags) {
+      GstTagList *tags;
+      gint i;
+      gchar *s;
+
+      if (iface_tags && ffmpegmux->tags) {
+        gst_tag_list_merge (iface_tags, ffmpegmux->tags,
+            GST_TAG_MERGE_APPEND);
+      } else if (iface_tags) {
+        tags = gst_tag_list_copy (iface_tags);
+      } else {
+        tags = gst_tag_list_copy (ffmpegmux->tags);
+      }
+
+      /* get the interesting ones */
+      if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
+        strncpy (ffmpegmux->context->title, s,
+            sizeof (ffmpegmux->context->title));
+      }
+      if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
+        strncpy (ffmpegmux->context->author, s,
+            sizeof (ffmpegmux->context->author));
+      }
+      if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
+        strncpy (ffmpegmux->context->copyright, s,
+            sizeof (ffmpegmux->context->copyright));
+      }
+      if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
+        strncpy (ffmpegmux->context->comment, s,
+            sizeof (ffmpegmux->context->comment));
+      }
+      if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
+        strncpy (ffmpegmux->context->album, s,
+            sizeof (ffmpegmux->context->album));
+      }
+      if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
+        strncpy (ffmpegmux->context->genre, s,
+            sizeof (ffmpegmux->context->genre));
+      }
+      if (gst_tag_list_get_uint (tags, GST_TAG_TRACK_NUMBER, &i)) {
+        ffmpegmux->context->track = i;
+      }
+      gst_tag_list_free (tags);
     }
 
     if (url_fopen (&ffmpegmux->context->pb,
@@ -352,7 +419,7 @@ gst_ffmpegmux_loop (GstElement * element)
       return;
     }
 
-    if (av_set_parameters (ffmpegmux->context, NULL)) {
+    if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
       GST_ELEMENT_ERROR (element, LIBRARY, INIT, (NULL),
           ("Failed to initialize muxer"));
       return;
@@ -362,7 +429,11 @@ gst_ffmpegmux_loop (GstElement * element)
     ffmpegmux->opened = TRUE;
 
     /* now open the mux format */
-    av_write_header (ffmpegmux->context);
+    if (av_write_header (ffmpegmux->context) < 0) {
+      GST_ELEMENT_ERROR (element, LIBRARY, SETTINGS, (NULL),
+          ("Failed to write file header - check codec settings"));
+      return;
+    }
   }
 
   /* take the one with earliest timestamp,
@@ -401,6 +472,7 @@ gst_ffmpegmux_loop (GstElement * element)
 
     /* set time */
     pkt.pts = GST_BUFFER_TIMESTAMP (buf) * AV_TIME_BASE / GST_SECOND;
+    pkt.dts = pkt.pts;
     pkt.data = GST_BUFFER_DATA (buf);
     pkt.size = GST_BUFFER_SIZE (buf);
     pkt.stream_index = bufnum;
@@ -430,6 +502,10 @@ gst_ffmpegmux_change_state (GstElement * element)
 
   switch (transition) {
     case GST_STATE_PAUSED_TO_READY:
+      if (ffmpegmux->tags) {
+        gst_tag_list_free (ffmpegmux->tags);
+        ffmpegmux->tags = NULL;
+      }
       if (ffmpegmux->opened) {
         url_fclose (&ffmpegmux->context->pb);
         ffmpegmux->opened = FALSE;
@@ -443,6 +519,20 @@ gst_ffmpegmux_change_state (GstElement * element)
   return GST_STATE_SUCCESS;
 }
 
+GstCaps *
+gst_ffmpegmux_get_id_caps (enum CodecID * id_list)
+{
+  GstCaps *caps, *t;
+  gint i;
+
+  caps = gst_caps_new_empty ();
+  for (i = 0; id_list[i] != CODEC_ID_NONE; i++) {
+    if ((t = gst_ffmpeg_codecid_to_caps (id_list[i], NULL, TRUE)))
+      gst_caps_append (caps, t);
+  }
+
+  return caps;
+}
 
 gboolean
 gst_ffmpegmux_register (GstPlugin * plugin)
@@ -458,6 +548,9 @@ gst_ffmpegmux_register (GstPlugin * plugin)
     0,
     (GInstanceInitFunc) gst_ffmpegmux_init,
   };
+  static const GInterfaceInfo tag_setter_info = {
+      NULL, NULL, NULL
+  };
   GType type;
   AVOutputFormat *in_plugin;
   GstFFMpegMuxClassParams *params;
@@ -471,35 +564,20 @@ gst_ffmpegmux_register (GstPlugin * plugin)
     gchar *type_name;
     gchar *p;
     GstCaps *srccaps, *audiosinkcaps, *videosinkcaps;
+    enum CodecID *video_ids = NULL, *audio_ids = NULL;
 
     /* Try to find the caps that belongs here */
     srccaps = gst_ffmpeg_formatid_to_caps (in_plugin->name);
     if (!srccaps) {
       goto next;
     }
-    /* This is a bit ugly, but we just take all formats
-     * for the pad template. We'll get an exact match
-     * when we open the stream */
-    audiosinkcaps = gst_caps_new_empty ();
-    videosinkcaps = gst_caps_new_empty ();
-    for (in_codec = first_avcodec; in_codec != NULL; in_codec = in_codec->next) {
-      GstCaps *temp = gst_ffmpeg_codecid_to_caps (in_codec->id, NULL, TRUE);
-
-      if (!temp) {
-        continue;
-      }
-      switch (in_codec->type) {
-        case CODEC_TYPE_VIDEO:
-          gst_caps_append (videosinkcaps, temp);
-          break;
-        case CODEC_TYPE_AUDIO:
-          gst_caps_append (audiosinkcaps, temp);
-          break;
-        default:
-          gst_caps_free (temp);
-          break;
-      }
+    if (!gst_ffmpeg_formatid_get_codecids (in_plugin->name,
+             &video_ids, &audio_ids)) {
+      gst_caps_free (srccaps);
+      goto next;
     }
+    videosinkcaps = video_ids ? gst_ffmpegmux_get_id_caps (video_ids) : NULL;
+    audiosinkcaps = audio_ids ? gst_ffmpegmux_get_id_caps (audio_ids) : NULL;
 
     /* construct the type */
     type_name = g_strdup_printf ("ffmux_%s", in_plugin->name);
@@ -515,6 +593,11 @@ gst_ffmpegmux_register (GstPlugin * plugin)
     /* if it's already registered, drop it */
     if (g_type_from_name (type_name)) {
       g_free (type_name);
+      gst_caps_free (srccaps);
+      if (audiosinkcaps)
+        gst_caps_free (audiosinkcaps);
+      if (videosinkcaps)
+        gst_caps_free (videosinkcaps);
       goto next;
     }
 
@@ -530,8 +613,15 @@ gst_ffmpegmux_register (GstPlugin * plugin)
 
     /* create the type now */
     type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
+    g_type_add_interface_static (type, GST_TYPE_TAG_SETTER,
+        &tag_setter_info);
     if (!gst_element_register (plugin, type_name, GST_RANK_NONE, type)) {
       g_free (type_name);
+      gst_caps_free (srccaps);
+      if (audiosinkcaps)
+        gst_caps_free (audiosinkcaps);
+      if (videosinkcaps)
+        gst_caps_free (videosinkcaps);
       return FALSE;
     }
 
index 69a6aea..2772b53 100644 (file)
@@ -39,7 +39,6 @@ struct _GstProtocolInfo
 {
   GstPad *pad;
 
-  int flags;
   GstByteStream *bs;
   gboolean eos;
 };
@@ -53,7 +52,6 @@ gst_ffmpegdata_open (URLContext * h, const char *filename, int flags)
   GST_LOG ("Opening %s", filename);
 
   info = g_new0 (GstProtocolInfo, 1);
-  info->flags = flags;
 
   /* we don't support R/W together */
   if (flags != URL_RDONLY && flags != URL_WRONLY) {
@@ -85,7 +83,6 @@ gst_ffmpegdata_open (URLContext * h, const char *filename, int flags)
 
   h->priv_data = (void *) info;
   h->is_streamed = FALSE;
-  h->flags = 0;
   h->max_packet_size = 0;
 
   return 0;
@@ -102,7 +99,7 @@ gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size)
 
   info = (GstProtocolInfo *) h->priv_data;
 
-  g_return_val_if_fail (info->flags == URL_RDONLY, AVERROR_IO);
+  g_return_val_if_fail (h->flags == URL_RDONLY, AVERROR_IO);
 
   bs = info->bs;
 
@@ -202,7 +199,7 @@ gst_ffmpegdata_write (URLContext * h, unsigned char *buf, int size)
 
   info = (GstProtocolInfo *) h->priv_data;
 
-  g_return_val_if_fail (info->flags == URL_WRONLY, -EIO);
+  g_return_val_if_fail (h->flags == URL_WRONLY, -EIO);
 
   /* create buffer and push data further */
   outbuf = gst_buffer_new_and_alloc (size);
@@ -225,20 +222,22 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
 
   info = (GstProtocolInfo *) h->priv_data;
 
-  /* get data (typefind hack) */
-  if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
-    gchar buf;
-    gst_ffmpegdata_peek (h, &buf, 1);
-  }
+  if (h->flags == URL_RDONLY) {
+    /* get data (typefind hack) */
+    if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
+      gchar buf;
+      gst_ffmpegdata_peek (h, &buf, 1);
+    }
 
-  /* hack in ffmpeg to get filesize... */
-  if (whence == SEEK_END && pos == -1)
-    return gst_bytestream_length (info->bs) - 1;
-  else if (whence == SEEK_END && pos == 0)
-    return gst_bytestream_length (info->bs);
-  /* another hack to get the current position... */
-  else if (whence == SEEK_CUR && pos == 0)
-    return gst_bytestream_tell (info->bs);
+    /* hack in ffmpeg to get filesize... */
+    if (whence == SEEK_END && pos == -1)
+      return gst_bytestream_length (info->bs) - 1;
+    else if (whence == SEEK_END && pos == 0)
+      return gst_bytestream_length (info->bs);
+    /* another hack to get the current position... */
+    else if (whence == SEEK_CUR && pos == 0)
+      return gst_bytestream_tell (info->bs);
+  }
 
   switch (whence) {
     case SEEK_SET:
@@ -255,7 +254,7 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
       break;
   }
 
-  switch (info->flags) {
+  switch (h->flags) {
     case URL_RDONLY: {
       GstEvent *event;
       guint8 *data;
@@ -303,7 +302,8 @@ gst_ffmpegdata_seek (URLContext * h, offset_t pos, int whence)
     }
 
     case URL_WRONLY:
-      gst_pad_push (info->pad, GST_DATA (gst_event_new_seek (seek_type, pos)));
+      gst_pad_push (info->pad,
+          GST_DATA (gst_event_new_seek (seek_type | GST_FORMAT_BYTES, pos)));
       /* this is screwy because there might be queues or scheduler-queued
        * buffers... Argh! */
       if (whence == SEEK_SET) {
@@ -331,7 +331,7 @@ gst_ffmpegdata_close (URLContext * h)
 
   GST_LOG ("Closing file");
 
-  switch (info->flags) {
+  switch (h->flags) {
     case URL_WRONLY:{
       /* send EOS - that closes down the stream */
       GstEvent *event = gst_event_new (GST_EVENT_EOS);