gst-plugins-nuvdemux/src/gstnuvdemux.c
author rosfran
Fri Oct 27 00:13:07 2006 +0100 (2006-10-27)
branchtrunk
changeset 58 3f9efc6c27bd
parent 56 24be83eaef89
child 60 98fa2109e716
permissions -rw-r--r--
[svn r59] Some minor fixes.
     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 
    57 #include "gst/gst-i18n-plugin.h"
    58 #include "gstnuvdemux.h"
    59 
    60 GST_DEBUG_CATEGORY_STATIC (nuvdemux_debug);
    61 #define GST_CAT_DEFAULT nuvdemux_debug
    62 
    63 
    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 void gst_nuv_demux_index_init( nuv_demux_index **p_idx );
    94 static void gst_nuv_demux_index_clean( 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 (GstPad * sinkpad);
   111 static GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size,
   112     gboolean move, GstBuffer ** buffer);
   113 static void gst_nuv_demux_reset (GstNuvDemux * nuv);
   114 static gboolean gst_nuv_demux_handle_sink_event (GstPad * sinkpad,
   115     GstEvent * event);
   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_chain_function (nuv->sinkpad,
   184       GST_DEBUG_FUNCPTR (gst_nuv_demux_chain));
   185 
   186   gst_pad_set_event_function (nuv->sinkpad, gst_nuv_demux_handle_sink_event);
   187 
   188   gst_element_add_pad (GST_ELEMENT (nuv), nuv->sinkpad);
   189   
   190   gst_nuv_demux_index_init( &nuv->index_entries );
   191 
   192   nuv->adapter = NULL;
   193   nuv->mpeg_buffer = NULL;
   194   nuv->h = NULL;
   195   nuv->eh = NULL;
   196   nuv->fh = NULL;
   197   gst_nuv_demux_reset (nuv);
   198 }
   199 
   200 static void
   201 gst_nuv_demux_finalize (GObject * object)
   202 {
   203   GstNuvDemux *nuv = GST_NUV_DEMUX (object);
   204 
   205   if (nuv->mpeg_buffer != NULL) {
   206     gst_buffer_unref (nuv->mpeg_buffer);
   207   }
   208   
   209   if ( nuv->index_entries != NULL ) {
   210   	gst_nuv_demux_index_clean( &nuv->index_entries );
   211   	nuv->index_entries = NULL;
   212   }
   213 
   214   gst_nuv_demux_destoy_src_pad (nuv);
   215   gst_nuv_demux_reset (nuv);
   216   if (nuv->adapter != NULL) {
   217     gst_object_unref (nuv->adapter);
   218   }
   219   G_OBJECT_CLASS (parent_class)->finalize (object);
   220 }
   221 
   222 
   223 /*****************************************************************************
   224  * Indexes (timecode offset conversion) functions
   225  *****************************************************************************/
   226 
   227 static void 
   228 gst_nuv_demux_index_init( nuv_demux_index **p_idx )
   229 {
   230 	*p_idx = g_new0( nuv_demux_index, 1 );
   231 	(*p_idx)->i_idx = 0;
   232 	(*p_idx)->i_idx_max = 0;
   233 }
   234 
   235 static void 
   236 gst_nuv_demux_index_clean( nuv_demux_index **p_idx )
   237 {
   238 	if ( *p_idx != NULL ) {
   239 		g_free( *p_idx );
   240 		*p_idx = NULL;
   241 	}
   242 
   243 }
   244 
   245 static void 
   246 gst_nuv_demux_index_append( GstNuvDemux *nuv, gint64 i_time, gint64 i_offset )
   247 {
   248 	nuv_demux_index *p_idx = nuv->index_entries;
   249 
   250 	//if ( p_idx == NULL )
   251 	//	return;
   252 
   253 	/* Be sure to append new entry (we don't insert point) */
   254 	if( p_idx != NULL && p_idx->i_idx > 0 && p_idx->idx[p_idx->i_idx-1].i_time >= i_time )
   255 		return;
   256 
   257 	/* */
   258 	if( p_idx->i_idx >= p_idx->i_idx_max )
   259 	{
   260 		if( p_idx->i_idx >= DEMUX_INDEX_SIZE_MAX )
   261 		{
   262 			/* Avoid too big index */
   263 			const gint64 i_length = p_idx->idx[p_idx->i_idx-1].i_time -
   264 				p_idx->idx[0].i_time;
   265 			const gint i_count = DEMUX_INDEX_SIZE_MAX/2;
   266 			gint i, j;
   267 
   268 			/* We try to reduce the resolution of the index by a factor 2 */
   269 			for( i = 1, j = 1; i < p_idx->i_idx; i++ )
   270 			{
   271 				if( p_idx->idx[i].i_time < j * i_length / i_count )
   272 					continue;
   273 
   274 				p_idx->idx[j++] = p_idx->idx[i];
   275 			}
   276 			p_idx->i_idx = j;
   277 
   278 			if( p_idx->i_idx > 3 * DEMUX_INDEX_SIZE_MAX / 4 )
   279 			{
   280 				/* We haven't created enough space
   281 				 * (This method won't create a good index but work for sure) */
   282 				for( i = 0; i < p_idx->i_idx/2; i++ )
   283 					p_idx->idx[i] = p_idx->idx[2*i];
   284 				p_idx->i_idx /= 2;
   285 			}
   286 		}
   287 		else
   288 		{
   289 			p_idx->i_idx_max += 1000;
   290 			//p_idx->idx = g_realloc( p_idx->idx,
   291 					//p_idx->i_idx_max*sizeof(nuv_demux_index_entry));
   292 		}
   293 	}
   294 
   295 	/* */
   296 	p_idx->idx[p_idx->i_idx].i_time = i_time;
   297 	p_idx->idx[p_idx->i_idx].i_offset = i_offset;
   298 
   299 	p_idx->i_idx++;
   300 }
   301 
   302 static gint64 
   303 gst_nuv_demux_index_convert_time( GstNuvDemux *nuv, gint64 i_time )
   304 {
   305 	nuv_demux_index *p_idx = nuv->index_entries;
   306 
   307 	g_return_val_if_fail( p_idx != NULL , i_time );
   308 
   309 	gint i_min = 0;
   310 	gint i_max = p_idx->i_idx-1;
   311 
   312 	/* Empty index */
   313 	if( p_idx->i_idx <= 0 )
   314 		return -1;
   315 
   316 	/* Special border case */
   317 	if( i_time <= p_idx->idx[0].i_time )
   318 		return p_idx->idx[0].i_offset;
   319 	if( i_time >= p_idx->idx[i_max].i_time )
   320 		return p_idx->idx[i_max].i_offset;
   321 
   322 	/* Dicho */
   323 	for( ;; )
   324 	{
   325 		gint i_med;
   326 
   327 		if( i_max - i_min <= 1 )
   328 			break;
   329 
   330 		i_med = (i_min+i_max)/2;
   331 		if( p_idx->idx[i_med].i_time < i_time )
   332 			i_min = i_med;
   333 		else if( p_idx->idx[i_med].i_time > i_time )
   334 			i_max = i_med;
   335 		else
   336 			return p_idx->idx[i_med].i_offset;
   337 	}
   338 
   339 	/* return nearest in time */
   340 	if( i_time - p_idx->idx[i_min].i_time < p_idx->idx[i_max].i_time - i_time )
   341 		return p_idx->idx[i_min].i_offset;
   342 	else
   343 		return p_idx->idx[i_max].i_offset;
   344 }
   345 
   346 static gint64
   347 gst_nuv_demux_index_find_offset( GstNuvDemux *nuv, gint64 i_offset )
   348 {
   349 	nuv_demux_index *p_idx = nuv->index_entries;
   350 
   351 	g_return_val_if_fail( p_idx != NULL && p_idx->idx != NULL, i_offset );
   352 
   353 	gint i_min = 0;
   354 	gint i_max = p_idx->i_idx-1;
   355 
   356 	/* Empty index */
   357 	if( p_idx->i_idx <= 0 )
   358 		return -1;
   359 
   360 	/* Special border case */
   361 	if( i_offset <= p_idx->idx[0].i_offset )
   362 		return p_idx->idx[0].i_offset;
   363 	if( i_offset == p_idx->idx[i_max].i_offset )
   364 		return p_idx->idx[i_max].i_offset;
   365 	if( i_offset > p_idx->idx[i_max].i_offset )
   366 		return -1;
   367 
   368 	/* Dicho */
   369 	for( ;; )
   370 	{
   371 		gint i_med;
   372 
   373 		if ( i_max - i_min <= 1 )
   374 			break;
   375 
   376 		i_med = (i_min+i_max)/2;
   377 		if( p_idx->idx[i_med].i_offset < i_offset )
   378 			i_min = i_med;
   379 		else if( p_idx->idx[i_med].i_offset > i_offset )
   380 			i_max = i_med;
   381 		else
   382 			return p_idx->idx[i_med].i_offset;
   383 	}
   384 
   385 	/* return nearest */
   386 	if( i_offset - p_idx->idx[i_min].i_offset < p_idx->idx[i_max].i_offset - i_offset )
   387 		return p_idx->idx[i_min].i_offset;
   388 	else
   389 		return p_idx->idx[i_max].i_offset;
   390 }
   391 
   392 /*****************************************************************************
   393  * Utils functions
   394  *****************************************************************************/
   395 
   396 static gboolean
   397 gst_nuv_demux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
   398 {
   399   gboolean res = FALSE;
   400 
   401   switch (GST_EVENT_TYPE (event)) {
   402     case GST_EVENT_NEWSEGMENT:
   403       res = TRUE;
   404       break;
   405     default:
   406       return gst_pad_event_default (sinkpad, event);
   407       break;
   408   }
   409 
   410   gst_event_unref (event);
   411   return res;
   412 }
   413 
   414 
   415 /* HeaderLoad:
   416  */
   417 static GstFlowReturn
   418 gst_nuv_demux_header_load (GstNuvDemux * nuv, nuv_header ** h_ret)
   419 {
   420   GstBuffer *buffer = NULL;
   421   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 72, TRUE, &buffer);
   422 
   423   if (res != GST_FLOW_OK)
   424     return res;
   425 
   426   nuv_header *h = g_new0 (nuv_header, 1);
   427 
   428   memcpy (h->id, buffer->data, 12);
   429   memcpy (h->version, buffer->data + 12, 5);
   430   h->i_width = GST_READ_UINT32_LE (&buffer->data[20]);
   431   h->i_height = GST_READ_UINT32_LE (&buffer->data[24]);
   432   h->i_width_desired = GST_READ_UINT32_LE (&buffer->data[28]);
   433   h->i_height_desired = GST_READ_UINT32_LE (&buffer->data[32]);
   434   h->i_mode = buffer->data[36];
   435   h->d_aspect = READ_DOUBLE_FROM_LE (&buffer->data[40]);
   436   h->d_fps = READ_DOUBLE_FROM_LE (&buffer->data[48]);
   437   h->i_video_blocks = GST_READ_UINT32_LE (&buffer->data[56]);
   438   h->i_audio_blocks = GST_READ_UINT32_LE (&buffer->data[60]);
   439   h->i_text_blocks = GST_READ_UINT32_LE (&buffer->data[64]);
   440   h->i_keyframe_distance = GST_READ_UINT32_LE (&buffer->data[68]);
   441 
   442   GST_DEBUG_OBJECT (nuv,
   443       "nuv: h=%s v=%s %dx%d a=%f fps=%f v=%d a=%d t=%d kfd=%d", h->id,
   444       h->version, h->i_width, h->i_height, h->d_aspect, h->d_fps,
   445       h->i_video_blocks, h->i_audio_blocks, h->i_text_blocks,
   446       h->i_keyframe_distance);
   447 
   448   *h_ret = h;
   449   gst_buffer_unref (buffer);
   450   return res;
   451 }
   452 
   453 static GstFlowReturn
   454 gst_nuv_demux_stream_header_data (GstNuvDemux * nuv)
   455 {
   456   GstFlowReturn res = gst_nuv_demux_header_load (nuv, &nuv->h);
   457 
   458   if (res == GST_FLOW_OK)
   459     nuv->state = GST_NUV_DEMUX_EXTRA_DATA;
   460   return res;
   461 }
   462 
   463 /*
   464  * Read NUV file tag
   465  */
   466 static GstFlowReturn
   467 gst_nuv_demux_stream_file_header (GstNuvDemux * nuv)
   468 {
   469   GstFlowReturn res = GST_FLOW_OK;
   470   GstBuffer *file_header = NULL;
   471 
   472   res = gst_nuv_demux_read_bytes (nuv, 12, FALSE, &file_header);
   473   if (res != GST_FLOW_OK) {
   474     return res;
   475   } else {
   476     if (strncmp ((gchar *) file_header->data, "MythTVVideo", 11) ||
   477         strncmp ((gchar *) file_header->data, "NuppelVideo", 11)) {
   478       nuv->state = GST_NUV_DEMUX_HEADER_DATA;
   479     } else {
   480       GST_DEBUG_OBJECT (nuv, "error parsing file header");
   481       nuv->state = GST_NUV_DEMUX_INVALID_DATA;
   482       res = GST_FLOW_ERROR;
   483     }
   484   }
   485   if (file_header != NULL) {
   486     gst_buffer_unref (file_header);
   487   }
   488   return res;
   489 }
   490 
   491 /* FrameHeaderLoad:
   492  */
   493 static GstFlowReturn
   494 gst_nuv_demux_frame_header_load (GstNuvDemux * nuv, nuv_frame_header ** h_ret)
   495 {
   496   unsigned char *data;
   497   nuv_frame_header *h;
   498   GstBuffer *buf = NULL;
   499 
   500   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 12, TRUE, &buf);
   501 
   502   if (res != GST_FLOW_OK) {
   503     if (buf != NULL) {
   504       gst_buffer_unref (buf);
   505     }
   506     return res;
   507   }
   508 
   509   h = g_new0 (nuv_frame_header, 1);
   510   data = buf->data;
   511 
   512   h->i_type = data[0];
   513   h->i_compression = data[1];
   514   h->i_keyframe = data[2];
   515   h->i_filters = data[3];
   516 
   517   h->i_timecode = GST_READ_UINT32_LE (&data[4]);
   518 
   519 
   520   h->i_length = GST_READ_UINT32_LE (&data[8]);
   521   GST_DEBUG_OBJECT (nuv, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%d l=%d",
   522       h->i_type,
   523       h->i_compression ? h->i_compression : ' ',
   524       h->i_keyframe ? h->i_keyframe : ' ',
   525       h->i_filters, h->i_timecode, h->i_length);
   526 
   527   *h_ret = h;
   528   gst_buffer_unref (buf);
   529   return GST_FLOW_OK;
   530 }
   531 
   532 static GstFlowReturn
   533 gst_nuv_demux_extended_header_load (GstNuvDemux * nuv,
   534     nuv_extended_header ** h_ret)
   535 {
   536   unsigned char *data;
   537   GstBuffer *buff = NULL;
   538   nuv_extended_header *h;
   539 
   540 
   541   GstFlowReturn res = gst_nuv_demux_read_bytes (nuv, 512, TRUE, &buff);
   542 
   543   if (res != GST_FLOW_OK) {
   544     if (buff != NULL) {
   545       gst_buffer_unref (buff);
   546     }
   547     return res;
   548   }
   549 
   550   h = g_new0 (nuv_extended_header, 1);
   551   data = buff->data;
   552   h->i_version = GST_READ_UINT32_LE (&data[0]);
   553   h->i_video_fcc = GST_MAKE_FOURCC (data[4], data[5], data[6], data[7]);
   554   h->i_audio_fcc = GST_MAKE_FOURCC (data[8], data[9], data[10], data[11]);
   555   h->i_audio_sample_rate = GST_READ_UINT32_LE (&data[12]);
   556   h->i_audio_bits_per_sample = GST_READ_UINT32_LE (&data[16]);
   557   h->i_audio_channels = GST_READ_UINT32_LE (&data[20]);
   558   h->i_audio_compression_ratio = GST_READ_UINT32_LE (&data[24]);
   559   h->i_audio_quality = GST_READ_UINT32_LE (&data[28]);
   560   h->i_rtjpeg_quality = GST_READ_UINT32_LE (&data[32]);
   561   h->i_rtjpeg_luma_filter = GST_READ_UINT32_LE (&data[36]);
   562   h->i_rtjpeg_chroma_filter = GST_READ_UINT32_LE (&data[40]);
   563   h->i_lavc_bitrate = GST_READ_UINT32_LE (&data[44]);
   564   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[48]);
   565   h->i_lavc_qmin = GST_READ_UINT32_LE (&data[52]);
   566   h->i_lavc_maxqdiff = GST_READ_UINT32_LE (&data[56]);
   567   h->i_seekable_offset = GST_READ_UINT64_LE (&data[60]);
   568   h->i_keyframe_adjust_offset = GST_READ_UINT64_LE (&data[68]);
   569 
   570   GST_DEBUG_OBJECT (nuv,
   571       "ex hdr: v=%d vffc=%4.4s afcc=%4.4s %dHz %dbits ach=%d acr=%d aq=%d"
   572       "rtjpeg q=%d lf=%d lc=%d lavc br=%d qmin=%d qmax=%d maxqdiff=%d seekableoff=%lld keyfao=%lld",
   573       h->i_version, (gchar *) & h->i_video_fcc, (gchar *) & h->i_audio_fcc,
   574       h->i_audio_sample_rate, h->i_audio_bits_per_sample, h->i_audio_channels,
   575       h->i_audio_compression_ratio, h->i_audio_quality, h->i_rtjpeg_quality,
   576       h->i_rtjpeg_luma_filter, h->i_rtjpeg_chroma_filter, h->i_lavc_bitrate,
   577       h->i_lavc_qmin, h->i_lavc_qmax, h->i_lavc_maxqdiff, h->i_seekable_offset,
   578       h->i_keyframe_adjust_offset);
   579 
   580   *h_ret = h;
   581   gst_buffer_unref (buff);
   582   return res;
   583 }
   584 
   585 static gboolean
   586 gst_nuv_demux_handle_src_event (GstPad * pad, GstEvent * event)
   587 {
   588   gst_event_unref (event);
   589   return FALSE;
   590 }
   591 
   592 
   593 static void
   594 gst_nuv_demux_create_pads (GstNuvDemux * nuv)
   595 {
   596   if (nuv->h->i_video_blocks != 0) {
   597     GstCaps *video_caps = NULL;
   598 
   599     nuv->src_video_pad =
   600         gst_pad_new_from_static_template (&video_src_template, "video_src");
   601 
   602     video_caps = gst_caps_new_simple ("video/x-divx",
   603         "divxversion", G_TYPE_INT, 4,
   604         "width", G_TYPE_INT, nuv->h->i_width,
   605         "height", G_TYPE_INT, nuv->h->i_height,
   606         "framerate", GST_TYPE_FRACTION, (gint) (nuv->h->d_fps * 1000.0f), 1000,
   607         "pixel-aspect-ratio", GST_TYPE_FRACTION,
   608         (gint) (nuv->h->d_aspect * 1000.0f), 1000, NULL);
   609 
   610     gst_pad_use_fixed_caps (nuv->src_video_pad);
   611     gst_pad_set_active (nuv->src_video_pad, TRUE);
   612     gst_pad_set_caps (nuv->src_video_pad, video_caps);
   613 
   614     gst_pad_set_event_function (nuv->src_video_pad,
   615         gst_nuv_demux_handle_src_event);
   616     gst_pad_set_active (nuv->src_video_pad, TRUE);
   617     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
   618 
   619     gst_caps_unref (video_caps);
   620   }
   621 
   622   if (nuv->h->i_audio_blocks != 0) {
   623     GstCaps *audio_caps = NULL;
   624 
   625     nuv->src_audio_pad =
   626         gst_pad_new_from_static_template (&audio_src_template, "audio_src");
   627 
   628     audio_caps = gst_caps_new_simple ("audio/mpeg",
   629         "rate", G_TYPE_INT, nuv->eh->i_audio_sample_rate,
   630         "format", GST_TYPE_FOURCC, nuv->eh->i_audio_fcc,
   631         "channels", G_TYPE_INT, nuv->eh->i_audio_channels,
   632         "mpegversion", G_TYPE_INT, nuv->eh->i_version, NULL);
   633 
   634     gst_pad_use_fixed_caps (nuv->src_audio_pad);
   635     gst_pad_set_active (nuv->src_audio_pad, TRUE);
   636     gst_pad_set_caps (nuv->src_audio_pad, audio_caps);
   637     gst_pad_set_active (nuv->src_audio_pad, TRUE);
   638     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
   639 
   640     gst_pad_set_event_function (nuv->src_audio_pad,
   641         gst_nuv_demux_handle_src_event);
   642 
   643     gst_caps_unref (audio_caps);
   644   }
   645 
   646   gst_element_no_more_pads (GST_ELEMENT (nuv));
   647 }
   648 
   649 static GstFlowReturn
   650 gst_nuv_demux_read_head_frame (GstNuvDemux * nuv)
   651 {
   652   GstFlowReturn ret = GST_FLOW_OK;
   653 
   654   ret = gst_nuv_demux_frame_header_load (nuv, &nuv->fh);
   655   if (ret != GST_FLOW_OK)
   656     return ret;
   657 
   658   nuv->state = GST_NUV_DEMUX_MOVI;
   659   return ret;
   660 }
   661 
   662 static GstFlowReturn
   663 gst_nuv_demux_stream_data (GstNuvDemux * nuv)
   664 {
   665   GstFlowReturn ret = GST_FLOW_OK;
   666   GstBuffer *buf = NULL;
   667   nuv_frame_header *h = nuv->fh;
   668 
   669   if (h->i_type == 'R')
   670     goto done;
   671     
   672    /* append the frame's header timecode field, and the actual offset */
   673   gst_nuv_demux_index_append( nuv, h->i_timecode, nuv->offset );
   674  
   675   if (h->i_length > 0) {
   676 	  ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf);
   677 	  if (ret != GST_FLOW_OK)
   678 		  return ret;
   679 
   680 	  /* search for a valid timecode in the indexes list (find the nearest valid timecode) */
   681 	  if ( h->i_timecode < 0 ) {
   682 		  /* convert this actual timecode to a valid index in the timecode's table */
   683 		  gint64 pos = gst_nuv_demux_index_convert_time( nuv, h->i_timecode );
   684 
   685 		  /* find the nearest empty frame slot */		  
   686 		  gint64 near_off = gst_nuv_demux_index_find_offset( nuv, pos );
   687 
   688 		  /* just get the timecode from the timecode's table */		  
   689 		  GST_BUFFER_TIMESTAMP (buf) = nuv->index_entries->idx[near_off].i_time * GST_MSECOND;
   690 	  } else {  
   691 		  GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND;
   692 	  }
   693   }
   694 
   695   switch (h->i_type) {
   696     case 'V':
   697     {
   698       if (h->i_length == 0)
   699         break;
   700 
   701       GST_BUFFER_OFFSET (buf) = nuv->video_offset;
   702       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_video_pad));
   703       ret = gst_pad_push (nuv->src_video_pad, buf);
   704       nuv->video_offset++;
   705       break;
   706     }
   707     case 'A':
   708     {
   709       if (h->i_length == 0)
   710         break;
   711 
   712       GST_BUFFER_OFFSET (buf) = nuv->audio_offset;
   713       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_audio_pad));
   714       ret = gst_pad_push (nuv->src_audio_pad, buf);
   715       nuv->audio_offset++;
   716       break;
   717     }
   718     case 'S':
   719     {
   720       switch (h->i_compression) {
   721         case 'V':
   722           gst_pad_push_event (nuv->src_video_pad,
   723               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1,
   724                   h->i_timecode));
   725           break;
   726         case 'A':
   727           gst_pad_push_event (nuv->src_audio_pad,
   728               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
   729           break;
   730         default:
   731           break;
   732       }
   733     }
   734     default:
   735       if (buf != NULL)
   736         gst_buffer_unref (buf);
   737 
   738       break;
   739   }
   740 
   741 done:
   742   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
   743   g_free (nuv->fh);
   744   nuv->fh = NULL;
   745   return ret;
   746 }
   747 
   748 static GstFlowReturn
   749 gst_nuv_demux_stream_mpeg_data (GstNuvDemux * nuv)
   750 {
   751   GstFlowReturn ret = GST_FLOW_OK;
   752 
   753   /* ffmpeg extra data */
   754   ret =
   755       gst_nuv_demux_read_bytes (nuv, nuv->mpeg_data_size, TRUE,
   756       &nuv->mpeg_buffer);
   757   if (ret != GST_FLOW_OK) {
   758     return GST_FLOW_ERROR;
   759   }
   760   GST_BUFFER_SIZE (nuv->mpeg_buffer) = nuv->mpeg_data_size;
   761   nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
   762   return ret;
   763 }
   764 
   765 static GstFlowReturn
   766 gst_nuv_demux_stream_extra_data (GstNuvDemux * nuv)
   767 {
   768   GstFlowReturn ret = GST_FLOW_OK;
   769 
   770   /* Load 'D' */
   771   nuv_frame_header *h;
   772 
   773   ret = gst_nuv_demux_frame_header_load (nuv, &h);
   774   if (ret != GST_FLOW_OK)
   775     return ret;
   776 
   777   if (h->i_type != 'D') {
   778     g_free (h);
   779     return GST_FLOW_ERROR;
   780   }
   781 
   782   if (h->i_length > 0) {
   783     if (h->i_compression == 'F') {
   784       nuv->state = GST_NUV_DEMUX_MPEG_DATA;
   785     } else {
   786       g_free (h);
   787       return GST_FLOW_ERROR;
   788     }
   789   } else {
   790     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER;
   791   }
   792 
   793   g_free (h);
   794   h = NULL;
   795   return ret;
   796 }
   797 
   798 static GstFlowReturn
   799 gst_nuv_demux_stream_extend_header_data (GstNuvDemux * nuv)
   800 {
   801   GstFlowReturn ret = GST_FLOW_OK;
   802 
   803   ret = gst_nuv_demux_extended_header_load (nuv, &nuv->eh);
   804   if (ret != GST_FLOW_OK)
   805     return ret;
   806 
   807   gst_nuv_demux_create_pads (nuv);
   808   nuv->state = GST_NUV_DEMUX_FRAME_HEADER;
   809   return ret;
   810 }
   811 
   812 static GstFlowReturn
   813 gst_nuv_demux_stream_extend_header (GstNuvDemux * nuv)
   814 {
   815   GstBuffer *buf = NULL;
   816   GstFlowReturn res = GST_FLOW_OK;
   817 
   818   res = gst_nuv_demux_read_bytes (nuv, 1, FALSE, &buf);
   819   if (res != GST_FLOW_OK) {
   820     if (buf != NULL) {
   821       gst_buffer_unref (buf);
   822     }
   823     return res;
   824   }
   825 
   826   if (buf->data[0] == 'X') {
   827     gst_buffer_unref (buf);
   828     buf = NULL;
   829     nuv_frame_header *h = NULL;
   830 
   831     res = gst_nuv_demux_frame_header_load (nuv, &h);
   832     if (res != GST_FLOW_OK)
   833       return res;
   834 
   835     if (h->i_length != 512) {
   836       g_free (h);
   837       return GST_FLOW_ERROR;
   838     }
   839     g_free (h);
   840     h = NULL;
   841     nuv->state = GST_NUV_DEMUX_EXTEND_HEADER_DATA;
   842   } else {
   843     nuv->state = GST_NUV_DEMUX_INVALID_DATA;
   844     g_object_unref (buf);
   845     GST_ELEMENT_WARNING (nuv, STREAM, FAILED,
   846         (_("incomplete NUV support")), ("incomplete NUV support"));
   847     return GST_FLOW_ERROR;
   848   }
   849   return res;
   850 }
   851 
   852 static GstFlowReturn
   853 gst_nuv_demux_play (GstPad * pad)
   854 {
   855   GstFlowReturn res = GST_FLOW_OK;
   856   GstNuvDemux *nuv = GST_NUV_DEMUX (GST_PAD_PARENT (pad));
   857 
   858   switch (nuv->state) {
   859     case GST_NUV_DEMUX_START:
   860       res = gst_nuv_demux_stream_file_header (nuv);
   861       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   862         goto pause;
   863       }
   864       if (nuv->state != GST_NUV_DEMUX_HEADER_DATA)
   865         break;
   866 
   867     case GST_NUV_DEMUX_HEADER_DATA:
   868       res = gst_nuv_demux_stream_header_data (nuv);
   869       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   870         goto pause;
   871       }
   872       if (nuv->state != GST_NUV_DEMUX_EXTRA_DATA)
   873         break;
   874 
   875     case GST_NUV_DEMUX_EXTRA_DATA:
   876       res = gst_nuv_demux_stream_extra_data (nuv);
   877       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   878         goto pause;
   879       }
   880       if (nuv->state != GST_NUV_DEMUX_MPEG_DATA)
   881         break;
   882 
   883     case GST_NUV_DEMUX_MPEG_DATA:
   884       res = gst_nuv_demux_stream_mpeg_data (nuv);
   885       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   886         goto pause;
   887       }
   888 
   889       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER)
   890         break;
   891 
   892     case GST_NUV_DEMUX_EXTEND_HEADER:
   893       res = gst_nuv_demux_stream_extend_header (nuv);
   894       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   895         goto pause;
   896       }
   897       if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER_DATA)
   898         break;
   899 
   900     case GST_NUV_DEMUX_EXTEND_HEADER_DATA:
   901       res = gst_nuv_demux_stream_extend_header_data (nuv);
   902       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   903         goto pause;
   904       }
   905 
   906       if (nuv->state != GST_NUV_DEMUX_FRAME_HEADER)
   907         break;
   908 
   909     case GST_NUV_DEMUX_FRAME_HEADER:
   910       res = gst_nuv_demux_read_head_frame (nuv);
   911       if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
   912         goto pause;
   913       }
   914       if (nuv->state != GST_NUV_DEMUX_MOVI)
   915         break;
   916 
   917     case GST_NUV_DEMUX_MOVI:
   918       res = gst_nuv_demux_stream_data (nuv);
   919       if ((res != GST_FLOW_OK) && (res != GST_FLOW_CUSTOM_ERROR)) {
   920         goto pause;
   921       }
   922       break;
   923     case GST_NUV_DEMUX_INVALID_DATA:
   924       goto pause;
   925       break;
   926     default:
   927       g_assert_not_reached ();
   928   }
   929 
   930   GST_DEBUG_OBJECT (nuv, "state: %d res:%s", nuv->state,
   931       gst_flow_get_name (res));
   932 
   933   return GST_FLOW_OK;
   934 
   935 pause:
   936   GST_LOG_OBJECT (nuv, "pausing task, reason %s", gst_flow_get_name (res));
   937   gst_pad_pause_task (nuv->sinkpad);
   938   if (GST_FLOW_IS_FATAL (res)) {
   939     GST_ELEMENT_ERROR (nuv, STREAM, FAILED,
   940         (_("Internal data stream error.")),
   941         ("streaming stopped, reason %s", gst_flow_get_name (res)));
   942 
   943     gst_nuv_demux_send_eos (nuv);
   944   }
   945   return res;
   946 }
   947 
   948 static void
   949 gst_nuv_demux_send_eos (GstNuvDemux * nuv)
   950 {
   951   gst_element_post_message (GST_ELEMENT (nuv),
   952       gst_message_new_segment_done (GST_OBJECT (nuv), GST_FORMAT_TIME, -1));
   953 
   954   if (nuv->src_video_pad)
   955     gst_pad_push_event (nuv->src_video_pad, gst_event_new_eos ());
   956   if (nuv->src_audio_pad)
   957     gst_pad_push_event (nuv->src_audio_pad, gst_event_new_eos ());
   958 }
   959 
   960 static GstFlowReturn
   961 gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move,
   962     GstBuffer ** buffer)
   963 {
   964   GstFlowReturn ret = GST_FLOW_OK;
   965 
   966   if (size == 0) {
   967     *buffer = gst_buffer_new ();
   968     return ret;
   969   }
   970 
   971   if (nuv->mode == 0) {
   972     ret = gst_pad_pull_range (nuv->sinkpad, nuv->offset, size, buffer);
   973     if (ret == GST_FLOW_OK) {
   974       if (move) {
   975         nuv->offset += size;
   976       }
   977       /* got eos */
   978     } else if (ret == GST_FLOW_UNEXPECTED) {
   979       gst_nuv_demux_send_eos (nuv);
   980       return GST_FLOW_WRONG_STATE;
   981     }
   982   } else {
   983     if (gst_adapter_available (nuv->adapter) < size)
   984       return GST_FLOW_ERROR_NO_DATA;
   985 
   986     if (move) {
   987       *buffer = gst_adapter_take_buffer (nuv->adapter, size);
   988     } else {
   989       guint8 *data = NULL;
   990 
   991       data = (guint8 *) gst_adapter_peek (nuv->adapter, size);
   992       *buffer = gst_buffer_new ();
   993       gst_buffer_set_data (*buffer, data, size);
   994     }
   995   }
   996   return ret;
   997 }
   998 
   999 static gboolean
  1000 gst_nuv_demux_sink_activate (GstPad * sinkpad)
  1001 {
  1002   gboolean res = TRUE;
  1003   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
  1004 
  1005   if (gst_pad_check_pull_range (sinkpad)) {
  1006     nuv->mode = 0;
  1007     nuv->adapter = NULL;
  1008     res = gst_pad_activate_pull (sinkpad, TRUE);
  1009   } else {
  1010     nuv->mode = 1;
  1011     nuv->adapter = gst_adapter_new ();
  1012     res = gst_pad_activate_push (sinkpad, TRUE);
  1013   }
  1014 
  1015   g_object_unref (nuv);
  1016   return res;
  1017 }
  1018 
  1019 static gboolean
  1020 gst_nuv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
  1021 {
  1022   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (sinkpad));
  1023 
  1024   if (active) {
  1025     gst_pad_start_task (sinkpad, (GstTaskFunction) gst_nuv_demux_loop, sinkpad);
  1026   } else {
  1027     gst_pad_stop_task (sinkpad);
  1028   }
  1029   gst_object_unref (nuv);
  1030 
  1031   return TRUE;
  1032 }
  1033 
  1034 static GstFlowReturn
  1035 gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf)
  1036 {
  1037   GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad));
  1038 
  1039   gst_adapter_push (nuv->adapter, buf);
  1040 
  1041   return gst_nuv_demux_play (pad);
  1042 }
  1043 
  1044 static void
  1045 gst_nuv_demux_loop (GstPad * pad)
  1046 {
  1047   gst_nuv_demux_play (pad);
  1048 }
  1049 
  1050 static void
  1051 gst_nuv_demux_reset (GstNuvDemux * nuv)
  1052 {
  1053   nuv->state = GST_NUV_DEMUX_START;
  1054   nuv->mode = 0;
  1055   nuv->offset = 0;
  1056   nuv->video_offset = 0;
  1057   nuv->audio_offset = 0;
  1058 
  1059   if (nuv->adapter != NULL)
  1060     gst_adapter_clear (nuv->adapter);
  1061 
  1062   if (nuv->mpeg_buffer != NULL) {
  1063     gst_buffer_unref (nuv->mpeg_buffer);
  1064     nuv->mpeg_buffer = NULL;
  1065   }
  1066 
  1067   g_free (nuv->h);
  1068   nuv->h = NULL;
  1069 
  1070   g_free (nuv->eh);
  1071   nuv->eh = NULL;
  1072 
  1073   g_free (nuv->fh);
  1074   nuv->fh = NULL;
  1075 }
  1076 
  1077 static void
  1078 gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv)
  1079 {
  1080   if (nuv->src_video_pad) {
  1081     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
  1082     nuv->src_video_pad = NULL;
  1083   }
  1084 
  1085   if (nuv->src_audio_pad) {
  1086     gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_audio_pad);
  1087     nuv->src_audio_pad = NULL;
  1088   }
  1089 }
  1090 
  1091 static GstStateChangeReturn
  1092 gst_nuv_demux_change_state (GstElement * element, GstStateChange transition)
  1093 {
  1094   GstStateChangeReturn ret;
  1095 
  1096   switch (transition) {
  1097     case GST_STATE_CHANGE_READY_TO_PAUSED:
  1098       break;
  1099     default:
  1100       break;
  1101   }
  1102 
  1103   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
  1104   if (ret == GST_STATE_CHANGE_FAILURE)
  1105     goto done;
  1106 
  1107   switch (transition) {
  1108     case GST_STATE_CHANGE_PAUSED_TO_READY:
  1109       gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (element));
  1110       gst_nuv_demux_reset (GST_NUV_DEMUX (element));
  1111       break;
  1112     default:
  1113       break;
  1114   }
  1115 
  1116 done:
  1117   return ret;
  1118 }
  1119 
  1120 static gboolean
  1121 plugin_init (GstPlugin * plugin)
  1122 {
  1123 #ifdef ENABLE_NLS
  1124   setlocale (LC_ALL, "");
  1125   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  1126 #endif /* ENABLE_NLS */
  1127 
  1128   if (!gst_element_register (plugin, "nuvdemux", GST_RANK_SECONDARY,
  1129           GST_TYPE_NUV_DEMUX)) {
  1130     return FALSE;
  1131   }
  1132   return TRUE;
  1133 }
  1134 
  1135 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
  1136     GST_VERSION_MINOR,
  1137     "nuvdemux",
  1138     "Demuxes and muxes audio and video",
  1139     plugin_init, VERSION, "LGPL", "NuvDemux", "")