gst-gpac/src/gpacmux.c
author melunko
Thu Feb 21 22:49:45 2008 +0000 (2008-02-21)
branchtrunk
changeset 920 e3e434d3ed83
parent 917 be87d28e8371
permissions -rw-r--r--
[svn r929] Fixed eos problems and more than one track limitation
     1 
     2 #ifdef HAVE_CONFIG_H
     3 #include "config.h"
     4 #endif
     5 
     6 #include <gst/gst.h>
     7 #include <gst/base/gstcollectpads.h>
     8 
     9 #include <stdlib.h> 
    10 #include <string.h>
    11 
    12 /* gpac includes */
    13 #include <gpac/isomedia.h>
    14 
    15 #define GST_TYPE_GPAC_MUX (gst_gpac_mux_get_type())
    16 #define GST_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GPAC_MUX, GstGpacMux))
    17 #define GST_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GPAC_MUX, GstGpacMux))
    18 #define GST_IS_GPAC_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GPAC_MUX))
    19 #define GST_IS_GPAC_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GPAC_MUX))
    20 
    21 typedef struct _GstGpacMux GstGpacMux;
    22 typedef struct _GstGpacMuxClass GstGpacMuxClass;
    23 
    24 typedef enum
    25 {
    26   GST_GPAC_PAD_STATE_CONTROL = 0,
    27   GST_GPAC_PAD_STATE_DATA = 1
    28 }
    29 GstGpacPadState;
    30 
    31 typedef struct
    32 {
    33   GstCollectData collect;       /* we extend the CollectData */
    34 
    35   gint track_number;
    36   guint32 di; /* outDescriptionIndex */
    37 
    38   GstBuffer *buffer;
    39   GstBuffer *next_buffer;
    40 
    41   guint32 frame_count;
    42   gboolean is_video;
    43 
    44   gboolean eos;
    45 
    46 } GstGpacPad;
    47 
    48 struct _GstGpacMux
    49 {
    50   GstElement element;
    51 
    52   GstPad *srcpad;
    53   GstCollectPads *collect;
    54   gint active_pads;
    55 
    56   GF_ISOFile *file;
    57 
    58 };
    59 
    60 struct _GstGpacMuxClass
    61 {
    62   GstElementClass parent_class;
    63 };
    64 
    65 /* elementfactory information */
    66 static const GstElementDetails gst_gpac_mux_details =
    67 GST_ELEMENT_DETAILS ("Gpac muxer",
    68     "Codec/Muxer",
    69     "mux mp4 streams",
    70     "Hallyson Melo <hallyson.melo@indt.org.br");
    71 
    72 /* GpacMux signals and args */
    73 enum
    74 {
    75   /* FILL ME */
    76   LAST_SIGNAL
    77 };
    78 
    79 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    80     GST_PAD_SRC,
    81     GST_PAD_ALWAYS,
    82     GST_STATIC_CAPS ("video/quicktime")
    83     );
    84 
    85 static GstStaticPadTemplate audio_sink_factory = GST_STATIC_PAD_TEMPLATE ("audio_%d",
    86     GST_PAD_SINK,
    87     GST_PAD_REQUEST,
    88     GST_STATIC_CAPS (
    89         "audio/mpeg, " 
    90         "mpegversion = (int) 4, " 
    91         "channels = (int) { 1, 2 }, " 
    92         "rate = (int) [ 8000, 96000 ]")
    93     );
    94     //GST_STATIC_CAPS ("audio/mpeg, mpegversion = 1, layer = 3")
    95 
    96 static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video_%d",
    97     GST_PAD_SINK,
    98     GST_PAD_REQUEST,
    99     GST_STATIC_CAPS ("video/mpeg, mpegversion = 4")
   100    );
   101 
   102 static void gst_gpac_mux_base_init (gpointer g_class);
   103 static void gst_gpac_mux_class_init (GstGpacMuxClass * klass);
   104 static void gst_gpac_mux_init (GstGpacMux * gpac_mux);
   105 static void gst_gpac_mux_finalize (GObject * object);
   106 
   107 static GstFlowReturn
   108 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux);
   109 
   110 static gboolean gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event);
   111 static GstPad *gst_gpac_mux_request_new_pad (GstElement * element,
   112     GstPadTemplate * templ, const gchar * name);
   113 static void gst_gpac_mux_release_pad (GstElement * element, GstPad * pad);
   114 
   115 static GstStateChangeReturn gst_gpac_mux_change_state (GstElement * element,
   116     GstStateChange transition);
   117 
   118 static GstGpacPad* gst_gpac_mux_queue_pads (GstGpacMux * gpac_mux);
   119 static GstFlowReturn gst_gpac_mux_process_pad (GstGpacMux * gpac_mux, GstGpacPad *pad);
   120 static gboolean gst_gpac_mux_all_pads_eos (GstCollectPads * pads);
   121 static gint gst_gpac_mux_compare_pads (GstGpacMux * ogg_mux, GstGpacPad *first,
   122     GstGpacPad *second);
   123 
   124 
   125 static GstElementClass *parent_class = NULL;
   126 
   127 GType
   128 gst_gpac_mux_get_type (void)
   129 {
   130   static GType gpac_mux_type = 0;
   131 
   132   if (G_UNLIKELY (gpac_mux_type == 0)) {
   133     static const GTypeInfo gpac_mux_info = {
   134       sizeof (GstGpacMuxClass),
   135       gst_gpac_mux_base_init,
   136       NULL,
   137       (GClassInitFunc) gst_gpac_mux_class_init,
   138       NULL,
   139       NULL,
   140       sizeof (GstGpacMux),
   141       0,
   142       (GInstanceInitFunc) gst_gpac_mux_init,
   143     };
   144 
   145     gpac_mux_type =
   146         g_type_register_static (GST_TYPE_ELEMENT, "GstGpacMux", &gpac_mux_info,
   147         0);
   148   }
   149   return gpac_mux_type;
   150 }
   151 
   152 static void
   153 gst_gpac_mux_base_init (gpointer g_class)
   154 {
   155   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   156 
   157   gst_element_class_add_pad_template (element_class,
   158       gst_static_pad_template_get (&src_factory));
   159   gst_element_class_add_pad_template (element_class,
   160       gst_static_pad_template_get (&audio_sink_factory));
   161   gst_element_class_add_pad_template (element_class,
   162       gst_static_pad_template_get (&video_sink_factory));
   163 
   164   gst_element_class_set_details (element_class, &gst_gpac_mux_details);
   165 }
   166 
   167 static void
   168 gst_gpac_mux_class_init (GstGpacMuxClass * klass)
   169 {
   170   GObjectClass *gobject_class;
   171   GstElementClass *gstelement_class;
   172 
   173   gobject_class = (GObjectClass *) klass;
   174   gstelement_class = (GstElementClass *) klass;
   175 
   176   parent_class = g_type_class_peek_parent (klass);
   177 
   178   gobject_class->finalize = gst_gpac_mux_finalize;
   179 
   180   gstelement_class->request_new_pad = gst_gpac_mux_request_new_pad;
   181   gstelement_class->release_pad = gst_gpac_mux_release_pad;
   182 
   183   gstelement_class->change_state = gst_gpac_mux_change_state;
   184 
   185 }
   186 
   187 static void
   188 gst_gpac_mux_init (GstGpacMux * gpac_mux)
   189 {
   190   GstElementClass *klass = GST_ELEMENT_GET_CLASS (gpac_mux);
   191 
   192   gpac_mux->srcpad =
   193       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
   194           "src"), "src");
   195   gst_pad_set_event_function (gpac_mux->srcpad, gst_gpac_mux_handle_src_event);
   196   gst_element_add_pad (GST_ELEMENT (gpac_mux), gpac_mux->srcpad);
   197 
   198   gpac_mux->collect = gst_collect_pads_new ();
   199   gst_collect_pads_set_function (gpac_mux->collect,
   200       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_gpac_mux_collected),
   201       gpac_mux);
   202 
   203   /* Opens gpac library */
   204   /* FIXME */
   205   gpac_mux->file = gf_isom_open("/tmp/gpac.mp4", GF_ISOM_OPEN_WRITE, NULL);
   206   gf_isom_set_storage_mode(gpac_mux->file, GF_ISOM_STORE_FLAT /*STREAMABLE*/);
   207 
   208   //gst_gpac_mux_clear (gpac_mux);
   209 }
   210 
   211 static void
   212 gst_gpac_mux_finalize (GObject * object)
   213 {
   214   GstGpacMux *gpac_mux;
   215 
   216   gpac_mux = GST_GPAC_MUX (object);
   217 
   218   if (gpac_mux->collect) {
   219     gst_object_unref (gpac_mux->collect);
   220     gpac_mux->collect = NULL;
   221   }
   222 
   223   G_OBJECT_CLASS (parent_class)->finalize (object);
   224 }
   225 
   226 static void
   227 gst_gpac_mux_gpac_pad_destroy_notify (GstCollectData * data)
   228 {
   229   GstGpacPad *gpacpad = (GstGpacPad *) data;
   230   GstBuffer *buf;
   231 
   232 }
   233 
   234 static GstPadLinkReturn
   235 gst_gpac_mux_sinkconnect (GstPad * pad, GstPad * peer)
   236 {
   237   GstGpacMux *gpac_mux;
   238 
   239   gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
   240 
   241   GST_DEBUG_OBJECT (gpac_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
   242 
   243   gst_object_unref (gpac_mux);
   244 
   245   return GST_PAD_LINK_OK;
   246 }
   247 
   248 static GstPad *
   249 gst_gpac_mux_request_new_pad (GstElement * element,
   250     GstPadTemplate * templ, const gchar * req_name)
   251 {
   252   GstGpacMux *gpac_mux;
   253   GstPad *newpad;
   254   GstElementClass *klass;
   255   gchar *padname = NULL;
   256   gint serial;
   257   gboolean is_video = FALSE;
   258 
   259   g_return_val_if_fail (templ != NULL, NULL);
   260 
   261   if (templ->direction != GST_PAD_SINK)
   262     goto wrong_direction;
   263 
   264   g_return_val_if_fail (GST_IS_GPAC_MUX (element), NULL);
   265   gpac_mux = GST_GPAC_MUX (element);
   266 
   267   klass = GST_ELEMENT_GET_CLASS (element);
   268 
   269     if (req_name == NULL || strlen (req_name) < 6) {
   270       /* no name given when requesting the pad, use random serial number */
   271       serial = rand ();
   272     } else {
   273       /* parse serial number from requested padname */
   274       serial = atoi (&req_name[5]);
   275     }
   276 
   277   if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
   278     is_video = TRUE;      
   279     padname = g_strdup_printf ("video_%d", serial);
   280   } else if (templ != gst_element_class_get_pad_template (klass, "audio_%d")) {
   281     goto wrong_template;
   282   } else {
   283     padname = g_strdup_printf ("audio_%d", serial);
   284   }
   285 
   286 
   287   {
   288     
   289     /* create new pad with the name */
   290     GST_DEBUG_OBJECT (gpac_mux, "Creating new pad for serial %d", serial);
   291 
   292     newpad = gst_pad_new_from_template (templ, padname);
   293     g_free (padname);
   294 
   295     /* construct our own wrapper data structure for the pad to
   296      * keep track of its status */
   297     {
   298       GstGpacPad *gpacpad;
   299 
   300       gpacpad = (GstGpacPad *)
   301           gst_collect_pads_add_pad_full (gpac_mux->collect, newpad,
   302           sizeof (GstGpacPad), gst_gpac_mux_gpac_pad_destroy_notify);
   303       gpac_mux->active_pads++;
   304 
   305       /* gpac new track */
   306       gpacpad->is_video = is_video;
   307       if (gpacpad->is_video) {
   308         gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, 
   309           GF_ISOM_MEDIA_VISUAL, 10 * 1000 /* time scale */);
   310       } else {
   311         gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, 
   312           GF_ISOM_MEDIA_AUDIO, 48000 /*time scale */);
   313         
   314       }
   315       if (gpacpad->track_number == 0) {
   316         g_warning ("Error while adding the new gpac track");
   317       } else {
   318         gf_isom_set_track_enabled(gpac_mux->file, gpacpad->track_number, 1);
   319         if (is_video) {
   320           GF_ESD *esd = gf_odf_desc_esd_new (0);
   321           esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
   322 
   323           gf_isom_new_mpeg4_description( gpac_mux->file, gpacpad->track_number,
   324                         esd, NULL, NULL, &(gpacpad->di));
   325 
   326            gf_isom_set_visual_info (gpac_mux->file, gpacpad->track_number, gpacpad->di, 320, 240);//fixme
   327           //gf_isom_set_cts_packing (gpac_mux->file, gpacpad->track_number, 0);
   328         } else {
   329           GF_ESD *esd = gf_odf_desc_esd_new (2);
   330           esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
   331 
   332           gf_isom_new_mpeg4_description(gpac_mux->file, gpacpad->track_number,
   333                          esd, NULL, NULL, &(gpacpad->di));
   334 
   335           gf_isom_set_audio_info(gpac_mux->file, gpacpad->track_number, 
   336                           gpacpad->di, 48000, 2 /*num channels */, 16);
   337         }
   338       }
   339     }
   340   }
   341 
   342   /* setup some pad functions */
   343   gst_pad_set_link_function (newpad, gst_gpac_mux_sinkconnect);
   344   /* dd the pad to the element */
   345   gst_element_add_pad (element, newpad);
   346 
   347   return newpad;
   348 
   349   /* ERRORS */
   350 wrong_direction:
   351   {
   352     g_warning ("gpac_mux: request pad that is not a SINK pad\n");
   353     return NULL;
   354   }
   355 wrong_template:
   356   {
   357     g_warning ("gpac_mux: this is not our template!\n");
   358     return NULL;
   359   }
   360 }
   361 
   362 static void
   363 gst_gpac_mux_release_pad (GstElement * element, GstPad * pad)
   364 {
   365   GstGpacMux *gpac_mux;
   366 
   367   gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
   368 
   369   gst_collect_pads_remove_pad (gpac_mux->collect, pad);
   370   gst_element_remove_pad (element, pad);
   371 }
   372 
   373 /* handle events */
   374 static gboolean
   375 gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event)
   376 {
   377   GstEventType type;
   378 
   379   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
   380 
   381   switch (type) {
   382     case GST_EVENT_SEEK:
   383       /* disable seeking for now */
   384       return FALSE;
   385     default:
   386       break;
   387   }
   388 
   389   return gst_pad_event_default (pad, event);
   390 }
   391 
   392 static GstFlowReturn
   393 gst_gpac_mux_push_buffer (GstGpacMux * mux, GstBuffer * buffer)
   394 {
   395   GstCaps *caps;
   396 #if 0
   397   /* fix up OFFSET and OFFSET_END again */
   398   GST_BUFFER_OFFSET (buffer) = mux->offset;
   399   mux->offset += GST_BUFFER_SIZE (buffer);
   400   GST_BUFFER_OFFSET_END (buffer) = mux->offset;
   401 
   402   /* Ensure we have monotonically increasing timestamps in the output. */
   403   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
   404     if (mux->last_ts != GST_CLOCK_TIME_NONE &&
   405         GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts)
   406       GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
   407     else
   408       mux->last_ts = GST_BUFFER_TIMESTAMP (buffer);
   409   }
   410 #endif
   411   
   412   caps = gst_pad_get_negotiated_caps (mux->srcpad);
   413   gst_buffer_set_caps (buffer, caps);
   414   if (caps != NULL) {
   415     gst_caps_unref (caps);
   416   }
   417 
   418   return gst_pad_push (mux->srcpad, buffer);
   419 }
   420 
   421 static GstFlowReturn
   422 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux)
   423 {
   424   GstFlowReturn ret = GST_FLOW_OK;
   425   GstGpacPad *pad;
   426   gint active_before;
   427 
   428   GST_LOG_OBJECT (gpac_mux, "collected");
   429 
   430   active_before = gpac_mux->active_pads;
   431 
   432   pad = gst_gpac_mux_queue_pads (gpac_mux);
   433   if (!pad) {
   434     return GST_FLOW_WRONG_STATE;
   435   }
   436 
   437   if (pad->buffer) {
   438     ret = gst_gpac_mux_process_pad (gpac_mux, pad);
   439   }
   440 
   441   if (gpac_mux->active_pads < active_before) {
   442     /* If the active pad count went down, this mean at least one pad has gone
   443      * EOS. Since CollectPads only calls _collected() once when all pads are
   444      * EOS, and our code doesn't _pop() from all pads we need to check that by
   445      * peeking on all pads, else we won't be called again and the muxing will
   446      * not terminate (push out EOS). */
   447           printf ("XXXX um pad foi desativado %" GST_PTR_FORMAT "\n", pad);
   448 
   449     /* if all the pads have been removed, flush all pending data */
   450     if ((ret == GST_FLOW_OK) && gst_gpac_mux_all_pads_eos (pads)) {
   451       GST_LOG_OBJECT (gpac_mux, "no pads remaining, flushing data");
   452 
   453       do {
   454         pad = gst_gpac_mux_queue_pads (gpac_mux);
   455         if (pad)
   456           ret = gst_gpac_mux_process_pad (gpac_mux, pad);
   457       } while ((ret == GST_FLOW_OK) && (pad != NULL));
   458 
   459       /* gpac file close (eos) */
   460       // Fixme: this should flush all data to src pad
   461       // Fixme: where to release gpac_mux->file?
   462       printf ("CCCCCCCCCCCCCCCCcclosing the file\n");
   463       gf_isom_close (gpac_mux->file);
   464 
   465 
   466       GST_DEBUG_OBJECT (gpac_mux, "Pushing EOS");
   467       gst_pad_push_event (gpac_mux->srcpad, gst_event_new_eos ());
   468     }
   469   }
   470 
   471   return ret;
   472 
   473 }
   474 
   475 static gboolean
   476 gst_gpac_mux_all_pads_eos (GstCollectPads * pads)
   477 {
   478   GSList *walk;
   479   gboolean alleos = TRUE;
   480 
   481   walk = pads->data;
   482   while (walk) {
   483     GstBuffer *buf;
   484     GstCollectData *data = (GstCollectData *) walk->data;
   485 
   486     buf = gst_collect_pads_peek (pads, data);
   487     if (buf) {
   488       alleos = FALSE;
   489       gst_buffer_unref (buf);
   490       break;
   491     }
   492     walk = walk->next;
   493   }
   494 
   495   return alleos;
   496 }
   497 
   498 
   499 static GstFlowReturn
   500 gst_gpac_mux_process_pad (GstGpacMux *gpac_mux, GstGpacPad *pad)
   501 {
   502   GstFlowReturn ret = GST_FLOW_OK;
   503   GF_ISOSample *sample;
   504 
   505   if (pad->buffer == NULL) {
   506           printf ("Buffer is null, wrong state\n");
   507       return GST_FLOW_WRONG_STATE;
   508   }
   509 
   510   /* gpac output */
   511   printf ("xxxxxx buffer size: %d\n", GST_BUFFER_SIZE(pad->buffer)); fflush (stdout);
   512   printf ("xxxx frames %d\n", pad->frame_count);
   513 
   514   sample = gf_isom_sample_new();
   515   sample->dataLength = GST_BUFFER_SIZE(pad->buffer);
   516   sample->data = GST_BUFFER_DATA(pad->buffer);
   517   sample->CTS_Offset = 0;
   518 
   519   if (pad->is_video) {
   520     sample->IsRAP = 0;
   521     sample->DTS += 1000*pad->frame_count;
   522   } else {
   523     sample->IsRAP = 0;
   524     sample->DTS = 2048*pad->frame_count;
   525   }
   526 
   527   gf_isom_add_sample(gpac_mux->file, pad->track_number, 
   528                      pad->di, sample);
   529   sample->data = NULL;
   530 
   531   gf_isom_sample_del(&sample);
   532 
   533 
   534   /* gstreamer output (push) */
   535   ret = gst_gpac_mux_push_buffer (gpac_mux, pad->buffer);
   536   pad->buffer = NULL;
   537 
   538   if (ret != GST_FLOW_OK) {
   539   }
   540 
   541   pad->frame_count++;
   542  
   543   return ret;
   544 }
   545 
   546 /* reset all variables in the gpac pads. */
   547 static void
   548 gst_gpac_mux_init_collectpads (GstCollectPads * collect)
   549 {
   550   GSList *iter;
   551 
   552   iter = collect->data;
   553   while (iter) {
   554     GstGpacPad *gpacpad = (GstGpacPad *) iter->data;
   555 
   556     //gpac_stream_init (&gpacpad->stream, gpacpad->serial);
   557     //gpacpad->packetno = 0;
   558     //gpacpad->pageno = 0;
   559     //gpacpad->eos = FALSE;
   560     /* we assume there will be some control data first for this pad */
   561     //gpacpad->state = GST_GPAC_PAD_STATE_CONTROL;
   562     //gpacpad->new_page = TRUE;
   563     //gpacpad->first_delta = FALSE;
   564     //gpacpad->prev_delta = FALSE;
   565     //gpacpad->pagebuffers = g_queue_new ();
   566 
   567     iter = g_slist_next (iter);
   568   }
   569 }
   570 
   571 /* Clear all buffers from the collectpads object */
   572 static void
   573 gst_gpac_mux_clear_collectpads (GstCollectPads * collect)
   574 {
   575   GSList *iter;
   576 
   577   for (iter = collect->data; iter; iter = g_slist_next (iter)) {
   578     GstGpacPad *gpacpad = (GstGpacPad *) iter->data;
   579     GstBuffer *buf;
   580 
   581     //gpac_stream_clear (&gpacpad->stream);
   582 /*
   583     while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
   584       gst_buffer_unref (buf);
   585     }
   586     g_queue_free (gpacpad->pagebuffers);
   587     gpacpad->pagebuffers = NULL;*/
   588   }
   589 }
   590 
   591 static GstStateChangeReturn
   592 gst_gpac_mux_change_state (GstElement * element, GstStateChange transition)
   593 {
   594   GstGpacMux *gpac_mux;
   595   GstStateChangeReturn ret;
   596 
   597   gpac_mux = GST_GPAC_MUX (element);
   598 
   599   switch (transition) {
   600     case GST_STATE_CHANGE_NULL_TO_READY:
   601       break;
   602     case GST_STATE_CHANGE_READY_TO_PAUSED:
   603       //gst_gpac_mux_clear (gpac_mux);
   604       //gst_gpac_mux_init_collectpads (gpac_mux->collect);
   605       gst_collect_pads_start (gpac_mux->collect);
   606       break;
   607     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
   608       break;
   609     case GST_STATE_CHANGE_PAUSED_TO_READY:
   610       gst_collect_pads_stop (gpac_mux->collect);
   611       break;
   612     default:
   613       break;
   614   }
   615 
   616   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
   617 
   618   switch (transition) {
   619     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
   620       break;
   621     case GST_STATE_CHANGE_PAUSED_TO_READY:
   622       gst_gpac_mux_clear_collectpads (gpac_mux->collect);
   623       break;
   624     case GST_STATE_CHANGE_READY_TO_NULL:
   625       break;
   626     default:
   627       break;
   628   }
   629 
   630   return ret;
   631 
   632 }
   633 
   634 static GstGpacPad *
   635 gst_gpac_mux_queue_pads (GstGpacMux * gpac_mux)
   636 {
   637   GstGpacPad *bestpad = NULL;//, *still_hungry = NULL;
   638   GSList *iter;
   639 
   640   /* try to make sure we have a buffer from each usable pad first */
   641   iter = gpac_mux->collect->data;
   642   while (iter) {
   643     GstGpacPad *pad;
   644     GstCollectData *data;
   645 
   646     data = (GstCollectData *) iter->data;
   647     pad = (GstGpacPad *) data;
   648 
   649     iter = g_slist_next (iter);
   650 
   651     GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
   652 
   653     /* try to get a new buffer for this pad if needed and possible */
   654     if (pad->buffer == NULL) {
   655       GstBuffer *buf;
   656 
   657       buf = gst_collect_pads_pop (gpac_mux->collect, data);
   658       GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
   659 
   660       /* On EOS we get a NULL buffer */
   661       if (buf == NULL) {
   662               printf ("EENENENENENEND OF STREAM EOS\n");
   663         GST_DEBUG_OBJECT (data->pad, "EOS on pad");
   664         if (!pad->eos) {
   665           /* it's no longer active */
   666           gpac_mux->active_pads--;
   667           pad->eos = TRUE;
   668 
   669         }
   670       }
   671 
   672       pad->buffer = buf;
   673     } 
   674 
   675     /* we should have a buffer now, see if it is the best pad to
   676      * pull on */
   677       if (gst_gpac_mux_compare_pads (gpac_mux, bestpad, pad) > 0) {
   678         GST_LOG_OBJECT (data->pad,
   679             "new best pad, with buffers %" GST_PTR_FORMAT, pad->buffer);
   680         bestpad = pad;
   681       }
   682   }
   683  
   684   return bestpad;
   685 }
   686 
   687 static gint
   688 gst_gpac_mux_compare_pads (GstGpacMux * ogg_mux, GstGpacPad *first,
   689     GstGpacPad *second)
   690 {
   691   guint64 firsttime, secondtime;
   692 
   693   /* if the first pad doesn't contain anything or is even NULL, return
   694    * the second pad as best candidate and vice versa */
   695   if (first == NULL || (first->buffer == NULL))
   696     return 1;
   697   if (second == NULL || (second->buffer == NULL))
   698     return -1;
   699 
   700   /* no timestamp on first buffer, it must go first */
   701   if (first->buffer)
   702     firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
   703   if (firsttime == GST_CLOCK_TIME_NONE)
   704     return -1;
   705 
   706   /* no timestamp on second buffer, it must go first */
   707   if (second->buffer)
   708     secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
   709   if (secondtime == GST_CLOCK_TIME_NONE)
   710     return 1;
   711 
   712   /* first buffer has higher timestamp, second one should go first */
   713   if (secondtime < firsttime)
   714     return 1;
   715   /* second buffer has higher timestamp, first one should go first */
   716   else if (secondtime > firsttime)
   717     return -1;
   718 
   719   /* same priority if all of the above failed */
   720   return 0;
   721 }
   722 
   723 
   724 
   725 static gboolean
   726 gst_gpac_mux_plugin_init (GstPlugin * plugin)
   727 {
   728   return gst_element_register (plugin, "gpacmux", GST_RANK_NONE,
   729       GST_TYPE_GPAC_MUX);
   730 }
   731 
   732 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
   733                   GST_VERSION_MINOR,
   734                   "gpacmux",
   735                   "Muxes audio and video",
   736                   gst_gpac_mux_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
   737                   GST_PACKAGE_ORIGIN)
   738