gst-gpac/src/gpacmux.c
author melunko
Thu Feb 21 17:54:48 2008 +0000 (2008-02-21)
branchtrunk
changeset 919 ec8c246e9d29
child 920 e3e434d3ed83
permissions -rw-r--r--
[svn r928] Added some information at README
     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   guint32 frame_count;
    39   gboolean is_video;
    40 
    41 } GstGpacPad;
    42 
    43 struct _GstGpacMux
    44 {
    45   GstElement element;
    46 
    47   GstPad *srcpad;
    48   GstCollectPads *collect;
    49 
    50   GF_ISOFile *file;
    51 
    52 };
    53 
    54 struct _GstGpacMuxClass
    55 {
    56   GstElementClass parent_class;
    57 };
    58 
    59 /* elementfactory information */
    60 static const GstElementDetails gst_gpac_mux_details =
    61 GST_ELEMENT_DETAILS ("Gpac muxer",
    62     "Codec/Muxer",
    63     "mux mp4 streams",
    64     "Hallyson Melo <hallyson.melo@indt.org.br");
    65 
    66 /* GpacMux signals and args */
    67 enum
    68 {
    69   /* FILL ME */
    70   LAST_SIGNAL
    71 };
    72 
    73 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    74     GST_PAD_SRC,
    75     GST_PAD_ALWAYS,
    76     GST_STATIC_CAPS ("video/quicktime")
    77     );
    78 
    79 static GstStaticPadTemplate audio_sink_factory = GST_STATIC_PAD_TEMPLATE ("audio_%d",
    80     GST_PAD_SINK,
    81     GST_PAD_REQUEST,
    82     GST_STATIC_CAPS (
    83         "audio/mpeg, " 
    84         "mpegversion = (int) 4, " 
    85         "channels = (int) { 1, 2 }, " 
    86         "rate = (int) [ 8000, 96000 ]")
    87     );
    88     //GST_STATIC_CAPS ("audio/mpeg, mpegversion = 1, layer = 3")
    89 
    90 static GstStaticPadTemplate video_sink_factory = GST_STATIC_PAD_TEMPLATE ("video_%d",
    91     GST_PAD_SINK,
    92     GST_PAD_REQUEST,
    93     GST_STATIC_CAPS ("video/mpeg, mpegversion = 4")
    94    );
    95 
    96 static void gst_gpac_mux_base_init (gpointer g_class);
    97 static void gst_gpac_mux_class_init (GstGpacMuxClass * klass);
    98 static void gst_gpac_mux_init (GstGpacMux * gpac_mux);
    99 static void gst_gpac_mux_finalize (GObject * object);
   100 
   101 static GstFlowReturn
   102 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux);
   103 
   104 static gboolean gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event);
   105 static GstPad *gst_gpac_mux_request_new_pad (GstElement * element,
   106     GstPadTemplate * templ, const gchar * name);
   107 static void gst_gpac_mux_release_pad (GstElement * element, GstPad * pad);
   108 
   109 static GstStateChangeReturn gst_gpac_mux_change_state (GstElement * element,
   110     GstStateChange transition);
   111 
   112 static GstElementClass *parent_class = NULL;
   113 
   114 GType
   115 gst_gpac_mux_get_type (void)
   116 {
   117   static GType gpac_mux_type = 0;
   118 
   119   if (G_UNLIKELY (gpac_mux_type == 0)) {
   120     static const GTypeInfo gpac_mux_info = {
   121       sizeof (GstGpacMuxClass),
   122       gst_gpac_mux_base_init,
   123       NULL,
   124       (GClassInitFunc) gst_gpac_mux_class_init,
   125       NULL,
   126       NULL,
   127       sizeof (GstGpacMux),
   128       0,
   129       (GInstanceInitFunc) gst_gpac_mux_init,
   130     };
   131 
   132     gpac_mux_type =
   133         g_type_register_static (GST_TYPE_ELEMENT, "GstGpacMux", &gpac_mux_info,
   134         0);
   135   }
   136   return gpac_mux_type;
   137 }
   138 
   139 static void
   140 gst_gpac_mux_base_init (gpointer g_class)
   141 {
   142   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
   143 
   144   gst_element_class_add_pad_template (element_class,
   145       gst_static_pad_template_get (&src_factory));
   146   gst_element_class_add_pad_template (element_class,
   147       gst_static_pad_template_get (&audio_sink_factory));
   148   gst_element_class_add_pad_template (element_class,
   149       gst_static_pad_template_get (&video_sink_factory));
   150 
   151   gst_element_class_set_details (element_class, &gst_gpac_mux_details);
   152 }
   153 
   154 static void
   155 gst_gpac_mux_class_init (GstGpacMuxClass * klass)
   156 {
   157   GObjectClass *gobject_class;
   158   GstElementClass *gstelement_class;
   159 
   160   gobject_class = (GObjectClass *) klass;
   161   gstelement_class = (GstElementClass *) klass;
   162 
   163   parent_class = g_type_class_peek_parent (klass);
   164 
   165   gobject_class->finalize = gst_gpac_mux_finalize;
   166 
   167   gstelement_class->request_new_pad = gst_gpac_mux_request_new_pad;
   168   gstelement_class->release_pad = gst_gpac_mux_release_pad;
   169 
   170   gstelement_class->change_state = gst_gpac_mux_change_state;
   171 
   172 }
   173 
   174 static void
   175 gst_gpac_mux_init (GstGpacMux * gpac_mux)
   176 {
   177   GstElementClass *klass = GST_ELEMENT_GET_CLASS (gpac_mux);
   178 
   179   gpac_mux->srcpad =
   180       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
   181           "src"), "src");
   182   gst_pad_set_event_function (gpac_mux->srcpad, gst_gpac_mux_handle_src_event);
   183   gst_element_add_pad (GST_ELEMENT (gpac_mux), gpac_mux->srcpad);
   184 
   185   gpac_mux->collect = gst_collect_pads_new ();
   186   gst_collect_pads_set_function (gpac_mux->collect,
   187       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_gpac_mux_collected),
   188       gpac_mux);
   189 
   190   /* Opens gpac library */
   191   /* FIXME */
   192   gpac_mux->file = gf_isom_open("/tmp/gpac.mp4", GF_ISOM_OPEN_WRITE, NULL);
   193   gf_isom_set_storage_mode(gpac_mux->file, GF_ISOM_STORE_FLAT /*STREAMABLE*/);
   194 
   195   //gst_gpac_mux_clear (gpac_mux);
   196 }
   197 
   198 static void
   199 gst_gpac_mux_finalize (GObject * object)
   200 {
   201   GstGpacMux *gpac_mux;
   202 
   203   gpac_mux = GST_GPAC_MUX (object);
   204 
   205   if (gpac_mux->collect) {
   206     gst_object_unref (gpac_mux->collect);
   207     gpac_mux->collect = NULL;
   208   }
   209 
   210   G_OBJECT_CLASS (parent_class)->finalize (object);
   211 }
   212 
   213 static void
   214 gst_gpac_mux_gpac_pad_destroy_notify (GstCollectData * data)
   215 {
   216   GstGpacPad *gpacpad = (GstGpacPad *) data;
   217   GstBuffer *buf;
   218 
   219 /*  if (gpacpad->pagebuffers) {
   220     while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
   221       gst_buffer_unref (buf);
   222     }
   223     g_queue_free (gpacpad->pagebuffers);
   224     gpacpad->pagebuffers = NULL;
   225   }*/
   226 }
   227 
   228 static GstPadLinkReturn
   229 gst_gpac_mux_sinkconnect (GstPad * pad, GstPad * peer)
   230 {
   231   GstGpacMux *gpac_mux;
   232 
   233   gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
   234 
   235   GST_DEBUG_OBJECT (gpac_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
   236 
   237   gst_object_unref (gpac_mux);
   238 
   239   return GST_PAD_LINK_OK;
   240 }
   241 
   242 static GstPad *
   243 gst_gpac_mux_request_new_pad (GstElement * element,
   244     GstPadTemplate * templ, const gchar * req_name)
   245 {
   246   GstGpacMux *gpac_mux;
   247   GstPad *newpad;
   248   GstElementClass *klass;
   249   gchar *padname = NULL;
   250   gint serial;
   251   gboolean is_video = FALSE;
   252 
   253   g_return_val_if_fail (templ != NULL, NULL);
   254 
   255   if (templ->direction != GST_PAD_SINK)
   256     goto wrong_direction;
   257 
   258   g_return_val_if_fail (GST_IS_GPAC_MUX (element), NULL);
   259   gpac_mux = GST_GPAC_MUX (element);
   260 
   261   klass = GST_ELEMENT_GET_CLASS (element);
   262 
   263     if (req_name == NULL || strlen (req_name) < 6) {
   264       /* no name given when requesting the pad, use random serial number */
   265       serial = rand ();
   266     } else {
   267       /* parse serial number from requested padname */
   268       serial = atoi (&req_name[5]);
   269     }
   270 
   271   if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
   272     is_video = TRUE;      
   273     padname = g_strdup_printf ("video_%d", serial);
   274   } else if (templ != gst_element_class_get_pad_template (klass, "audio_%d")) {
   275     goto wrong_template;
   276   } else {
   277     padname = g_strdup_printf ("audio_%d", serial);
   278   }
   279 
   280 
   281   {
   282     
   283     /* create new pad with the name */
   284     GST_DEBUG_OBJECT (gpac_mux, "Creating new pad for serial %d", serial);
   285     printf ("XXXX new pad from template\n");
   286     newpad = gst_pad_new_from_template (templ, padname);
   287     g_free (padname);
   288 
   289     /* construct our own wrapper data structure for the pad to
   290      * keep track of its status */
   291     {
   292       GstGpacPad *gpacpad;
   293 
   294       gpacpad = (GstGpacPad *)
   295           gst_collect_pads_add_pad_full (gpac_mux->collect, newpad,
   296           sizeof (GstGpacPad), gst_gpac_mux_gpac_pad_destroy_notify);
   297 
   298       /* gpac new track */
   299       gpacpad->is_video = is_video;
   300       if (gpacpad->is_video) {
   301         gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, 
   302           GF_ISOM_MEDIA_VISUAL, 10 * 1000 /* time scale */);
   303       } else {
   304         gpacpad->track_number = gf_isom_new_track(gpac_mux->file, 0, 
   305           GF_ISOM_MEDIA_AUDIO, 48000 /*time scale */);
   306         
   307       }
   308       if (gpacpad->track_number == 0) {
   309         g_warning ("Error while adding the new gpac track");
   310       } else {
   311         gf_isom_set_track_enabled(gpac_mux->file, gpacpad->track_number, 1);
   312         if (is_video) {
   313           GF_ESD *esd = gf_odf_desc_esd_new (0);
   314           esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
   315 
   316           gf_isom_new_mpeg4_description( gpac_mux->file, gpacpad->track_number,
   317                         esd, NULL, NULL, &(gpacpad->di));
   318 
   319            gf_isom_set_visual_info (gpac_mux->file, gpacpad->track_number, gpacpad->di, 320, 240);//fixme
   320           //gf_isom_set_cts_packing (gpac_mux->file, gpacpad->track_number, 0);
   321         } else {
   322           GF_ESD *esd = gf_odf_desc_esd_new (2);
   323           esd->ESID = gf_isom_get_track_id(gpac_mux->file, gpacpad->track_number);
   324 
   325           gf_isom_new_mpeg4_description(gpac_mux->file, gpacpad->track_number,
   326                          esd, NULL, NULL, &(gpacpad->di));
   327 
   328           gf_isom_set_audio_info(gpac_mux->file, gpacpad->track_number, 
   329                           gpacpad->di, 48000, 2 /*num channels */, 16);
   330         }
   331       }
   332     }
   333   }
   334 
   335   /* setup some pad functions */
   336   gst_pad_set_link_function (newpad, gst_gpac_mux_sinkconnect);
   337   /* dd the pad to the element */
   338   gst_element_add_pad (element, newpad);
   339 
   340   return newpad;
   341 
   342   /* ERRORS */
   343 wrong_direction:
   344   {
   345     g_warning ("gpac_mux: request pad that is not a SINK pad\n");
   346     return NULL;
   347   }
   348 wrong_template:
   349   {
   350     g_warning ("gpac_mux: this is not our template!\n");
   351     return NULL;
   352   }
   353 }
   354 
   355 static void
   356 gst_gpac_mux_release_pad (GstElement * element, GstPad * pad)
   357 {
   358   GstGpacMux *gpac_mux;
   359 
   360   gpac_mux = GST_GPAC_MUX (gst_pad_get_parent (pad));
   361 
   362   gst_collect_pads_remove_pad (gpac_mux->collect, pad);
   363   gst_element_remove_pad (element, pad);
   364 }
   365 
   366 /* handle events */
   367 static gboolean
   368 gst_gpac_mux_handle_src_event (GstPad * pad, GstEvent * event)
   369 {
   370   GstEventType type;
   371 
   372   type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
   373 
   374   switch (type) {
   375     case GST_EVENT_SEEK:
   376       /* disable seeking for now */
   377       return FALSE;
   378     default:
   379       break;
   380   }
   381 
   382   return gst_pad_event_default (pad, event);
   383 }
   384 
   385 static GstFlowReturn
   386 gst_gpac_mux_push_buffer (GstGpacMux * mux, GstBuffer * buffer)
   387 {
   388   GstCaps *caps;
   389 #if 0
   390   /* fix up OFFSET and OFFSET_END again */
   391   GST_BUFFER_OFFSET (buffer) = mux->offset;
   392   mux->offset += GST_BUFFER_SIZE (buffer);
   393   GST_BUFFER_OFFSET_END (buffer) = mux->offset;
   394 
   395   /* Ensure we have monotonically increasing timestamps in the output. */
   396   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
   397     if (mux->last_ts != GST_CLOCK_TIME_NONE &&
   398         GST_BUFFER_TIMESTAMP (buffer) < mux->last_ts)
   399       GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
   400     else
   401       mux->last_ts = GST_BUFFER_TIMESTAMP (buffer);
   402   }
   403 #endif
   404   
   405   caps = gst_pad_get_negotiated_caps (mux->srcpad);
   406   gst_buffer_set_caps (buffer, caps);
   407   if (caps != NULL) {
   408     gst_caps_unref (caps);
   409   }
   410 
   411   return gst_pad_push (mux->srcpad, buffer);
   412 }
   413 
   414 static gboolean
   415 all_pads_eos (GstCollectPads * pads)
   416 {
   417   GSList *iter;
   418   gboolean alleos = TRUE;
   419 
   420   iter = pads->data;
   421   while (iter) {
   422     GstBuffer *buf;
   423     GstCollectData *data = (GstCollectData *) iter->data;
   424 
   425     buf = gst_collect_pads_peek (pads, data);
   426     if (buf) {
   427       alleos = FALSE;
   428       gst_buffer_unref (buf);
   429       goto beach;
   430     }
   431     iter = iter->next;
   432   }
   433 beach:
   434   return alleos;
   435 }
   436  
   437 static GstFlowReturn
   438 gst_gpac_mux_collected (GstCollectPads * pads, GstGpacMux * gpac_mux)
   439 {
   440   GstFlowReturn ret = GST_FLOW_OK;
   441   GSList *iter;
   442   GstBuffer *buf;
   443   gint i = 0;
   444 
   445   GST_LOG_OBJECT (gpac_mux, "collected");
   446 
   447   iter = gpac_mux->collect->data;
   448   while (iter) {
   449     GstCollectData *data = (GstCollectData *) iter->data;
   450     GstGpacPad *pad = (GstGpacPad *) data;
   451 
   452     buf = gst_collect_pads_pop (gpac_mux->collect, data);
   453   if (buf == NULL) {
   454     iter = g_slist_next (iter);
   455       continue;
   456   }
   457     GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
   458 
   459     /* gpac output */
   460     printf ("xxxxxx buffer size: %d\n", GST_BUFFER_SIZE(buf)); fflush (stdout);
   461     if (pad->frame_count < 300) {
   462       GF_ISOSample *sample = gf_isom_sample_new();
   463 
   464        sample->dataLength = GST_BUFFER_SIZE(buf);
   465        sample->data = GST_BUFFER_DATA(buf);
   466        sample->CTS_Offset = 0;
   467 
   468       if (pad->is_video) {
   469         sample->IsRAP = 0;
   470         sample->DTS += 1000*pad->frame_count;
   471       } else {
   472         sample->IsRAP = 0;
   473         sample->DTS = 2048*pad->frame_count;
   474       }
   475 
   476       gf_isom_add_sample(gpac_mux->file, pad->track_number, 
   477                       pad->di, sample);
   478       sample->data = NULL;
   479 
   480       gf_isom_sample_del(&sample);
   481 
   482       printf ("xxxx frames %d\n", pad->frame_count);
   483 
   484     } else if (pad->frame_count == 300) {
   485       printf ("XXX closing gpac output file\n"); fflush (stdout);
   486       gf_isom_close (gpac_mux->file);
   487     }
   488 
   489     /* gstreamer output (push) */
   490     if (gst_gpac_mux_push_buffer (gpac_mux, buf) != GST_FLOW_OK) {
   491       printf ("EEEEEEEE push failed\n");
   492     }
   493 
   494     iter = g_slist_next (iter);
   495     pad->frame_count++;
   496     i++;
   497   }
   498 
   499   /* fixme */
   500   return ret;
   501 }
   502 
   503 /* reset all variables in the gpac pads. */
   504 static void
   505 gst_gpac_mux_init_collectpads (GstCollectPads * collect)
   506 {
   507   GSList *walk;
   508 
   509   walk = collect->data;
   510   while (walk) {
   511     GstGpacPad *gpacpad = (GstGpacPad *) walk->data;
   512 
   513     //ogg_stream_init (&gpacpad->stream, gpacpad->serial);
   514     //gpacpad->packetno = 0;
   515     //gpacpad->pageno = 0;
   516     //gpacpad->eos = FALSE;
   517     /* we assume there will be some control data first for this pad */
   518     //gpacpad->state = GST_GPAC_PAD_STATE_CONTROL;
   519     //gpacpad->new_page = TRUE;
   520     //gpacpad->first_delta = FALSE;
   521     //gpacpad->prev_delta = FALSE;
   522     //gpacpad->pagebuffers = g_queue_new ();
   523 
   524     walk = g_slist_next (walk);
   525   }
   526 }
   527 
   528 /* Clear all buffers from the collectpads object */
   529 static void
   530 gst_gpac_mux_clear_collectpads (GstCollectPads * collect)
   531 {
   532   GSList *walk;
   533 
   534   for (walk = collect->data; walk; walk = g_slist_next (walk)) {
   535     GstGpacPad *gpacpad = (GstGpacPad *) walk->data;
   536     GstBuffer *buf;
   537 
   538     //gpac_stream_clear (&gpacpad->stream);
   539 /*
   540     while ((buf = g_queue_pop_head (gpacpad->pagebuffers)) != NULL) {
   541       gst_buffer_unref (buf);
   542     }
   543     g_queue_free (gpacpad->pagebuffers);
   544     gpacpad->pagebuffers = NULL;*/
   545   }
   546 }
   547 
   548 static GstStateChangeReturn
   549 gst_gpac_mux_change_state (GstElement * element, GstStateChange transition)
   550 {
   551   GstGpacMux *gpac_mux;
   552   GstStateChangeReturn ret;
   553 
   554   gpac_mux = GST_GPAC_MUX (element);
   555 
   556   switch (transition) {
   557     case GST_STATE_CHANGE_NULL_TO_READY:
   558       break;
   559     case GST_STATE_CHANGE_READY_TO_PAUSED:
   560       //gst_gpac_mux_clear (gpac_mux);
   561       //gst_gpac_mux_init_collectpads (gpac_mux->collect);
   562       gst_collect_pads_start (gpac_mux->collect);
   563       break;
   564     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
   565       break;
   566     case GST_STATE_CHANGE_PAUSED_TO_READY:
   567       gst_collect_pads_stop (gpac_mux->collect);
   568       break;
   569     default:
   570       break;
   571   }
   572 
   573   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
   574 
   575   switch (transition) {
   576     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
   577       break;
   578     case GST_STATE_CHANGE_PAUSED_TO_READY:
   579       gst_gpac_mux_clear_collectpads (gpac_mux->collect);
   580       break;
   581     case GST_STATE_CHANGE_READY_TO_NULL:
   582       break;
   583     default:
   584       break;
   585   }
   586 
   587   return ret;
   588 
   589 }
   590 
   591 static gboolean
   592 gst_gpac_mux_plugin_init (GstPlugin * plugin)
   593 {
   594   return gst_element_register (plugin, "gpacmux", GST_RANK_NONE,
   595       GST_TYPE_GPAC_MUX);
   596 }
   597 
   598 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
   599                   GST_VERSION_MINOR,
   600                   "gpacmux",
   601                   "Muxes audio and video",
   602                   gst_gpac_mux_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
   603                   GST_PACKAGE_ORIGIN)
   604