gst-plugins-nuvdemux/src/gstnuvdemux.c
author rosfran
Mon Nov 06 22:59:51 2006 +0000 (2006-11-06)
branchtrunk
changeset 69 f7c91518d8f4
parent 62 2a552f003c4e
child 73 959df0ca7621
permissions -rw-r--r--
[svn r70] Renamed these remote_encoder source files to recorder.
     1 /* GStreamer
     2  * Copyright (C) <2006> Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
     3  *                      Rosfran Borges <rosfran.borges@indt.org.br>
     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 /* Element-Checklist-Version: 5 */
    21 
    22 /**
    23  * SECTION:element-nuvdemux
    24  *
    25  * <refsect2>
    26  * <para>
    27  * Demuxes an .nuv file into raw or compressed audio and/or video streams.
    28  * </para>
    29  * <para>
    30  * This element currently only supports pull-based scheduling.
    31  * </para>
    32  * <title>Example launch line</title>
    33  * <para>
    34  * <programlisting>
    35  * gst-launch filesrc test.nuv ! nuvdemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
    36  * </programlisting>
    37  * Play (parse and decode) an .nuv file and try to output it to
    38  * an automatically detected soundcard and videosink. If the NUV file contains
    39  * compressed audio or video data, this will only work if you have the
    40  * right decoder elements/plugins installed.
    41  * </para>
    42  * </refsect2>
    43  *
    44  */
    45 
    46 #ifdef HAVE_CONFIG_H
    47 #include "config.h"
    48 #endif
    49 
    50 #include <gst/gst.h>
    51 #include <gst/gsterror.h>
    52 #include <gst/gstplugin.h>
    53 #include <string.h>
    54 
    55 #define GETTEXT_PACKAGE "gst-plugins-nuvdemux-0.10.3"
    56 #include <glib/gi18n.h>
    57 #include "gstnuvdemux.h"
    58 
    59 GST_DEBUG_CATEGORY_STATIC (nuvdemux_debug);
    60 #define GST_CAT_DEFAULT nuvdemux_debug
    61 
    62 
    63 #define DEMUX_INDEX_SIZE_MAX (100000)
    64 #define GST_FLOW_ERROR_NO_DATA  -101
    65 
    66 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_EVENT);
    67 
    68 static const GstElementDetails gst_nuv_demux_details =
    69 GST_ELEMENT_DETAILS ("Nuv demuxer",
    70     "Codec/Demuxer",
    71     "Demultiplex a .nuv file into audio and video",
    72     "Renato Araujo Oliveira Filho <renato.filho@indt.org.br>,"
    73     "Rosfran Borges <rosfran.borges@indt.org.br>");
    74 
    75 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
    76     GST_PAD_SINK,
    77     GST_PAD_ALWAYS,
    78     GST_STATIC_CAPS ("video/x-nuv"));
    79 
    80 static GstStaticPadTemplate audio_src_template =
    81 GST_STATIC_PAD_TEMPLATE ("audio_src",
    82     GST_PAD_SRC,
    83     GST_PAD_SOMETIMES,
    84     GST_STATIC_CAPS_ANY);
    85 
    86 static GstStaticPadTemplate video_src_template =
    87 GST_STATIC_PAD_TEMPLATE ("video_src",
    88     GST_PAD_SRC,
    89     GST_PAD_SOMETIMES,
    90     GST_STATIC_CAPS_ANY);
    91 
    92 /* NUV Demux indexes init/dispose callers */
    93 static nuv_demux_index* gst_nuv_demux_index_new (void);
    94 static void gst_nuv_demux_index_destroy (nuv_demux_index *p_idx);
    95 
    96 /* NUV Demux indexes manipulation functions */
    97 static gint64 gst_nuv_demux_index_find_offset( GstNuvDemux *nuv, gint64 i_offset );
    98 static void gst_nuv_demux_index_append( GstNuvDemux *nuv, gint64 i_time, gint64 i_offset );
    99 static gint64 gst_nuv_demux_index_convert_time( GstNuvDemux *nuv, gint64 i_time );
   100 
   101 /* NUV Demux plug-in time-line functions */
   102 static void gst_nuv_demux_finalize (GObject * object);
   103 static GstStateChangeReturn gst_nuv_demux_change_state (GstElement * element,
   104     GstStateChange transition);
   105 static void gst_nuv_demux_loop (GstPad * pad);
   106 static GstFlowReturn gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf);
   107 static GstFlowReturn gst_nuv_demux_play (GstPad * pad);
   108 static gboolean gst_nuv_demux_sink_activate_pull (GstPad * sinkpad,
   109     gboolean active);
   110 static gboolean gst_nuv_demux_sink_activate_push (GstPad * pad, 
   111     gboolean active);
   112 static gboolean gst_nuv_demux_sink_activate (GstPad * sinkpad);
   113 static GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size,
   114     gboolean move, GstBuffer ** buffer);
   115 static void gst_nuv_demux_reset (GstNuvDemux * nuv);
   116 static void gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv);
   117 static void gst_nuv_demux_send_eos (GstNuvDemux * nuv);
   118 
   119 /* GObject methods */
   120 GST_BOILERPLATE (GstNuvDemux, gst_nuv_demux, GstElement, GST_TYPE_ELEMENT);
   121 
   122 #if G_BYTE_ORDER == G_BIG_ENDIAN
   123 static inline gdouble
   124 _gdouble_swap_le_be (gdouble * d)
   125 {
   126   union
   127   {
   128     guint64 i;
   129     gdouble d;
   130   } u;
   131 
   132   u.d = *d;
   133   u.i = GUINT64_SWAP_LE_BE (u.i);
   134   return u.d;
   135 }
   136 
   137 #define READ_DOUBLE_FROM_LE(d) (_gdouble_swap_le_be((gdouble* ) d))
   138 #else /* G_BYTE_ORDER != G_BIG_ENDIAN */
   139 #define READ_DOUBLE_FROM_LE(d) *((gdouble* ) (d))
   140 #endif /* G_BYTE_ORDER != G_BIG_ENDIAN */
   141 
   142 static void
   143 gst_nuv_demux_base_init (gpointer klass)
   144 {
   145   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   146 
   147   gst_element_class_add_pad_template (element_class,
   148       gst_static_pad_template_get (&audio_src_template));
   149 
   150   gst_element_class_add_pad_template (element_class,
   151       gst_static_pad_template_get (&video_src_template));
   152 
   153   gst_element_class_add_pad_template (element_class,
   154       gst_static_pad_template_get (&sink_template));
   155   gst_element_class_set_details (element_class, &gst_nuv_demux_details);
   156 }
   157 
   158 static void
   159 gst_nuv_demux_class_init (GstNuvDemuxClass * klass)
   160 {
   161   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
   162   GObjectClass *gobject_class = (GObjectClass *) klass;
   163 
   164   GST_DEBUG_CATEGORY_INIT (nuvdemux_debug, "nuvdemux",
   165       0, "Demuxer for NUV streams");
   166 
   167   parent_class = g_type_class_peek_parent (klass);
   168 
   169   gobject_class->finalize = gst_nuv_demux_finalize;
   170   gstelement_class->change_state = gst_nuv_demux_change_state;
   171 }
   172 
   173 static void
   174 gst_nuv_demux_init (GstNuvDemux * nuv, GstNuvDemuxClass * nuv_class)
   175 {
   176   nuv->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
   177 
   178   gst_pad_set_activate_function (nuv->sinkpad, gst_nuv_demux_sink_activate);
   179 
   180   gst_pad_set_activatepull_function (nuv->sinkpad,
   181       gst_nuv_demux_sink_activate_pull);
   182 
   183   gst_pad_set_activatepush_function (nuv->sinkpad, 
   184       gst_nuv_demux_sink_activate_push);
   185 
   186   gst_pad_set_chain_function (nuv->sinkpad,
   187       GST_DEBUG_FUNCPTR (gst_nuv_demux_chain));
   188 
   189   gst_element_add_pad (GST_ELEMENT (nuv), nuv->sinkpad);
   190   
   191   nuv->index_entries = gst_nuv_demux_index_new ();
   192 
   193   nuv->adapter = NULL;
   194   nuv->mpeg_buffer = NULL;
   195   nuv->h = NULL;
   196   nuv->eh = NULL;
   197   nuv->fh = NULL;
   198   gst_nuv_demux_reset (nuv);
   199 }
   200 
   201 static void
   202 gst_nuv_demux_finalize (GObject * object)
   203 {
   204   GstNuvDemux *nuv = GST_NUV_DEMUX (object);
   205 
   206   if (nuv->mpeg_buffer != NULL) {
   207     gst_buffer_unref (nuv->mpeg_buffer);
   208   }
   209   
   210   if ( nuv->index_entries != NULL ) {
   211   	gst_nuv_demux_index_destroy (nuv->index_entries);
   212   	nuv->index_entries = NULL;
   213   }
   214 
   215   gst_nuv_demux_destoy_src_pad (nuv);
   216   gst_nuv_demux_reset (nuv);
   217   if (nuv->adapter != NULL) {
   218     gst_object_unref (nuv->adapter);
   219   }
   220   G_OBJECT_CLASS (parent_class)->finalize (object);
   221 }
   222 
   223 
   224 /*****************************************************************************
   225  * Indexes (timecode offset conversion) functions
   226  *****************************************************************************/
   227 
   228 static nuv_demux_index*
   229 gst_nuv_demux_index_new (void)
   230 {
   231 	return g_new0 (nuv_demux_index, 1);
   232 }
   233 
   234 static void 
   235 gst_nuv_demux_index_destroy (nuv_demux_index *p_idx)
   236 {
   237 	if (p_idx != NULL) {
   238         g_free (p_idx->idx);        
   239         p_idx->idx = NULL;
   240 		g_free (p_idx);
   241 	}
   242 }
   243 
   244 static void 
   245 gst_nuv_demux_index_append( GstNuvDemux *nuv, gint64 i_time, gint64 i_offset )
   246 {
   247 	nuv_demux_index *p_idx = nuv->index_entries;
   248 
   249 	//if ( p_idx == NULL )
   250 	//	return;
   251 
   252 	/* Be sure to append new entry (we don't insert point) */
   253 	if (p_idx != NULL && p_idx->i_idx > 0 && p_idx->idx[p_idx->i_idx-1].i_time >= i_time)
   254 		return;
   255 
   256 	/* */
   257 	if (p_idx->i_idx >= p_idx->i_idx_max)
   258 	{
   259 		if (p_idx->i_idx >= DEMUX_INDEX_SIZE_MAX)
   260 		{
   261 			/* Avoid too big index */
   262 			const gint64 i_length = p_idx->idx[p_idx->i_idx-1].i_time -
   263 				p_idx->idx[0].i_time;
   264 			const gint i_count = DEMUX_INDEX_SIZE_MAX/2;
   265 			gint i, j;
   266 
   267 			/* We try to reduce the resolution of the index by a factor 2 */
   268 			for (i = 1, j = 1; i < p_idx->i_idx; i++)
   269 			{
   270 				if (p_idx->idx[i].i_time < j * i_length / i_count)
   271 					continue;
   272 
   273 				p_idx->idx[j++] = p_idx->idx[i];
   274 			}
   275 			p_idx->i_idx = j;
   276 
   277 			if (p_idx->i_idx > 3 * DEMUX_INDEX_SIZE_MAX / 4)
   278 			{
   279 				/* We haven't created enough space
   280 				 * (This method won't create a good index but work for sure) */
   281 				for (i = 0; i < p_idx->i_idx/2; i++)
   282 					p_idx->idx[i] = p_idx->idx[2*i];
   283 				p_idx->i_idx /= 2;
   284 			}
   285 		}
   286 		else
   287 		{
   288 			p_idx->i_idx_max += 1000;
   289 			p_idx->idx = g_realloc (p_idx->idx, 
   290                     p_idx->i_idx_max*sizeof(nuv_demux_index_entry));
   291 		}
   292 	}
   293 
   294 	/* */
   295 	p_idx->idx[p_idx->i_idx].i_time = i_time;
   296 	p_idx->idx[p_idx->i_idx].i_offset = i_offset;
   297 
   298 	p_idx->i_idx++;
   299 }
   300 
   301 static gint64 
   302 gst_nuv_demux_index_convert_time( GstNuvDemux *nuv, gint64 i_time )
   303 {
   304 	nuv_demux_index *p_idx = nuv->index_entries;
   305 
   306 	g_return_val_if_fail( p_idx != NULL , i_time );
   307 
   308 	gint i_min = 0;
   309 	gint i_max = p_idx->i_idx-1;
   310 
   311 	/* Empty index */
   312 	if( p_idx->i_idx <= 0 )
   313 		return -1;
   314 
   315 	/* Special border case */
   316 	if( i_time <= p_idx->idx[0].i_time )
   317 		return p_idx->idx[0].i_offset;
   318 	if( i_time >= p_idx->idx[i_max].i_time )
   319 		return p_idx->idx[i_max].i_offset;
   320 
   321 	/* Dicho */
   322 	for( ;; )
   323 	{
   324 		gint i_med;
   325 
   326 		if( i_max - i_min <= 1 )
   327 			break;
   328 
   329 		i_med = (i_min+i_max)/2;
   330 		if( p_idx->idx[i_med].i_time < i_time )
   331 			i_min = i_med;
   332 		else if( p_idx->idx[i_med].i_time > i_time )
   333 			i_max = i_med;
   334 		else
   335 			return p_idx->idx[i_med].i_offset;
   336 	}
   337 
   338 	/* return nearest in time */
   339 	if( i_time - p_idx->idx[i_min].i_time < p_idx->idx[i_max].i_time - i_time )
   340 		return p_idx->idx[i_min].i_offset;
   341 	else
   342 		return p_idx->idx[i_max].i_offset;
   343 }
   344 
   345 static gint64
   346 gst_nuv_demux_index_find_offset( GstNuvDemux *nuv, gint64 i_offset )
   347 {
   348 	nuv_demux_index *p_idx = nuv->index_entries;
   349 
   350 	g_return_val_if_fail( p_idx != NULL && p_idx->idx != NULL, i_offset );
   351 
   352 	gint i_min = 0;
   353 	gint i_max = p_idx->i_idx-1;
   354 
   355 	/* Empty index */
   356 	if( p_idx->i_idx <= 0 )
   357 		return -1;
   358 
   359 	/* Special border case */
   360 	if( i_offset <= p_idx->idx[0].i_offset )
   361 		return p_idx->idx[0].i_offset;
   362 	if( i_offset == p_idx->idx[i_max].i_offset )
   363 		return p_idx->idx[i_max].i_offset;
   364 	if( i_offset > p_idx->idx[i_max].i_offset )
   365 		return -1;
   366 
   367 	/* Dicho */
   368 	for( ;; )
   369 	{
   370 		gint i_med;
   371 
   372 		if ( i_max - i_min <= 1 )
   373 			break;
   374 
   375 		i_med = (i_min+i_max)/2;
   376 		if( p_idx->idx[i_med].i_offset < i_offset )
   377 			i_min = i_med;
   378 		else if( p_idx->idx[i_med].i_offset > i_offset )
   379 			i_max = i_med;
   380 		else
   381 			return p_idx->idx[i_med].i_offset;
   382 	}
   383 
   384 	/* return nearest */
   385 	if( i_offset - p_idx->idx[i_min].i_offset < p_idx->idx[i_max].i_offset - i_offset )
   386 		return p_idx->idx[i_min].i_offset;
   387 	else
   388 		return p_idx->idx[i_max].i_offset;
   389 }
   390 
   391 /* HeaderLoad:
   392  */
   393 static GstFlowReturn
   394 gst_nuv_demux_header_load (GstNuvDemux * nuv, nuv_header ** h_ret)
   395 {
   396   GstBuffer *buffer = NULL;
   397   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 72, TRUE, &buffer);
   398 
   399   if (res != GST_FLOW_OK)
   400     return res;
   401 
   402   nuv_header *h = g_new0 (nuv_header, 1);
   403 
   404   memcpy (h->id, buffer->data, 12);
   405   memcpy (h->version, buffer->data + 12, 5);
   406   h->i_width = GST_READ_UINT32_LE (&buffer->data[20]);
   407   h->i_height = GST_READ_UINT32_LE (&buffer->data[24]);
   408   h->i_width_desired = GST_READ_UINT32_LE (&buffer->data[28]);
   409   h->i_height_desired = GST_READ_UINT32_LE (&buffer->data[32]);
   410   h->i_mode = buffer->data[36];
   411   h->d_aspect = READ_DOUBLE_FROM_LE (&buffer->data[40]);
   412   h->d_fps = READ_DOUBLE_FROM_LE (&buffer->data[48]);
   413   h->i_video_blocks = GST_READ_UINT32_LE (&buffer->data[56]);
   414   h->i_audio_blocks = GST_READ_UINT32_LE (&buffer->data[60]);
   415   h->i_text_blocks = GST_READ_UINT32_LE (&buffer->data[64]);
   416   h->i_keyframe_distance = GST_READ_UINT32_LE (&buffer->data[68]);
   417 
   418   GST_DEBUG_OBJECT (nuv,
   419       "nuv: h=%s v=%s %dx%d a=%f fps=%f v=%d a=%d t=%d kfd=%d", h->id,
   420       h->version, h->i_width, h->i_height, h->d_aspect, h->d_fps,
   421       h->i_video_blocks, h->i_audio_blocks, h->i_text_blocks,
   422       h->i_keyframe_distance);
   423 
   424   *h_ret = h;
   425   gst_buffer_unref (buffer);
   426   return res;
   427 }
   428 
   429 static GstFlowReturn
   430 gst_nuv_demux_stream_header_data (GstNuvDemux * nuv)
   431 {
   432   GstFlowReturn res = gst_nuv_demux_header_load (nuv, &nuv->h);
   433 
   434   if (res == GST_FLOW_OK)
   435     nuv->state = GST_NUV_DEMUX_EXTRA_DATA;
   436   return res;
   437 }
   438 
   439 /*
   440  * Read NUV file tag
   441  */
   442 static GstFlowReturn
   443 gst_nuv_demux_stream_file_header (GstNuvDemux * nuv)
   444 {
   445   GstFlowReturn res = GST_FLOW_OK;
   446   GstBuffer *file_header = NULL;
   447 
   448   res = gst_nuv_demux_read_bytes (nuv, 12, FALSE, &file_header);
   449   if (res != GST_FLOW_OK) {
   450     return res;
   451   } else {
   452     if (strncmp ((gchar *) file_header->data, "MythTVVideo", 11) ||
   453         strncmp ((gchar *) file_header->data, "NuppelVideo", 11)) {
   454       nuv->state = GST_NUV_DEMUX_HEADER_DATA;
   455     } else {
   456       GST_DEBUG_OBJECT (nuv, "error parsing file header");
   457       nuv->state = GST_NUV_DEMUX_INVALID_DATA;
   458       res = GST_FLOW_ERROR;
   459     }
   460   }
   461   if (file_header != NULL) {
   462     gst_buffer_unref (file_header);
   463   }
   464   return res;
   465 }
   466 
   467 /* FrameHeaderLoad:
   468  */
   469 static GstFlowReturn
   470 gst_nuv_demux_frame_header_load (GstNuvDemux * nuv, nuv_frame_header ** h_ret)
   471 {
   472   unsigned char *data;
   473   nuv_frame_header *h;
   474   GstBuffer *buf = NULL;
   475 
   476   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 12, TRUE, &buf);
   477 
   478   if (res != GST_FLOW_OK) {
   479     if (buf != NULL) {
   480       gst_buffer_unref (buf);
   481     }
   482     return res;
   483   }
   484 
   485   h = g_new0 (nuv_frame_header, 1);
   486   data = buf->data;
   487 
   488   h->i_type = data[0];
   489   h->i_compression = data[1];
   490   h->i_keyframe = data[2];
   491   h->i_filters = data[3];
   492 
   493   h->i_timecode = GST_READ_UINT32_LE (&data[4]);
   494 
   495 
   496   h->i_length = GST_READ_UINT32_LE (&data[8]);
   497   GST_DEBUG_OBJECT (nuv, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%d l=%d",
   498       h->i_type,
   499       h->i_compression ? h->i_compression : ' ',
   500       h->i_keyframe ? h->i_keyframe : ' ',
   501       h->i_filters, h->i_timecode, h->i_length);
   502 
   503   *h_ret = h;
   504   gst_buffer_unref (buf);
   505   return GST_FLOW_OK;
   506 }
   507 
   508 static GstFlowReturn
   509 gst_nuv_demux_extended_header_load (GstNuvDemux * nuv,
   510     nuv_extended_header ** h_ret)
   511 {
   512   unsigned char *data;
   513   GstBuffer *buff = NULL;
   514   nuv_extended_header *h;
   515 
   516 
   517   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 512, TRUE, &buff);
   518 
   519   if (res != GST_FLOW_OK) {
   520     if (buff != NULL) {
   521       gst_buffer_unref (buff);
   522     }
   523     return res;
   524   }
   525 
   526   h = g_new0 (nuv_extended_header, 1);
   527   data = buff->data;
   528   h->i_version = GST_READ_UINT32_LE (&data[0]);
   529   h->i_video_fcc = GST_MAKE_FOURCC (data[4], data[5], data[6], data[7]);
   530   h->i_audio_fcc = GST_MAKE_FOURCC (data[8], data[9], data[10], data[11]);
   531   h->i_audio_sample_rate = GST_READ_UINT32_LE (&data[12]);
   532   h->i_audio_bits_per_sample = GST_READ_UINT32_LE (&data[16]);
   533   h->i_audio_channels = GST_READ_UINT32_LE (&data[20]);
   534   h->i_audio_compression_ratio = GST_READ_UINT32_LE (&data[24]);
   535   h->i_audio_quality = GST_READ_UINT32_LE (&data[28]);
   536   h->i_rtjpeg_quality = GST_READ_UINT32_LE (&data[32]);
   537   h->i_rtjpeg_luma_filter = GST_READ_UINT32_LE (&data[36]);
   538   h->i_rtjpeg_chroma_filter = GST_READ_UINT32_LE (&data[40]);
   539   h->i_lavc_bitrate = GST_READ_UINT32_LE (&data[44]);
   540   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[48]);
   541   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[52]);
   542   h->i_lavc_maxqdiff = GST_READ_UINT32_LE (&data[56]);
   543   h->i_seekable_offset = GST_READ_UINT64_LE (&data[60]);
   544   h->i_keyframe_adjust_offset = GST_READ_UINT64_LE (&data[68]);
   545 
   546   GST_DEBUG_OBJECT (nuv,
   547       "ex hdr: v=%d vffc=%4.4s afcc=%4.4s %dHz %dbits ach=%d acr=%d aq=%d"
   548       "rtjpeg q=%d lf=%d lc=%d lavc br=%d qmin=%d qmax=%d maxqdiff=%d seekableoff=%lld keyfao=%lld",
   549       h->i_version, (gchar *) & h->i_video_fcc, (gchar *) & h->i_audio_fcc,
   550       h->i_audio_sample_rate, h->i_audio_bits_per_sample, h->i_audio_channels,
   551       h->i_audio_compression_ratio, h->i_audio_quality, h->i_rtjpeg_quality,
   552       h->i_rtjpeg_luma_filter, h->i_rtjpeg_chroma_filter, h->i_lavc_bitrate,
   553       h->i_lavc_qmin, h->i_lavc_qmax, h->i_lavc_maxqdiff, h->i_seekable_offset,
   554       h->i_keyframe_adjust_offset);
   555 
   556   *h_ret = h;
   557   gst_buffer_unref (buff);
   558   return res;
   559 }
   560 
   561 static gboolean
   562 gst_nuv_demux_handle_src_event (GstPad * pad, GstEvent * event)
   563 {
   564   gst_event_unref (event);
   565   return FALSE;
   566 }
   567 
   568 
   569 static void
   570 gst_nuv_demux_create_pads (GstNuvDemux * nuv)
   571 {
   572   if (nuv->h->i_video_blocks != 0) {
   573     GstCaps *video_caps = NULL;
   574 
   575     nuv->src_video_pad =
   576         gst_pad_new_from_static_template (&video_src_template, "video_src");
   577 
   578     video_caps = gst_caps_new_simple ("video/x-divx",
   579         "divxversion", G_TYPE_INT, 4,
   580         "width", G_TYPE_INT, nuv->h->i_width,
   581         "height", G_TYPE_INT, nuv->h->i_height,
   582         "framerate", GST_TYPE_FRACTION, (gint) (nuv->h->d_fps * 1000.0f), 1000,
   583         "pixel-aspect-ratio", GST_TYPE_FRACTION,
   584         (gint) (nuv->h->d_aspect * 1000.0f), 1000, NULL);
   585 
   586     gst_pad_use_fixed_caps (nuv->src_video_pad);
   587     gst_pad_set_active (nuv->src_video_pad, TRUE);
   588     gst_pad_set_caps (nuv->src_video_pad, video_caps);
   589 
   590     gst_pad_set_active (nuv->src_video_pad, TRUE);
   591     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
   592 
   593     gst_pad_set_event_function (nuv->src_video_pad,
   594         gst_nuv_demux_handle_src_event);
   595 
   596     gst_caps_unref (video_caps);
   597   }
   598 
   599   if (nuv->h->i_audio_blocks != 0) {
   600     GstCaps *audio_caps = NULL;
   601 
   602     nuv->src_audio_pad =
   603         gst_pad_new_from_static_template (&audio_src_template, "audio_src");
   604 
   605     audio_caps = gst_caps_new_simple ("audio/mpeg",
   606         "rate", G_TYPE_INT, nuv->eh->i_audio_sample_rate,
   607         "format", GST_TYPE_FOURCC, nuv->eh->i_audio_fcc,
   608         "channels", G_TYPE_INT, nuv->eh->i_audio_channels,
   609         "mpegversion", G_TYPE_INT, nuv->eh->i_version, NULL);
   610 
   611     gst_pad_use_fixed_caps (nuv->src_audio_pad);
   612     gst_pad_set_active (nuv->src_audio_pad, TRUE);
   613     gst_pad_set_caps (nuv->src_audio_pad, audio_caps);
   614     gst_pad_set_active (nuv->src_audio_pad, TRUE);
   615     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
   616 
   617     gst_pad_set_event_function (nuv->src_audio_pad,
   618         gst_nuv_demux_handle_src_event);
   619 
   620     gst_caps_unref (audio_caps);
   621   }
   622 
   623   gst_element_no_more_pads (GST_ELEMENT (nuv));
   624 }
   625 
   626 static GstFlowReturn
   627 gst_nuv_demux_read_head_frame (GstNuvDemux * nuv)
   628 {
   629   GstFlowReturn ret = GST_FLOW_OK;
   630 
   631   ret = gst_nuv_demux_frame_header_load (nuv, &nuv->fh);
   632   if (ret != GST_FLOW_OK)
   633     return ret;
   634 
   635   nuv->state = GST_NUV_DEMUX_MOVI;
   636   return ret;
   637 }
   638 
   639 static GstFlowReturn
   640 gst_nuv_demux_stream_data (GstNuvDemux * nuv)
   641 {
   642   GstFlowReturn ret = GST_FLOW_OK;
   643   GstBuffer *buf = NULL;
   644   nuv_frame_header *h = nuv->fh;
   645 
   646   if (h->i_type == 'R')
   647     goto done;
   648     
   649 
   650  GST_DEBUG_OBJECT (nuv, "starting append index");
   651    /* append the frame's header timecode field, and the actual offset */
   652   gst_nuv_demux_index_append( nuv, h->i_timecode, nuv->offset );
   653  
   654   if (h->i_length > 0) {
   655 	  ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf);
   656 	  if (ret != GST_FLOW_OK)
   657 		  return ret;
   658 
   659 	  /* search for a valid timecode in the indexes list (find the nearest valid timecode) */
   660 	  if ( h->i_timecode < 0 ) {
   661 		  /* convert this actual timecode to a valid index in the timecode's table */
   662 		  gint64 pos = gst_nuv_demux_index_convert_time (nuv, h->i_timecode);
   663 
   664 		  /* find the nearest empty frame slot */		  
   665 		  gint64 near_off = gst_nuv_demux_index_find_offset  (nuv, pos);
   666 
   667 		  /* just get the timecode from the timecode's table */		  
   668 		  GST_BUFFER_TIMESTAMP (buf) = nuv->index_entries->idx[near_off].i_time * GST_MSECOND;
   669 	  } else {  
   670 		  GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND;
   671 	  }
   672   }
   673 
   674  GST_DEBUG_OBJECT (nuv, "index appended");
   675 
   676   switch (h->i_type) {
   677     case 'V':
   678     {
   679       if (h->i_length == 0)
   680         break;
   681 
   682       GST_BUFFER_OFFSET (buf) = nuv->video_offset;
   683       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_video_pad));
   684       ret = gst_pad_push (nuv->src_video_pad, buf);
   685       if (ret != GST_FLOW_OK) {
   686         GST_WARNING_OBJECT (nuv, "error pushing on srcpad %s, is linked? = %d",
   687             gst_pad_get_name (nuv->src_video_pad), gst_pad_is_linked (nuv->src_video_pad));
   688       }
   689       nuv->video_offset++;
   690       break;
   691     }
   692     case 'A':
   693     {
   694       if (h->i_length == 0)
   695         break;
   696 
   697       GST_BUFFER_OFFSET (buf) = nuv->audio_offset;
   698       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_audio_pad));
   699       ret = gst_pad_push (nuv->src_audio_pad, buf);
   700       if (ret != GST_FLOW_OK) {
   701         GST_WARNING_OBJECT (nuv, "Error pushing on srcpad %s, is linked? = %d",
   702             gst_pad_get_name (nuv->src_audio_pad), gst_pad_is_linked (nuv->src_audio_pad));
   703       }
   704       nuv->audio_offset++;
   705       break;
   706     }
   707     case 'S':
   708     {
   709         /*
   710       switch (h->i_compression) {
   711         case 'V':
   712           GST_DEBUG_OBJECT (nuv, "Video New Segment");
   713           nuv->video_offset = 0;
   714           gst_pad_push_event (nuv->src_video_pad,
   715               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1,
   716                   0));
   717           break;
   718         case 'A':
   719           GST_DEBUG_OBJECT (nuv, "Audio New Segment");
   720           gst_pad_push_event (nuv->src_audio_pad,
   721               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
   722           break;
   723         default:
   724           break;
   725       }
   726       */
   727     }
   728     default:
   729       if (buf != NULL)
   730         gst_buffer_unref (buf);
   731 
   732       break;
   733   }
   734 
   735  GST_DEBUG_OBJECT (nuv, "data sent");
   736 
   737 done:
   738   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
   739   g_free (nuv->fh);
   740   nuv->fh = NULL;
   741   return ret;
   742 }
   743 
   744 static GstFlowReturn
   745 gst_nuv_demux_stream_mpeg_data (GstNuvDemux * nuv)
   746 {
   747   GstFlowReturn ret = GST_FLOW_OK;
   748 
   749   /* ffmpeg extra data */
   750   ret =
   751       gst_nuv_demux_read_bytes (nuv, nuv->mpeg_data_size, TRUE,
   752       &nuv->mpeg_buffer);
   753   if (ret != GST_FLOW_OK) {
   754     return GST_FLOW_ERROR;
   755   }
   756   GST_BUFFER_SIZE (nuv->mpeg_buffer) = nuv->mpeg_data_size;
   757   nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
   758   return ret;
   759 }
   760 
   761 static GstFlowReturn
   762 gst_nuv_demux_stream_extra_data (GstNuvDemux * nuv)
   763 {
   764   GstFlowReturn ret = GST_FLOW_OK;
   765 
   766   /* Load 'D' */
   767   nuv_frame_header *h;
   768 
   769   ret = gst_nuv_demux_frame_header_load (nuv, &h);
   770   if (ret != GST_FLOW_OK)
   771     return ret;
   772 
   773   if (h->i_type != 'D') {
   774     g_free (h);
   775     return GST_FLOW_ERROR;
   776   }
   777 
   778   if (h->i_length > 0) {
   779     if (h->i_compression == 'F') {
   780       nuv->state = GST_NUV_DEMUX_MPEG_DATA;
   781     } else {
   782       g_free (h);
   783       return GST_FLOW_ERROR;
   784     }
   785   } else {
   786     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
   787   }
   788 
   789   g_free (h);
   790   h = NULL;
   791   return ret;
   792 }
   793 
   794 static GstFlowReturn
   795 gst_nuv_demux_stream_extend_header_data (GstNuvDemux * nuv)
   796 {
   797   GstFlowReturn ret = GST_FLOW_OK;
   798 
   799   ret = gst_nuv_demux_extended_header_load (nuv, &nuv->eh);
   800   if (ret != GST_FLOW_OK)
   801     return ret;
   802 
   803   gst_nuv_demux_create_pads (nuv);
   804   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
   805   return ret;
   806 }
   807 
   808 static GstFlowReturn
   809 gst_nuv_demux_stream_extend_header (GstNuvDemux * nuv)
   810 {
   811   GstBuffer *buf = NULL;
   812   GstFlowReturn res = GST_FLOW_OK;
   813 
   814   res = gst_nuv_demux_read_bytes (nuv, 1, FALSE, &buf);
   815   if (res != GST_FLOW_OK) {
   816     if (buf != NULL) {
   817       gst_buffer_unref (buf);
   818     }
   819     return res;
   820   }
   821 
   822   if (buf->data[0] == 'X') {
   823     gst_buffer_unref (buf);
   824     buf = NULL;
   825     nuv_frame_header *h = NULL;
   826 
   827     res = gst_nuv_demux_frame_header_load (nuv, &h);
   828     if (res != GST_FLOW_OK)
   829       return res;
   830 
   831     if (h->i_length != 512) {
   832       g_free (h);
   833       return GST_FLOW_ERROR;
   834     }
   835     g_free (h);
   836     h = NULL;
   837     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER_DATA;
   838   } else {
   839     nuv->state = GST_NUV_DEMUX_INVALID_DATA;
   840     g_object_unref (buf);
   841     GST_ELEMENT_WARNING (nuv, STREAM, FAILED,
   842         (_("incomplete NUV support")), ("incomplete NUV support"));
   843     return GST_FLOW_ERROR;
   844   }
   845   return res;
   846 }
   847 
   848 static GstFlowReturn
   849 gst_nuv_demux_play (GstPad * pad)
   850 {
   851   GstFlowReturn res = GST_FLOW_OK;
   852   GstNuvDemux *nuv = GST_NUV_DEMUX (GST_PAD_PARENT (pad));
   853 
   854   switch (nuv->state) {
   855     case GST_NUV_DEMUX_START:
   856       res = gst_nuv_demux_stream_file_header (nuv);
   857       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   858         goto pause;
   859       }
   860       if (nuv->state != GST_NUV_DEMUX_HEADER_DATA)
   861         break;
   862 
   863     case GST_NUV_DEMUX_HEADER_DATA:
   864       res = gst_nuv_demux_stream_header_data (nuv);
   865       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   866         goto pause;
   867       }
   868       if (nuv->state != GST_NUV_DEMUX_EXTRA_DATA)
   869         break;
   870 
   871     case GST_NUV_DEMUX_EXTRA_DATA:
   872       res = gst_nuv_demux_stream_extra_data (nuv);
   873       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   874         goto pause;
   875       }
   876       if (nuv->state != GST_NUV_DEMUX_MPEG_DATA)
   877         break;
   878 
   879     case GST_NUV_DEMUX_MPEG_DATA:
   880       res = gst_nuv_demux_stream_mpeg_data (nuv);
   881       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   882         goto pause;
   883       }
   884 
   885       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER)
   886         break;
   887 
   888     case GST_NUV_DEMUX_EXTEND_HEADER:
   889       res = gst_nuv_demux_stream_extend_header (nuv);
   890       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   891         goto pause;
   892       }
   893       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER_DATA)
   894         break;
   895 
   896     case GST_NUV_DEMUX_EXTEND_HEADER_DATA:
   897       res = gst_nuv_demux_stream_extend_header_data (nuv);
   898       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   899         goto pause;
   900       }
   901 
   902       if (nuv->state != GST_NUV_DEMUX_FRAME_HEADER)
   903         break;
   904 
   905     case GST_NUV_DEMUX_FRAME_HEADER:
   906       res = gst_nuv_demux_read_head_frame (nuv);
   907       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   908         goto pause;
   909       }
   910       if (nuv->state != GST_NUV_DEMUX_MOVI)
   911         break;
   912 
   913     case GST_NUV_DEMUX_MOVI:
   914       res = gst_nuv_demux_stream_data (nuv);
   915       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   916         goto pause;
   917       }
   918       break;
   919     case GST_NUV_DEMUX_INVALID_DATA:
   920       goto pause;
   921       break;
   922     default:
   923       g_assert_not_reached ();
   924   }
   925 
   926   GST_DEBUG_OBJECT (nuv, "state: %d res:%s", nuv->state,
   927       gst_flow_get_name (res));
   928 
   929   return GST_FLOW_OK;
   930 
   931 pause:
   932   g_debug ("PAUSE");
   933 
   934   GST_LOG_OBJECT (nuv, "pausing task, reason %s", gst_flow_get_name (res));
   935   gst_pad_pause_task (nuv->sinkpad);
   936   if (GST_FLOW_IS_FATAL (res)) {
   937     GST_ELEMENT_ERROR (nuv, STREAM, FAILED,
   938         (_("Internal data stream error.")),
   939         ("streaming stopped, reason %s", gst_flow_get_name (res)));
   940 
   941     gst_nuv_demux_send_eos (nuv);
   942   }
   943   return res;
   944 }
   945 
   946 static void
   947 gst_nuv_demux_send_eos (GstNuvDemux * nuv)
   948 {
   949   gst_element_post_message (GST_ELEMENT (nuv),
   950       gst_message_new_segment_done (GST_OBJECT (nuv), GST_FORMAT_TIME, -1));
   951 
   952   if (nuv->src_video_pad)
   953     gst_pad_push_event (nuv->src_video_pad, gst_event_new_eos ());
   954   if (nuv->src_audio_pad)
   955     gst_pad_push_event (nuv->src_audio_pad, gst_event_new_eos ());
   956 }
   957 
   958 static GstFlowReturn
   959 gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move,
   960     GstBuffer ** buffer)
   961 {
   962   GstFlowReturn ret = GST_FLOW_OK;
   963 
   964   if (size == 0) {
   965     *buffer = gst_buffer_new ();
   966     return ret;
   967   }
   968 
   969   if (nuv->mode == 0) {
   970     ret = gst_pad_pull_range (nuv->sinkpad, nuv->offset, size, buffer);
   971     if (ret == GST_FLOW_OK) {
   972       if (move) {
   973         nuv->offset += size;
   974       }
   975       /* got eos */
   976     } else if (ret == GST_FLOW_UNEXPECTED) {
   977       gst_nuv_demux_send_eos (nuv);
   978       return GST_FLOW_WRONG_STATE;
   979     }
   980   } else {
   981     if (gst_adapter_available (nuv->adapter) < size)
   982       return GST_FLOW_ERROR_NO_DATA;
   983 
   984     if (move) {
   985       guint8 *data = NULL;
   986 
   987       data = (guint8 *) gst_adapter_take (nuv->adapter, size);
   988       *buffer = gst_buffer_new ();
   989       gst_buffer_set_data (*buffer, data, size);
   990     } else {
   991       guint8 *data = NULL;
   992 
   993       data = (guint8 *) gst_adapter_peek (nuv->adapter, size);
   994       *buffer = gst_buffer_new ();
   995       gst_buffer_set_data (*buffer, data, size);
   996     }
   997   }
   998   return ret;
   999 }
  1000 
  1001 static gboolean
  1002 gst_nuv_demux_sink_activate (GstPad * sinkpad)
  1003 {
  1004   gboolean res = TRUE;
  1005   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
  1006 
  1007   if (gst_pad_check_pull_range (sinkpad)) {
  1008     nuv->mode = 0;
  1009     nuv->adapter = NULL;
  1010     res = gst_pad_activate_pull (sinkpad, TRUE);
  1011     GST_DEBUG_OBJECT (nuv, "starting PULL mode");
  1012   } else {
  1013     nuv->mode = 1;
  1014     nuv->adapter = gst_adapter_new ();
  1015     res = gst_pad_activate_push (sinkpad, TRUE);
  1016     GST_DEBUG_OBJECT (nuv, "starting PUSH mode");
  1017   }
  1018 
  1019   g_object_unref (nuv);
  1020   return res;
  1021 }
  1022 
  1023 static gboolean
  1024 gst_nuv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
  1025 {
  1026   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
  1027 
  1028   if (active) {
  1029     gst_pad_start_task (sinkpad, (GstTaskFunction) gst_nuv_demux_loop, sinkpad);
  1030   } else {
  1031     gst_pad_stop_task (sinkpad);
  1032   }
  1033   gst_object_unref (nuv);
  1034 
  1035   return TRUE;
  1036 }
  1037 
  1038 static gboolean
  1039 gst_nuv_demux_sink_activate_push (GstPad * pad, gboolean active)
  1040 {
  1041   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad));
  1042 
  1043   if (active) {
  1044     GST_DEBUG_OBJECT (nuv, "activating push/chain function");
  1045   } else {
  1046     GST_DEBUG_OBJECT (nuv, "deactivating push/chain function");
  1047   }
  1048 
  1049   gst_object_unref (nuv);
  1050 
  1051   return TRUE;
  1052 }
  1053 
  1054 static GstFlowReturn
  1055 gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf)
  1056 {
  1057   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad));
  1058 
  1059   gst_adapter_push (nuv->adapter, buf);
  1060 
  1061   GST_DEBUG_OBJECT (nuv, "PUSH adapter %d", gst_adapter_available (nuv->adapter));
  1062 
  1063   gst_object_unref (nuv);
  1064 
  1065   return gst_nuv_demux_play (pad);
  1066 }
  1067 
  1068 static void
  1069 gst_nuv_demux_loop (GstPad * pad)
  1070 {
  1071   gst_nuv_demux_play (pad);
  1072 }
  1073 
  1074 static void
  1075 gst_nuv_demux_reset (GstNuvDemux * nuv)
  1076 {
  1077   nuv->state = GST_NUV_DEMUX_START;
  1078   nuv->mode = 0;
  1079   nuv->offset = 0;
  1080   nuv->video_offset = 0;
  1081   nuv->audio_offset = 0;
  1082 
  1083   if (nuv->adapter != NULL)
  1084     gst_adapter_clear (nuv->adapter);
  1085 
  1086   if (nuv->mpeg_buffer != NULL) {
  1087     gst_buffer_unref (nuv->mpeg_buffer);
  1088     nuv->mpeg_buffer = NULL;
  1089   }
  1090 
  1091   g_free (nuv->h);
  1092   nuv->h = NULL;
  1093 
  1094   g_free (nuv->eh);
  1095   nuv->eh = NULL;
  1096 
  1097   g_free (nuv->fh);
  1098   nuv->fh = NULL;
  1099 }
  1100 
  1101 static void
  1102 gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv)
  1103 {
  1104   if (nuv->src_video_pad) {
  1105     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
  1106     nuv->src_video_pad = NULL;
  1107   }
  1108 
  1109   if (nuv->src_audio_pad) {
  1110     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
  1111     nuv->src_audio_pad = NULL;
  1112   }
  1113 }
  1114 
  1115 static GstStateChangeReturn
  1116 gst_nuv_demux_change_state (GstElement * element, GstStateChange transition)
  1117 {
  1118   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  1119   
  1120   switch (transition) {
  1121     case GST_STATE_CHANGE_READY_TO_PAUSED:
  1122       break;
  1123     default:
  1124       break;
  1125   }
  1126 
  1127   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  1128   if (ret == GST_STATE_CHANGE_FAILURE)
  1129     goto done;
  1130 
  1131   switch (transition) {
  1132     case GST_STATE_CHANGE_PAUSED_TO_READY:
  1133       gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (element));
  1134       gst_nuv_demux_reset (GST_NUV_DEMUX (element));
  1135       break;
  1136     default:
  1137       break;
  1138   }
  1139 
  1140 done:
  1141   return ret;
  1142 }
  1143 
  1144 static gboolean
  1145 plugin_init (GstPlugin * plugin)
  1146 {
  1147 #ifdef ENABLE_NLS
  1148   setlocale (LC_ALL, "");
  1149 #endif /* ENABLE_NLS */
  1150 
  1151   if (!gst_element_register (plugin, "nuvdemux", GST_RANK_SECONDARY,
  1152           GST_TYPE_NUV_DEMUX)) {
  1153     return FALSE;
  1154   }
  1155   return TRUE;
  1156 }
  1157 
  1158 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
  1159     GST_VERSION_MINOR,
  1160     "nuvdemux",
  1161     "Demuxes and muxes audio and video",
  1162     plugin_init, VERSION, "LGPL", "NuvDemux", "")