gst_plugins_base/ext/ogg/gstoggdemux.c
branchRCL_3
changeset 30 7e817e7e631c
parent 0 0e761a78d257
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
       
     1 /* GStreamer
       
     2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
       
     3  *
       
     4  * gstoggdemux.c: ogg stream demuxer
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 /**
       
    23  * SECTION:element-oggdemux
       
    24  * @short_description: a demuxer for ogg files
       
    25  *
       
    26  * <refsect2>
       
    27  * <para>
       
    28  * This element demuxes ogg files into their encoded audio and video components.
       
    29  * </para>
       
    30  * <title>Example pipelines</title>
       
    31  * <para>
       
    32  * <programlisting>
       
    33  * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
       
    34  * </programlisting>
       
    35  * Decodes the vorbis audio stored inside an ogg container.
       
    36  * </para>
       
    37  * </refsect2>
       
    38  *
       
    39  * Last reviewed on 2006-12-30 (0.10.5)
       
    40  */
       
    41 
       
    42 
       
    43 #ifdef HAVE_CONFIG_H
       
    44 #include "config.h"
       
    45 #endif
       
    46 #include <string.h>
       
    47 #include <gst/gst-i18n-plugin.h>
       
    48 #include <gst/base/gsttypefindhelper.h>
       
    49 
       
    50 #include "gstoggdemux.h"
       
    51 
       
    52 static const GstElementDetails gst_ogg_demux_details =
       
    53 GST_ELEMENT_DETAILS ("Ogg demuxer",
       
    54     "Codec/Demuxer",
       
    55     "demux ogg streams (info about ogg: http://xiph.org)",
       
    56     "Wim Taymans <wim@fluendo.com>");
       
    57 
       
    58 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
       
    59 #define SKELETON_FISHEAD_SIZE 64
       
    60 #define SKELETON_FISBONE_MIN_SIZE 52
       
    61 
       
    62 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
       
    63 
       
    64 GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
       
    65 GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
       
    66 #define GST_CAT_DEFAULT gst_ogg_demux_debug
       
    67 
       
    68 static ogg_page *
       
    69 gst_ogg_page_copy (ogg_page * page)
       
    70 {
       
    71   ogg_page *p = g_new0 (ogg_page, 1);
       
    72 
       
    73   /* make a copy of the page */
       
    74   p->header = g_memdup (page->header, page->header_len);
       
    75   p->header_len = page->header_len;
       
    76   p->body = g_memdup (page->body, page->body_len);
       
    77   p->body_len = page->body_len;
       
    78 
       
    79   return p;
       
    80 }
       
    81 
       
    82 static void
       
    83 gst_ogg_page_free (ogg_page * page)
       
    84 {
       
    85   g_free (page->header);
       
    86   g_free (page->body);
       
    87   g_free (page);
       
    88 }
       
    89 
       
    90 static GstStaticPadTemplate internaltemplate =
       
    91 GST_STATIC_PAD_TEMPLATE ("internal",
       
    92     GST_PAD_SINK,
       
    93     GST_PAD_ALWAYS,
       
    94     GST_STATIC_CAPS_ANY);
       
    95 
       
    96 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
       
    97     GstOggChain * chain);
       
    98 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
       
    99     GstOggChain * chain, GstEvent * event);
       
   100 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
       
   101 
       
   102 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
       
   103     GstEvent * event);
       
   104 static gboolean gst_ogg_demux_receive_event (GstElement * element,
       
   105     GstEvent * event);
       
   106 
       
   107 static void gst_ogg_pad_class_init (GstOggPadClass * klass);
       
   108 static void gst_ogg_pad_init (GstOggPad * pad);
       
   109 static void gst_ogg_pad_dispose (GObject * object);
       
   110 static void gst_ogg_pad_finalize (GObject * object);
       
   111 
       
   112 #if 0
       
   113 static const GstFormat *gst_ogg_pad_formats (GstPad * pad);
       
   114 static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad);
       
   115 #endif
       
   116 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
       
   117 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
       
   118 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
       
   119 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
       
   120 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
       
   121     glong serialno);
       
   122 
       
   123 static gboolean gst_ogg_pad_query_convert (GstOggPad * pad,
       
   124     GstFormat src_format, gint64 src_val,
       
   125     GstFormat * dest_format, gint64 * dest_val);
       
   126 static GstClockTime gst_annodex_granule_to_time (gint64 granulepos,
       
   127     gint64 granulerate_n, gint64 granulerate_d, guint8 granuleshift);
       
   128 
       
   129 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
       
   130     GstOggPad * pad, GstFlowReturn ret);
       
   131 
       
   132 static GstPadClass *ogg_pad_parent_class = NULL;
       
   133 
       
   134 static GType
       
   135 gst_ogg_pad_get_type (void)
       
   136 {
       
   137   static GType ogg_pad_type = 0;
       
   138 
       
   139   if (!ogg_pad_type) {
       
   140     static const GTypeInfo ogg_pad_info = {
       
   141       sizeof (GstOggPadClass),
       
   142       NULL,
       
   143       NULL,
       
   144       (GClassInitFunc) gst_ogg_pad_class_init,
       
   145       NULL,
       
   146       NULL,
       
   147       sizeof (GstOggPad),
       
   148       0,
       
   149       (GInstanceInitFunc) gst_ogg_pad_init,
       
   150     };
       
   151 
       
   152     ogg_pad_type =
       
   153         g_type_register_static (GST_TYPE_PAD, "GstOggPad", &ogg_pad_info, 0);
       
   154   }
       
   155   return ogg_pad_type;
       
   156 }
       
   157 
       
   158 static void
       
   159 gst_ogg_pad_class_init (GstOggPadClass * klass)
       
   160 {
       
   161   GObjectClass *gobject_class;
       
   162 
       
   163   gobject_class = (GObjectClass *) klass;
       
   164 
       
   165   ogg_pad_parent_class = g_type_class_peek_parent (klass);
       
   166 
       
   167   gobject_class->dispose = gst_ogg_pad_dispose;
       
   168   gobject_class->finalize = gst_ogg_pad_finalize;
       
   169 }
       
   170 
       
   171 static void
       
   172 gst_ogg_pad_init (GstOggPad * pad)
       
   173 {
       
   174   gst_pad_set_event_function (GST_PAD (pad),
       
   175       GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
       
   176   gst_pad_set_getcaps_function (GST_PAD (pad),
       
   177       GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
       
   178   gst_pad_set_query_type_function (GST_PAD (pad),
       
   179       GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
       
   180   gst_pad_set_query_function (GST_PAD (pad),
       
   181       GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
       
   182 
       
   183   pad->mode = GST_OGG_PAD_MODE_INIT;
       
   184 
       
   185   pad->first_granule = -1;
       
   186   pad->current_granule = -1;
       
   187 
       
   188   pad->start_time = GST_CLOCK_TIME_NONE;
       
   189   pad->first_time = GST_CLOCK_TIME_NONE;
       
   190 
       
   191   pad->have_type = FALSE;
       
   192   pad->continued = NULL;
       
   193   pad->headers = NULL;
       
   194 }
       
   195 
       
   196 static void
       
   197 gst_ogg_pad_dispose (GObject * object)
       
   198 {
       
   199   GstOggPad *pad = GST_OGG_PAD (object);
       
   200   GstPad **elem_pad_p;
       
   201   GstElement **element_p;
       
   202   GstPad **elem_out_p;
       
   203 
       
   204   if (pad->element)
       
   205     gst_element_set_state (pad->element, GST_STATE_NULL);
       
   206 
       
   207   elem_pad_p = &pad->elem_pad;
       
   208   element_p = &pad->element;
       
   209   elem_out_p = &pad->elem_out;
       
   210   gst_object_replace ((GstObject **) elem_pad_p, NULL);
       
   211   gst_object_replace ((GstObject **) element_p, NULL);
       
   212   gst_object_replace ((GstObject **) elem_out_p, NULL);
       
   213 
       
   214   pad->chain = NULL;
       
   215   pad->ogg = NULL;
       
   216 
       
   217   g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
       
   218   g_list_free (pad->headers);
       
   219   pad->headers = NULL;
       
   220 
       
   221   /* clear continued pages */
       
   222   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
       
   223   g_list_free (pad->continued);
       
   224   pad->continued = NULL;
       
   225 
       
   226   ogg_stream_reset (&pad->stream);
       
   227 
       
   228   G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
       
   229 }
       
   230 
       
   231 static void
       
   232 gst_ogg_pad_finalize (GObject * object)
       
   233 {
       
   234   GstOggPad *pad = GST_OGG_PAD (object);
       
   235 
       
   236   ogg_stream_clear (&pad->stream);
       
   237 
       
   238   G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
       
   239 }
       
   240 
       
   241 #if 0
       
   242 static const GstFormat *
       
   243 gst_ogg_pad_formats (GstPad * pad)
       
   244 {
       
   245   static GstFormat src_formats[] = {
       
   246     GST_FORMAT_DEFAULT,         /* time */
       
   247     GST_FORMAT_TIME,            /* granulepos */
       
   248     0
       
   249   };
       
   250   static GstFormat sink_formats[] = {
       
   251     GST_FORMAT_BYTES,
       
   252     GST_FORMAT_DEFAULT,         /* bytes */
       
   253     0
       
   254   };
       
   255 
       
   256   return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
       
   257 }
       
   258 #endif
       
   259 
       
   260 #if 0
       
   261 static const GstEventMask *
       
   262 gst_ogg_pad_event_masks (GstPad * pad)
       
   263 {
       
   264   static const GstEventMask src_event_masks[] = {
       
   265     {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
       
   266     {0,}
       
   267   };
       
   268 
       
   269   return src_event_masks;
       
   270 }
       
   271 #endif
       
   272 
       
   273 static const GstQueryType *
       
   274 gst_ogg_pad_query_types (GstPad * pad)
       
   275 {
       
   276   static const GstQueryType query_types[] = {
       
   277     GST_QUERY_DURATION,
       
   278     GST_QUERY_SEEKING,
       
   279     0
       
   280   };
       
   281 
       
   282   return query_types;
       
   283 }
       
   284 
       
   285 static GstCaps *
       
   286 gst_ogg_pad_getcaps (GstPad * pad)
       
   287 {
       
   288   return gst_caps_ref (GST_PAD_CAPS (pad));
       
   289 }
       
   290 
       
   291 static gboolean
       
   292 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
       
   293 {
       
   294   gboolean res = TRUE;
       
   295   GstOggDemux *ogg;
       
   296   GstOggPad *cur;
       
   297 
       
   298   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
       
   299   cur = GST_OGG_PAD (pad);
       
   300 
       
   301   switch (GST_QUERY_TYPE (query)) {
       
   302     case GST_QUERY_DURATION:
       
   303     {
       
   304       GstFormat format;
       
   305 
       
   306       gst_query_parse_duration (query, &format, NULL);
       
   307       /* can only get position in time */
       
   308       if (format != GST_FORMAT_TIME)
       
   309         goto wrong_format;
       
   310 
       
   311       /* can only return the total time position */
       
   312       /* FIXME, return time for this specific stream */
       
   313       gst_query_set_duration (query, GST_FORMAT_TIME, ogg->total_time);
       
   314       break;
       
   315     }
       
   316     case GST_QUERY_SEEKING:
       
   317     {
       
   318       GstFormat format;
       
   319 
       
   320       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
       
   321       if (format == GST_FORMAT_TIME) {
       
   322         gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->seekable,
       
   323             0, ogg->total_time);
       
   324       } else {
       
   325         res = FALSE;
       
   326       }
       
   327       break;
       
   328     }
       
   329 
       
   330     default:
       
   331       res = gst_pad_query_default (pad, query);
       
   332       break;
       
   333   }
       
   334 done:
       
   335   gst_object_unref (ogg);
       
   336 
       
   337   return res;
       
   338 
       
   339   /* ERRORS */
       
   340 wrong_format:
       
   341   {
       
   342     GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
       
   343     res = FALSE;
       
   344     goto done;
       
   345   }
       
   346 }
       
   347 
       
   348 static gboolean
       
   349 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
       
   350 {
       
   351   gboolean res;
       
   352   GstOggDemux *ogg;
       
   353 
       
   354   ogg = GST_OGG_DEMUX (element);
       
   355 
       
   356   switch (GST_EVENT_TYPE (event)) {
       
   357     case GST_EVENT_SEEK:
       
   358       /* can't seek if we are not seekable, FIXME could pass the
       
   359        * seek query upstream after converting it to bytes using
       
   360        * the average bitrate of the stream. */
       
   361       if (!ogg->seekable) {
       
   362         GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
       
   363         goto error;
       
   364       }
       
   365 
       
   366       /* now do the seek */
       
   367       res = gst_ogg_demux_perform_seek (ogg, event);
       
   368       gst_event_unref (event);
       
   369       break;
       
   370     default:
       
   371       GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
       
   372       goto error;
       
   373   }
       
   374 
       
   375   return res;
       
   376 
       
   377   /* ERRORS */
       
   378 error:
       
   379   {
       
   380     GST_DEBUG_OBJECT (ogg, "error handling event");
       
   381     gst_event_unref (event);
       
   382     return FALSE;
       
   383   }
       
   384 }
       
   385 
       
   386 static gboolean
       
   387 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
       
   388 {
       
   389   gboolean res;
       
   390   GstOggDemux *ogg;
       
   391   GstOggPad *cur;
       
   392 
       
   393   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
       
   394   cur = GST_OGG_PAD (pad);
       
   395 
       
   396   switch (GST_EVENT_TYPE (event)) {
       
   397     case GST_EVENT_SEEK:
       
   398       /* can't seek if we are not seekable, FIXME could pass the
       
   399        * seek query upstream after converting it to bytes using
       
   400        * the average bitrate of the stream. */
       
   401       if (!ogg->seekable) {
       
   402         GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
       
   403         goto error;
       
   404       }
       
   405 
       
   406       /* now do the seek */
       
   407       res = gst_ogg_demux_perform_seek (ogg, event);
       
   408       gst_event_unref (event);
       
   409       break;
       
   410     default:
       
   411       res = gst_pad_event_default (pad, event);
       
   412       break;
       
   413   }
       
   414 done:
       
   415   gst_object_unref (ogg);
       
   416 
       
   417   return res;
       
   418 
       
   419   /* ERRORS */
       
   420 error:
       
   421   {
       
   422     GST_DEBUG_OBJECT (ogg, "error handling event");
       
   423     gst_event_unref (event);
       
   424     res = FALSE;
       
   425     goto done;
       
   426   }
       
   427 }
       
   428 
       
   429 static void
       
   430 gst_ogg_pad_reset (GstOggPad * pad)
       
   431 {
       
   432   ogg_stream_reset (&pad->stream);
       
   433 
       
   434   /* clear continued pages */
       
   435   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
       
   436   g_list_free (pad->continued);
       
   437   pad->continued = NULL;
       
   438 
       
   439   pad->last_ret = GST_FLOW_OK;
       
   440 }
       
   441 
       
   442 /* the filter function for selecting the elements we can use in
       
   443  * autoplugging */
       
   444 static gboolean
       
   445 gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
       
   446 {
       
   447   guint rank;
       
   448   const gchar *klass;
       
   449 
       
   450   /* we only care about element factories */
       
   451   if (!GST_IS_ELEMENT_FACTORY (feature))
       
   452     return FALSE;
       
   453 
       
   454   klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
       
   455   /* only demuxers and decoders can play */
       
   456   if (strstr (klass, "Demux") == NULL &&
       
   457       strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
       
   458     return FALSE;
       
   459   }
       
   460 
       
   461   /* only select elements with autoplugging rank */
       
   462   rank = gst_plugin_feature_get_rank (feature);
       
   463   if (rank < GST_RANK_MARGINAL)
       
   464     return FALSE;
       
   465 
       
   466   GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
       
   467   /* now see if it is compatible with the caps */
       
   468   {
       
   469     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
       
   470     const GList *templates;
       
   471     GList *walk;
       
   472 
       
   473     /* get the templates from the element factory */
       
   474     templates = gst_element_factory_get_static_pad_templates (factory);
       
   475 
       
   476     for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
       
   477       GstStaticPadTemplate *templ = walk->data;
       
   478 
       
   479       /* we only care about the sink templates */
       
   480       if (templ->direction == GST_PAD_SINK) {
       
   481         GstCaps *intersect;
       
   482         GstCaps *scaps;
       
   483         gboolean empty;
       
   484 
       
   485         /* try to intersect the caps with the caps of the template */
       
   486         scaps = gst_static_caps_get (&templ->static_caps);
       
   487         intersect = gst_caps_intersect (caps, scaps);
       
   488         gst_caps_unref (scaps);
       
   489 
       
   490         empty = gst_caps_is_empty (intersect);
       
   491         gst_caps_unref (intersect);
       
   492 
       
   493         /* check if the intersection is empty */
       
   494         if (!empty) {
       
   495           /* non empty intersection, we can use this element */
       
   496           goto found;
       
   497         }
       
   498       }
       
   499     }
       
   500   }
       
   501   return FALSE;
       
   502 
       
   503 found:
       
   504   return TRUE;
       
   505 }
       
   506 
       
   507 /* function used to sort element features */
       
   508 static gint
       
   509 compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
       
   510 {
       
   511   gint diff;
       
   512 
       
   513   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
       
   514   if (diff != 0)
       
   515     return diff;
       
   516   return strcmp (gst_plugin_feature_get_name (f2),
       
   517       gst_plugin_feature_get_name (f1));
       
   518 }
       
   519 
       
   520 /* called when the skeleton fishead is found. Caller ensures the packet is
       
   521  * precisely the correct size; we don't re-check this here. */
       
   522 static void
       
   523 gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
       
   524 {
       
   525   GstOggDemux *ogg = pad->ogg;
       
   526   guint8 *data = packet->packet;
       
   527   guint16 major, minor;
       
   528   gint64 prestime_n, prestime_d;
       
   529   gint64 basetime_n, basetime_d;
       
   530 
       
   531   /* skip "fishead\0" */
       
   532   data += 8;
       
   533   major = GST_READ_UINT16_LE (data);
       
   534   data += 2;
       
   535   minor = GST_READ_UINT16_LE (data);
       
   536   data += 2;
       
   537   prestime_n = (gint64) GST_READ_UINT64_LE (data);
       
   538   data += 8;
       
   539   prestime_d = (gint64) GST_READ_UINT64_LE (data);
       
   540   data += 8;
       
   541   basetime_n = (gint64) GST_READ_UINT64_LE (data);
       
   542   data += 8;
       
   543   basetime_d = (gint64) GST_READ_UINT64_LE (data);
       
   544   data += 8;
       
   545 
       
   546   ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
       
   547   ogg->have_fishead = TRUE;
       
   548   pad->is_skeleton = TRUE;
       
   549   pad->start_time = GST_CLOCK_TIME_NONE;
       
   550   pad->first_granule = -1;
       
   551   pad->first_time = GST_CLOCK_TIME_NONE;
       
   552   GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
       
   553       GST_TIME_FORMAT ")", GST_TIME_ARGS (ogg->basetime));
       
   554 }
       
   555 
       
   556 /* function called when a skeleton fisbone is found. Caller ensures that
       
   557  * the packet length is sufficient */
       
   558 static void
       
   559 gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
       
   560 {
       
   561   GstOggPad *fisbone_pad;
       
   562   gint64 start_granule;
       
   563   guint32 serialno;
       
   564   guint8 *data = packet->packet;
       
   565 
       
   566   /* skip "fisbone\0" */
       
   567   data += 8;
       
   568   /* skip headers offset */
       
   569   data += 4;
       
   570   serialno = GST_READ_UINT32_LE (data);
       
   571 
       
   572   fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
       
   573   if (fisbone_pad) {
       
   574     if (fisbone_pad->have_fisbone)
       
   575       /* already parsed */
       
   576       return;
       
   577 
       
   578     fisbone_pad->have_fisbone = TRUE;
       
   579 
       
   580     data += 4;
       
   581     /* skip number of headers */
       
   582     data += 4;
       
   583     fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
       
   584     data += 8;
       
   585     fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
       
   586     data += 8;
       
   587     start_granule = GST_READ_UINT64_LE (data);
       
   588     data += 8;
       
   589     fisbone_pad->preroll = GST_READ_UINT32_LE (data);
       
   590     data += 4;
       
   591     fisbone_pad->granuleshift = GST_READ_UINT8 (data);
       
   592     data += 1;
       
   593     /* padding */
       
   594     data += 3;
       
   595 
       
   596     fisbone_pad->start_time = gst_annodex_granule_to_time (start_granule,
       
   597         fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
       
   598         fisbone_pad->granuleshift);
       
   599 
       
   600     GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
       
   601         "(serialno: %08x start time: %" GST_TIME_FORMAT
       
   602         " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
       
   603         " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
       
   604         serialno, GST_TIME_ARGS (fisbone_pad->start_time),
       
   605         fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
       
   606         fisbone_pad->preroll, fisbone_pad->granuleshift);
       
   607   } else {
       
   608     GST_WARNING_OBJECT (pad->ogg,
       
   609         "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
       
   610         serialno);
       
   611   }
       
   612 }
       
   613 
       
   614 /* function called to convert a granulepos to a timestamp */
       
   615 static gboolean
       
   616 gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
       
   617     gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
       
   618 {
       
   619   gboolean res;
       
   620 
       
   621   if (src_val == -1) {
       
   622     *dest_val = -1;
       
   623     return TRUE;
       
   624   }
       
   625 
       
   626   if (!pad->have_fisbone && pad->elem_pad == NULL)
       
   627     return FALSE;
       
   628 
       
   629   switch (src_format) {
       
   630     case GST_FORMAT_DEFAULT:
       
   631       if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
       
   632         *dest_val = gst_annodex_granule_to_time (src_val,
       
   633             pad->granulerate_n, pad->granulerate_d, pad->granuleshift);
       
   634 
       
   635         res = TRUE;
       
   636       } else {
       
   637         if (pad->elem_pad == NULL)
       
   638           res = FALSE;
       
   639         else
       
   640           res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
       
   641               dest_format, dest_val);
       
   642       }
       
   643 
       
   644       break;
       
   645     default:
       
   646       if (pad->elem_pad == NULL)
       
   647         res = FALSE;
       
   648       else
       
   649         res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
       
   650             dest_format, dest_val);
       
   651   }
       
   652 
       
   653   return res;
       
   654 }
       
   655 
       
   656 /* function called by the internal decoder elements when it outputs
       
   657  * a buffer. We use it to get the first timestamp of the stream 
       
   658  */
       
   659 static GstFlowReturn
       
   660 gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
       
   661 {
       
   662   GstOggPad *oggpad;
       
   663   GstOggDemux *ogg;
       
   664   GstClockTime timestamp;
       
   665 
       
   666   oggpad = gst_pad_get_element_private (pad);
       
   667   ogg = GST_OGG_DEMUX (oggpad->ogg);
       
   668 
       
   669   timestamp = GST_BUFFER_TIMESTAMP (buffer);
       
   670   GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
       
   671       GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
       
   672       timestamp);
       
   673 
       
   674   if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
       
   675     oggpad->start_time = timestamp;
       
   676     GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
       
   677         GST_TIME_ARGS (timestamp));
       
   678   }
       
   679 
       
   680   gst_buffer_unref (buffer);
       
   681 
       
   682   return GST_FLOW_OK;
       
   683 }
       
   684 
       
   685 static void
       
   686 internal_element_pad_added_cb (GstElement * element, GstPad * pad,
       
   687     GstOggPad * oggpad)
       
   688 {
       
   689   if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
       
   690     if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
       
   691       GST_ERROR ("Really couldn't find a valid pad");
       
   692     }
       
   693     oggpad->dynamic = FALSE;
       
   694     g_signal_handler_disconnect (element, oggpad->padaddedid);
       
   695     oggpad->padaddedid = 0;
       
   696   }
       
   697 }
       
   698 
       
   699 /* runs typefind on the packet, which is assumed to be the first
       
   700  * packet in the stream.
       
   701  * 
       
   702  * Based on the type returned from the typefind function, an element
       
   703  * is created to help in conversion between granulepos and timestamps
       
   704  * so that we can do decent seeking.
       
   705  */
       
   706 static gboolean
       
   707 gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
       
   708 {
       
   709   GstBuffer *buf;
       
   710   GstCaps *caps;
       
   711   GstElement *element = NULL;
       
   712 
       
   713 #ifndef GST_DISABLE_GST_DEBUG
       
   714   GstOggDemux *ogg = pad->ogg;
       
   715 #endif
       
   716 
       
   717   if (GST_PAD_CAPS (pad) != NULL)
       
   718     return TRUE;
       
   719 
       
   720   /* The ogg spec defines that the first packet of an ogg stream must identify
       
   721    * the stream. Therefore ogg can use a simplified approach to typefinding
       
   722    * and only needs to check the first packet */
       
   723   buf = gst_buffer_new ();
       
   724   GST_BUFFER_DATA (buf) = packet->packet;
       
   725   GST_BUFFER_SIZE (buf) = packet->bytes;
       
   726   GST_BUFFER_OFFSET (buf) = 0;
       
   727 
       
   728   caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
       
   729   gst_buffer_unref (buf);
       
   730 
       
   731   if (caps == NULL) {
       
   732     GST_WARNING_OBJECT (ogg,
       
   733         "couldn't find caps for stream with serial %08x", pad->serialno);
       
   734     caps = gst_caps_new_simple ("application/octet-stream", NULL);
       
   735   } else {
       
   736     /* ogg requires you to use a decoder element to define the
       
   737      * meaning of granulepos etc so we make one. We also do this if
       
   738      * we are in the streaming mode to calculate the first timestamp. */
       
   739     GList *factories;
       
   740 
       
   741     GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
       
   742 
       
   743     /* first filter out the interesting element factories */
       
   744     factories = gst_default_registry_feature_filter (
       
   745         (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
       
   746 
       
   747     /* sort them according to their ranks */
       
   748     factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
       
   749 
       
   750     /* then pick the first factory to create an element */
       
   751     if (factories) {
       
   752       element =
       
   753           gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
       
   754           NULL);
       
   755       if (element) {
       
   756         GstPadTemplate *template;
       
   757 
       
   758         /* this is ours */
       
   759         gst_object_ref (element);
       
   760         gst_object_sink (GST_OBJECT (element));
       
   761 
       
   762         /* FIXME, it might not be named "sink" */
       
   763         pad->elem_pad = gst_element_get_pad (element, "sink");
       
   764         gst_element_set_state (element, GST_STATE_PAUSED);
       
   765         template = gst_static_pad_template_get (&internaltemplate);
       
   766         pad->elem_out = gst_pad_new_from_template (template, "internal");
       
   767         gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
       
   768         gst_pad_set_element_private (pad->elem_out, pad);
       
   769         gst_pad_set_active (pad->elem_out, TRUE);
       
   770         gst_object_unref (template);
       
   771 
       
   772         /* and this pad may not be named src.. */
       
   773         /* And it might also not exist at this time... */
       
   774         {
       
   775           GstPad *p;
       
   776 
       
   777           p = gst_element_get_pad (element, "src");
       
   778           if (p) {
       
   779             gst_pad_link (p, pad->elem_out);
       
   780             gst_object_unref (p);
       
   781           } else {
       
   782             pad->dynamic = TRUE;
       
   783             pad->padaddedid = g_signal_connect (G_OBJECT (element),
       
   784                 "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
       
   785           }
       
   786         }
       
   787       }
       
   788     }
       
   789     g_list_free (factories);
       
   790   }
       
   791   pad->element = element;
       
   792 
       
   793   gst_pad_set_caps (GST_PAD (pad), caps);
       
   794   gst_caps_unref (caps);
       
   795 
       
   796   return TRUE;
       
   797 }
       
   798 
       
   799 /* send packet to internal element */
       
   800 static GstFlowReturn
       
   801 gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
       
   802 {
       
   803   GstBuffer *buf;
       
   804   GstFlowReturn ret;
       
   805 
       
   806 #ifndef GST_DISABLE_GST_DEBUG
       
   807   GstOggDemux *ogg = pad->ogg;
       
   808 #endif
       
   809 
       
   810   /* initialize our internal decoder with packets */
       
   811   if (!pad->elem_pad)
       
   812     goto no_decoder;
       
   813 
       
   814   GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
       
   815 
       
   816   buf = gst_buffer_new_and_alloc (packet->bytes);
       
   817   memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
       
   818   gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
       
   819   GST_BUFFER_OFFSET (buf) = -1;
       
   820   GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
       
   821 
       
   822   ret = gst_pad_chain (pad->elem_pad, buf);
       
   823   if (GST_FLOW_IS_FATAL (ret))
       
   824     goto decoder_error;
       
   825 
       
   826   return ret;
       
   827 
       
   828 no_decoder:
       
   829   {
       
   830     GST_WARNING_OBJECT (ogg,
       
   831         "pad %p does not have elem_pad, no decoder ?", pad);
       
   832     return GST_FLOW_ERROR;
       
   833   }
       
   834 decoder_error:
       
   835   {
       
   836     GST_WARNING_OBJECT (ogg, "internal decoder error");
       
   837     return GST_FLOW_ERROR;
       
   838   }
       
   839 }
       
   840 
       
   841 /* queue data, basically takes the packet, puts it in a buffer and store the
       
   842  * buffer in the headers list.
       
   843  */
       
   844 static GstFlowReturn
       
   845 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
       
   846 {
       
   847   GstBuffer *buf;
       
   848 
       
   849 #ifndef GST_DISABLE_GST_DEBUG
       
   850   GstOggDemux *ogg = pad->ogg;
       
   851 #endif
       
   852 
       
   853   GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad, pad->serialno);
       
   854 
       
   855   buf = gst_buffer_new_and_alloc (packet->bytes);
       
   856   memcpy (buf->data, packet->packet, packet->bytes);
       
   857   GST_BUFFER_OFFSET (buf) = -1;
       
   858   GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
       
   859   pad->headers = g_list_append (pad->headers, buf);
       
   860 
       
   861   /* we are ok now */
       
   862   return GST_FLOW_OK;
       
   863 }
       
   864 
       
   865 /* send packet to internal element */
       
   866 static GstFlowReturn
       
   867 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
       
   868 {
       
   869   GstBuffer *buf;
       
   870   GstFlowReturn ret, cret;
       
   871   GstOggDemux *ogg = pad->ogg;
       
   872   GstFormat format;
       
   873   gint64 current_time;
       
   874   GstOggChain *chain;
       
   875 
       
   876   GST_DEBUG_OBJECT (ogg,
       
   877       "%p streaming to peer serial %08x", pad, pad->serialno);
       
   878 
       
   879   ret =
       
   880       gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
       
   881       GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (pad), &buf);
       
   882 
       
   883   /* combine flows */
       
   884   cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
       
   885   if (ret != GST_FLOW_OK)
       
   886     goto no_buffer;
       
   887 
       
   888   /* copy packet in buffer */
       
   889   memcpy (buf->data, packet->packet, packet->bytes);
       
   890 
       
   891   GST_BUFFER_OFFSET (buf) = -1;
       
   892   GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
       
   893 
       
   894   /* Mark discont on the buffer */
       
   895   if (pad->discont) {
       
   896     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
       
   897     pad->discont = FALSE;
       
   898   }
       
   899 
       
   900   ret = gst_pad_push (GST_PAD_CAST (pad), buf);
       
   901 
       
   902   /* combine flows */
       
   903   cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
       
   904 
       
   905   /* we're done with skeleton stuff */
       
   906   if (pad->is_skeleton)
       
   907     goto done;
       
   908 
       
   909   /* check if valid granulepos, then we can calculate the current
       
   910    * position */
       
   911   if (packet->granulepos < 0)
       
   912     goto done;
       
   913 
       
   914   /* store current granule pos */
       
   915   ogg->current_granule = packet->granulepos;
       
   916 
       
   917   /* convert to time */
       
   918   format = GST_FORMAT_TIME;
       
   919   if (!gst_ogg_pad_query_convert (pad,
       
   920           GST_FORMAT_DEFAULT, packet->granulepos, &format,
       
   921           (gint64 *) & current_time))
       
   922     goto convert_failed;
       
   923 
       
   924   /* convert to stream time */
       
   925   if ((chain = pad->chain))
       
   926     current_time = current_time - chain->segment_start + chain->begin_time;
       
   927 
       
   928   /* and store as the current position */
       
   929   gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
       
   930 
       
   931   GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
       
   932       GST_TIME_ARGS (current_time));
       
   933 
       
   934 done:
       
   935   /* return combined flow result */
       
   936   return cret;
       
   937 
       
   938   /* special cases */
       
   939 no_buffer:
       
   940   {
       
   941     GST_DEBUG_OBJECT (ogg,
       
   942         "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
       
   943         pad, pad->serialno, ret, gst_flow_get_name (ret),
       
   944         cret, gst_flow_get_name (cret));
       
   945     goto done;
       
   946   }
       
   947 convert_failed:
       
   948   {
       
   949     GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
       
   950     goto done;
       
   951   }
       
   952 }
       
   953 
       
   954 /* submit a packet to the oggpad, this function will run the
       
   955  * typefind code for the pad if this is the first packet for this
       
   956  * stream 
       
   957  */
       
   958 static GstFlowReturn
       
   959 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
       
   960 {
       
   961   gint64 granule;
       
   962   GstFlowReturn ret;
       
   963 
       
   964   GstOggDemux *ogg = pad->ogg;
       
   965 
       
   966   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
       
   967 
       
   968   if (!pad->have_type) {
       
   969     if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
       
   970         !memcmp (packet->packet, "fishead\0", 8)) {
       
   971       gst_ogg_pad_parse_skeleton_fishead (pad, packet);
       
   972     }
       
   973     gst_ogg_pad_typefind (pad, packet);
       
   974     pad->have_type = TRUE;
       
   975   }
       
   976 
       
   977   if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
       
   978       !memcmp (packet->packet, "fisbone\0", 8)) {
       
   979     gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
       
   980   }
       
   981 
       
   982   granule = packet->granulepos;
       
   983   if (granule != -1) {
       
   984     GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
       
   985     ogg->current_granule = granule;
       
   986     pad->current_granule = granule;
       
   987     /* granulepos 0 and -1 are considered header packets.
       
   988      * Note that since theora is busted, it can create non-header
       
   989      * packets with 0 granulepos. We will correct for this when our
       
   990      * internal decoder produced a frame and we don't have a
       
   991      * granulepos because in that case the granulpos must have been 0 */
       
   992     if (pad->first_granule == -1 && granule != 0) {
       
   993       GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
       
   994           granule);
       
   995       pad->first_granule = granule;
       
   996     }
       
   997   }
       
   998 
       
   999   if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
       
  1000     return GST_FLOW_OK;
       
  1001   }
       
  1002 
       
  1003   /* no start time known, stream to internal plugin to
       
  1004    * get time. always stream to the skeleton decoder */
       
  1005   if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
       
  1006     ret = gst_ogg_demux_chain_elem_pad (pad, packet);
       
  1007   }
       
  1008   /* we know the start_time of the pad data, see if we
       
  1009    * can activate the complete chain if this is a dynamic
       
  1010    * chain. */
       
  1011   if (pad->start_time != GST_CLOCK_TIME_NONE) {
       
  1012     GstOggChain *chain = pad->chain;
       
  1013 
       
  1014     /* correction for busted ogg, if the internal decoder outputed
       
  1015      * a timestamp but we did not get a granulepos, it must have
       
  1016      * been 0 and the time is therefore also 0 */
       
  1017     if (pad->first_granule == -1) {
       
  1018       GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
       
  1019       pad->first_granule = 0;
       
  1020       pad->first_time = 0;
       
  1021     }
       
  1022 
       
  1023     /* check if complete chain has start time */
       
  1024     if (chain == ogg->building_chain) {
       
  1025 
       
  1026       /* see if we have enough info to activate the chain, we have enough info
       
  1027        * when all streams have a valid start time. */
       
  1028       if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
       
  1029         GstEvent *event;
       
  1030 
       
  1031         GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
       
  1032             GST_TIME_ARGS (chain->segment_start));
       
  1033         GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
       
  1034             GST_TIME_ARGS (chain->segment_stop));
       
  1035         GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
       
  1036             GST_TIME_ARGS (chain->begin_time));
       
  1037 
       
  1038         /* create the newsegment event we are going to send out */
       
  1039         event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
       
  1040             GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
       
  1041             chain->begin_time);
       
  1042 
       
  1043         gst_ogg_demux_activate_chain (ogg, chain, event);
       
  1044 
       
  1045         ogg->building_chain = NULL;
       
  1046       }
       
  1047     }
       
  1048   }
       
  1049 
       
  1050   /* if we are building a chain, store buffer for when we activate
       
  1051    * it. This path is taken if we operate in streaming mode. */
       
  1052   if (ogg->building_chain) {
       
  1053     ret = gst_ogg_demux_queue_data (pad, packet);
       
  1054   }
       
  1055   /* else we are completely streaming to the peer */
       
  1056   else {
       
  1057     ret = gst_ogg_demux_chain_peer (pad, packet);
       
  1058   }
       
  1059   return ret;
       
  1060 }
       
  1061 
       
  1062 /* flush at most @npackets from the stream layer. All packets if 
       
  1063  * @npackets is 0;
       
  1064  */
       
  1065 static GstFlowReturn
       
  1066 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
       
  1067 {
       
  1068   GstFlowReturn result = GST_FLOW_OK;
       
  1069   gboolean done = FALSE;
       
  1070   GstOggDemux *ogg;
       
  1071 
       
  1072   ogg = pad->ogg;
       
  1073 
       
  1074   while (!done) {
       
  1075     int ret;
       
  1076     ogg_packet packet;
       
  1077 
       
  1078     ret = ogg_stream_packetout (&pad->stream, &packet);
       
  1079     switch (ret) {
       
  1080       case 0:
       
  1081         GST_LOG_OBJECT (ogg, "packetout done");
       
  1082         done = TRUE;
       
  1083         break;
       
  1084       case -1:
       
  1085         GST_LOG_OBJECT (ogg, "packetout discont");
       
  1086         gst_ogg_chain_mark_discont (pad->chain);
       
  1087         break;
       
  1088       case 1:
       
  1089         GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
       
  1090         result = gst_ogg_pad_submit_packet (pad, &packet);
       
  1091         if (GST_FLOW_IS_FATAL (result))
       
  1092           goto could_not_submit;
       
  1093         break;
       
  1094       default:
       
  1095         GST_WARNING_OBJECT (ogg,
       
  1096             "invalid return value %d for ogg_stream_packetout, resetting stream",
       
  1097             ret);
       
  1098         gst_ogg_pad_reset (pad);
       
  1099         break;
       
  1100     }
       
  1101     if (npackets > 0) {
       
  1102       npackets--;
       
  1103       done = (npackets == 0);
       
  1104     }
       
  1105   }
       
  1106   return result;
       
  1107 
       
  1108   /* ERRORS */
       
  1109 could_not_submit:
       
  1110   {
       
  1111     GST_WARNING_OBJECT (ogg,
       
  1112         "could not submit packet for stream %08x, error: %d", pad->serialno,
       
  1113         result);
       
  1114     gst_ogg_pad_reset (pad);
       
  1115     return result;
       
  1116   }
       
  1117 }
       
  1118 
       
  1119 /* submit a page to an oggpad, this function will then submit all
       
  1120  * the packets in the page.
       
  1121  */
       
  1122 static GstFlowReturn
       
  1123 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
       
  1124 {
       
  1125   GstFlowReturn result = GST_FLOW_OK;
       
  1126   GstOggDemux *ogg;
       
  1127   gboolean continued = FALSE;
       
  1128 
       
  1129   ogg = pad->ogg;
       
  1130 
       
  1131   /* for negative rates we read pages backwards and must therefore be carefull
       
  1132    * with continued pages */
       
  1133   if (ogg->segment.rate < 0.0) {
       
  1134     gint npackets;
       
  1135 
       
  1136     continued = ogg_page_continued (page);
       
  1137 
       
  1138     /* number of completed packets in the page */
       
  1139     npackets = ogg_page_packets (page);
       
  1140     if (!continued) {
       
  1141       /* page is not continued so it contains at least one packet start. It's
       
  1142        * possible that no packet ends on this page (npackets == 0). In that
       
  1143        * case, the next (continued) page(s) we kept contain the remainder of the
       
  1144        * packets. We mark npackets=1 to make us start decoding the pages in the
       
  1145        * remainder of the algorithm. */
       
  1146       if (npackets == 0)
       
  1147         npackets = 1;
       
  1148     }
       
  1149     GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
       
  1150 
       
  1151     if (npackets == 0) {
       
  1152       GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
       
  1153       goto done;
       
  1154     }
       
  1155   }
       
  1156 
       
  1157   if (ogg_stream_pagein (&pad->stream, page) != 0)
       
  1158     goto choked;
       
  1159 
       
  1160   /* flush all packets in the stream layer, this might not give a packet if
       
  1161    * the page had no packets finishing on the page (npackets == 0). */
       
  1162   result = gst_ogg_pad_stream_out (pad, 0);
       
  1163 
       
  1164   if (pad->continued) {
       
  1165     ogg_packet packet;
       
  1166 
       
  1167     /* now send the continued pages to the stream layer */
       
  1168     while (pad->continued) {
       
  1169       ogg_page *p = (ogg_page *) pad->continued->data;
       
  1170 
       
  1171       GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
       
  1172       if (ogg_stream_pagein (&pad->stream, p) != 0)
       
  1173         goto choked;
       
  1174 
       
  1175       pad->continued = g_list_delete_link (pad->continued, pad->continued);
       
  1176 
       
  1177       /* free the page */
       
  1178       gst_ogg_page_free (p);
       
  1179     }
       
  1180 
       
  1181     GST_LOG_OBJECT (ogg, "flushing last continued packet");
       
  1182     /* flush 1 continued packet in the stream layer */
       
  1183     result = gst_ogg_pad_stream_out (pad, 1);
       
  1184 
       
  1185     /* flush all remaining packets, we pushed them in the previous round.
       
  1186      * We don't use _reset() because we still want to get the discont when
       
  1187      * we submit a next page. */
       
  1188     while (ogg_stream_packetout (&pad->stream, &packet) != 0);
       
  1189   }
       
  1190 
       
  1191 done:
       
  1192   /* keep continued pages (only in reverse mode) */
       
  1193   if (continued) {
       
  1194     ogg_page *p = gst_ogg_page_copy (page);
       
  1195 
       
  1196     GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
       
  1197     pad->continued = g_list_prepend (pad->continued, p);
       
  1198   }
       
  1199 
       
  1200   return result;
       
  1201 
       
  1202 choked:
       
  1203   {
       
  1204     GST_WARNING_OBJECT (ogg,
       
  1205         "ogg stream choked on page (serial %08x), resetting stream",
       
  1206         pad->serialno);
       
  1207     gst_ogg_pad_reset (pad);
       
  1208     /* we continue to recover */
       
  1209     return GST_FLOW_OK;
       
  1210   }
       
  1211 }
       
  1212 
       
  1213 
       
  1214 static GstOggChain *
       
  1215 gst_ogg_chain_new (GstOggDemux * ogg)
       
  1216 {
       
  1217   GstOggChain *chain = g_new0 (GstOggChain, 1);
       
  1218 
       
  1219   GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
       
  1220   chain->ogg = ogg;
       
  1221   chain->offset = -1;
       
  1222   chain->bytes = -1;
       
  1223   chain->have_bos = FALSE;
       
  1224   chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
       
  1225   chain->begin_time = GST_CLOCK_TIME_NONE;
       
  1226   chain->segment_start = GST_CLOCK_TIME_NONE;
       
  1227   chain->segment_stop = GST_CLOCK_TIME_NONE;
       
  1228   chain->total_time = GST_CLOCK_TIME_NONE;
       
  1229 
       
  1230   return chain;
       
  1231 }
       
  1232 
       
  1233 static void
       
  1234 gst_ogg_chain_free (GstOggChain * chain)
       
  1235 {
       
  1236   gint i;
       
  1237 
       
  1238   for (i = 0; i < chain->streams->len; i++) {
       
  1239     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1240 
       
  1241     gst_object_unref (pad);
       
  1242   }
       
  1243   g_array_free (chain->streams, TRUE);
       
  1244   g_free (chain);
       
  1245 }
       
  1246 
       
  1247 static void
       
  1248 gst_ogg_chain_mark_discont (GstOggChain * chain)
       
  1249 {
       
  1250   gint i;
       
  1251 
       
  1252   for (i = 0; i < chain->streams->len; i++) {
       
  1253     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1254 
       
  1255     pad->discont = TRUE;
       
  1256   }
       
  1257 }
       
  1258 
       
  1259 static void
       
  1260 gst_ogg_chain_reset (GstOggChain * chain)
       
  1261 {
       
  1262   gint i;
       
  1263 
       
  1264   for (i = 0; i < chain->streams->len; i++) {
       
  1265     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1266 
       
  1267     gst_ogg_pad_reset (pad);
       
  1268   }
       
  1269 }
       
  1270 
       
  1271 static GstOggPad *
       
  1272 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
       
  1273 {
       
  1274   GstOggPad *ret;
       
  1275   GstTagList *list;
       
  1276   gchar *name;
       
  1277 
       
  1278   GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
       
  1279       serialno, chain);
       
  1280 
       
  1281   ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
       
  1282   /* we own this one */
       
  1283   gst_object_ref (ret);
       
  1284   gst_object_sink (ret);
       
  1285 
       
  1286   GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
       
  1287   ret->discont = TRUE;
       
  1288 
       
  1289   ret->chain = chain;
       
  1290   ret->ogg = chain->ogg;
       
  1291 
       
  1292   ret->serialno = serialno;
       
  1293   if (ogg_stream_init (&ret->stream, serialno) != 0)
       
  1294     goto init_failed;
       
  1295 
       
  1296   name = g_strdup_printf ("serial_%08lx", serialno);
       
  1297   gst_object_set_name (GST_OBJECT (ret), name);
       
  1298   g_free (name);
       
  1299 
       
  1300   /* FIXME: either do something with it or remove it */
       
  1301   list = gst_tag_list_new ();
       
  1302   gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
       
  1303       NULL);
       
  1304   gst_tag_list_free (list);
       
  1305 
       
  1306   GST_DEBUG_OBJECT (chain->ogg,
       
  1307       "created new ogg src %p for stream with serial %08lx", ret, serialno);
       
  1308 
       
  1309   g_array_append_val (chain->streams, ret);
       
  1310 
       
  1311   return ret;
       
  1312 
       
  1313   /* ERRORS */
       
  1314 init_failed:
       
  1315   {
       
  1316     GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
       
  1317         serialno);
       
  1318     gst_object_unref (ret);
       
  1319     return NULL;
       
  1320   }
       
  1321 }
       
  1322 
       
  1323 static GstOggPad *
       
  1324 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
       
  1325 {
       
  1326   gint i;
       
  1327 
       
  1328   for (i = 0; i < chain->streams->len; i++) {
       
  1329     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1330 
       
  1331     if (pad->serialno == serialno)
       
  1332       return pad;
       
  1333   }
       
  1334   return NULL;
       
  1335 }
       
  1336 
       
  1337 static gboolean
       
  1338 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
       
  1339 {
       
  1340   return gst_ogg_chain_get_stream (chain, serialno) != NULL;
       
  1341 }
       
  1342 
       
  1343 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
       
  1344 
       
  1345 /* signals and args */
       
  1346 enum
       
  1347 {
       
  1348   /* FILL ME */
       
  1349   LAST_SIGNAL
       
  1350 };
       
  1351 
       
  1352 enum
       
  1353 {
       
  1354   ARG_0
       
  1355       /* FILL ME */
       
  1356 };
       
  1357 
       
  1358 static GstStaticPadTemplate ogg_demux_src_template_factory =
       
  1359 GST_STATIC_PAD_TEMPLATE ("src_%d",
       
  1360     GST_PAD_SRC,
       
  1361     GST_PAD_SOMETIMES,
       
  1362     GST_STATIC_CAPS_ANY);
       
  1363 
       
  1364 static GstStaticPadTemplate ogg_demux_sink_template_factory =
       
  1365     GST_STATIC_PAD_TEMPLATE ("sink",
       
  1366     GST_PAD_SINK,
       
  1367     GST_PAD_ALWAYS,
       
  1368     GST_STATIC_CAPS ("application/ogg; application/x-annodex")
       
  1369     );
       
  1370 
       
  1371 static void gst_ogg_demux_finalize (GObject * object);
       
  1372 
       
  1373 //static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
       
  1374 //static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
       
  1375 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
       
  1376     GstOggChain ** chain);
       
  1377 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
       
  1378     GstOggChain * chain);
       
  1379 
       
  1380 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
       
  1381 static void gst_ogg_demux_loop (GstOggPad * pad);
       
  1382 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
       
  1383 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
       
  1384 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
       
  1385     gboolean active);
       
  1386 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
       
  1387     gboolean active);
       
  1388 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
       
  1389     GstStateChange transition);
       
  1390 static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
       
  1391 
       
  1392 static void gst_ogg_print (GstOggDemux * demux);
       
  1393 
       
  1394 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
       
  1395 
       
  1396 static void
       
  1397 gst_ogg_demux_base_init (gpointer g_class)
       
  1398 {
       
  1399   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
  1400 
       
  1401   gst_element_class_set_details (element_class, &gst_ogg_demux_details);
       
  1402 
       
  1403   gst_element_class_add_pad_template (element_class,
       
  1404       gst_static_pad_template_get (&ogg_demux_sink_template_factory));
       
  1405   gst_element_class_add_pad_template (element_class,
       
  1406       gst_static_pad_template_get (&ogg_demux_src_template_factory));
       
  1407 }
       
  1408 static void
       
  1409 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
       
  1410 {
       
  1411   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
       
  1412   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
       
  1413 
       
  1414   gstelement_class->change_state = gst_ogg_demux_change_state;
       
  1415   gstelement_class->send_event = gst_ogg_demux_receive_event;
       
  1416 
       
  1417   gobject_class->finalize = gst_ogg_demux_finalize;
       
  1418 }
       
  1419 
       
  1420 static void
       
  1421 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
       
  1422 {
       
  1423   /* create the sink pad */
       
  1424   ogg->sinkpad =
       
  1425       gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
       
  1426       "sink");
       
  1427 
       
  1428   gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
       
  1429   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
       
  1430   gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
       
  1431   gst_pad_set_activatepull_function (ogg->sinkpad,
       
  1432       gst_ogg_demux_sink_activate_pull);
       
  1433   gst_pad_set_activatepush_function (ogg->sinkpad,
       
  1434       gst_ogg_demux_sink_activate_push);
       
  1435   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
       
  1436 
       
  1437   ogg->chain_lock = g_mutex_new ();
       
  1438   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
       
  1439 
       
  1440   ogg->newsegment = NULL;
       
  1441 }
       
  1442 
       
  1443 static void
       
  1444 gst_ogg_demux_finalize (GObject * object)
       
  1445 {
       
  1446   GstOggDemux *ogg;
       
  1447 
       
  1448   ogg = GST_OGG_DEMUX (object);
       
  1449 
       
  1450   g_array_free (ogg->chains, TRUE);
       
  1451   g_mutex_free (ogg->chain_lock);
       
  1452   ogg_sync_clear (&ogg->sync);
       
  1453 
       
  1454   if (ogg->newsegment)
       
  1455     gst_event_unref (ogg->newsegment);
       
  1456 
       
  1457   G_OBJECT_CLASS (parent_class)->finalize (object);
       
  1458 }
       
  1459 
       
  1460 static gboolean
       
  1461 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
       
  1462 {
       
  1463   gboolean res;
       
  1464   GstOggDemux *ogg;
       
  1465 
       
  1466   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
       
  1467 
       
  1468   switch (GST_EVENT_TYPE (event)) {
       
  1469     case GST_EVENT_NEWSEGMENT:
       
  1470       /* FIXME */
       
  1471       GST_DEBUG_OBJECT (ogg, "got a new segment event");
       
  1472       ogg_sync_reset (&ogg->sync);
       
  1473       gst_event_unref (event);
       
  1474       res = TRUE;
       
  1475       break;
       
  1476     case GST_EVENT_EOS:
       
  1477     default:
       
  1478       res = gst_pad_event_default (pad, event);
       
  1479       break;
       
  1480   }
       
  1481   gst_object_unref (ogg);
       
  1482 
       
  1483   return res;
       
  1484 }
       
  1485 
       
  1486 /* submit the given buffer to the ogg sync.
       
  1487  *
       
  1488  * Returns the number of bytes submited.
       
  1489  */
       
  1490 static GstFlowReturn
       
  1491 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
       
  1492 {
       
  1493   gint size;
       
  1494   guint8 *data;
       
  1495   gchar *oggbuffer;
       
  1496   GstFlowReturn ret = GST_FLOW_OK;
       
  1497 
       
  1498   size = GST_BUFFER_SIZE (buffer);
       
  1499   data = GST_BUFFER_DATA (buffer);
       
  1500 
       
  1501   GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
       
  1502   if (G_UNLIKELY (size == 0))
       
  1503     goto done;
       
  1504 
       
  1505   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
       
  1506   if (G_UNLIKELY (oggbuffer == NULL))
       
  1507     goto no_buffer;
       
  1508 
       
  1509   memcpy (oggbuffer, data, size);
       
  1510   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
       
  1511     goto write_failed;
       
  1512 
       
  1513 done:
       
  1514   gst_buffer_unref (buffer);
       
  1515 
       
  1516   return ret;
       
  1517 
       
  1518   /* ERRORS */
       
  1519 no_buffer:
       
  1520   {
       
  1521     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
       
  1522         (NULL), ("failed to get ogg sync buffer"));
       
  1523     ret = GST_FLOW_ERROR;
       
  1524     goto done;
       
  1525   }
       
  1526 write_failed:
       
  1527   {
       
  1528     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
       
  1529         (NULL), ("failed to write %d bytes to the sync buffer", size));
       
  1530     ret = GST_FLOW_ERROR;
       
  1531     goto done;
       
  1532   }
       
  1533 }
       
  1534 
       
  1535 /* in random access mode this code updates the current read position
       
  1536  * and resets the ogg sync buffer so that the next read will happen
       
  1537  * from this new location.
       
  1538  */
       
  1539 static void
       
  1540 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
       
  1541 {
       
  1542   GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
       
  1543 
       
  1544   ogg->offset = offset;
       
  1545   ogg_sync_reset (&ogg->sync);
       
  1546 }
       
  1547 
       
  1548 /* read more data from the current offset and submit to
       
  1549  * the ogg sync layer.
       
  1550  */
       
  1551 static GstFlowReturn
       
  1552 gst_ogg_demux_get_data (GstOggDemux * ogg)
       
  1553 {
       
  1554   GstFlowReturn ret;
       
  1555   GstBuffer *buffer;
       
  1556 
       
  1557   GST_LOG_OBJECT (ogg, "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
       
  1558       ogg->offset, ogg->length);
       
  1559   if (ogg->offset == ogg->length)
       
  1560     goto eos;
       
  1561 
       
  1562   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
       
  1563   if (ret != GST_FLOW_OK)
       
  1564     goto error;
       
  1565 
       
  1566   ret = gst_ogg_demux_submit_buffer (ogg, buffer);
       
  1567 
       
  1568   return ret;
       
  1569 
       
  1570   /* ERROR */
       
  1571 eos:
       
  1572   {
       
  1573     GST_LOG_OBJECT (ogg, "reached EOS");
       
  1574     return GST_FLOW_UNEXPECTED;
       
  1575   }
       
  1576 error:
       
  1577   {
       
  1578     GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
       
  1579         gst_flow_get_name (ret));
       
  1580     return ret;
       
  1581   }
       
  1582 }
       
  1583 
       
  1584 /* Read the next page from the current offset.
       
  1585  * boundary: number of bytes ahead we allow looking for;
       
  1586  * -1 if no boundary
       
  1587  *
       
  1588  * @offset will contain the offset the next page starts at when this function
       
  1589  * returns GST_FLOW_OK.
       
  1590  *
       
  1591  * GST_FLOW_UNEXPECTED is returned on EOS.
       
  1592  *
       
  1593  * GST_FLOW_LIMIT is returned when we did not find a page before the
       
  1594  * boundary. If @boundary is -1, this is never returned.
       
  1595  *
       
  1596  * Any other error returned while retrieving data from the peer is returned as
       
  1597  * is.
       
  1598  */
       
  1599 static GstFlowReturn
       
  1600 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
       
  1601     gint64 * offset)
       
  1602 {
       
  1603   gint64 end_offset = 0;
       
  1604   GstFlowReturn ret;
       
  1605 
       
  1606   GST_LOG_OBJECT (ogg,
       
  1607       "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
       
  1608       G_GINT64_FORMAT, ogg->offset, boundary);
       
  1609 
       
  1610   if (boundary > 0)
       
  1611     end_offset = ogg->offset + boundary;
       
  1612 
       
  1613   while (TRUE) {
       
  1614     glong more;
       
  1615 
       
  1616     if (boundary > 0 && ogg->offset >= end_offset)
       
  1617       goto boundary_reached;
       
  1618 
       
  1619     more = ogg_sync_pageseek (&ogg->sync, og);
       
  1620 
       
  1621     GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
       
  1622 
       
  1623     if (more < 0) {
       
  1624       /* skipped n bytes */
       
  1625       GST_LOG_OBJECT (ogg, "skipped %ld bytes", more);
       
  1626       ogg->offset -= more;
       
  1627     } else if (more == 0) {
       
  1628       /* we need more data */
       
  1629       if (boundary == 0)
       
  1630         goto boundary_reached;
       
  1631 
       
  1632       GST_LOG_OBJECT (ogg, "need more data");
       
  1633       ret = gst_ogg_demux_get_data (ogg);
       
  1634       if (ret != GST_FLOW_OK)
       
  1635         break;
       
  1636     } else {
       
  1637       gint64 res_offset = ogg->offset;
       
  1638 
       
  1639       /* got a page.  Return the offset at the page beginning,
       
  1640          advance the internal offset past the page end */
       
  1641       if (offset)
       
  1642         *offset = res_offset;
       
  1643       ret = GST_FLOW_OK;
       
  1644 
       
  1645       ogg->offset += more;
       
  1646       /* need to reset as we do not keep track of the bytes we
       
  1647        * sent to the sync layer */
       
  1648       ogg_sync_reset (&ogg->sync);
       
  1649 
       
  1650       GST_LOG_OBJECT (ogg,
       
  1651           "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
       
  1652           G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
       
  1653           ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og));
       
  1654       break;
       
  1655     }
       
  1656   }
       
  1657   GST_LOG_OBJECT (ogg, "returning %d", ret);
       
  1658 
       
  1659   return ret;
       
  1660 
       
  1661   /* ERRORS */
       
  1662 boundary_reached:
       
  1663   {
       
  1664     GST_LOG_OBJECT (ogg,
       
  1665         "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
       
  1666         ogg->offset, end_offset);
       
  1667     return GST_FLOW_LIMIT;
       
  1668   }
       
  1669 }
       
  1670 
       
  1671 /* from the current offset, find the previous page, seeking backwards
       
  1672  * until we find the page. 
       
  1673  */
       
  1674 static GstFlowReturn
       
  1675 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
       
  1676 {
       
  1677   GstFlowReturn ret;
       
  1678   gint64 begin = ogg->offset;
       
  1679   gint64 end = begin;
       
  1680   gint64 cur_offset = -1;
       
  1681 
       
  1682   while (cur_offset == -1) {
       
  1683     begin -= CHUNKSIZE;
       
  1684     if (begin < 0)
       
  1685       begin = 0;
       
  1686 
       
  1687     /* seek CHUNKSIZE back */
       
  1688     gst_ogg_demux_seek (ogg, begin);
       
  1689 
       
  1690     /* now continue reading until we run out of data, if we find a page
       
  1691      * start, we save it. It might not be the final page as there could be
       
  1692      * another page after this one. */
       
  1693     while (ogg->offset < end) {
       
  1694       gint64 new_offset;
       
  1695 
       
  1696       ret =
       
  1697           gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
       
  1698       /* we hit the upper limit, offset contains the last page start */
       
  1699       if (ret == GST_FLOW_LIMIT)
       
  1700         break;
       
  1701       /* something went wrong */
       
  1702       if (ret == GST_FLOW_UNEXPECTED)
       
  1703         new_offset = 0;
       
  1704       else if (ret != GST_FLOW_OK)
       
  1705         return ret;
       
  1706 
       
  1707       /* offset is next page start */
       
  1708       cur_offset = new_offset;
       
  1709     }
       
  1710   }
       
  1711 
       
  1712   /* we have the offset.  Actually snork and hold the page now */
       
  1713   gst_ogg_demux_seek (ogg, cur_offset);
       
  1714   ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
       
  1715   if (ret != GST_FLOW_OK)
       
  1716     /* this shouldn't be possible */
       
  1717     return ret;
       
  1718 
       
  1719   if (offset)
       
  1720     *offset = cur_offset;
       
  1721 
       
  1722   return ret;
       
  1723 }
       
  1724 
       
  1725 static gboolean
       
  1726 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
       
  1727 {
       
  1728   gint i;
       
  1729   GstOggChain *chain = ogg->current_chain;
       
  1730 
       
  1731   if (chain == NULL)
       
  1732     return TRUE;
       
  1733 
       
  1734   GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
       
  1735 
       
  1736   /* send EOS on all the pads */
       
  1737   for (i = 0; i < chain->streams->len; i++) {
       
  1738     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1739 
       
  1740     gst_pad_push_event (GST_PAD_CAST (pad), gst_event_new_eos ());
       
  1741 
       
  1742     GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
       
  1743 
       
  1744     /* deactivate first */
       
  1745     gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
       
  1746 
       
  1747     gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
       
  1748   }
       
  1749   /* if we cannot seek back to the chain, we can destroy the chain 
       
  1750    * completely */
       
  1751   if (!ogg->seekable) {
       
  1752     gst_ogg_chain_free (chain);
       
  1753   }
       
  1754   ogg->current_chain = NULL;
       
  1755 
       
  1756   return TRUE;
       
  1757 }
       
  1758 
       
  1759 static gboolean
       
  1760 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
       
  1761     GstEvent * event)
       
  1762 {
       
  1763   gint i;
       
  1764 
       
  1765   if (chain == ogg->current_chain) {
       
  1766     if (event)
       
  1767       gst_event_unref (event);
       
  1768     return TRUE;
       
  1769   }
       
  1770 
       
  1771   gst_ogg_demux_deactivate_current_chain (ogg);
       
  1772 
       
  1773   /* FIXME, should not be called with NULL */
       
  1774   if (chain == NULL) {
       
  1775     ogg->current_chain = chain;
       
  1776     return TRUE;
       
  1777   }
       
  1778 
       
  1779   GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
       
  1780 
       
  1781   /* first add the pads */
       
  1782   for (i = 0; i < chain->streams->len; i++) {
       
  1783     GstOggPad *pad;
       
  1784 
       
  1785     pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1786     GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
       
  1787 
       
  1788     /* mark discont */
       
  1789     pad->discont = TRUE;
       
  1790     pad->last_ret = GST_FLOW_OK;
       
  1791 
       
  1792     /* activate first */
       
  1793     gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
       
  1794 
       
  1795     gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
       
  1796   }
       
  1797 
       
  1798   gst_element_no_more_pads (GST_ELEMENT (ogg));
       
  1799   ogg->current_chain = chain;
       
  1800 
       
  1801   /* FIXME, must be sent from the streaming thread */
       
  1802   if (event)
       
  1803     gst_ogg_demux_send_event (ogg, event);
       
  1804 
       
  1805   GST_DEBUG_OBJECT (ogg, "starting chain");
       
  1806 
       
  1807   /* then send out any queued buffers */
       
  1808   for (i = 0; i < chain->streams->len; i++) {
       
  1809     GList *headers;
       
  1810     GstOggPad *pad;
       
  1811 
       
  1812     pad = g_array_index (chain->streams, GstOggPad *, i);
       
  1813 
       
  1814     for (headers = pad->headers; headers; headers = g_list_next (headers)) {
       
  1815       GstBuffer *buffer = GST_BUFFER (headers->data);
       
  1816 
       
  1817       if (pad->discont) {
       
  1818         GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
       
  1819         pad->discont = FALSE;
       
  1820       }
       
  1821 
       
  1822       /* we don't care about the return value here */
       
  1823       gst_pad_push (GST_PAD_CAST (pad), buffer);
       
  1824     }
       
  1825     /* and free the headers */
       
  1826     g_list_free (pad->headers);
       
  1827     pad->headers = NULL;
       
  1828   }
       
  1829   return TRUE;
       
  1830 }
       
  1831 
       
  1832 /* 
       
  1833  * do seek to time @position, return FALSE or chain and TRUE
       
  1834  */
       
  1835 static gboolean
       
  1836 gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
       
  1837     GstOggChain ** rchain)
       
  1838 {
       
  1839   GstOggChain *chain = NULL;
       
  1840   gint64 begin, end;
       
  1841   gint64 begintime, endtime;
       
  1842   gint64 target;
       
  1843   gint64 best;
       
  1844   gint64 total;
       
  1845   gint64 result = 0;
       
  1846   GstFlowReturn ret;
       
  1847   gint i;
       
  1848 
       
  1849   /* first find the chain to search in */
       
  1850   total = ogg->total_time;
       
  1851   if (ogg->chains->len == 0)
       
  1852     goto no_chains;
       
  1853 
       
  1854   for (i = ogg->chains->len - 1; i >= 0; i--) {
       
  1855     chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  1856     total -= chain->total_time;
       
  1857     if (position >= total)
       
  1858       break;
       
  1859   }
       
  1860 
       
  1861   begin = chain->offset;
       
  1862   end = chain->end_offset;
       
  1863   begintime = chain->begin_time;
       
  1864   endtime = chain->begin_time + chain->total_time;
       
  1865   target = position - total + begintime;
       
  1866   if (accurate) {
       
  1867     /* FIXME, seek 4 seconds early to catch keyframes, better implement
       
  1868      * keyframe detection. */
       
  1869     target = target - (gint64) 4 *GST_SECOND;
       
  1870   }
       
  1871   target = MAX (target, 0);
       
  1872   best = begin;
       
  1873 
       
  1874   GST_DEBUG_OBJECT (ogg,
       
  1875       "seeking to %" GST_TIME_FORMAT " in chain %p",
       
  1876       GST_TIME_ARGS (position), chain);
       
  1877   GST_DEBUG_OBJECT (ogg,
       
  1878       "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
       
  1879       end);
       
  1880   GST_DEBUG_OBJECT (ogg,
       
  1881       "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
       
  1882       GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
       
  1883   GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
       
  1884 
       
  1885   /* perform the seek */
       
  1886   while (begin < end) {
       
  1887     gint64 bisect;
       
  1888 
       
  1889     if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
       
  1890       bisect = begin;
       
  1891     } else {
       
  1892       /* take a (pretty decent) guess, avoiding overflow */
       
  1893       gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
       
  1894 
       
  1895       bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
       
  1896 
       
  1897       if (bisect <= begin)
       
  1898         bisect = begin;
       
  1899       GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
       
  1900     }
       
  1901     gst_ogg_demux_seek (ogg, bisect);
       
  1902 
       
  1903     while (begin < end) {
       
  1904       ogg_page og;
       
  1905 
       
  1906       GST_DEBUG_OBJECT (ogg,
       
  1907           "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
       
  1908           ", end %" G_GINT64_FORMAT, bisect, begin, end);
       
  1909 
       
  1910       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
       
  1911       GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
       
  1912           result);
       
  1913 
       
  1914       if (ret == GST_FLOW_LIMIT) {
       
  1915         /* we hit the upper limit, go back a bit */
       
  1916         if (bisect <= begin + 1) {
       
  1917           end = begin;          /* found it */
       
  1918         } else {
       
  1919           if (bisect == 0)
       
  1920             goto seek_error;
       
  1921 
       
  1922           bisect -= CHUNKSIZE;
       
  1923           if (bisect <= begin)
       
  1924             bisect = begin + 1;
       
  1925 
       
  1926           gst_ogg_demux_seek (ogg, bisect);
       
  1927         }
       
  1928       } else if (ret == GST_FLOW_OK) {
       
  1929         /* found offset of next ogg page */
       
  1930         gint64 granulepos;
       
  1931         GstClockTime granuletime;
       
  1932         GstFormat format;
       
  1933         GstOggPad *pad;
       
  1934 
       
  1935         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
       
  1936             result);
       
  1937         granulepos = ogg_page_granulepos (&og);
       
  1938         if (granulepos == -1) {
       
  1939           GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
       
  1940           continue;
       
  1941         }
       
  1942 
       
  1943         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
       
  1944         if (pad == NULL || pad->is_skeleton)
       
  1945           continue;
       
  1946 
       
  1947         format = GST_FORMAT_TIME;
       
  1948         if (!gst_ogg_pad_query_convert (pad,
       
  1949                 GST_FORMAT_DEFAULT, granulepos, &format,
       
  1950                 (gint64 *) & granuletime)) {
       
  1951           GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
       
  1952           granuletime = target;
       
  1953         } else {
       
  1954           if (granuletime < pad->start_time)
       
  1955             continue;
       
  1956           GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
       
  1957               GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
       
  1958 
       
  1959           granuletime -= pad->start_time;
       
  1960         }
       
  1961 
       
  1962         GST_DEBUG_OBJECT (ogg,
       
  1963             "found page with granule %" G_GINT64_FORMAT " and time %"
       
  1964             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
       
  1965 
       
  1966         if (granuletime < target) {
       
  1967           best = result;        /* raw offset of packet with granulepos */
       
  1968           begin = ogg->offset;  /* raw offset of next page */
       
  1969           begintime = granuletime;
       
  1970 
       
  1971           if (target - begintime > GST_SECOND)
       
  1972             break;
       
  1973 
       
  1974           bisect = begin;       /* *not* begin + 1 */
       
  1975         } else {
       
  1976           if (bisect <= begin + 1) {
       
  1977             end = begin;        /* found it */
       
  1978           } else {
       
  1979             if (end == ogg->offset) {   /* we're pretty close - we'd be stuck in */
       
  1980               end = result;
       
  1981               bisect -= CHUNKSIZE;      /* an endless loop otherwise. */
       
  1982               if (bisect <= begin)
       
  1983                 bisect = begin + 1;
       
  1984               gst_ogg_demux_seek (ogg, bisect);
       
  1985             } else {
       
  1986               end = result;
       
  1987               endtime = granuletime;
       
  1988               break;
       
  1989             }
       
  1990           }
       
  1991         }
       
  1992       } else
       
  1993         goto seek_error;
       
  1994     }
       
  1995   }
       
  1996 
       
  1997   ogg->offset = best;
       
  1998   *rchain = chain;
       
  1999 
       
  2000   return TRUE;
       
  2001 
       
  2002 no_chains:
       
  2003   {
       
  2004     GST_DEBUG_OBJECT (ogg, "no chains");
       
  2005     return FALSE;
       
  2006   }
       
  2007 seek_error:
       
  2008   {
       
  2009     GST_DEBUG_OBJECT (ogg, "got a seek error");
       
  2010     return FALSE;
       
  2011   }
       
  2012 }
       
  2013 
       
  2014 /* does not take ownership of the event */
       
  2015 static gboolean
       
  2016 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
       
  2017 {
       
  2018   GstOggChain *chain = NULL;
       
  2019   gboolean res;
       
  2020   gboolean flush, accurate;
       
  2021   GstFormat format;
       
  2022   gdouble rate;
       
  2023   GstSeekFlags flags;
       
  2024   GstSeekType cur_type, stop_type;
       
  2025   gint64 cur, stop;
       
  2026   gboolean update;
       
  2027 
       
  2028   if (event) {
       
  2029     GST_DEBUG_OBJECT (ogg, "seek with event");
       
  2030 
       
  2031     gst_event_parse_seek (event, &rate, &format, &flags,
       
  2032         &cur_type, &cur, &stop_type, &stop);
       
  2033 
       
  2034     /* we can only seek on time */
       
  2035     if (format != GST_FORMAT_TIME) {
       
  2036       GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
       
  2037       goto error;
       
  2038     }
       
  2039   } else {
       
  2040     GST_DEBUG_OBJECT (ogg, "seek without event");
       
  2041 
       
  2042     flags = 0;
       
  2043     rate = 1.0;
       
  2044   }
       
  2045 
       
  2046   GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
       
  2047 
       
  2048   flush = flags & GST_SEEK_FLAG_FLUSH;
       
  2049   accurate = flags & GST_SEEK_FLAG_ACCURATE;
       
  2050 
       
  2051   /* first step is to unlock the streaming thread if it is
       
  2052    * blocked in a chain call, we do this by starting the flush. because
       
  2053    * we cannot yet hold any streaming lock, we have to protect the chains
       
  2054    * with their own lock. */
       
  2055   if (flush) {
       
  2056     gint i;
       
  2057 
       
  2058     gst_pad_push_event (ogg->sinkpad, gst_event_new_flush_start ());
       
  2059 
       
  2060     GST_CHAIN_LOCK (ogg);
       
  2061     for (i = 0; i < ogg->chains->len; i++) {
       
  2062       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  2063       gint j;
       
  2064 
       
  2065       for (j = 0; j < chain->streams->len; j++) {
       
  2066         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
       
  2067 
       
  2068         gst_pad_push_event (GST_PAD (pad), gst_event_new_flush_start ());
       
  2069       }
       
  2070     }
       
  2071     GST_CHAIN_UNLOCK (ogg);
       
  2072   } else {
       
  2073     gst_pad_pause_task (ogg->sinkpad);
       
  2074   }
       
  2075 
       
  2076   /* now grab the stream lock so that streaming cannot continue, for
       
  2077    * non flushing seeks when the element is in PAUSED this could block
       
  2078    * forever. */
       
  2079   GST_PAD_STREAM_LOCK (ogg->sinkpad);
       
  2080 
       
  2081   if (ogg->segment_running && !flush) {
       
  2082     /* create the segment event to close the current segment */
       
  2083     if ((chain = ogg->current_chain)) {
       
  2084       GstEvent *newseg;
       
  2085 
       
  2086       newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
       
  2087           GST_FORMAT_TIME, ogg->segment.start + chain->segment_start,
       
  2088           ogg->segment.last_stop + chain->segment_start, ogg->segment.time);
       
  2089 
       
  2090       /* send segment on old chain, FIXME, must be sent from streaming thread. */
       
  2091       gst_ogg_demux_send_event (ogg, newseg);
       
  2092     }
       
  2093   }
       
  2094 
       
  2095   if (event) {
       
  2096     gst_segment_set_seek (&ogg->segment, rate, format, flags,
       
  2097         cur_type, cur, stop_type, stop, &update);
       
  2098   }
       
  2099 
       
  2100   GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
       
  2101       GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
       
  2102       GST_TIME_ARGS (ogg->segment.stop));
       
  2103 
       
  2104   /* we need to stop flushing on the srcpad as we're going to use it
       
  2105    * next. We can do this as we have the STREAM lock now. */
       
  2106   gst_pad_push_event (ogg->sinkpad, gst_event_new_flush_stop ());
       
  2107 
       
  2108   {
       
  2109     gint i;
       
  2110 
       
  2111     /* reset all ogg streams now, need to do this from within the lock to
       
  2112      * make sure the streaming thread is not messing with the stream */
       
  2113     for (i = 0; i < ogg->chains->len; i++) {
       
  2114       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  2115 
       
  2116       gst_ogg_chain_reset (chain);
       
  2117     }
       
  2118   }
       
  2119 
       
  2120   res = gst_ogg_demux_do_seek (ogg, ogg->segment.last_stop, accurate, &chain);
       
  2121 
       
  2122   /* seek failed, make sure we continue the current chain */
       
  2123   if (!res) {
       
  2124     chain = ogg->current_chain;
       
  2125   }
       
  2126 
       
  2127   if (!chain)
       
  2128     goto no_chain;
       
  2129 
       
  2130   /* now we have a new position, prepare for streaming again */
       
  2131   {
       
  2132     GstEvent *event;
       
  2133     gint64 stop;
       
  2134     gint64 start;
       
  2135     gint64 last_stop, begin_time;
       
  2136 
       
  2137     /* we have to send the flush to the old chain, not the new one */
       
  2138     if (flush)
       
  2139       gst_ogg_demux_send_event (ogg, gst_event_new_flush_stop ());
       
  2140 
       
  2141     /* we need this to see how far inside the chain we need to start */
       
  2142     if (chain->begin_time != GST_CLOCK_TIME_NONE)
       
  2143       begin_time = chain->begin_time;
       
  2144     else
       
  2145       begin_time = 0;
       
  2146 
       
  2147     /* segment.start gives the start over all chains, we calculate the amount
       
  2148      * of time into this chain we need to start */
       
  2149     start = ogg->segment.start - begin_time;
       
  2150     if (chain->segment_start != GST_CLOCK_TIME_NONE)
       
  2151       start += chain->segment_start;
       
  2152 
       
  2153     if ((stop = ogg->segment.stop) == -1)
       
  2154       stop = ogg->segment.duration;
       
  2155 
       
  2156     /* segment.stop gives the stop time over all chains, calculate the amount of
       
  2157      * time we need to stop in this chain */
       
  2158     if (stop != -1) {
       
  2159       if (stop > begin_time)
       
  2160         stop -= begin_time;
       
  2161       else
       
  2162         stop = 0;
       
  2163       stop += chain->segment_start;
       
  2164       /* we must stop when this chain ends and switch to the next chain to play
       
  2165        * the remainder of the segment. */
       
  2166       stop = MIN (stop, chain->segment_stop);
       
  2167     }
       
  2168 
       
  2169     last_stop = ogg->segment.last_stop - begin_time;
       
  2170     if (chain->segment_start != GST_CLOCK_TIME_NONE)
       
  2171       last_stop += chain->segment_start;
       
  2172 
       
  2173     /* create the segment event we are going to send out */
       
  2174     if (ogg->segment.rate >= 0.0)
       
  2175       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
       
  2176           ogg->segment.format, last_stop, stop, ogg->segment.time);
       
  2177     else
       
  2178       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
       
  2179           ogg->segment.format, start, last_stop, ogg->segment.time);
       
  2180 
       
  2181     if (chain != ogg->current_chain) {
       
  2182       /* switch to different chain, send segment on new chain */
       
  2183       gst_ogg_demux_activate_chain (ogg, chain, event);
       
  2184     } else {
       
  2185       /* mark discont and send segment on current chain */
       
  2186       gst_ogg_chain_mark_discont (chain);
       
  2187       /* This event should be sent from the streaming thread (sink pad task) */
       
  2188       if (ogg->newsegment)
       
  2189         gst_event_unref (ogg->newsegment);
       
  2190       ogg->newsegment = event;
       
  2191     }
       
  2192 
       
  2193     /* notify start of new segment */
       
  2194     if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
       
  2195       gst_element_post_message (GST_ELEMENT (ogg),
       
  2196           gst_message_new_segment_start (GST_OBJECT (ogg),
       
  2197               GST_FORMAT_TIME, ogg->segment.last_stop));
       
  2198     }
       
  2199 
       
  2200     ogg->segment_running = TRUE;
       
  2201     /* restart our task since it might have been stopped when we did the 
       
  2202      * flush. */
       
  2203     gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
       
  2204         ogg->sinkpad);
       
  2205   }
       
  2206 
       
  2207   /* streaming can continue now */
       
  2208   GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
       
  2209 
       
  2210   return res;
       
  2211 
       
  2212 error:
       
  2213   {
       
  2214     GST_DEBUG_OBJECT (ogg, "seek failed");
       
  2215     return FALSE;
       
  2216   }
       
  2217 no_chain:
       
  2218   {
       
  2219     GST_DEBUG_OBJECT (ogg, "no chain to seek in");
       
  2220     GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
       
  2221     return FALSE;
       
  2222   }
       
  2223 }
       
  2224 
       
  2225 /* finds each bitstream link one at a time using a bisection search
       
  2226  * (has to begin by knowing the offset of the lb's initial page).
       
  2227  * Recurses for each link so it can alloc the link storage after
       
  2228  * finding them all, then unroll and fill the cache at the same time 
       
  2229  */
       
  2230 static GstFlowReturn
       
  2231 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
       
  2232     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
       
  2233 {
       
  2234   gint64 endsearched = end;
       
  2235   gint64 next = end;
       
  2236   ogg_page og;
       
  2237   GstFlowReturn ret;
       
  2238   gint64 offset;
       
  2239   GstOggChain *nextchain;
       
  2240 
       
  2241   GST_LOG_OBJECT (ogg,
       
  2242       "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
       
  2243       ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
       
  2244 
       
  2245   /* the below guards against garbage seperating the last and
       
  2246    * first pages of two links. */
       
  2247   while (searched < endsearched) {
       
  2248     gint64 bisect;
       
  2249 
       
  2250     if (endsearched - searched < CHUNKSIZE) {
       
  2251       bisect = searched;
       
  2252     } else {
       
  2253       bisect = (searched + endsearched) / 2;
       
  2254     }
       
  2255 
       
  2256     gst_ogg_demux_seek (ogg, bisect);
       
  2257     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
       
  2258 
       
  2259     if (ret == GST_FLOW_UNEXPECTED) {
       
  2260       endsearched = bisect;
       
  2261     } else if (ret == GST_FLOW_OK) {
       
  2262       glong serial = ogg_page_serialno (&og);
       
  2263 
       
  2264       if (!gst_ogg_chain_has_stream (chain, serial)) {
       
  2265         endsearched = bisect;
       
  2266         next = offset;
       
  2267       } else {
       
  2268         searched = offset + og.header_len + og.body_len;
       
  2269       }
       
  2270     } else
       
  2271       return ret;
       
  2272   }
       
  2273 
       
  2274   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
       
  2275 
       
  2276   chain->end_offset = searched;
       
  2277   ret = gst_ogg_demux_read_end_chain (ogg, chain);
       
  2278   if (ret != GST_FLOW_OK)
       
  2279     return ret;
       
  2280 
       
  2281   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
       
  2282 
       
  2283   gst_ogg_demux_seek (ogg, next);
       
  2284   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
       
  2285   if (ret == GST_FLOW_UNEXPECTED) {
       
  2286     nextchain = NULL;
       
  2287     ret = GST_FLOW_OK;
       
  2288     GST_LOG_OBJECT (ogg, "no next chain");
       
  2289   } else if (ret != GST_FLOW_OK)
       
  2290     goto done;
       
  2291 
       
  2292   if (searched < end && nextchain != NULL) {
       
  2293     ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
       
  2294         end, nextchain, m + 1);
       
  2295     if (ret != GST_FLOW_OK)
       
  2296       goto done;
       
  2297   }
       
  2298   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
       
  2299 
       
  2300   g_array_insert_val (ogg->chains, 0, chain);
       
  2301 
       
  2302 done:
       
  2303   return ret;
       
  2304 }
       
  2305 
       
  2306 /* read a chain from the ogg file. This code will
       
  2307  * read all BOS pages and will create and return a GstOggChain 
       
  2308  * structure with the results. 
       
  2309  * 
       
  2310  * This function will also read N pages from each stream in the
       
  2311  * chain and submit them to the decoders. When the decoder has
       
  2312  * decoded the first buffer, we know the timestamp of the first
       
  2313  * page in the chain.
       
  2314  */
       
  2315 static GstFlowReturn
       
  2316 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
       
  2317 {
       
  2318   GstFlowReturn ret;
       
  2319   GstOggChain *chain = NULL;
       
  2320   gint64 offset = ogg->offset;
       
  2321   ogg_page op;
       
  2322   gboolean done;
       
  2323   gint i;
       
  2324 
       
  2325   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
       
  2326 
       
  2327   /* first read the BOS pages, do typefind on them, create
       
  2328    * the decoders, send data to the decoders. */
       
  2329   while (TRUE) {
       
  2330     GstOggPad *pad;
       
  2331     glong serial;
       
  2332 
       
  2333     ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
       
  2334     if (ret != GST_FLOW_OK) {
       
  2335       GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
       
  2336       break;
       
  2337     }
       
  2338     if (!ogg_page_bos (&op)) {
       
  2339       GST_WARNING_OBJECT (ogg, "page is not BOS page");
       
  2340       break;
       
  2341     }
       
  2342 
       
  2343     if (chain == NULL) {
       
  2344       chain = gst_ogg_chain_new (ogg);
       
  2345       chain->offset = offset;
       
  2346     }
       
  2347 
       
  2348     serial = ogg_page_serialno (&op);
       
  2349     if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
       
  2350       GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
       
  2351           serial);
       
  2352       continue;
       
  2353     }
       
  2354 
       
  2355     pad = gst_ogg_chain_new_stream (chain, serial);
       
  2356     gst_ogg_pad_submit_page (pad, &op);
       
  2357   }
       
  2358 
       
  2359   if (ret != GST_FLOW_OK || chain == NULL) {
       
  2360     if (ret != GST_FLOW_UNEXPECTED) {
       
  2361       GST_WARNING_OBJECT (ogg, "failed to read chain");
       
  2362     } else {
       
  2363       GST_DEBUG_OBJECT (ogg, "done reading chains");
       
  2364     }
       
  2365     if (chain) {
       
  2366       gst_ogg_chain_free (chain);
       
  2367     }
       
  2368     if (res_chain)
       
  2369       *res_chain = NULL;
       
  2370     return ret;
       
  2371   }
       
  2372 
       
  2373   chain->have_bos = TRUE;
       
  2374   GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
       
  2375 
       
  2376   /* now read pages until we receive a buffer from each of the
       
  2377    * stream decoders, this will tell us the timestamp of the
       
  2378    * first packet in the chain then */
       
  2379 
       
  2380   /* save the offset to the first non bos page in the chain: if searching for
       
  2381    * pad->first_time we read past the end of the chain, we'll seek back to this
       
  2382    * position
       
  2383    */
       
  2384   offset = ogg->offset;
       
  2385 
       
  2386   done = FALSE;
       
  2387   while (!done) {
       
  2388     glong serial;
       
  2389     gboolean known_serial = FALSE;
       
  2390     GstFlowReturn ret;
       
  2391 
       
  2392     serial = ogg_page_serialno (&op);
       
  2393     done = TRUE;
       
  2394     for (i = 0; i < chain->streams->len; i++) {
       
  2395       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  2396 
       
  2397       GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT, pad->serialno,
       
  2398           GST_TIME_ARGS (pad->start_time));
       
  2399 
       
  2400       if (pad->serialno == serial) {
       
  2401         known_serial = TRUE;
       
  2402 
       
  2403         /* submit the page now, this will fill in the start_time when the
       
  2404          * internal decoder finds it */
       
  2405         gst_ogg_pad_submit_page (pad, &op);
       
  2406 
       
  2407         if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
       
  2408           /* got EOS on a pad before we could find its start_time.
       
  2409            * We have no chance of finding a start_time for every pad so
       
  2410            * stop searching for the other start_time(s).
       
  2411            */
       
  2412           done = TRUE;
       
  2413           break;
       
  2414         }
       
  2415       }
       
  2416       /* the timestamp will be filled in when we submit the pages */
       
  2417       if (!pad->is_skeleton)
       
  2418         done &= (pad->start_time != GST_CLOCK_TIME_NONE);
       
  2419 
       
  2420       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->serialno, done);
       
  2421     }
       
  2422 
       
  2423     /* we read a page not belonging to the current chain: seek back to the
       
  2424      * beginning of the chain
       
  2425      */
       
  2426     if (!known_serial) {
       
  2427       GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
       
  2428       gst_ogg_demux_seek (ogg, offset);
       
  2429       break;
       
  2430     }
       
  2431 
       
  2432     if (!done) {
       
  2433       ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
       
  2434       if (ret != GST_FLOW_OK)
       
  2435         break;
       
  2436     }
       
  2437   }
       
  2438   GST_LOG_OBJECT (ogg, "done reading chain");
       
  2439   /* now we can fill in the missing info using queries */
       
  2440   for (i = 0; i < chain->streams->len; i++) {
       
  2441     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  2442     GstFormat target;
       
  2443 
       
  2444     if (pad->is_skeleton)
       
  2445       continue;
       
  2446 
       
  2447     GST_LOG_OBJECT (ogg, "convert first granule %" G_GUINT64_FORMAT " to time ",
       
  2448         pad->first_granule);
       
  2449 
       
  2450     target = GST_FORMAT_TIME;
       
  2451     if (!gst_ogg_pad_query_convert (pad,
       
  2452             GST_FORMAT_DEFAULT, pad->first_granule, &target,
       
  2453             (gint64 *) & pad->first_time)) {
       
  2454       GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
       
  2455     } else {
       
  2456       GST_LOG_OBJECT (ogg, "converted to first time %" GST_TIME_FORMAT,
       
  2457           GST_TIME_ARGS (pad->first_time));
       
  2458     }
       
  2459 
       
  2460     pad->mode = GST_OGG_PAD_MODE_STREAMING;
       
  2461   }
       
  2462 
       
  2463   if (res_chain)
       
  2464     *res_chain = chain;
       
  2465 
       
  2466   return GST_FLOW_OK;
       
  2467 }
       
  2468 
       
  2469 /* read the last pages from the ogg stream to get the final
       
  2470  * page end_offsets.
       
  2471  */
       
  2472 static GstFlowReturn
       
  2473 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
       
  2474 {
       
  2475   gint64 begin = chain->end_offset;
       
  2476   gint64 end = begin;
       
  2477   gint64 last_granule = -1;
       
  2478   GstOggPad *last_pad = NULL;
       
  2479   GstFormat target;
       
  2480   GstFlowReturn ret;
       
  2481   gboolean done = FALSE;
       
  2482   ogg_page og;
       
  2483   gint i;
       
  2484 
       
  2485   while (!done) {
       
  2486     begin -= CHUNKSIZE;
       
  2487     if (begin < 0)
       
  2488       begin = 0;
       
  2489 
       
  2490     gst_ogg_demux_seek (ogg, begin);
       
  2491 
       
  2492     /* now continue reading until we run out of data, if we find a page
       
  2493      * start, we save it. It might not be the final page as there could be
       
  2494      * another page after this one. */
       
  2495     while (ogg->offset < end) {
       
  2496       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
       
  2497 
       
  2498       if (ret == GST_FLOW_LIMIT)
       
  2499         break;
       
  2500       if (ret != GST_FLOW_OK)
       
  2501         return ret;
       
  2502 
       
  2503       for (i = 0; i < chain->streams->len; i++) {
       
  2504         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  2505 
       
  2506         if (pad->is_skeleton)
       
  2507           continue;
       
  2508 
       
  2509         if (pad->serialno == ogg_page_serialno (&og)) {
       
  2510           gint64 granulepos = ogg_page_granulepos (&og);
       
  2511 
       
  2512           if (last_granule < granulepos) {
       
  2513             last_granule = granulepos;
       
  2514             last_pad = pad;
       
  2515           }
       
  2516           done = TRUE;
       
  2517           break;
       
  2518         }
       
  2519       }
       
  2520     }
       
  2521   }
       
  2522 
       
  2523   target = GST_FORMAT_TIME;
       
  2524   if (last_granule == -1 || !gst_ogg_pad_query_convert (last_pad,
       
  2525           GST_FORMAT_DEFAULT, last_granule, &target,
       
  2526           (gint64 *) & chain->segment_stop)) {
       
  2527     GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
       
  2528     chain->segment_stop = GST_CLOCK_TIME_NONE;
       
  2529   }
       
  2530 
       
  2531   return GST_FLOW_OK;
       
  2532 }
       
  2533 
       
  2534 /* find a pad with a given serial number
       
  2535  */
       
  2536 static GstOggPad *
       
  2537 gst_ogg_demux_find_pad (GstOggDemux * ogg, int serialno)
       
  2538 {
       
  2539   GstOggPad *pad;
       
  2540   gint i;
       
  2541 
       
  2542   /* first look in building chain if any */
       
  2543   if (ogg->building_chain) {
       
  2544     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
       
  2545     if (pad)
       
  2546       return pad;
       
  2547   }
       
  2548 
       
  2549   /* then look in current chain if any */
       
  2550   if (ogg->current_chain) {
       
  2551     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
       
  2552     if (pad)
       
  2553       return pad;
       
  2554   }
       
  2555 
       
  2556   for (i = 0; i < ogg->chains->len; i++) {
       
  2557     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  2558 
       
  2559     pad = gst_ogg_chain_get_stream (chain, serialno);
       
  2560     if (pad)
       
  2561       return pad;
       
  2562   }
       
  2563   return NULL;
       
  2564 }
       
  2565 
       
  2566 /* find a chain with a given serial number
       
  2567  */
       
  2568 static GstOggChain *
       
  2569 gst_ogg_demux_find_chain (GstOggDemux * ogg, int serialno)
       
  2570 {
       
  2571   GstOggPad *pad;
       
  2572 
       
  2573   pad = gst_ogg_demux_find_pad (ogg, serialno);
       
  2574   if (pad) {
       
  2575     return pad->chain;
       
  2576   }
       
  2577   return NULL;
       
  2578 }
       
  2579 
       
  2580 /* returns TRUE if all streams have valid start time */
       
  2581 static gboolean
       
  2582 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
       
  2583 {
       
  2584   gint i;
       
  2585   gboolean res = TRUE;
       
  2586 
       
  2587   chain->total_time = GST_CLOCK_TIME_NONE;
       
  2588   chain->segment_start = G_MAXUINT64;
       
  2589 
       
  2590   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
       
  2591 
       
  2592   for (i = 0; i < chain->streams->len; i++) {
       
  2593     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  2594 
       
  2595     if (pad->is_skeleton)
       
  2596       continue;
       
  2597 
       
  2598     /*  can do this if the pad start time is not defined */
       
  2599     if (pad->start_time == GST_CLOCK_TIME_NONE)
       
  2600       res = FALSE;
       
  2601     else
       
  2602       chain->segment_start = MIN (chain->segment_start, pad->start_time);
       
  2603   }
       
  2604 
       
  2605   if (chain->segment_stop != GST_CLOCK_TIME_NONE
       
  2606       && chain->segment_start != G_MAXUINT64)
       
  2607     chain->total_time = chain->segment_stop - chain->segment_start;
       
  2608 
       
  2609   GST_DEBUG_OBJECT (ogg, "return %d", res);
       
  2610 
       
  2611   return res;
       
  2612 }
       
  2613 
       
  2614 static void
       
  2615 gst_ogg_demux_collect_info (GstOggDemux * ogg)
       
  2616 {
       
  2617   gint i;
       
  2618 
       
  2619   /* collect all info */
       
  2620   ogg->total_time = 0;
       
  2621 
       
  2622   for (i = 0; i < ogg->chains->len; i++) {
       
  2623     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  2624 
       
  2625     chain->begin_time = ogg->total_time;
       
  2626 
       
  2627     gst_ogg_demux_collect_chain_info (ogg, chain);
       
  2628 
       
  2629     ogg->total_time += chain->total_time;
       
  2630   }
       
  2631   gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
       
  2632 }
       
  2633 
       
  2634 /* find all the chains in the ogg file, this reads the first and
       
  2635  * last page of the ogg stream, if they match then the ogg file has
       
  2636  * just one chain, else we do a binary search for all chains.
       
  2637  */
       
  2638 static GstFlowReturn
       
  2639 gst_ogg_demux_find_chains (GstOggDemux * ogg)
       
  2640 {
       
  2641   ogg_page og;
       
  2642   GstPad *peer;
       
  2643   GstFormat format;
       
  2644   gboolean res;
       
  2645   gulong serialno;
       
  2646   GstOggChain *chain;
       
  2647   GstFlowReturn ret;
       
  2648 
       
  2649   /* get peer to figure out length */
       
  2650   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
       
  2651     goto no_peer;
       
  2652 
       
  2653   /* find length to read last page, we store this for later use. */
       
  2654   format = GST_FORMAT_BYTES;
       
  2655   res = gst_pad_query_duration (peer, &format, &ogg->length);
       
  2656   gst_object_unref (peer);
       
  2657   if (!res || ogg->length <= 0)
       
  2658     goto no_length;
       
  2659 
       
  2660   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
       
  2661 
       
  2662   /* read chain from offset 0, this is the first chain of the
       
  2663    * ogg file. */
       
  2664   gst_ogg_demux_seek (ogg, 0);
       
  2665   ret = gst_ogg_demux_read_chain (ogg, &chain);
       
  2666   if (ret != GST_FLOW_OK)
       
  2667     goto no_first_chain;
       
  2668 
       
  2669   /* read page from end offset, we use this page to check if its serial
       
  2670    * number is contained in the first chain. If this is the case then
       
  2671    * this ogg is not a chained ogg and we can skip the scanning. */
       
  2672   gst_ogg_demux_seek (ogg, ogg->length);
       
  2673   ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
       
  2674   if (ret != GST_FLOW_OK)
       
  2675     goto no_last_page;
       
  2676 
       
  2677   serialno = ogg_page_serialno (&og);
       
  2678 
       
  2679   if (!gst_ogg_chain_has_stream (chain, serialno)) {
       
  2680     /* the last page is not in the first stream, this means we should
       
  2681      * find all the chains in this chained ogg. */
       
  2682     ret =
       
  2683         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
       
  2684         0);
       
  2685   } else {
       
  2686     /* we still call this function here but with an empty range so that
       
  2687      * we can reuse the setup code in this routine. */
       
  2688     ret =
       
  2689         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
       
  2690         chain, 0);
       
  2691   }
       
  2692   if (ret != GST_FLOW_OK)
       
  2693     goto done;
       
  2694 
       
  2695   /* all fine, collect and print */
       
  2696   gst_ogg_demux_collect_info (ogg);
       
  2697 
       
  2698   /* dump our chains and streams */
       
  2699   gst_ogg_print (ogg);
       
  2700 
       
  2701 done:
       
  2702   return ret;
       
  2703 
       
  2704   /*** error cases ***/
       
  2705 no_peer:
       
  2706   {
       
  2707     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
       
  2708     return GST_FLOW_NOT_LINKED;
       
  2709   }
       
  2710 no_length:
       
  2711   {
       
  2712     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
       
  2713     return GST_FLOW_NOT_SUPPORTED;
       
  2714   }
       
  2715 no_first_chain:
       
  2716   {
       
  2717     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
       
  2718     return GST_FLOW_ERROR;
       
  2719   }
       
  2720 no_last_page:
       
  2721   {
       
  2722     GST_DEBUG_OBJECT (ogg, "can't get last page");
       
  2723     if (chain)
       
  2724       gst_ogg_chain_free (chain);
       
  2725     return ret;
       
  2726   }
       
  2727 }
       
  2728 
       
  2729 static GstFlowReturn
       
  2730 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
       
  2731 {
       
  2732   GstOggPad *pad;
       
  2733   gint64 granule;
       
  2734   guint serialno;
       
  2735   GstFlowReturn result = GST_FLOW_OK;
       
  2736 
       
  2737   serialno = ogg_page_serialno (page);
       
  2738   granule = ogg_page_granulepos (page);
       
  2739 
       
  2740   GST_LOG_OBJECT (ogg,
       
  2741       "processing ogg page (serial %08x, pageno %ld, granulepos %"
       
  2742       G_GINT64_FORMAT ", bos %d)",
       
  2743       serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
       
  2744 
       
  2745   if (ogg_page_bos (page)) {
       
  2746     GstOggChain *chain;
       
  2747 
       
  2748     /* first page */
       
  2749     /* see if we know about the chain already */
       
  2750     chain = gst_ogg_demux_find_chain (ogg, serialno);
       
  2751     if (chain) {
       
  2752       GstEvent *event;
       
  2753 
       
  2754       /* create the newsegment event we are going to send out */
       
  2755       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
       
  2756           GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
       
  2757           chain->begin_time);
       
  2758 
       
  2759       GST_DEBUG_OBJECT (ogg,
       
  2760           "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
       
  2761           ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (chain->segment_start),
       
  2762           GST_TIME_ARGS (chain->segment_stop),
       
  2763           GST_TIME_ARGS (chain->begin_time));
       
  2764 
       
  2765       /* activate it as it means we have a non-header */
       
  2766       gst_ogg_demux_activate_chain (ogg, chain, event);
       
  2767       pad = gst_ogg_demux_find_pad (ogg, serialno);
       
  2768     } else {
       
  2769       GstClockTime chain_time;
       
  2770       GstOggChain *current_chain;
       
  2771       gint64 current_time;
       
  2772 
       
  2773       /* this can only happen in non-seekabe mode */
       
  2774       if (ogg->seekable)
       
  2775         goto unknown_chain;
       
  2776 
       
  2777       current_chain = ogg->current_chain;
       
  2778       current_time = ogg->segment.last_stop;
       
  2779 
       
  2780       if (current_chain) {
       
  2781         /* remove existing pads */
       
  2782         gst_ogg_demux_deactivate_current_chain (ogg);
       
  2783       }
       
  2784       /* time of new chain is current time */
       
  2785       chain_time = current_time;
       
  2786 
       
  2787       if (ogg->building_chain == NULL) {
       
  2788         GstOggChain *newchain;
       
  2789 
       
  2790         newchain = gst_ogg_chain_new (ogg);
       
  2791         newchain->offset = 0;
       
  2792         /* set new chain begin time aligned with end time of old chain */
       
  2793         newchain->begin_time = chain_time;
       
  2794         GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
       
  2795             GST_TIME_ARGS (chain_time));
       
  2796 
       
  2797         /* and this is the one we are building now */
       
  2798         ogg->building_chain = newchain;
       
  2799       }
       
  2800       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
       
  2801     }
       
  2802   } else {
       
  2803     pad = gst_ogg_demux_find_pad (ogg, serialno);
       
  2804   }
       
  2805   if (pad) {
       
  2806     result = gst_ogg_pad_submit_page (pad, page);
       
  2807   } else {
       
  2808     /* no pad, this is pretty fatal. This means an ogg page without bos
       
  2809      * has been seen for this serialno. could just ignore it too... */
       
  2810     goto unknown_pad;
       
  2811   }
       
  2812   return result;
       
  2813 
       
  2814   /* ERRORS */
       
  2815 unknown_chain:
       
  2816   {
       
  2817     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
       
  2818         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
       
  2819     return GST_FLOW_ERROR;
       
  2820   }
       
  2821 unknown_pad:
       
  2822   {
       
  2823     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
       
  2824         (NULL), ("unknown ogg pad for serial %08x detected", serialno));
       
  2825     return GST_FLOW_ERROR;
       
  2826   }
       
  2827 }
       
  2828 
       
  2829 /* streaming mode, receive a buffer, parse it, create pads for
       
  2830  * the serialno, submit pages and packets to the oggpads
       
  2831  */
       
  2832 static GstFlowReturn
       
  2833 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
       
  2834 {
       
  2835   GstOggDemux *ogg;
       
  2836   gint ret;
       
  2837   GstFlowReturn result = GST_FLOW_OK;
       
  2838 
       
  2839   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
       
  2840 
       
  2841   GST_DEBUG_OBJECT (ogg, "chain");
       
  2842   result = gst_ogg_demux_submit_buffer (ogg, buffer);
       
  2843 
       
  2844   while (result == GST_FLOW_OK) {
       
  2845     ogg_page page;
       
  2846 
       
  2847     ret = ogg_sync_pageout (&ogg->sync, &page);
       
  2848     if (ret == 0)
       
  2849       /* need more data */
       
  2850       break;
       
  2851     if (ret == -1) {
       
  2852       /* discontinuity in the pages */
       
  2853       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
       
  2854     } else {
       
  2855       result = gst_ogg_demux_handle_page (ogg, &page);
       
  2856     }
       
  2857   }
       
  2858   return result;
       
  2859 }
       
  2860 
       
  2861 static void
       
  2862 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
       
  2863 {
       
  2864   GstOggChain *chain = ogg->current_chain;
       
  2865 
       
  2866   if (chain) {
       
  2867     gint i;
       
  2868 
       
  2869     for (i = 0; i < chain->streams->len; i++) {
       
  2870       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
       
  2871 
       
  2872       gst_event_ref (event);
       
  2873       GST_DEBUG_OBJECT (ogg, "Pushing event on pad %p", pad);
       
  2874       gst_pad_push_event (GST_PAD (pad), event);
       
  2875     }
       
  2876   }
       
  2877   gst_event_unref (event);
       
  2878 }
       
  2879 
       
  2880 static GstFlowReturn
       
  2881 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
       
  2882     GstFlowReturn ret)
       
  2883 {
       
  2884   GstOggChain *chain;
       
  2885 
       
  2886   /* store the value */
       
  2887   pad->last_ret = ret;
       
  2888 
       
  2889   /* any other error that is not-linked can be returned right
       
  2890    * away */
       
  2891   if (ret != GST_FLOW_NOT_LINKED)
       
  2892     goto done;
       
  2893 
       
  2894   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
       
  2895   chain = ogg->current_chain;
       
  2896   if (chain) {
       
  2897     gint i;
       
  2898 
       
  2899     for (i = 0; i < chain->streams->len; i++) {
       
  2900       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
       
  2901 
       
  2902       ret = opad->last_ret;
       
  2903       /* some other return value (must be SUCCESS but we can return
       
  2904        * other values as well) */
       
  2905       if (ret != GST_FLOW_NOT_LINKED)
       
  2906         goto done;
       
  2907     }
       
  2908     /* if we get here, all other pads were unlinked and we return
       
  2909      * NOT_LINKED then */
       
  2910   }
       
  2911 done:
       
  2912   return ret;
       
  2913 }
       
  2914 
       
  2915 static GstFlowReturn
       
  2916 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
       
  2917 {
       
  2918   GstFlowReturn ret;
       
  2919   GstBuffer *buffer;
       
  2920 
       
  2921   if (ogg->offset == ogg->length) {
       
  2922     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
       
  2923         " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
       
  2924     ret = GST_FLOW_UNEXPECTED;
       
  2925     goto done;
       
  2926   }
       
  2927 
       
  2928   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
       
  2929   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
       
  2930   if (ret != GST_FLOW_OK) {
       
  2931     GST_LOG_OBJECT (ogg, "Failed pull_range");
       
  2932     goto done;
       
  2933   }
       
  2934 
       
  2935   ogg->offset += GST_BUFFER_SIZE (buffer);
       
  2936 
       
  2937   if (G_UNLIKELY (ogg->newsegment)) {
       
  2938     gst_ogg_demux_send_event (ogg, ogg->newsegment);
       
  2939     ogg->newsegment = NULL;
       
  2940   }
       
  2941 
       
  2942   ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
       
  2943   if (ret != GST_FLOW_OK) {
       
  2944     GST_LOG_OBJECT (ogg, "Failed demux_chain");
       
  2945     goto done;
       
  2946   }
       
  2947 
       
  2948   /* check for the end of the segment */
       
  2949   if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
       
  2950     if (ogg->segment.last_stop > ogg->segment.stop) {
       
  2951       ret = GST_FLOW_UNEXPECTED;
       
  2952       goto done;
       
  2953     }
       
  2954   }
       
  2955 done:
       
  2956   return ret;
       
  2957 }
       
  2958 
       
  2959 /* reverse mode.
       
  2960  *
       
  2961  * We read the pages backwards and send the packets forwards. The first packet
       
  2962  * in the page will be pushed with the DISCONT flag set.
       
  2963  *
       
  2964  * Special care has to be taken for continued pages, which we can only decode
       
  2965  * when we have the previous page(s).
       
  2966  */
       
  2967 static GstFlowReturn
       
  2968 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
       
  2969 {
       
  2970   GstFlowReturn ret;
       
  2971   ogg_page page;
       
  2972   gint64 offset;
       
  2973 
       
  2974   if (ogg->offset == 0) {
       
  2975     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
       
  2976         " == 0", ogg->offset);
       
  2977     ret = GST_FLOW_UNEXPECTED;
       
  2978     goto done;
       
  2979   }
       
  2980 
       
  2981   GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
       
  2982   ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
       
  2983   if (ret != GST_FLOW_OK)
       
  2984     goto done;
       
  2985 
       
  2986   ogg->offset = offset;
       
  2987 
       
  2988   if (G_UNLIKELY (ogg->newsegment)) {
       
  2989     gst_ogg_demux_send_event (ogg, ogg->newsegment);
       
  2990     ogg->newsegment = NULL;
       
  2991   }
       
  2992 
       
  2993   ret = gst_ogg_demux_handle_page (ogg, &page);
       
  2994   if (ret != GST_FLOW_OK)
       
  2995     goto done;
       
  2996 
       
  2997   /* check for the end of the segment */
       
  2998   if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
       
  2999     if (ogg->segment.last_stop <= ogg->segment.start) {
       
  3000       ret = GST_FLOW_UNEXPECTED;
       
  3001       goto done;
       
  3002     }
       
  3003   }
       
  3004 done:
       
  3005   return ret;
       
  3006 }
       
  3007 
       
  3008 /* random access code
       
  3009  *
       
  3010  * - first find all the chains and streams by scanning the file.
       
  3011  * - then get and chain buffers, just like the streaming case.
       
  3012  * - when seeking, we can use the chain info to perform the seek.
       
  3013  */
       
  3014 static void
       
  3015 gst_ogg_demux_loop (GstOggPad * pad)
       
  3016 {
       
  3017   GstOggDemux *ogg;
       
  3018   GstFlowReturn ret;
       
  3019   GstEvent *event;
       
  3020 
       
  3021   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
       
  3022 
       
  3023   if (ogg->need_chains) {
       
  3024     gboolean res;
       
  3025 
       
  3026     /* this is the only place where we write chains and thus need to lock. */
       
  3027     GST_CHAIN_LOCK (ogg);
       
  3028     ret = gst_ogg_demux_find_chains (ogg);
       
  3029     GST_CHAIN_UNLOCK (ogg);
       
  3030     if (ret != GST_FLOW_OK)
       
  3031       goto chain_read_failed;
       
  3032 
       
  3033     ogg->need_chains = FALSE;
       
  3034 
       
  3035     GST_OBJECT_LOCK (ogg);
       
  3036     ogg->running = TRUE;
       
  3037     event = ogg->event;
       
  3038     ogg->event = NULL;
       
  3039     GST_OBJECT_UNLOCK (ogg);
       
  3040 
       
  3041     /* and seek to configured positions without FLUSH */
       
  3042     res = gst_ogg_demux_perform_seek (ogg, event);
       
  3043     if (event)
       
  3044       gst_event_unref (event);
       
  3045 
       
  3046     if (!res)
       
  3047       goto seek_failed;
       
  3048   }
       
  3049 
       
  3050   if (ogg->segment.rate >= 0.0)
       
  3051     ret = gst_ogg_demux_loop_forward (ogg);
       
  3052   else
       
  3053     ret = gst_ogg_demux_loop_reverse (ogg);
       
  3054 
       
  3055   if (ret != GST_FLOW_OK)
       
  3056     goto pause;
       
  3057 
       
  3058   return;
       
  3059 
       
  3060   /* ERRORS */
       
  3061 chain_read_failed:
       
  3062   {
       
  3063     /* error was posted */
       
  3064     goto pause;
       
  3065   }
       
  3066 seek_failed:
       
  3067   {
       
  3068     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
       
  3069         ("failed to start demuxing ogg"));
       
  3070     ret = GST_FLOW_ERROR;
       
  3071     goto pause;
       
  3072   }
       
  3073 pause:
       
  3074   {
       
  3075     const gchar *reason = gst_flow_get_name (ret);
       
  3076 
       
  3077     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
       
  3078     ogg->segment_running = FALSE;
       
  3079     gst_pad_pause_task (ogg->sinkpad);
       
  3080 
       
  3081     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
       
  3082       if (ret == GST_FLOW_UNEXPECTED) {
       
  3083         /* perform EOS logic */
       
  3084         if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
       
  3085           gint64 stop;
       
  3086 
       
  3087           /* for segment playback we need to post when (in stream time)
       
  3088            * we stopped, this is either stop (when set) or the duration. */
       
  3089           if ((stop = ogg->segment.stop) == -1)
       
  3090             stop = ogg->segment.duration;
       
  3091 
       
  3092           GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
       
  3093           gst_element_post_message (GST_ELEMENT (ogg),
       
  3094               gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
       
  3095                   stop));
       
  3096         } else {
       
  3097           /* normal playback, send EOS to all linked pads */
       
  3098           GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
       
  3099           gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
       
  3100         }
       
  3101       } else {
       
  3102         GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
       
  3103             (_("Internal data stream error.")),
       
  3104             ("stream stopped, reason %s", reason));
       
  3105         gst_ogg_demux_send_event (ogg, gst_event_new_eos ());
       
  3106       }
       
  3107     }
       
  3108     return;
       
  3109   }
       
  3110 }
       
  3111 
       
  3112 static void
       
  3113 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
       
  3114 {
       
  3115   gint i;
       
  3116 
       
  3117   gst_ogg_demux_deactivate_current_chain (ogg);
       
  3118 
       
  3119   GST_CHAIN_LOCK (ogg);
       
  3120   for (i = 0; i < ogg->chains->len; i++) {
       
  3121     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  3122 
       
  3123     gst_ogg_chain_free (chain);
       
  3124   }
       
  3125   ogg->chains = g_array_set_size (ogg->chains, 0);
       
  3126   GST_CHAIN_UNLOCK (ogg);
       
  3127 }
       
  3128 
       
  3129 /* this function is called when the pad is activated and should start
       
  3130  * processing data.
       
  3131  *
       
  3132  * We check if we can do random access to decide if we work push or
       
  3133  * pull based.
       
  3134  */
       
  3135 static gboolean
       
  3136 gst_ogg_demux_sink_activate (GstPad * sinkpad)
       
  3137 {
       
  3138   if (gst_pad_check_pull_range (sinkpad)) {
       
  3139     GST_DEBUG_OBJECT (sinkpad, "activating pull");
       
  3140     return gst_pad_activate_pull (sinkpad, TRUE);
       
  3141   } else {
       
  3142     GST_DEBUG_OBJECT (sinkpad, "activating push");
       
  3143     return gst_pad_activate_push (sinkpad, TRUE);
       
  3144   }
       
  3145 }
       
  3146 
       
  3147 /* this function gets called when we activate ourselves in push mode.
       
  3148  * We cannot seek (ourselves) in the stream */
       
  3149 static gboolean
       
  3150 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
       
  3151 {
       
  3152   GstOggDemux *ogg;
       
  3153 
       
  3154   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
       
  3155 
       
  3156   ogg->seekable = FALSE;
       
  3157 
       
  3158   return TRUE;
       
  3159 }
       
  3160 
       
  3161 /* this function gets called when we activate ourselves in pull mode.
       
  3162  * We can perform  random access to the resource and we start a task
       
  3163  * to start reading */
       
  3164 static gboolean
       
  3165 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
       
  3166 {
       
  3167   GstOggDemux *ogg;
       
  3168 
       
  3169   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
       
  3170 
       
  3171   if (active) {
       
  3172     ogg->need_chains = TRUE;
       
  3173     ogg->seekable = TRUE;
       
  3174 
       
  3175     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
       
  3176         sinkpad);
       
  3177   } else {
       
  3178     return gst_pad_stop_task (sinkpad);
       
  3179   }
       
  3180 }
       
  3181 
       
  3182 static GstStateChangeReturn
       
  3183 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
       
  3184 {
       
  3185   GstOggDemux *ogg;
       
  3186   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
       
  3187 
       
  3188   ogg = GST_OGG_DEMUX (element);
       
  3189 
       
  3190   switch (transition) {
       
  3191     case GST_STATE_CHANGE_NULL_TO_READY:
       
  3192       ogg->basetime = 0;
       
  3193       ogg->have_fishead = FALSE;
       
  3194       ogg_sync_init (&ogg->sync);
       
  3195       break;
       
  3196     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  3197       ogg_sync_reset (&ogg->sync);
       
  3198       ogg->current_granule = -1;
       
  3199       ogg->running = FALSE;
       
  3200       ogg->segment_running = FALSE;
       
  3201       gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
       
  3202       break;
       
  3203     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       
  3204       break;
       
  3205     default:
       
  3206       break;
       
  3207   }
       
  3208 
       
  3209   result = parent_class->change_state (element, transition);
       
  3210 
       
  3211   switch (transition) {
       
  3212     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
  3213       break;
       
  3214     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  3215       gst_ogg_demux_clear_chains (ogg);
       
  3216       GST_OBJECT_LOCK (ogg);
       
  3217       ogg->running = FALSE;
       
  3218       ogg->segment_running = FALSE;
       
  3219       ogg->have_fishead = FALSE;
       
  3220       GST_OBJECT_UNLOCK (ogg);
       
  3221       break;
       
  3222     case GST_STATE_CHANGE_READY_TO_NULL:
       
  3223       ogg_sync_clear (&ogg->sync);
       
  3224       break;
       
  3225     default:
       
  3226       break;
       
  3227   }
       
  3228   return result;
       
  3229 }
       
  3230 
       
  3231 static GstClockTime
       
  3232 gst_annodex_granule_to_time (gint64 granulepos, gint64 granulerate_n,
       
  3233     gint64 granulerate_d, guint8 granuleshift)
       
  3234 {
       
  3235   gint64 keyindex, keyoffset;
       
  3236   gint64 granulerate;
       
  3237   GstClockTime res;
       
  3238 
       
  3239   if (granulepos == 0 || granulerate_n == 0 || granulerate_d == 0)
       
  3240     return 0;
       
  3241 
       
  3242   if (granuleshift != 0) {
       
  3243     keyindex = granulepos >> granuleshift;
       
  3244     keyoffset = granulepos - (keyindex << granuleshift);
       
  3245     granulepos = keyindex + keyoffset;
       
  3246   }
       
  3247 
       
  3248   /* GST_SECOND / (granulerate_n / granulerate_d) */
       
  3249   granulerate = gst_util_uint64_scale (GST_SECOND,
       
  3250       granulerate_d, granulerate_n);
       
  3251   res = gst_util_uint64_scale (granulepos, granulerate, 1);
       
  3252   return res;
       
  3253 }
       
  3254 
       
  3255 gboolean
       
  3256 gst_ogg_demux_plugin_init (GstPlugin * plugin)
       
  3257 {
       
  3258   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
       
  3259   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
       
  3260       "ogg demuxer setup stage when parsing pipeline");
       
  3261 
       
  3262 #if ENABLE_NLS
       
  3263   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
       
  3264       LOCALEDIR);
       
  3265   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
       
  3266 #endif
       
  3267 
       
  3268   return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
       
  3269       GST_TYPE_OGG_DEMUX);
       
  3270 }
       
  3271 
       
  3272 /* prints all info about the element */
       
  3273 #undef GST_CAT_DEFAULT
       
  3274 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
       
  3275 
       
  3276 #ifdef GST_DISABLE_GST_DEBUG
       
  3277 
       
  3278 static void
       
  3279 gst_ogg_print (GstOggDemux * ogg)
       
  3280 {
       
  3281   /* NOP */
       
  3282 }
       
  3283 
       
  3284 #else /* !GST_DISABLE_GST_DEBUG */
       
  3285 
       
  3286 static void
       
  3287 gst_ogg_print (GstOggDemux * ogg)
       
  3288 {
       
  3289   guint j, i;
       
  3290 
       
  3291   GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
       
  3292   GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
       
  3293       GST_TIME_ARGS (ogg->total_time));
       
  3294 
       
  3295   for (i = 0; i < ogg->chains->len; i++) {
       
  3296     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
       
  3297 
       
  3298     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
       
  3299     GST_INFO_OBJECT (ogg, "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
       
  3300         chain->offset, chain->end_offset);
       
  3301     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
       
  3302         GST_TIME_ARGS (chain->begin_time));
       
  3303     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
       
  3304         GST_TIME_ARGS (chain->total_time));
       
  3305     GST_INFO_OBJECT (ogg, "  segment start: %" GST_TIME_FORMAT,
       
  3306         GST_TIME_ARGS (chain->segment_start));
       
  3307     GST_INFO_OBJECT (ogg, "  segment stop:  %" GST_TIME_FORMAT,
       
  3308         GST_TIME_ARGS (chain->segment_stop));
       
  3309 
       
  3310     for (j = 0; j < chain->streams->len; j++) {
       
  3311       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
       
  3312 
       
  3313       GST_INFO_OBJECT (ogg, "  stream %08x:", stream->serialno);
       
  3314       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
       
  3315           GST_TIME_ARGS (stream->start_time));
       
  3316       GST_INFO_OBJECT (ogg, "   first granulepos: %" G_GINT64_FORMAT,
       
  3317           stream->first_granule);
       
  3318       GST_INFO_OBJECT (ogg, "   first time:       %" GST_TIME_FORMAT,
       
  3319           GST_TIME_ARGS (stream->first_time));
       
  3320     }
       
  3321   }
       
  3322 }
       
  3323 #endif /* GST_DISABLE_GST_DEBUG */