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