# HG changeset patch
# User rosfran
# Date 1161885314 -3600
# Node ID 24be83eaef891408dceface921c5d05fc9b4e805
# Parent  e5dd2fbecd6495e190eaa047311dcc5316c28959
[svn r57] Fixed problem with the audio frame timestamps.

diff -r e5dd2fbecd64 -r 24be83eaef89 gst-plugins-nuvdemux/src/gstnuvdemux.c
--- a/gst-plugins-nuvdemux/src/gstnuvdemux.c	Thu Oct 26 15:28:10 2006 +0100
+++ b/gst-plugins-nuvdemux/src/gstnuvdemux.c	Thu Oct 26 18:55:14 2006 +0100
@@ -52,8 +52,7 @@
 #include <gst/gstplugin.h>
 #include <string.h>
 
-//#include "gst/gst-i18n-plugin.h"
-#include <glib/gi18n.h>
+#include "gst/gst-i18n-plugin.h"
 #include "gstnuvdemux.h"
 
 GST_DEBUG_CATEGORY_STATIC (nuvdemux_debug);
@@ -89,6 +88,15 @@
     GST_STATIC_CAPS_ANY);
 
 /* NUV Demux indexes init/dispose callers */
+static void gst_nuv_demux_index_init( nuv_demux_index *p_idx );
+static void gst_nuv_demux_index_clean( nuv_demux_index *p_idx );
+
+/* NUV Demux indexes manipulation functions */
+static gint64 gst_nuv_demux_index_find_offset( GstNuvDemux *nuv, gint64 i_offset );
+static void gst_nuv_demux_index_append( GstNuvDemux *nuv, gint64 i_time, gint64 i_offset );
+static gint64 gst_nuv_demux_index_convert_time( GstNuvDemux *nuv, gint64 i_time );
+
+/* NUV Demux plug-in time-line functions */
 static void gst_nuv_demux_finalize (GObject * object);
 static GstStateChangeReturn gst_nuv_demux_change_state (GstElement * element,
     GstStateChange transition);
@@ -129,6 +137,17 @@
 #define READ_DOUBLE_FROM_LE(d) *((gdouble* ) (d))
 #endif /* G_BYTE_ORDER != G_BIG_ENDIAN */
 
+static gint32 
+gst_nuv_demux_getdwle (void const * _p)
+{
+    guint8 * p = (guint8 *)_p;
+	g_print( "[%s] shifting bits: {%d, %d, %d, %d}\n", __FUNCTION__, 
+			  ((guint32)p[3] << 24), ((guint32)p[2] << 16),
+              ((guint32)p[1] << 8), p[0] );
+    return ( ((guint32)p[3] << 24) | ((guint32)p[2] << 16)
+              | ((guint32)p[1] << 8) | p[0] );
+    //return ( (gint)p[3] | (gint)p[2] | (gint)p[1] | p[0] );
+}
 
 static void
 gst_nuv_demux_base_init (gpointer klass)
@@ -177,6 +196,8 @@
   gst_pad_set_event_function (nuv->sinkpad, gst_nuv_demux_handle_sink_event);
 
   gst_element_add_pad (GST_ELEMENT (nuv), nuv->sinkpad);
+  
+  gst_nuv_demux_index_init( nuv->index_entries );
 
   nuv->adapter = NULL;
   nuv->mpeg_buffer = NULL;
@@ -194,6 +215,11 @@
   if (nuv->mpeg_buffer != NULL) {
     gst_buffer_unref (nuv->mpeg_buffer);
   }
+  
+  if ( nuv->index_entries != NULL ) {
+  	gst_nuv_demux_index_clean( nuv->index_entries );
+  	nuv->index_entries = NULL;
+  }
 
   gst_nuv_demux_destoy_src_pad (nuv);
   gst_nuv_demux_reset (nuv);
@@ -203,8 +229,178 @@
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 /*****************************************************************************
- * Utils fucntions
+ * Indexes (timecode offset conversion) functions
+ *****************************************************************************/
+
+static void 
+gst_nuv_demux_index_init( nuv_demux_index *p_idx )
+{
+		p_idx = g_new0( nuv_demux_index, 1 );
+    p_idx->i_idx = 0;
+    p_idx->i_idx_max = 0;
+    p_idx->idx = g_new0( nuv_demux_index_entry, DEMUX_INDEX_SIZE_MAX );
+}
+
+static void 
+gst_nuv_demux_index_clean( nuv_demux_index *p_idx )
+{
+    if( p_idx->idx )
+    {
+        g_free( p_idx->idx );
+    }
+}
+
+static void 
+gst_nuv_demux_index_append( GstNuvDemux *nuv, gint64 i_time, gint64 i_offset )
+{
+	nuv_demux_index *p_idx = nuv->index_entries;
+	
+	if ( p_idx == NULL || p_idx->idx == NULL )
+		return;
+
+    /* Be sure to append new entry (we don't insert point) */
+    if(  p_idx != NULL && p_idx->i_idx > 0 && p_idx->idx[p_idx->i_idx-1].i_time >= i_time )
+        return;
+
+    /* */
+    if( p_idx->i_idx >= p_idx->i_idx_max )
+    {
+        if( p_idx->i_idx >= DEMUX_INDEX_SIZE_MAX )
+        {
+            /* Avoid too big index */
+            const gint64 i_length = p_idx->idx[p_idx->i_idx-1].i_time -
+                                                        p_idx->idx[0].i_time;
+            const gint i_count = DEMUX_INDEX_SIZE_MAX/2;
+            gint i, j;
+
+            /* We try to reduce the resolution of the index by a factor 2 */
+            for( i = 1, j = 1; i < p_idx->i_idx; i++ )
+            {
+                if( p_idx->idx[i].i_time < j * i_length / i_count )
+                    continue;
+
+                p_idx->idx[j++] = p_idx->idx[i];
+            }
+            p_idx->i_idx = j;
+
+            if( p_idx->i_idx > 3 * DEMUX_INDEX_SIZE_MAX / 4 )
+            {
+                /* We haven't created enough space
+                 * (This method won't create a good index but work for sure) */
+                for( i = 0; i < p_idx->i_idx/2; i++ )
+                    p_idx->idx[i] = p_idx->idx[2*i];
+                p_idx->i_idx /= 2;
+            }
+        }
+        else
+        {
+            p_idx->i_idx_max += 1000;
+            p_idx->idx = g_realloc( p_idx->idx,
+                                  p_idx->i_idx_max*sizeof(nuv_demux_index_entry));
+        }
+    }
+
+    /* */
+    p_idx->idx[p_idx->i_idx].i_time = i_time;
+    p_idx->idx[p_idx->i_idx].i_offset = i_offset;
+
+    p_idx->i_idx++;
+}
+
+static gint64 
+gst_nuv_demux_index_convert_time( GstNuvDemux *nuv, gint64 i_time )
+{
+	nuv_demux_index *p_idx = nuv->index_entries;
+	
+	g_return_val_if_fail( p_idx != NULL, i_time );
+	
+    gint i_min = 0;
+    gint i_max = p_idx->i_idx-1;
+
+    /* Empty index */
+    if( p_idx->i_idx <= 0 )
+        return -1;
+
+    /* Special border case */
+    if( i_time <= p_idx->idx[0].i_time )
+        return p_idx->idx[0].i_offset;
+    if( i_time >= p_idx->idx[i_max].i_time )
+        return p_idx->idx[i_max].i_offset;
+
+    /* Dicho */
+    for( ;; )
+    {
+        gint i_med;
+
+        if( i_max - i_min <= 1 )
+            break;
+
+        i_med = (i_min+i_max)/2;
+        if( p_idx->idx[i_med].i_time < i_time )
+            i_min = i_med;
+        else if( p_idx->idx[i_med].i_time > i_time )
+            i_max = i_med;
+        else
+            return p_idx->idx[i_med].i_offset;
+    }
+
+    /* return nearest in time */
+    if( i_time - p_idx->idx[i_min].i_time < p_idx->idx[i_max].i_time - i_time )
+        return p_idx->idx[i_min].i_offset;
+    else
+        return p_idx->idx[i_max].i_offset;
+}
+
+static gint64
+gst_nuv_demux_index_find_offset( GstNuvDemux *nuv, gint64 i_offset )
+{
+	nuv_demux_index *p_idx = nuv->index_entries;
+	
+	g_return_val_if_fail( p_idx != NULL, i_offset );
+	
+    gint i_min = 0;
+    gint i_max = p_idx->i_idx-1;
+
+    /* Empty index */
+    if( p_idx->i_idx <= 0 )
+        return -1;
+
+    /* Special border case */
+    if( i_offset <= p_idx->idx[0].i_offset )
+        return p_idx->idx[0].i_offset;
+    if( i_offset == p_idx->idx[i_max].i_offset )
+        return p_idx->idx[i_max].i_offset;
+    if( i_offset > p_idx->idx[i_max].i_offset )
+        return -1;
+
+    /* Dicho */
+    for( ;; )
+    {
+        gint i_med;
+
+        if ( i_max - i_min <= 1 )
+            break;
+
+        i_med = (i_min+i_max)/2;
+        if( p_idx->idx[i_med].i_offset < i_offset )
+            i_min = i_med;
+        else if( p_idx->idx[i_med].i_offset > i_offset )
+            i_max = i_med;
+        else
+            return p_idx->idx[i_med].i_offset;
+    }
+
+    /* return nearest */
+    if( i_offset - p_idx->idx[i_min].i_offset < p_idx->idx[i_max].i_offset - i_offset )
+        return p_idx->idx[i_min].i_offset;
+    else
+        return p_idx->idx[i_max].i_offset;
+}
+
+/*****************************************************************************
+ * Utils functions
  *****************************************************************************/
 
 static gboolean
@@ -328,9 +524,9 @@
   h->i_keyframe = data[2];
   h->i_filters = data[3];
 
-  h->i_timecode = GST_READ_UINT32_LE (&data[4]);
+  h->i_timecode = gst_nuv_demux_getdwle( &data[4] ); //GST_READ_UINT32_LE (&data[4]);
   h->i_length = GST_READ_UINT32_LE (&data[8]);
-  GST_DEBUG_OBJECT (nuv, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%u l=%u",
+  GST_DEBUG_OBJECT (nuv, "frame hdr: t=%c c=%c k=%d f=0x%x timecode=%d l=%d",
       h->i_type,
       h->i_compression ? h->i_compression : ' ',
       h->i_keyframe ? h->i_keyframe : ' ',
@@ -427,6 +623,7 @@
         gst_nuv_demux_handle_src_event);
     gst_pad_set_active (nuv->src_video_pad, TRUE);
     gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_video_pad);
+
     gst_caps_unref (video_caps);
   }
 
@@ -450,6 +647,7 @@
 
     gst_pad_set_event_function (nuv->src_audio_pad,
         gst_nuv_demux_handle_src_event);
+
     gst_caps_unref (audio_caps);
   }
 
@@ -478,17 +676,36 @@
 
   if (h->i_type == 'R')
     goto done;
-
-  ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf);
-  if (ret != GST_FLOW_OK) {
-    return ret;
+    
+	/* append the frame's header timecode field, and the actual offset */
+  gst_nuv_demux_index_append( nuv, h->i_timecode, nuv->offset );
+  
+  if (h->i_length > 0) {
+    ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf);
+    if (ret != GST_FLOW_OK)
+      return ret;
+      
+    /* search for a valid timecode in the indexes list (find the nearest valid timecode) */
+	  if ( h->i_timecode < 0 ) {
+	  	/* convert this actual timecode to a valid index in the timecode's table */
+		  gint64 pos = gst_nuv_demux_index_convert_time( nuv, h->i_timecode );
+		  
+		  /* find the nearest empty frame slot */		  
+		  gint64 near_off = gst_nuv_demux_index_find_offset( nuv, pos );
+		  
+		  /* just get the timecode from the timecode's table */		  
+		  GST_BUFFER_TIMESTAMP (buf) = nuv->index_entries->idx[near_off].i_time * GST_MSECOND;
+	  } else {  
+	  	GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND;
+	  }
   }
 
-  GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND;
-
   switch (h->i_type) {
     case 'V':
     {
+      if (h->i_length == 0)
+        break;
+
       GST_BUFFER_OFFSET (buf) = nuv->video_offset;
       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_video_pad));
       ret = gst_pad_push (nuv->src_video_pad, buf);
@@ -497,6 +714,9 @@
     }
     case 'A':
     {
+      if (h->i_length == 0)
+        break;
+
       GST_BUFFER_OFFSET (buf) = nuv->audio_offset;
       gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_audio_pad));
       ret = gst_pad_push (nuv->src_audio_pad, buf);
@@ -508,18 +728,22 @@
       switch (h->i_compression) {
         case 'V':
           gst_pad_push_event (nuv->src_video_pad,
-              gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
+              gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1,
+                  h->i_timecode));
           break;
         case 'A':
           gst_pad_push_event (nuv->src_audio_pad,
               gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0));
-
           break;
         default:
           break;
       }
+    }
+    default:
+      if (buf != NULL)
+        gst_buffer_unref (buf);
+
       break;
-    }
   }
 
 done:
@@ -700,7 +924,7 @@
 
     case GST_NUV_DEMUX_MOVI:
       res = gst_nuv_demux_stream_data (nuv);
-      if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
+      if ((res != GST_FLOW_OK) && (res != GST_FLOW_CUSTOM_ERROR)) {
         goto pause;
       }
       break;
@@ -768,10 +992,7 @@
       return GST_FLOW_ERROR_NO_DATA;
 
     if (move) {
-      guint8 *data = NULL;
-      data = (guint8 *) gst_adapter_take (nuv->adapter, size);
-      *buffer = gst_buffer_new ();
-      gst_buffer_set_data (*buffer, data, size);
+      *buffer = gst_adapter_take_buffer (nuv->adapter, size);
     } else {
       guint8 *data = NULL;
 
diff -r e5dd2fbecd64 -r 24be83eaef89 gst-plugins-nuvdemux/src/gstnuvdemux.h
--- a/gst-plugins-nuvdemux/src/gstnuvdemux.h	Thu Oct 26 15:28:10 2006 +0100
+++ b/gst-plugins-nuvdemux/src/gstnuvdemux.h	Thu Oct 26 18:55:14 2006 +0100
@@ -38,6 +38,23 @@
 #define GST_IS_NUV_DEMUX_CLASS(klass) \
   (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_NUV_DEMUX))
 
+#define DEMUX_INDEX_SIZE_MAX (100000)
+
+/* Indexes (timecodes/offsets) conversion structures */
+typedef struct
+{
+    gint64 i_time;
+    gint64 i_offset;
+
+} nuv_demux_index_entry;
+
+typedef struct
+{
+    gint i_idx;
+    gint i_idx_max;
+
+    nuv_demux_index_entry *idx;
+} nuv_demux_index;
 
 /* */
 typedef struct
@@ -92,9 +109,9 @@
                            0x02: gauss 5 pixel (8,1,1,1,1)/12
                            0x04: cartoon filter */
 
-    guint32 i_timecode;     /* ms */
+    gint32 i_timecode;     /* ms */
 
-    guint32 i_length;       /* V,A,T: length of following data
+    gint i_length;       /* V,A,T: length of following data
                            S: length of packet correl */
 } nuv_frame_header;
 
@@ -160,6 +177,13 @@
   nuv_header *h;
   nuv_extended_header *eh;
   nuv_frame_header *fh;
+  
+  /* index */
+  nuv_demux_index *index_entries;
+  guint          index_size;
+  guint64        index_offset;
+  guint          current_entry;
+
 } GstNuvDemux;
 
 typedef struct _GstNuvDemuxClass {