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