diff -r 8bff300ebf50 -r 24be83eaef89 gst-plugins-nuvdemux/src/gstnuvdemux.c --- a/gst-plugins-nuvdemux/src/gstnuvdemux.c Tue Oct 24 15:02:26 2006 +0100 +++ b/gst-plugins-nuvdemux/src/gstnuvdemux.c Thu Oct 26 18:55:14 2006 +0100 @@ -52,8 +52,7 @@ #include #include -//#include "gst/gst-i18n-plugin.h" -#include +#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;