gst-gmyth/concatmux/gstconcatmux.c
author renatofilho
Thu Jun 14 18:22:32 2007 +0100 (2007-06-14)
branchtrunk
changeset 753 8ee634511c1e
parent 620 bc9827e02466
child 754 cb885ee44618
permissions -rw-r--r--
[svn r759] fixed indent using GNU Style
     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 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE("sink_%d",
   105 																   GST_PAD_SINK,
   106 																   GST_PAD_REQUEST,
   107 																   GST_STATIC_CAPS_ANY);
   108 
   109 static void gst_concat_mux_base_init(gpointer g_class);
   110 static void gst_concat_mux_class_init(GstConcatMuxClass * klass);
   111 static void gst_concat_mux_init(GstConcatMux * concat_mux);
   112 
   113 static void gst_concat_mux_finalize(GObject * object);
   114 
   115 static gboolean gst_concat_mux_handle_src_event(GstPad * pad,
   116 												GstEvent * event);
   117 static gboolean gst_concat_mux_handle_sink_event(GstPad * pad,
   118 												 GstEvent * event);
   119 
   120 static GstPad *gst_concat_mux_request_new_pad(GstElement * element,
   121 											  GstPadTemplate * templ,
   122 											  const gchar * name);
   123 static GstStateChangeReturn gst_concat_mux_change_state(GstElement * element,
   124 														GstStateChange
   125 														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 	{
   140 	  static const GTypeInfo concat_mux_info = {
   141 		sizeof(GstConcatMuxClass),
   142 		gst_concat_mux_base_init,
   143 		NULL,
   144 		(GClassInitFunc) gst_concat_mux_class_init,
   145 		NULL,
   146 		NULL,
   147 		sizeof(GstConcatMux),
   148 		0,
   149 		(GInstanceInitFunc) gst_concat_mux_init,
   150 	  };
   151 
   152 	  concat_mux_type =
   153 		g_type_register_static(GST_TYPE_ELEMENT, "GstConcatMux",
   154 							   &concat_mux_info, 0);
   155 	}
   156   return concat_mux_type;
   157 }
   158 
   159 static void
   160 gst_concat_mux_base_init(gpointer g_class)
   161 {
   162   GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
   163 
   164   gst_element_class_add_pad_template(element_class,
   165 									 gst_static_pad_template_get
   166 									 (&src_factory));
   167   gst_element_class_add_pad_template(element_class,
   168 									 gst_static_pad_template_get
   169 									 (&sink_factory));
   170 
   171   gst_element_class_set_details(element_class, &gst_concat_mux_details);
   172 }
   173 
   174 static void
   175 gst_concat_mux_class_init(GstConcatMuxClass * klass)
   176 {
   177   GObjectClass *gobject_class;
   178   GstElementClass *gstelement_class;
   179 
   180   gobject_class = (GObjectClass *) klass;
   181   gstelement_class = (GstElementClass *) klass;
   182 
   183   parent_class = g_type_class_peek_parent(klass);
   184 
   185   gobject_class->finalize = gst_concat_mux_finalize;
   186 
   187   gstelement_class->request_new_pad = gst_concat_mux_request_new_pad;
   188   gstelement_class->change_state = gst_concat_mux_change_state;
   189 }
   190 
   191 static void
   192 gst_concat_mux_init(GstConcatMux * concat_mux)
   193 {
   194   GstElementClass *klass = GST_ELEMENT_GET_CLASS(concat_mux);
   195 
   196   concat_mux->srcpad =
   197 	gst_pad_new_from_template(gst_element_class_get_pad_template(klass,
   198 																 "src"),
   199 							  "src");
   200   gst_pad_set_event_function(concat_mux->srcpad,
   201 							 gst_concat_mux_handle_src_event);
   202   gst_element_add_pad(GST_ELEMENT(concat_mux), concat_mux->srcpad);
   203 }
   204 
   205 static void
   206 gst_concat_mux_finalize(GObject * object)
   207 {
   208   GstConcatMux *concat_mux;
   209 
   210   concat_mux = GST_CONCAT_MUX(object);
   211   gst_concat_mux_clear(GST_CONCAT_MUX(object));
   212 
   213   G_OBJECT_CLASS(parent_class)->finalize(object);
   214 }
   215 
   216 static void
   217 gst_concat_mux_free_pad(gpointer data, gpointer user_data)
   218 {
   219   GMutex *mux;
   220 
   221   mux = gst_pad_get_element_private(GST_PAD(data));
   222   g_mutex_unlock(mux);
   223   g_mutex_free(mux);
   224   gst_object_unref(GST_OBJECT(data));
   225 }
   226 
   227 static void
   228 gst_concat_mux_clear(GstConcatMux * mux)
   229 {
   230   mux->resync = TRUE;
   231   mux->timeoffset = 0;
   232   mux->offset = 0;
   233   mux->negotiated = FALSE;
   234   mux->done = TRUE;
   235   if (mux->sinks != NULL)
   236 	{
   237 	  g_slist_foreach(mux->sinks, gst_concat_mux_free_pad, mux);
   238 	  g_slist_free(mux->sinks);
   239 	  mux->sinks = NULL;
   240 	}
   241 }
   242 
   243 
   244 static GstPadLinkReturn
   245 gst_concat_mux_sinkconnect(GstPad * pad, GstPad * peer)
   246 {
   247   gchar *pad_name = NULL;
   248   GstConcatMux *concat_mux;
   249 
   250   concat_mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
   251 
   252   if (concat_mux->sink_caps != NULL)
   253 	{
   254 	  GstCaps *peer_caps = gst_pad_get_caps(peer);
   255 	  GstCaps *intersect;
   256 
   257 	  intersect = gst_caps_intersect(concat_mux->sink_caps, peer_caps);
   258 	  if (intersect == NULL)
   259 		{
   260 		  gst_caps_unref(peer_caps);
   261 		  return GST_PAD_LINK_NOFORMAT;
   262 		}
   263 	  gst_caps_unref(peer_caps);
   264 	  gst_caps_unref(intersect);
   265 	}
   266   else
   267 	{
   268 	  concat_mux->sink_caps = gst_pad_get_caps(pad);
   269 	}
   270 
   271   pad_name = gst_pad_get_name(pad);
   272 
   273   GST_DEBUG_OBJECT(concat_mux, "sinkconnect triggered on %s", pad_name);
   274 
   275   g_free(pad_name);
   276 
   277   gst_object_unref(concat_mux);
   278 
   279   return GST_PAD_LINK_OK;
   280 }
   281 
   282 static GstPad *
   283 gst_concat_mux_request_new_pad(GstElement * element,
   284 							   GstPadTemplate * templ, const gchar * req_name)
   285 {
   286   GstConcatMux *concat_mux;
   287   GstPad *newpad;
   288   GstElementClass *klass = GST_ELEMENT_GET_CLASS(element);
   289   GMutex *mutex;
   290 
   291   g_return_val_if_fail(templ != NULL, NULL);
   292 
   293   if (templ->direction != GST_PAD_SINK)
   294 	{
   295 	  g_warning("concat_mux: request pad that is not a SINK pad\n");
   296 	  return NULL;
   297 	}
   298 
   299   g_return_val_if_fail(GST_IS_CONCAT_MUX(element), NULL);
   300 
   301   concat_mux = GST_CONCAT_MUX(element);
   302 
   303   if (templ == gst_element_class_get_pad_template(klass, "sink_%d"))
   304 	{
   305 	  gchar *name;
   306 
   307 	 /* create new pad with the name */
   308 	  name = g_strdup_printf("sink_%02d", concat_mux->numpads);
   309 	  g_debug("NEw pad %s", name);
   310 	  newpad = gst_pad_new_from_template(templ, name);
   311 	  g_free(name);
   312 	  concat_mux->sinks = g_slist_append(concat_mux->sinks, newpad);
   313 	  g_debug("New sink %p / %d", newpad, g_slist_length(concat_mux->sinks));
   314 	  concat_mux->numpads++;
   315 	}
   316   else
   317 	{
   318 	  g_warning("concat_mux: this is not our template!\n");
   319 	  return NULL;
   320 	}
   321 
   322   mutex = g_mutex_new();
   323 
   324   if (concat_mux->sinkpad == NULL)
   325 	{
   326 	  concat_mux->sinkpad = newpad;
   327 	}
   328   else
   329 	{
   330 	  g_mutex_lock(mutex);
   331 	}
   332 
   333   gst_pad_set_element_private(newpad, mutex);
   334  /* setup some pad functions */
   335   gst_pad_set_link_function(newpad, gst_concat_mux_sinkconnect);
   336   gst_pad_set_event_function(newpad, gst_concat_mux_handle_sink_event);
   337   gst_pad_set_chain_function(newpad, gst_concat_mux_chain);
   338 
   339  /* add the pad to the element */
   340   gst_element_add_pad(element, newpad);
   341 
   342   return newpad;
   343 }
   344 
   345 /* handle events */
   346 static gboolean
   347 gst_concat_mux_handle_src_event(GstPad * pad, GstEvent * event)
   348 {
   349   GstConcatMux *concat_mux;
   350   GstEventType type;
   351 
   352   concat_mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
   353 
   354   type = event ? GST_EVENT_TYPE(event) : GST_EVENT_UNKNOWN;
   355 
   356   switch (type)
   357 	{
   358 	case GST_EVENT_SEEK:
   359 	 /* disable seeking for now */
   360 	  return FALSE;
   361 	default:
   362 	  break;
   363 	}
   364 
   365   gst_object_unref(concat_mux);
   366 
   367   return gst_pad_event_default(pad, event);
   368 }
   369 
   370 /* handle events */
   371 static gboolean
   372 gst_concat_mux_handle_sink_event(GstPad * pad, GstEvent * event)
   373 {
   374   GstConcatMux *mux;
   375   GstEventType type;
   376 
   377   mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
   378 
   379   type = event ? GST_EVENT_TYPE(event) : GST_EVENT_UNKNOWN;
   380 
   381   switch (type)
   382 	{
   383 	case GST_EVENT_EOS:
   384 	  {
   385 		mux->resync = TRUE;
   386 		g_debug("sink EOS %p / %d", pad, g_slist_length(mux->sinks));
   387 	   /* mark pad eos */
   388 		mux->sinks = g_slist_remove(mux->sinks, pad);
   389 		g_debug("sink len %d", g_slist_length(mux->sinks));
   390 		if (g_slist_length(mux->sinks) != 0)
   391 		  {
   392 			GMutex *mutex;
   393 			mux->sinkpad = mux->sinks->data;
   394 			mutex = (GMutex *) gst_pad_get_element_private(mux->sinkpad);
   395 			g_mutex_unlock(mutex);
   396 			g_debug("sink pad %p", mux->sinkpad);
   397 			return TRUE;
   398 		  }
   399 
   400 		g_debug("sink list is empty");
   401 	  }
   402 	default:
   403 	  break;
   404 	}
   405 
   406   gst_object_unref(mux);
   407 
   408   return gst_pad_event_default(pad, event);
   409 }
   410 
   411 static GstFlowReturn
   412 gst_concat_mux_chain(GstPad * pad, GstBuffer * buf)
   413 {
   414   GstConcatMux *mux = (GstConcatMux *) GST_PAD_PARENT(pad);
   415   GstBuffer *databuf = NULL;
   416   GstFlowReturn ret = GST_FLOW_OK;
   417   GMutex *mutex;
   418 
   419 
   420   mutex = (GMutex *) gst_pad_get_element_private(pad);
   421 
   422   g_mutex_lock(mutex);
   423   if (mux->done)
   424 	{
   425 	  g_debug("DONE pad %p", pad);
   426 	  g_mutex_unlock(mutex);
   427 	  return GST_FLOW_OK;
   428 	}
   429 
   430   databuf = gst_buffer_make_metadata_writable(buf);
   431 
   432   if (!mux->negotiated)
   433 	{
   434 	 /*
   435 	    GstCaps *newcaps;
   436 	    newcaps = gst_pad_get_caps (mux->sinkpad);
   437 
   438 	    g_debug ("CAPS: %s",gst_caps_to_string (newcaps));
   439 
   440 	    if (!gst_pad_set_caps (mux->srcpad, newcaps))
   441 	    goto nego_error;
   442 	  */
   443 	  mux->negotiated = TRUE;
   444 	}
   445 
   446  /*
   447     g_debug ("Running [%s]\n"
   448     "\tTOFFSET    [%"G_GUINT64_FORMAT"]\n"
   449     "\tB_TSTAMP   [%"G_GUINT64_FORMAT"]\n"
   450     "\tB_DURATION [%"G_GUINT64_FORMAT"]\n"
   451     "\tOFFSET     [%"G_GUINT64_FORMAT"]\n"
   452     "\tB_OFFSET   [%"G_GUINT64_FORMAT"]",
   453     gst_element_get_name (mux),
   454     mux->timeoffset, 
   455     GST_BUFFER_TIMESTAMP (databuf),
   456     GST_BUFFER_DURATION (databuf),
   457     mux->offset,
   458     GST_BUFFER_OFFSET (databuf));
   459   */
   460 
   461 
   462   if (mux->resync)
   463 	{
   464 	  g_debug("RESYNC [%s]", gst_element_get_name(mux));
   465 	  mux->timeoffset += GST_BUFFER_TIMESTAMP(databuf);
   466 	  GST_BUFFER_TIMESTAMP(databuf) = mux->timeoffset;
   467 	  mux->timeoffset += GST_BUFFER_DURATION(databuf);
   468 
   469 	  mux->offset += GST_BUFFER_OFFSET(databuf);
   470 	  GST_BUFFER_OFFSET(databuf) = mux->offset;
   471 	  mux->offset += GST_BUFFER_SIZE(databuf);
   472 	  mux->resync = FALSE;
   473 	}
   474   else
   475 	{
   476 
   477 	  GST_BUFFER_TIMESTAMP(databuf) = mux->timeoffset;
   478 	  mux->timeoffset += GST_BUFFER_DURATION(databuf);
   479 
   480 	  GST_BUFFER_OFFSET(databuf) = mux->offset;
   481 	  mux->offset += GST_BUFFER_SIZE(databuf);
   482 	}
   483 
   484   gst_buffer_set_caps(databuf, GST_PAD_CAPS(pad));
   485   ret = gst_pad_push(mux->srcpad, databuf);
   486 
   487  //gst_buffer_unref (buf);
   488 
   489   g_mutex_unlock(mutex);
   490   return ret;
   491 /*
   492 nego_error:
   493   {
   494     GST_WARNING_OBJECT (mux, "failed to set caps");
   495     GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
   496     return GST_FLOW_NOT_NEGOTIATED;
   497   }
   498   */
   499  /*
   500     no_caps:
   501     {
   502     GST_WARNING_OBJECT (mux, "no caps on the incoming buffer %p", best->buffer);
   503     GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
   504     ret = GST_FLOW_NOT_NEGOTIATED;
   505     goto beach;
   506     }
   507   */
   508 }
   509 
   510 static GstStateChangeReturn
   511 gst_concat_mux_change_state(GstElement * element, GstStateChange transition)
   512 {
   513   GstConcatMux *concat_mux;
   514   GstStateChangeReturn ret;
   515 
   516   concat_mux = GST_CONCAT_MUX(element);
   517 
   518   switch (transition)
   519 	{
   520 	case GST_STATE_CHANGE_READY_TO_PAUSED:
   521 	  concat_mux->done = FALSE;
   522 	  concat_mux->resync = TRUE;
   523 	  GST_DEBUG_OBJECT(concat_mux, "starting collect pads");
   524 	  break;
   525 	case GST_STATE_CHANGE_PAUSED_TO_READY:
   526 	  GST_DEBUG_OBJECT(concat_mux, "stopping collect pads");
   527 	  gst_concat_mux_clear(concat_mux);
   528 	  break;
   529 	default:
   530 	  break;
   531 	}
   532 
   533   ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
   534   if (ret == GST_STATE_CHANGE_FAILURE)
   535 	return ret;
   536 
   537   switch (transition)
   538 	{
   539 	default:
   540 	  break;
   541 	}
   542 
   543   return ret;
   544 }
   545 
   546 gboolean
   547 gst_concat_mux_plugin_init(GstPlugin * plugin)
   548 {
   549 #ifdef ENABLE_NLS
   550   setlocale(LC_ALL, "");
   551   bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
   552 #endif /* ENABLE_NLS */
   553 
   554   GST_DEBUG_CATEGORY_INIT(gst_concat_mux_debug, "concatmux", 0,
   555 						  "concat muxer");
   556 
   557   return gst_element_register(plugin, "concatmux", GST_RANK_NONE,
   558 							  GST_TYPE_CONCAT_MUX);
   559 }
   560 
   561 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
   562 				  GST_VERSION_MINOR,
   563 				  "concatmux",
   564 				  "Concat streamers",
   565 				  gst_concat_mux_plugin_init, VERSION, GST_LICENSE,
   566 				  GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)