gst-gmyth/concatmux/gstconcatmux.c
author renatofilho
Tue May 01 16:31:26 2007 +0100 (2007-05-01)
branchtrunk
changeset 609 4aac30345986
child 611 aa1f309ec686
permissions -rw-r--r--
[svn r615] added concat element
     1 /* concat muxer plugin for GStreamer
     2  * Copyright (C) 2004 Renato Filhps <renato.filho@indt.org.br>
     3  *
     4  * This library is free software; you can redistribute it and/or
     5  * modify it under the terms of the GNU Library General Public
     6  * License as published by the Free Software Foundation; either
     7  * version 2 of the License, or (at your option) any later version.
     8  *
     9  * This library is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    12  * Library General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU Library General Public
    15  * License along with this library; if not, write to the
    16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    17  * Boston, MA 02111-1307, USA.
    18  */
    19 
    20 /**
    21  * SECTION:element-concatmux
    22  * @short_description: Concat that takes one or several digital streams
    23  * and muxes them to a single stream.
    24  *
    25  * <refsect2>
    26  * <title>Sample pipelines</title>
    27  * <para>
    28  * Here is a simple pipeline to concat 2 files into a another file:
    29  * <programlisting>
    30  * gst-launch concatmux name=m ! filesink location=output.txt  filesrc location=file_a.txt !  m. filesrc location=file_b.txt ! m.
    31  * </programlisting>
    32  * </para>
    33  * </refsect2>
    34  */
    35 
    36 #ifdef HAVE_CONFIG_H
    37 #include "config.h"
    38 #endif
    39 
    40 #include <gst/gst.h>
    41 #include <gst/base/gstcollectpads.h>
    42 
    43 #include <string.h>
    44 
    45 GST_DEBUG_CATEGORY_STATIC (gst_concat_mux_debug);
    46 #define GST_CAT_DEFAULT gst_concat_mux_debug
    47 
    48 #define GST_TYPE_CONCAT_MUX (gst_concat_mux_get_type())
    49 #define GST_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONCAT_MUX, GstConcatMux))
    50 #define GST_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONCAT_MUX, GstConcatMux))
    51 #define GST_IS_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONCAT_MUX))
    52 #define GST_IS_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONCAT_MUX))
    53 
    54 typedef struct _GstConcatMux GstConcatMux;
    55 typedef struct _GstConcatMuxClass GstConcatMuxClass;
    56 
    57 /**
    58  * GstConcatMux:
    59  *
    60  * The opaque #GstConcatMux structure.
    61  */
    62 struct _GstConcatMux
    63 {
    64   GstElement element;
    65 
    66   /* Caps */
    67   GstCaps *sink_caps;
    68 
    69   /* pad */
    70   GstPad *srcpad;
    71   GstPad *sinkpad;
    72 
    73   /* sinkpads */
    74   GSList *sinks;
    75   gint numpads;
    76 
    77   /* offset in stream */
    78   guint64 offset;
    79   guint64 timeoffset;
    80   guint64 start_time;
    81 
    82   gboolean negotiated;
    83   gboolean resync;
    84   gboolean done;
    85 };
    86 
    87 struct _GstConcatMuxClass
    88 {
    89   GstElementClass parent_class;
    90 };
    91 
    92 /* elementfactory information */
    93 static const GstElementDetails gst_concat_mux_details =
    94 GST_ELEMENT_DETAILS ("Concat muxer",
    95     "Codec/Muxer",
    96     "mux concat streams",
    97     "Renato Filho <renato.filho@indt.org>");
    98 
    99 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
   100     GST_PAD_SRC,
   101     GST_PAD_ALWAYS,
   102     GST_STATIC_CAPS_ANY
   103     );
   104 
   105 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
   106     GST_PAD_SINK,
   107     GST_PAD_REQUEST,
   108     GST_STATIC_CAPS_ANY         
   109     );
   110 
   111 static void gst_concat_mux_base_init (gpointer g_class);
   112 static void gst_concat_mux_class_init (GstConcatMuxClass * klass);
   113 static void gst_concat_mux_init (GstConcatMux * concat_mux);
   114 
   115 static void gst_concat_mux_finalize (GObject * object);
   116 
   117 static gboolean gst_concat_mux_handle_src_event (GstPad * pad,
   118     GstEvent * event);
   119 static gboolean gst_concat_mux_handle_sink_event (GstPad * pad,
   120     GstEvent * event);
   121 
   122 static GstPad *gst_concat_mux_request_new_pad (GstElement * element,
   123     GstPadTemplate * templ, const gchar * name);
   124 static GstStateChangeReturn gst_concat_mux_change_state (GstElement *
   125     element, GstStateChange transition);
   126 
   127 static GstFlowReturn gst_concat_mux_chain (GstPad * pad,  GstBuffer * buf);
   128 static void gst_concat_mux_clear (GstConcatMux *mux);
   129 
   130 
   131 static GstElementClass *parent_class = NULL;
   132 
   133 GType
   134 gst_concat_mux_get_type (void)
   135 {
   136   static GType concat_mux_type = 0;
   137 
   138   if (!concat_mux_type) {
   139     static const GTypeInfo concat_mux_info = {
   140       sizeof (GstConcatMuxClass),
   141       gst_concat_mux_base_init,
   142       NULL,
   143       (GClassInitFunc) gst_concat_mux_class_init,
   144       NULL,
   145       NULL,
   146       sizeof (GstConcatMux),
   147       0,
   148       (GInstanceInitFunc) gst_concat_mux_init,
   149     };
   150 
   151     concat_mux_type =
   152         g_type_register_static (GST_TYPE_ELEMENT, "GstConcatMux",
   153         &concat_mux_info, 0);
   154   }
   155   return concat_mux_type;
   156 }
   157 
   158 static void
   159 gst_concat_mux_base_init (gpointer g_class)
   160 {
   161   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   162 
   163   gst_element_class_add_pad_template (element_class,
   164       gst_static_pad_template_get (&src_factory));
   165   gst_element_class_add_pad_template (element_class,
   166       gst_static_pad_template_get (&sink_factory));
   167 
   168   gst_element_class_set_details (element_class, &gst_concat_mux_details);
   169 }
   170 
   171 static void
   172 gst_concat_mux_class_init (GstConcatMuxClass * klass)
   173 {
   174   GObjectClass *gobject_class;
   175   GstElementClass *gstelement_class;
   176 
   177   gobject_class = (GObjectClass *) klass;
   178   gstelement_class = (GstElementClass *) klass;
   179 
   180   parent_class = g_type_class_peek_parent (klass);
   181 
   182   gobject_class->finalize = gst_concat_mux_finalize;
   183 
   184   gstelement_class->request_new_pad = gst_concat_mux_request_new_pad;
   185   gstelement_class->change_state = gst_concat_mux_change_state;
   186 }
   187 
   188 static void
   189 gst_concat_mux_init (GstConcatMux * concat_mux)
   190 {
   191   GstElementClass *klass = GST_ELEMENT_GET_CLASS (concat_mux);
   192 
   193   concat_mux->srcpad =
   194       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
   195           "src"), "src");
   196   gst_pad_set_event_function (concat_mux->srcpad,
   197       gst_concat_mux_handle_src_event);
   198   gst_element_add_pad (GST_ELEMENT (concat_mux), concat_mux->srcpad);
   199 }
   200 
   201 static void
   202 gst_concat_mux_finalize (GObject * object)
   203 {
   204   GstConcatMux *concat_mux;
   205 
   206   concat_mux = GST_CONCAT_MUX (object);
   207   gst_concat_mux_clear (GST_CONCAT_MUX (object));
   208 
   209   G_OBJECT_CLASS (parent_class)->finalize (object);
   210 }
   211 
   212 static void
   213 gst_concat_mux_free_pad (gpointer data,
   214                             gpointer user_data)
   215 {
   216    GMutex *mux;
   217 
   218    mux = gst_pad_get_element_private (GST_PAD (data));
   219    g_mutex_unlock (mux);
   220    g_mutex_free (mux);
   221    gst_object_unref (GST_OBJECT (data)); 
   222 }
   223 
   224 static void
   225 gst_concat_mux_clear (GstConcatMux *mux)
   226 {
   227   mux->resync = TRUE;
   228   mux->timeoffset = 0;
   229   mux->offset = 0;
   230   mux->negotiated = FALSE;
   231   mux->done = TRUE;
   232   if (mux->sinks != NULL) {
   233     g_slist_foreach (mux->sinks, gst_concat_mux_free_pad, mux);
   234     g_slist_free (mux->sinks);
   235     mux->sinks = NULL;
   236   }
   237 }
   238 
   239 
   240 static GstPadLinkReturn
   241 gst_concat_mux_sinkconnect (GstPad * pad, GstPad * peer)
   242 {
   243   gchar *pad_name = NULL;
   244   GstConcatMux *concat_mux;
   245 
   246   concat_mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
   247 
   248   if (concat_mux->sink_caps != NULL) {
   249       GstCaps *peer_caps = gst_pad_get_caps (peer);
   250       GstCaps *intersect;
   251 
   252       intersect =  gst_caps_intersect (concat_mux->sink_caps, peer_caps);
   253       if (intersect == NULL) {
   254           gst_caps_unref (peer_caps);
   255           return GST_PAD_LINK_NOFORMAT;
   256       }
   257       gst_caps_unref (peer_caps);
   258       gst_caps_unref (intersect);
   259   } else {
   260       concat_mux->sink_caps = gst_pad_get_caps (pad);
   261   }
   262 
   263   pad_name = gst_pad_get_name (pad);
   264 
   265   GST_DEBUG_OBJECT (concat_mux, "sinkconnect triggered on %s", pad_name);
   266 
   267   g_free (pad_name);
   268 
   269   gst_object_unref (concat_mux);
   270 
   271   return GST_PAD_LINK_OK;
   272 }
   273 
   274 static GstPad *
   275 gst_concat_mux_request_new_pad (GstElement * element,
   276     GstPadTemplate * templ, const gchar * req_name)
   277 {
   278   GstConcatMux *concat_mux;
   279   GstPad *newpad;
   280   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
   281   GMutex *mutex;
   282 
   283   g_return_val_if_fail (templ != NULL, NULL);
   284 
   285   if (templ->direction != GST_PAD_SINK) {
   286     g_warning ("concat_mux: request pad that is not a SINK pad\n");
   287     return NULL;
   288   }
   289 
   290   g_return_val_if_fail (GST_IS_CONCAT_MUX (element), NULL);
   291 
   292   concat_mux = GST_CONCAT_MUX (element);
   293 
   294   if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
   295     gchar *name;
   296 
   297     /* create new pad with the name */
   298     name = g_strdup_printf ("sink_%02d", concat_mux->numpads);
   299     g_debug ("NEw pad %s", name);
   300     newpad = gst_pad_new_from_template (templ, name);
   301     g_free (name);
   302     concat_mux->sinks = g_slist_append (concat_mux->sinks, newpad);
   303     g_debug ("New sink %p / %d", newpad, g_slist_length (concat_mux->sinks));
   304     concat_mux->numpads++;
   305   } else {
   306     g_warning ("concat_mux: this is not our template!\n");
   307     return NULL;
   308   }
   309 
   310   mutex = g_mutex_new ();
   311 
   312   if (concat_mux->sinkpad == NULL) {
   313       concat_mux->sinkpad = newpad;
   314   }
   315   else {
   316       g_mutex_lock (mutex);
   317   }
   318 
   319   gst_pad_set_element_private (newpad, mutex);
   320   /* setup some pad functions */
   321   gst_pad_set_link_function (newpad, gst_concat_mux_sinkconnect);
   322   gst_pad_set_event_function (newpad, gst_concat_mux_handle_sink_event);
   323   gst_pad_set_chain_function (newpad, gst_concat_mux_chain);
   324 
   325   /* add the pad to the element */
   326   gst_element_add_pad (element, newpad);
   327 
   328   return newpad;
   329 }
   330 
   331 /* handle events */
   332 static gboolean
   333 gst_concat_mux_handle_src_event (GstPad * pad, GstEvent * event)
   334 {
   335   GstConcatMux *concat_mux;
   336   GstEventType type;
   337 
   338   concat_mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
   339 
   340   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
   341 
   342   switch (type) {
   343     case GST_EVENT_SEEK:
   344       /* disable seeking for now */
   345       return FALSE;
   346     default:
   347       break;
   348   }
   349 
   350   gst_object_unref (concat_mux);
   351 
   352   return gst_pad_event_default (pad, event);
   353 }
   354 
   355 /* handle events */
   356 static gboolean
   357 gst_concat_mux_handle_sink_event (GstPad * pad, GstEvent * event)
   358 {
   359   GstConcatMux *mux;
   360   GstEventType type;
   361 
   362   mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
   363 
   364   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
   365 
   366   switch (type) {
   367     case GST_EVENT_EOS:
   368     {
   369         mux->resync = TRUE;
   370         g_debug ("sink EOS %p / %d", pad, g_slist_length (mux->sinks));
   371         /* mark pad eos */
   372         mux->sinks  = g_slist_remove (mux->sinks, pad);
   373         g_debug ("sink len %d", g_slist_length (mux->sinks));
   374         if (g_slist_length (mux->sinks) != 0) {
   375            GMutex *mutex;
   376            mux->sinkpad = mux->sinks->data;
   377            mutex = (GMutex *) gst_pad_get_element_private (mux->sinkpad);
   378            g_mutex_unlock (mutex);
   379            g_debug ("sink pad %p", mux->sinkpad);
   380            return TRUE;
   381         }
   382 
   383         g_debug ("sink list is empty");
   384     }
   385     default:
   386       break;
   387   }
   388 
   389   gst_object_unref (mux);
   390 
   391   return gst_pad_event_default (pad, event);
   392 }
   393 
   394 static GstFlowReturn
   395 gst_concat_mux_chain (GstPad * pad,  GstBuffer * buf)
   396 {
   397   GstConcatMux *mux = (GstConcatMux *) GST_PAD_PARENT (pad);
   398   GstBuffer *databuf = NULL;
   399   GstFlowReturn ret = GST_FLOW_OK;
   400   GMutex *mutex;
   401 
   402 
   403   mutex = (GMutex*) gst_pad_get_element_private (pad);
   404 
   405   g_mutex_lock (mutex);
   406   if (mux->done) {
   407       g_debug ("DONE pad %p", pad);
   408       return GST_FLOW_OK;
   409   }
   410 
   411   databuf = gst_buffer_make_metadata_writable (buf);
   412 
   413   if (!mux->negotiated) {
   414       /*
   415     GstCaps *newcaps;
   416     newcaps = gst_pad_get_caps (mux->sinkpad);
   417 
   418     g_debug ("CAPS: %s",gst_caps_to_string (newcaps));
   419 
   420     if (!gst_pad_set_caps (mux->srcpad, newcaps))
   421       goto nego_error;
   422     */
   423     mux->negotiated = TRUE;            
   424   } 
   425 
   426   g_debug ("Running [%s]\n"
   427         "\tTOFFSET    [%"G_GUINT64_FORMAT"]\n"
   428         "\tB_TSTAMP   [%"G_GUINT64_FORMAT"]\n"
   429         "\tB_DURATION [%"G_GUINT64_FORMAT"]\n"
   430         "\tOFFSET     [%"G_GUINT64_FORMAT"]\n"
   431         "\tB_OFFSET   [%"G_GUINT64_FORMAT"]",
   432         gst_element_get_name (mux),
   433         mux->timeoffset, 
   434         GST_BUFFER_TIMESTAMP (databuf),
   435         GST_BUFFER_DURATION (databuf),
   436         mux->offset,
   437         GST_BUFFER_OFFSET (databuf));
   438 
   439 
   440   if (mux->resync) {
   441     g_debug ("RESYNC [%s]", gst_element_get_name (mux));
   442     mux->timeoffset += GST_BUFFER_TIMESTAMP (databuf);
   443     GST_BUFFER_TIMESTAMP (databuf) = mux->timeoffset;
   444     mux->timeoffset += GST_BUFFER_DURATION (databuf);
   445 
   446     mux->offset += GST_BUFFER_OFFSET (databuf);
   447     GST_BUFFER_OFFSET (databuf) = mux->offset;
   448     mux->offset += GST_BUFFER_SIZE (databuf);
   449     mux->resync = FALSE;
   450   } else {
   451 
   452     GST_BUFFER_TIMESTAMP (databuf) = mux->timeoffset;
   453     mux->timeoffset += GST_BUFFER_DURATION (databuf);
   454 
   455     GST_BUFFER_OFFSET (databuf) = mux->offset;
   456     mux->offset += GST_BUFFER_SIZE (databuf);
   457   }
   458 
   459   gst_buffer_set_caps (databuf, GST_PAD_CAPS (pad));
   460   ret = gst_pad_push (mux->srcpad, databuf);
   461 
   462   //gst_buffer_unref (buf);
   463 
   464   g_mutex_unlock (mutex);
   465   return ret;
   466 /*
   467 nego_error:
   468   {
   469     GST_WARNING_OBJECT (mux, "failed to set caps");
   470     GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
   471     return GST_FLOW_NOT_NEGOTIATED;
   472   }
   473   */
   474   /*
   475 no_caps:
   476   {
   477     GST_WARNING_OBJECT (mux, "no caps on the incoming buffer %p", best->buffer);
   478     GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
   479     ret = GST_FLOW_NOT_NEGOTIATED;
   480     goto beach;
   481   }
   482   */
   483 }
   484 
   485 static GstStateChangeReturn
   486 gst_concat_mux_change_state (GstElement * element, GstStateChange transition)
   487 {
   488   GstConcatMux *concat_mux;
   489   GstStateChangeReturn ret;
   490 
   491   concat_mux = GST_CONCAT_MUX (element);
   492 
   493   switch (transition) {
   494     case GST_STATE_CHANGE_READY_TO_PAUSED:
   495       concat_mux->done = FALSE;
   496       concat_mux->resync = TRUE;
   497       GST_DEBUG_OBJECT (concat_mux, "starting collect pads");
   498       break;
   499     case GST_STATE_CHANGE_PAUSED_TO_READY:
   500       GST_DEBUG_OBJECT (concat_mux, "stopping collect pads");
   501       gst_concat_mux_clear (concat_mux);
   502       break;
   503     default:
   504       break;
   505   }
   506 
   507   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
   508   if (ret == GST_STATE_CHANGE_FAILURE)
   509     return ret;
   510 
   511   switch (transition) {
   512     default:
   513       break;
   514   }
   515 
   516   return ret;
   517 }
   518 
   519 gboolean
   520 gst_concat_mux_plugin_init (GstPlugin * plugin)
   521 {
   522 #ifdef ENABLE_NLS
   523   setlocale (LC_ALL, "");
   524   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
   525 #endif /* ENABLE_NLS */
   526 
   527   GST_DEBUG_CATEGORY_INIT (gst_concat_mux_debug, "concatmux", 0,
   528       "concat muxer");
   529       
   530   return gst_element_register (plugin, "concatmux", GST_RANK_NONE,
   531       GST_TYPE_CONCAT_MUX);
   532 }
   533 
   534 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
   535     GST_VERSION_MINOR,
   536     "concat muxer",
   537     "Concat streamers",
   538      plugin_init, VERSION, "LGPL", "ConcatMux", "")
   539