gst_plugins_base/ext/ogg/gstogmparse.c
changeset 0 0e761a78d257
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer OGM parsing
       
     2  * Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
       
     3  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public
       
    16  * License along with this library; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 #ifdef HAVE_CONFIG_H
       
    22 #include "config.h"
       
    23 #endif
       
    24 
       
    25 #include <string.h>
       
    26 
       
    27 #include <gst/gst.h>
       
    28 #include <gst/tag/tag.h>
       
    29 #include <gst/riff/riff-media.h>
       
    30 #include <gst/riff/riff-read.h>
       
    31 
       
    32 GST_DEBUG_CATEGORY_STATIC (gst_ogm_parse_debug);
       
    33 #define GST_CAT_DEFAULT gst_ogm_parse_debug
       
    34 
       
    35 #define GST_TYPE_OGM_VIDEO_PARSE (gst_ogm_video_parse_get_type())
       
    36 #define GST_IS_OGM_VIDEO_PARSE(obj) \
       
    37   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_VIDEO_PARSE))
       
    38 
       
    39 #define GST_TYPE_OGM_AUDIO_PARSE (gst_ogm_audio_parse_get_type())
       
    40 #define GST_IS_OGM_AUDIO_PARSE(obj) \
       
    41   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_AUDIO_PARSE))
       
    42 
       
    43 #define GST_TYPE_OGM_TEXT_PARSE (gst_ogm_text_parse_get_type())
       
    44 #define GST_IS_OGM_TEXT_PARSE(obj) \
       
    45   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_TEXT_PARSE))
       
    46 
       
    47 #define GST_TYPE_OGM_PARSE (gst_ogm_parse_get_type())
       
    48 #define GST_OGM_PARSE(obj) \
       
    49   (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OGM_PARSE, GstOgmParse))
       
    50 #define GST_OGM_PARSE_CLASS(klass) \
       
    51   (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OGM_PARSE, GstOgmParse))
       
    52 #define GST_IS_OGM_PARSE(obj) \
       
    53   (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OGM_PARSE))
       
    54 #define GST_IS_OGM_PARSE_CLASS(klass) \
       
    55   (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OGM_PARSE))
       
    56 #define GST_OGM_PARSE_GET_CLASS(obj) \
       
    57   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OGM_PARSE, GstOgmParseClass))
       
    58 
       
    59 static const GstElementDetails gst_ogm_audio_parse_details =
       
    60 GST_ELEMENT_DETAILS ("OGM audio stream parser",
       
    61     "Codec/Decoder/Audio",
       
    62     "parse an OGM audio header and stream",
       
    63     "Ronald Bultje <rbultje@ronald.bitfreak.net>");
       
    64 
       
    65 static const GstElementDetails gst_ogm_video_parse_details =
       
    66 GST_ELEMENT_DETAILS ("OGM video stream parser",
       
    67     "Codec/Decoder/Video",
       
    68     "parse an OGM video header and stream",
       
    69     "Ronald Bultje <rbultje@ronald.bitfreak.net>");
       
    70 
       
    71 static const GstElementDetails gst_ogm_text_parse_details =
       
    72 GST_ELEMENT_DETAILS ("OGM text stream parser",
       
    73     "Codec/Decoder/Subtitle",
       
    74     "parse an OGM text header and stream",
       
    75     "Ronald Bultje <rbultje@ronald.bitfreak.net>");
       
    76 
       
    77 typedef struct _stream_header_video
       
    78 {
       
    79   gint32 width;
       
    80   gint32 height;
       
    81 } stream_header_video;
       
    82 
       
    83 typedef struct _stream_header_audio
       
    84 {
       
    85   gint16 channels;
       
    86   gint16 blockalign;
       
    87   gint32 avgbytespersec;
       
    88 } stream_header_audio;
       
    89 
       
    90 /* sizeof(stream_header) might differ due to structure packing and
       
    91  * alignment differences on some architectures, so not using that */
       
    92 #define OGM_STREAM_HEADER_SIZE (8+4+4+8+8+4+4+4+8)
       
    93 
       
    94 typedef struct _stream_header
       
    95 {
       
    96   gchar streamtype[8];
       
    97   gchar subtype[4 + 1];
       
    98 
       
    99   /* size of the structure */
       
   100   gint32 size;
       
   101 
       
   102   /* in reference time */
       
   103   gint64 time_unit;
       
   104 
       
   105   gint64 samples_per_unit;
       
   106 
       
   107   /* in media time */
       
   108   gint32 default_len;
       
   109 
       
   110   gint32 buffersize;
       
   111   gint32 bits_per_sample;
       
   112 
       
   113   union
       
   114   {
       
   115     stream_header_video video;
       
   116     stream_header_audio audio;
       
   117     /* text has no additional data */
       
   118   } s;
       
   119 } stream_header;
       
   120 
       
   121 typedef struct _GstOgmParse
       
   122 {
       
   123   GstElement element;
       
   124 
       
   125   /* pads */
       
   126   GstPad *srcpad, *sinkpad;
       
   127   GstPadTemplate *srcpadtempl;
       
   128 
       
   129   /* we need to cache events that we receive before creating the source pad */
       
   130   GList *cached_events;
       
   131 
       
   132   /* audio or video */
       
   133   stream_header hdr;
       
   134 
       
   135   /* expected next granulepos (used for timestamp guessing) */
       
   136   guint64 next_granulepos;
       
   137 } GstOgmParse;
       
   138 
       
   139 typedef struct _GstOgmParseClass
       
   140 {
       
   141   GstElementClass parent_class;
       
   142 } GstOgmParseClass;
       
   143 
       
   144 static GstStaticPadTemplate sink_factory_video =
       
   145 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
       
   146     GST_STATIC_CAPS ("application/x-ogm-video"));
       
   147 static GstStaticPadTemplate sink_factory_audio =
       
   148 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
       
   149     GST_STATIC_CAPS ("application/x-ogm-audio"));
       
   150 static GstStaticPadTemplate sink_factory_text =
       
   151 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
       
   152     GST_STATIC_CAPS ("application/x-ogm-text"));
       
   153 static GstPadTemplate *video_src_templ, *audio_src_templ, *text_src_templ;
       
   154 
       
   155 static GType gst_ogm_audio_parse_get_type (void);
       
   156 static GType gst_ogm_video_parse_get_type (void);
       
   157 static GType gst_ogm_text_parse_get_type (void);
       
   158 static GType gst_ogm_parse_get_type (void);
       
   159 
       
   160 static void gst_ogm_audio_parse_base_init (GstOgmParseClass * klass);
       
   161 static void gst_ogm_video_parse_base_init (GstOgmParseClass * klass);
       
   162 static void gst_ogm_text_parse_base_init (GstOgmParseClass * klass);
       
   163 static void gst_ogm_parse_class_init (GstOgmParseClass * klass);
       
   164 static void gst_ogm_parse_init (GstOgmParse * ogm);
       
   165 static void gst_ogm_video_parse_init (GstOgmParse * ogm);
       
   166 static void gst_ogm_audio_parse_init (GstOgmParse * ogm);
       
   167 static void gst_ogm_text_parse_init (GstOgmParse * ogm);
       
   168 
       
   169 static const GstQueryType *gst_ogm_parse_get_sink_querytypes (GstPad * pad);
       
   170 static gboolean gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event);
       
   171 static gboolean gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query);
       
   172 static gboolean gst_ogm_parse_sink_convert (GstPad * pad, GstFormat src_format,
       
   173     gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
       
   174 
       
   175 static GstFlowReturn gst_ogm_parse_chain (GstPad * pad, GstBuffer * buffer);
       
   176 
       
   177 static GstStateChangeReturn gst_ogm_parse_change_state (GstElement * element,
       
   178     GstStateChange transition);
       
   179 
       
   180 static GstElementClass *parent_class = NULL;
       
   181 
       
   182 static GType
       
   183 gst_ogm_parse_get_type (void)
       
   184 {
       
   185   static GType ogm_parse_type = 0;
       
   186 
       
   187   if (!ogm_parse_type) {
       
   188     static const GTypeInfo ogm_parse_info = {
       
   189       sizeof (GstOgmParseClass),
       
   190       NULL,
       
   191       NULL,
       
   192       (GClassInitFunc) gst_ogm_parse_class_init,
       
   193       NULL,
       
   194       NULL,
       
   195       sizeof (GstOgmParse),
       
   196       0,
       
   197       (GInstanceInitFunc) gst_ogm_parse_init,
       
   198     };
       
   199 
       
   200     ogm_parse_type =
       
   201         g_type_register_static (GST_TYPE_ELEMENT,
       
   202         "GstOgmParse", &ogm_parse_info, 0);
       
   203   }
       
   204 
       
   205   return ogm_parse_type;
       
   206 }
       
   207 
       
   208 static GType
       
   209 gst_ogm_audio_parse_get_type (void)
       
   210 {
       
   211   static GType ogm_audio_parse_type = 0;
       
   212 
       
   213   if (!ogm_audio_parse_type) {
       
   214     static const GTypeInfo ogm_audio_parse_info = {
       
   215       sizeof (GstOgmParseClass),
       
   216       (GBaseInitFunc) gst_ogm_audio_parse_base_init,
       
   217       NULL,
       
   218       NULL,
       
   219       NULL,
       
   220       NULL,
       
   221       sizeof (GstOgmParse),
       
   222       0,
       
   223       (GInstanceInitFunc) gst_ogm_audio_parse_init,
       
   224     };
       
   225 
       
   226     ogm_audio_parse_type =
       
   227         g_type_register_static (GST_TYPE_OGM_PARSE,
       
   228         "GstOgmAudioParse", &ogm_audio_parse_info, 0);
       
   229   }
       
   230 
       
   231   return ogm_audio_parse_type;
       
   232 }
       
   233 
       
   234 static GType
       
   235 gst_ogm_video_parse_get_type (void)
       
   236 {
       
   237   static GType ogm_video_parse_type = 0;
       
   238 
       
   239   if (!ogm_video_parse_type) {
       
   240     static const GTypeInfo ogm_video_parse_info = {
       
   241       sizeof (GstOgmParseClass),
       
   242       (GBaseInitFunc) gst_ogm_video_parse_base_init,
       
   243       NULL,
       
   244       NULL,
       
   245       NULL,
       
   246       NULL,
       
   247       sizeof (GstOgmParse),
       
   248       0,
       
   249       (GInstanceInitFunc) gst_ogm_video_parse_init,
       
   250     };
       
   251 
       
   252     ogm_video_parse_type =
       
   253         g_type_register_static (GST_TYPE_OGM_PARSE,
       
   254         "GstOgmVideoParse", &ogm_video_parse_info, 0);
       
   255   }
       
   256 
       
   257   return ogm_video_parse_type;
       
   258 }
       
   259 
       
   260 static GType
       
   261 gst_ogm_text_parse_get_type (void)
       
   262 {
       
   263   static GType ogm_text_parse_type = 0;
       
   264 
       
   265   if (!ogm_text_parse_type) {
       
   266     static const GTypeInfo ogm_text_parse_info = {
       
   267       sizeof (GstOgmParseClass),
       
   268       (GBaseInitFunc) gst_ogm_text_parse_base_init,
       
   269       NULL,
       
   270       NULL,
       
   271       NULL,
       
   272       NULL,
       
   273       sizeof (GstOgmParse),
       
   274       0,
       
   275       (GInstanceInitFunc) gst_ogm_text_parse_init,
       
   276     };
       
   277 
       
   278     ogm_text_parse_type =
       
   279         g_type_register_static (GST_TYPE_OGM_PARSE,
       
   280         "GstOgmTextParse", &ogm_text_parse_info, 0);
       
   281   }
       
   282 
       
   283   return ogm_text_parse_type;
       
   284 }
       
   285 
       
   286 static void
       
   287 gst_ogm_audio_parse_base_init (GstOgmParseClass * klass)
       
   288 {
       
   289   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
       
   290   GstCaps *caps = gst_riff_create_audio_template_caps ();
       
   291 
       
   292   gst_element_class_set_details (element_class, &gst_ogm_audio_parse_details);
       
   293 
       
   294   gst_element_class_add_pad_template (element_class,
       
   295       gst_static_pad_template_get (&sink_factory_audio));
       
   296   audio_src_templ = gst_pad_template_new ("src",
       
   297       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
       
   298   gst_element_class_add_pad_template (element_class, audio_src_templ);
       
   299 }
       
   300 
       
   301 static void
       
   302 gst_ogm_video_parse_base_init (GstOgmParseClass * klass)
       
   303 {
       
   304   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
       
   305   GstCaps *caps = gst_riff_create_video_template_caps ();
       
   306 
       
   307   gst_element_class_set_details (element_class, &gst_ogm_video_parse_details);
       
   308 
       
   309   gst_element_class_add_pad_template (element_class,
       
   310       gst_static_pad_template_get (&sink_factory_video));
       
   311   video_src_templ = gst_pad_template_new ("src",
       
   312       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
       
   313   gst_element_class_add_pad_template (element_class, video_src_templ);
       
   314 }
       
   315 
       
   316 static void
       
   317 gst_ogm_text_parse_base_init (GstOgmParseClass * klass)
       
   318 {
       
   319   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
       
   320   GstCaps *caps = gst_caps_new_simple ("text/plain", NULL, NULL);
       
   321 
       
   322   gst_element_class_set_details (element_class, &gst_ogm_text_parse_details);
       
   323 
       
   324   gst_element_class_add_pad_template (element_class,
       
   325       gst_static_pad_template_get (&sink_factory_text));
       
   326   text_src_templ = gst_pad_template_new ("src",
       
   327       GST_PAD_SRC, GST_PAD_SOMETIMES, caps);
       
   328   gst_element_class_add_pad_template (element_class, text_src_templ);
       
   329 }
       
   330 
       
   331 static void
       
   332 gst_ogm_parse_class_init (GstOgmParseClass * klass)
       
   333 {
       
   334   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
       
   335 
       
   336   parent_class = g_type_class_peek_parent (klass);
       
   337 
       
   338   gstelement_class->change_state =
       
   339       GST_DEBUG_FUNCPTR (gst_ogm_parse_change_state);
       
   340 }
       
   341 
       
   342 static void
       
   343 gst_ogm_parse_init (GstOgmParse * ogm)
       
   344 {
       
   345   memset (&ogm->hdr, 0, sizeof (ogm->hdr));
       
   346   ogm->next_granulepos = 0;
       
   347   ogm->srcpad = NULL;
       
   348   ogm->cached_events = NULL;
       
   349 }
       
   350 
       
   351 static void
       
   352 gst_ogm_audio_parse_init (GstOgmParse * ogm)
       
   353 {
       
   354   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_audio, "sink");
       
   355   gst_pad_set_query_function (ogm->sinkpad,
       
   356       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
       
   357   gst_pad_set_chain_function (ogm->sinkpad,
       
   358       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
       
   359   gst_pad_set_event_function (ogm->sinkpad,
       
   360       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
       
   361   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
       
   362 
       
   363   ogm->srcpad = NULL;
       
   364   ogm->srcpadtempl = audio_src_templ;
       
   365 }
       
   366 
       
   367 static void
       
   368 gst_ogm_video_parse_init (GstOgmParse * ogm)
       
   369 {
       
   370   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_video, "sink");
       
   371   gst_pad_set_query_function (ogm->sinkpad,
       
   372       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
       
   373   gst_pad_set_chain_function (ogm->sinkpad,
       
   374       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
       
   375   gst_pad_set_event_function (ogm->sinkpad,
       
   376       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
       
   377   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
       
   378 
       
   379   ogm->srcpad = NULL;
       
   380   ogm->srcpadtempl = video_src_templ;
       
   381 }
       
   382 
       
   383 static void
       
   384 gst_ogm_text_parse_init (GstOgmParse * ogm)
       
   385 {
       
   386   ogm->sinkpad = gst_pad_new_from_static_template (&sink_factory_text, "sink");
       
   387   gst_pad_set_query_type_function (ogm->sinkpad,
       
   388       gst_ogm_parse_get_sink_querytypes);
       
   389   gst_pad_set_query_function (ogm->sinkpad,
       
   390       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_query));
       
   391   gst_pad_set_chain_function (ogm->sinkpad,
       
   392       GST_DEBUG_FUNCPTR (gst_ogm_parse_chain));
       
   393   gst_pad_set_event_function (ogm->sinkpad,
       
   394       GST_DEBUG_FUNCPTR (gst_ogm_parse_sink_event));
       
   395   gst_element_add_pad (GST_ELEMENT (ogm), ogm->sinkpad);
       
   396 
       
   397   ogm->srcpad = NULL;
       
   398   ogm->srcpadtempl = text_src_templ;
       
   399 }
       
   400 
       
   401 static const GstQueryType *
       
   402 gst_ogm_parse_get_sink_querytypes (GstPad * pad)
       
   403 {
       
   404   static const GstQueryType types[] = {
       
   405     GST_QUERY_POSITION,
       
   406     0
       
   407   };
       
   408 
       
   409   return types;
       
   410 }
       
   411 
       
   412 static gboolean
       
   413 gst_ogm_parse_sink_convert (GstPad * pad,
       
   414     GstFormat src_format, gint64 src_value,
       
   415     GstFormat * dest_format, gint64 * dest_value)
       
   416 {
       
   417   gboolean res = FALSE;
       
   418   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
       
   419 
       
   420   switch (src_format) {
       
   421     case GST_FORMAT_DEFAULT:
       
   422       switch (*dest_format) {
       
   423         case GST_FORMAT_TIME:
       
   424           switch (ogm->hdr.streamtype[0]) {
       
   425             case 'a':
       
   426               *dest_value = GST_SECOND * src_value / ogm->hdr.samples_per_unit;
       
   427               res = TRUE;
       
   428               break;
       
   429             case 'v':
       
   430             case 't':
       
   431               *dest_value = (GST_SECOND / 10000000) *
       
   432                   ogm->hdr.time_unit * src_value;
       
   433               res = TRUE;
       
   434               break;
       
   435             default:
       
   436               break;
       
   437           }
       
   438           break;
       
   439         default:
       
   440           break;
       
   441       }
       
   442       break;
       
   443     case GST_FORMAT_TIME:
       
   444       switch (*dest_format) {
       
   445         case GST_FORMAT_DEFAULT:
       
   446           switch (ogm->hdr.streamtype[0]) {
       
   447             case 'a':
       
   448               *dest_value = ogm->hdr.samples_per_unit * src_value / GST_SECOND;
       
   449               res = TRUE;
       
   450               break;
       
   451             case 'v':
       
   452             case 't':
       
   453               *dest_value = src_value /
       
   454                   ((GST_SECOND / 10000000) * ogm->hdr.time_unit);
       
   455               res = TRUE;
       
   456               break;
       
   457             default:
       
   458               break;
       
   459           }
       
   460           break;
       
   461         default:
       
   462           break;
       
   463       }
       
   464       break;
       
   465     default:
       
   466       break;
       
   467   }
       
   468 
       
   469   gst_object_unref (ogm);
       
   470   return res;
       
   471 }
       
   472 
       
   473 static gboolean
       
   474 gst_ogm_parse_sink_query (GstPad * pad, GstQuery * query)
       
   475 {
       
   476   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
       
   477   GstFormat format;
       
   478   gboolean res = FALSE;
       
   479 
       
   480   switch (GST_QUERY_TYPE (query)) {
       
   481     case GST_QUERY_POSITION:
       
   482     {
       
   483       gint64 val;
       
   484 
       
   485       gst_query_parse_position (query, &format, NULL);
       
   486 
       
   487       if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME)
       
   488         break;
       
   489 
       
   490       if ((res = gst_ogm_parse_sink_convert (pad,
       
   491                   GST_FORMAT_DEFAULT, ogm->next_granulepos, &format, &val))) {
       
   492         /* don't know the total length here.. */
       
   493         gst_query_set_position (query, format, val);
       
   494       }
       
   495       break;
       
   496     }
       
   497     case GST_QUERY_CONVERT:
       
   498     {
       
   499       GstFormat src_fmt, dest_fmt;
       
   500       gint64 src_val, dest_val;
       
   501 
       
   502       /* peel off input */
       
   503       gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
       
   504       if ((res = gst_ogm_parse_sink_convert (pad, src_fmt, src_val,
       
   505                   &dest_fmt, &dest_val))) {
       
   506         gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
       
   507       }
       
   508       break;
       
   509     }
       
   510     default:
       
   511       res = gst_pad_query_default (pad, query);
       
   512       break;
       
   513   }
       
   514 
       
   515   gst_object_unref (ogm);
       
   516   return res;
       
   517 }
       
   518 
       
   519 static GstFlowReturn
       
   520 gst_ogm_parse_stream_header (GstOgmParse * ogm, const guint8 * data, guint size)
       
   521 {
       
   522   GstCaps *caps = NULL;
       
   523 
       
   524   /* stream header */
       
   525   if (size < OGM_STREAM_HEADER_SIZE)
       
   526     goto buffer_too_small;
       
   527 
       
   528   if (!memcmp (data, "video\000\000\000", 8)) {
       
   529     ogm->hdr.s.video.width = GST_READ_UINT32_LE (&data[44]);
       
   530     ogm->hdr.s.video.height = GST_READ_UINT32_LE (&data[48]);
       
   531   } else if (!memcmp (data, "audio\000\000\000", 8)) {
       
   532     ogm->hdr.s.audio.channels = GST_READ_UINT32_LE (&data[44]);
       
   533     ogm->hdr.s.audio.blockalign = GST_READ_UINT32_LE (&data[46]);
       
   534     ogm->hdr.s.audio.avgbytespersec = GST_READ_UINT32_LE (&data[48]);
       
   535   } else if (!memcmp (data, "text\000\000\000\000", 8)) {
       
   536     /* nothing here */
       
   537   } else {
       
   538     goto cannot_decode;
       
   539   }
       
   540   memcpy (ogm->hdr.streamtype, &data[0], 8);
       
   541   memcpy (ogm->hdr.subtype, &data[8], 4);
       
   542   ogm->hdr.subtype[4] = '\0';
       
   543   ogm->hdr.size = GST_READ_UINT32_LE (&data[12]);
       
   544   ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[16]);
       
   545   ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[24]);
       
   546   ogm->hdr.default_len = GST_READ_UINT32_LE (&data[32]);
       
   547   ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[36]);
       
   548   ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[40]);
       
   549 
       
   550   switch (ogm->hdr.streamtype[0]) {
       
   551     case 'a':{
       
   552       guint codec_id = 0;
       
   553 
       
   554       if (sscanf (ogm->hdr.subtype, "%04x", &codec_id) != 1) {
       
   555         GST_WARNING_OBJECT (ogm, "cannot parse subtype %s", ogm->hdr.subtype);
       
   556       }
       
   557 
       
   558       caps =
       
   559           gst_riff_create_audio_caps (codec_id, NULL, NULL, NULL, NULL, NULL);
       
   560 
       
   561       if (caps == NULL) {
       
   562         GST_WARNING_OBJECT (ogm, "no audio caps for codec %u found", codec_id);
       
   563         caps = gst_caps_new_simple ("audio/x-ogm-unknown", "codec_id",
       
   564             G_TYPE_INT, (gint) codec_id, NULL);
       
   565       }
       
   566 
       
   567       gst_caps_set_simple (caps,
       
   568           "channels", G_TYPE_INT, ogm->hdr.s.audio.channels,
       
   569           "rate", G_TYPE_INT, ogm->hdr.samples_per_unit, NULL);
       
   570 
       
   571       GST_LOG_OBJECT (ogm, "Type: %s, subtype: 0x%04x, channels: %d, "
       
   572           "samplerate: %d, blockalign: %d, bps: %d, caps = %" GST_PTR_FORMAT,
       
   573           ogm->hdr.streamtype, codec_id, ogm->hdr.s.audio.channels,
       
   574           (gint) ogm->hdr.samples_per_unit, ogm->hdr.s.audio.blockalign,
       
   575           ogm->hdr.s.audio.avgbytespersec, caps);
       
   576       break;
       
   577     }
       
   578     case 'v':{
       
   579       guint32 fourcc;
       
   580 
       
   581       fourcc = GST_MAKE_FOURCC (ogm->hdr.subtype[0],
       
   582           ogm->hdr.subtype[1], ogm->hdr.subtype[2], ogm->hdr.subtype[3]);
       
   583 
       
   584       caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
       
   585 
       
   586       if (caps == NULL) {
       
   587         GST_WARNING_OBJECT (ogm, "could not find video caps for fourcc %"
       
   588             GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
       
   589         caps = gst_caps_new_simple ("video/x-ogm-unknown", "fourcc",
       
   590             GST_TYPE_FOURCC, fourcc, NULL);
       
   591         break;
       
   592       }
       
   593 
       
   594       GST_LOG_OBJECT (ogm, "Type: %s, subtype: %" GST_FOURCC_FORMAT
       
   595           ", size: %dx%d, timeunit: %" G_GINT64_FORMAT
       
   596           " (fps: %lf), s/u: %" G_GINT64_FORMAT ", "
       
   597           "def.len: %d, bufsize: %d, bps: %d, caps = %" GST_PTR_FORMAT,
       
   598           ogm->hdr.streamtype, GST_FOURCC_ARGS (fourcc),
       
   599           ogm->hdr.s.video.width, ogm->hdr.s.video.height,
       
   600           ogm->hdr.time_unit, 10000000. / ogm->hdr.time_unit,
       
   601           ogm->hdr.samples_per_unit, ogm->hdr.default_len,
       
   602           ogm->hdr.buffersize, ogm->hdr.bits_per_sample, caps);
       
   603 
       
   604       gst_caps_set_simple (caps,
       
   605           "width", G_TYPE_INT, ogm->hdr.s.video.width,
       
   606           "height", G_TYPE_INT, ogm->hdr.s.video.height,
       
   607           "framerate", GST_TYPE_FRACTION, 10000000, ogm->hdr.time_unit, NULL);
       
   608       break;
       
   609     }
       
   610     case 't':{
       
   611       GST_LOG_OBJECT (ogm, "Type: %s, s/u: %" G_GINT64_FORMAT
       
   612           ", timeunit=%" G_GINT64_FORMAT,
       
   613           ogm->hdr.streamtype, ogm->hdr.samples_per_unit, ogm->hdr.time_unit);
       
   614       caps = gst_caps_new_simple ("text/plain", NULL);
       
   615       break;
       
   616     }
       
   617     default:
       
   618       g_assert_not_reached ();
       
   619   }
       
   620 
       
   621   if (caps == NULL)
       
   622     goto cannot_decode;
       
   623 
       
   624   if (ogm->srcpad) {
       
   625     GstCaps *current_caps = GST_PAD_CAPS (ogm->srcpad);
       
   626 
       
   627     if (current_caps && caps && !gst_caps_is_equal (current_caps, caps)) {
       
   628       GST_WARNING_OBJECT (ogm, "Already an existing pad %s:%s",
       
   629           GST_DEBUG_PAD_NAME (ogm->srcpad));
       
   630       gst_pad_set_active (ogm->srcpad, FALSE);
       
   631       gst_element_remove_pad (GST_ELEMENT (ogm), ogm->srcpad);
       
   632       ogm->srcpad = NULL;
       
   633     } else {
       
   634       GST_DEBUG_OBJECT (ogm, "Existing pad has the same caps, do nothing");
       
   635     }
       
   636   }
       
   637 
       
   638   if (ogm->srcpad == NULL) {
       
   639     GList *l, *cached_events;
       
   640 
       
   641     ogm->srcpad = gst_pad_new_from_template (ogm->srcpadtempl, "src");
       
   642     gst_pad_use_fixed_caps (ogm->srcpad);
       
   643     gst_pad_set_caps (ogm->srcpad, caps);
       
   644     gst_pad_set_active (ogm->srcpad, TRUE);
       
   645     gst_element_add_pad (GST_ELEMENT (ogm), ogm->srcpad);
       
   646     GST_INFO_OBJECT (ogm, "Added pad %s:%s with caps %" GST_PTR_FORMAT,
       
   647         GST_DEBUG_PAD_NAME (ogm->srcpad), caps);
       
   648 
       
   649     GST_OBJECT_LOCK (ogm);
       
   650     cached_events = ogm->cached_events;
       
   651     ogm->cached_events = NULL;
       
   652     GST_OBJECT_UNLOCK (ogm);
       
   653 
       
   654     for (l = cached_events; l; l = l->next) {
       
   655       GstEvent *event = GST_EVENT_CAST (l->data);
       
   656 
       
   657       GST_DEBUG_OBJECT (ogm, "Pushing cached event %" GST_PTR_FORMAT, event);
       
   658       gst_pad_push_event (ogm->srcpad, event);
       
   659     }
       
   660     g_list_free (cached_events);
       
   661   }
       
   662 
       
   663   gst_caps_unref (caps);
       
   664 
       
   665   return GST_FLOW_OK;
       
   666 
       
   667 /* ERRORS */
       
   668 buffer_too_small:
       
   669   {
       
   670     GST_ELEMENT_ERROR (ogm, STREAM, WRONG_TYPE, ("Buffer too small"), (NULL));
       
   671     return GST_FLOW_ERROR;
       
   672   }
       
   673 cannot_decode:
       
   674   {
       
   675     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("unknown ogm format"));
       
   676     return GST_FLOW_ERROR;
       
   677   }
       
   678 }
       
   679 
       
   680 static GstFlowReturn
       
   681 gst_ogm_parse_comment_packet (GstOgmParse * ogm, GstBuffer * buf)
       
   682 {
       
   683   GstFlowReturn ret;
       
   684 
       
   685   if (ogm->srcpad == NULL) {
       
   686     GST_DEBUG ("no source pad");
       
   687     return GST_FLOW_WRONG_STATE;
       
   688   }
       
   689 
       
   690   /* if this is not a subtitle stream, push the vorbiscomment packet
       
   691    * on downstream, the respective decoder will handle it; if it is
       
   692    * a subtitle stream, we will have to handle the comment ourself */
       
   693   if (ogm->hdr.streamtype[0] == 't') {
       
   694     GstTagList *tags;
       
   695 
       
   696     tags = gst_tag_list_from_vorbiscomment_buffer (buf,
       
   697         (guint8 *) "\003vorbis", 7, NULL);
       
   698 
       
   699     if (tags) {
       
   700       GST_DEBUG_OBJECT (ogm, "tags = %" GST_PTR_FORMAT, tags);
       
   701       gst_element_found_tags_for_pad (GST_ELEMENT (ogm), ogm->srcpad, tags);
       
   702     } else {
       
   703       GST_DEBUG_OBJECT (ogm, "failed to extract tags from vorbis comment");
       
   704     }
       
   705     /* do not push packet downstream, just let parent unref it */
       
   706     ret = GST_FLOW_OK;
       
   707   } else {
       
   708     buf = gst_buffer_copy (buf);
       
   709     gst_buffer_set_caps (buf, GST_PAD_CAPS (ogm->srcpad));
       
   710     ret = gst_pad_push (ogm->srcpad, buf);
       
   711   }
       
   712 
       
   713   return ret;
       
   714 }
       
   715 
       
   716 static void
       
   717 gst_ogm_text_parse_strip_trailing_zeroes (GstOgmParse * ogm, GstBuffer * buf)
       
   718 {
       
   719   const guint8 *data;
       
   720   guint size;
       
   721 
       
   722   g_assert (gst_buffer_is_metadata_writable (buf));
       
   723 
       
   724   /* zeroes are not valid UTF-8 characters, so strip them from output */
       
   725   data = GST_BUFFER_DATA (buf);
       
   726   size = GST_BUFFER_SIZE (buf);
       
   727   while (size > 0 && data[size - 1] == '\0') {
       
   728     --size;
       
   729   }
       
   730 
       
   731   GST_BUFFER_SIZE (buf) = size;
       
   732 }
       
   733 
       
   734 static GstFlowReturn
       
   735 gst_ogm_parse_data_packet (GstOgmParse * ogm, GstBuffer * buf)
       
   736 {
       
   737   GstFlowReturn ret;
       
   738   const guint8 *data;
       
   739   GstBuffer *sbuf;
       
   740   gboolean keyframe;
       
   741   guint size, len, n, xsize = 0;
       
   742 
       
   743   data = GST_BUFFER_DATA (buf);
       
   744   size = GST_BUFFER_SIZE (buf);
       
   745 
       
   746   if ((data[0] & 0x01) != 0)
       
   747     goto invalid_startcode;
       
   748 
       
   749   /* data - push on */
       
   750   len = ((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1);
       
   751   keyframe = (((data[0] & 0x08) >> 3) != 0);
       
   752 
       
   753   if ((1 + len) > size)
       
   754     goto buffer_too_small;
       
   755 
       
   756   for (n = len; n > 0; n--) {
       
   757     xsize = (xsize << 8) | data[n];
       
   758   }
       
   759 
       
   760   GST_LOG_OBJECT (ogm, "[0x%02x] samples: %d, hdrbytes: %d, datasize: %d",
       
   761       data[0], xsize, len, size - len - 1);
       
   762 
       
   763   sbuf = gst_buffer_create_sub (buf, len + 1, size - len - 1);
       
   764 
       
   765   if (GST_BUFFER_OFFSET_END_IS_VALID (buf))
       
   766     ogm->next_granulepos = GST_BUFFER_OFFSET_END (buf);
       
   767 
       
   768   switch (ogm->hdr.streamtype[0]) {
       
   769     case 't':
       
   770     case 'v':{
       
   771       GstClockTime ts, next_ts;
       
   772       guint samples;
       
   773 
       
   774       samples = (ogm->hdr.streamtype[0] == 'v') ? 1 : xsize;
       
   775 
       
   776       if (!keyframe) {
       
   777         GST_BUFFER_FLAG_SET (sbuf, GST_BUFFER_FLAG_DELTA_UNIT);
       
   778       }
       
   779 
       
   780       /* shouldn't this be granulepos - samples? (tpm) */
       
   781       ts = gst_util_uint64_scale (ogm->next_granulepos,
       
   782           ogm->hdr.time_unit * GST_SECOND, 10000000);
       
   783       next_ts = gst_util_uint64_scale (ogm->next_granulepos + samples,
       
   784           ogm->hdr.time_unit * GST_SECOND, 10000000);
       
   785 
       
   786       GST_BUFFER_TIMESTAMP (sbuf) = ts;
       
   787       GST_BUFFER_DURATION (sbuf) = next_ts - ts;
       
   788 
       
   789       ogm->next_granulepos += samples;
       
   790 
       
   791       if (ogm->hdr.streamtype[0] == 't') {
       
   792         gst_ogm_text_parse_strip_trailing_zeroes (ogm, sbuf);
       
   793       }
       
   794       break;
       
   795     }
       
   796     case 'a':{
       
   797       GstClockTime ts, next_ts;
       
   798 
       
   799       /* shouldn't this be granulepos - samples? (tpm) */
       
   800       ts = gst_util_uint64_scale_int (ogm->next_granulepos,
       
   801           GST_SECOND, ogm->hdr.samples_per_unit);
       
   802       next_ts = gst_util_uint64_scale_int (ogm->next_granulepos + xsize,
       
   803           GST_SECOND, ogm->hdr.samples_per_unit);
       
   804 
       
   805       GST_BUFFER_TIMESTAMP (sbuf) = ts;
       
   806       GST_BUFFER_DURATION (sbuf) = next_ts - ts;
       
   807 
       
   808       ogm->next_granulepos += xsize;
       
   809       break;
       
   810     }
       
   811     default:
       
   812       g_assert_not_reached ();
       
   813       break;
       
   814   }
       
   815 
       
   816   if (ogm->srcpad) {
       
   817     gst_buffer_set_caps (sbuf, GST_PAD_CAPS (ogm->srcpad));
       
   818     GST_LOG_OBJECT (ogm, "Pushing buffer with ts=%" GST_TIME_FORMAT,
       
   819         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (sbuf)));
       
   820     ret = gst_pad_push (ogm->srcpad, sbuf);
       
   821     if (ret != GST_FLOW_OK) {
       
   822       GST_DEBUG_OBJECT (ogm, "Flow on %s:%s = %s",
       
   823           GST_DEBUG_PAD_NAME (ogm->srcpad), gst_flow_get_name (ret));
       
   824     }
       
   825   } else {
       
   826     ret = GST_FLOW_WRONG_STATE;
       
   827   }
       
   828 
       
   829   return ret;
       
   830 
       
   831 /* ERRORS */
       
   832 invalid_startcode:
       
   833   {
       
   834     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
       
   835         ("unexpected packet startcode 0x%02x", data[0]));
       
   836     return GST_FLOW_ERROR;
       
   837   }
       
   838 buffer_too_small:
       
   839   {
       
   840     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL),
       
   841         ("buffer too small, len+1=%u, size=%u", len + 1, size));
       
   842     return GST_FLOW_ERROR;
       
   843   }
       
   844 }
       
   845 
       
   846 static GstFlowReturn
       
   847 gst_ogm_parse_chain (GstPad * pad, GstBuffer * buf)
       
   848 {
       
   849   GstFlowReturn ret = GST_FLOW_OK;
       
   850   GstOgmParse *ogm = GST_OGM_PARSE (GST_PAD_PARENT (pad));
       
   851   guint8 *data = GST_BUFFER_DATA (buf);
       
   852   guint size = GST_BUFFER_SIZE (buf);
       
   853 
       
   854   if (size < 1)
       
   855     goto buffer_too_small;
       
   856 
       
   857   GST_LOG_OBJECT (ogm, "Packet with start code 0x%02x", data[0]);
       
   858 
       
   859   switch (data[0]) {
       
   860     case 0x01:{
       
   861       ret = gst_ogm_parse_stream_header (ogm, data + 1, size - 1);
       
   862       break;
       
   863     }
       
   864     case 0x03:{
       
   865       ret = gst_ogm_parse_comment_packet (ogm, buf);
       
   866       break;
       
   867     }
       
   868     default:{
       
   869       ret = gst_ogm_parse_data_packet (ogm, buf);
       
   870       break;
       
   871     }
       
   872   }
       
   873 
       
   874   gst_buffer_unref (buf);
       
   875 
       
   876   if (ret != GST_FLOW_OK) {
       
   877     GST_DEBUG_OBJECT (ogm, "Flow: %s", gst_flow_get_name (ret));
       
   878   }
       
   879 
       
   880   return ret;
       
   881 
       
   882 /* ERRORS */
       
   883 buffer_too_small:
       
   884   {
       
   885     GST_ELEMENT_ERROR (ogm, STREAM, DECODE, (NULL), ("buffer too small"));
       
   886     gst_buffer_unref (buf);
       
   887     return GST_FLOW_ERROR;
       
   888   }
       
   889 }
       
   890 
       
   891 static gboolean
       
   892 gst_ogm_parse_sink_event (GstPad * pad, GstEvent * event)
       
   893 {
       
   894   GstOgmParse *ogm = GST_OGM_PARSE (gst_pad_get_parent (pad));
       
   895   gboolean res;
       
   896 
       
   897   GST_LOG_OBJECT (ogm, "processing %s event", GST_EVENT_TYPE_NAME (event));
       
   898 
       
   899   GST_OBJECT_LOCK (ogm);
       
   900   if (ogm->srcpad == NULL) {
       
   901     ogm->cached_events = g_list_append (ogm->cached_events, event);
       
   902     GST_OBJECT_UNLOCK (ogm);
       
   903     res = TRUE;
       
   904   } else {
       
   905     GST_OBJECT_UNLOCK (ogm);
       
   906     res = gst_pad_event_default (pad, event);
       
   907   }
       
   908 
       
   909   gst_object_unref (ogm);
       
   910   return res;
       
   911 }
       
   912 
       
   913 static GstStateChangeReturn
       
   914 gst_ogm_parse_change_state (GstElement * element, GstStateChange transition)
       
   915 {
       
   916   GstStateChangeReturn ret;
       
   917   GstOgmParse *ogm = GST_OGM_PARSE (element);
       
   918 
       
   919   ret = parent_class->change_state (element, transition);
       
   920   if (ret != GST_STATE_CHANGE_SUCCESS)
       
   921     return ret;
       
   922 
       
   923   switch (transition) {
       
   924     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
   925       if (ogm->srcpad) {
       
   926         gst_pad_set_active (ogm->srcpad, FALSE);
       
   927         gst_element_remove_pad (element, ogm->srcpad);
       
   928         ogm->srcpad = NULL;
       
   929       }
       
   930       memset (&ogm->hdr, 0, sizeof (ogm->hdr));
       
   931       ogm->next_granulepos = 0;
       
   932       g_list_foreach (ogm->cached_events, (GFunc) gst_mini_object_unref, NULL);
       
   933       g_list_free (ogm->cached_events);
       
   934       ogm->cached_events = NULL;
       
   935       break;
       
   936     default:
       
   937       break;
       
   938   }
       
   939 
       
   940   return ret;
       
   941 }
       
   942 
       
   943 gboolean
       
   944 gst_ogm_parse_plugin_init (GstPlugin * plugin)
       
   945 {
       
   946   gst_riff_init ();
       
   947 
       
   948   GST_DEBUG_CATEGORY_INIT (gst_ogm_parse_debug, "ogmparse", 0, "ogm parser");
       
   949 
       
   950   return gst_element_register (plugin, "ogmaudioparse", GST_RANK_PRIMARY,
       
   951       GST_TYPE_OGM_AUDIO_PARSE) &&
       
   952       gst_element_register (plugin, "ogmvideoparse", GST_RANK_PRIMARY,
       
   953       GST_TYPE_OGM_VIDEO_PARSE) &&
       
   954       gst_element_register (plugin, "ogmtextparse", GST_RANK_PRIMARY,
       
   955       GST_TYPE_OGM_TEXT_PARSE);
       
   956 }