diff -r 9638cccfa1fe -r 4476d11d6b9a gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c --- a/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c Wed Mar 21 21:35:20 2007 +0000 +++ b/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c Thu Mar 22 18:54:35 2007 +0000 @@ -141,6 +141,7 @@ S: length of packet correl */ } nuv_frame_header; + /* FIXME Not sure of this one */ typedef struct { @@ -160,11 +161,18 @@ gint i_lavc_qmin; gint i_lavc_qmax; gint i_lavc_maxqdiff; - gint64 i_seekable_offset; + gint64 i_seekable_offset; gint64 i_keyframe_adjust_offset; } nuv_extended_header; +typedef struct +{ + gint64 timecode; + gint64 offset; + +} frame_index_data; + typedef enum { GST_NUV_DEMUX_START, GST_NUV_DEMUX_HEADER_DATA, @@ -172,6 +180,7 @@ GST_NUV_DEMUX_MPEG_DATA, GST_NUV_DEMUX_EXTEND_HEADER, GST_NUV_DEMUX_EXTEND_HEADER_DATA, + GST_NUV_DEMUX_INDEX_CREATE, GST_NUV_DEMUX_FRAME_HEADER, GST_NUV_DEMUX_MOVI, GST_NUV_DEMUX_INVALID_DATA @@ -196,15 +205,13 @@ /* NUV decoding state */ GstNuvDemuxState state; - GstSegment segment; - guint64 last_update; guint64 offset; - guint64 streamer_offset; /* duration information */ - gint64 duration_bytes; - gint64 duration_time; - gint64 duration_average; + guint64 duration_bytes; + guint64 duration_time; + guint64 segment_stop; + guint64 segment_start; /* segment control info */ gboolean new_audio_segment; @@ -220,10 +227,12 @@ nuv_frame_header fh; /* anothers info */ + gint64 header_lengh; gint64 time_start; gint64 time_diff; gint64 time_qos; guint64 last_frame_time; + GSList *index; }; @@ -256,8 +265,12 @@ static gboolean gst_nuv_demux_sink_activate_push (GstPad * pad, gboolean active); static gboolean gst_nuv_demux_sink_activate (GstPad * sinkpad); -static gboolean gst_nuv_demux_sink_event (GstPad *pad, GstEvent *event); +//static gboolean gst_nuv_demux_sink_event (GstPad *pad, GstEvent *event); static gboolean gst_nuv_demux_srcpad_event (GstPad * pad, GstEvent * event); +static frame_index_data * gst_nuv_demux_do_seek_index (GstNuvDemux *nuv, gint64 seek_pos, + gint64 segment_stop, GstFormat format); + + static GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move, GstBuffer ** buffer); @@ -267,6 +280,8 @@ static void gst_nuv_demux_update_duration (GstNuvDemux *nuv, guint64 current_timestamp); static gint64 gst_nuv_demux_get_bytes_duration (GstNuvDemux *nuv); static gint64 gst_nuv_demux_get_time_duration (GstNuvDemux *nuv); +static void gst_nuv_demux_create_seek_index (GstNuvDemux * nuv); + #if (GST_VERSION_MINOR == 10) && (GST_VERSION_MICRO < 6) GstBuffer * gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes); @@ -375,8 +390,10 @@ gst_nuv_demux_sink_activate_push); gst_pad_set_chain_function (nuv->priv->sinkpad, GST_DEBUG_FUNCPTR (gst_nuv_demux_chain)); +/* gst_pad_set_event_function (nuv->priv->sinkpad, gst_nuv_demux_sink_event); +*/ gst_element_add_pad (GST_ELEMENT (nuv), nuv->priv->sinkpad); @@ -523,6 +540,7 @@ gst_buffer_unref (buf); buf = NULL; } + return res; } @@ -599,21 +617,16 @@ switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: if (GST_CLOCK_TIME_IS_VALID (nuv->priv->last_frame_time)) { - gst_query_set_position (query, GST_FORMAT_TIME, nuv->priv->last_frame_time); res = TRUE; } break; case GST_QUERY_DURATION: { - gint64 duration = 0; - duration = gst_nuv_demux_get_time_duration (nuv); - if (duration == GST_CLOCK_TIME_NONE) { - duration = nuv->priv->duration_average; - } - if (duration != GST_CLOCK_TIME_NONE) { - gst_query_set_duration (query, GST_FORMAT_TIME, duration); - res = TRUE; + g_debug ("QUERY DURATION %" G_GUINT64_FORMAT, nuv->priv->duration_time); + if (nuv->priv->duration_time == GST_CLOCK_TIME_NONE) { + gst_query_set_duration (query, GST_FORMAT_TIME, nuv->priv->duration_time); + res = TRUE; } } break; @@ -636,8 +649,9 @@ gst_pad_set_active (pad, TRUE); gst_pad_use_fixed_caps (pad); gst_element_add_pad (GST_ELEMENT (nuv), pad); - gst_pad_set_event_function (pad, - GST_DEBUG_FUNCPTR (gst_nuv_demux_srcpad_event)); + + gst_pad_set_event_function (pad, + gst_nuv_demux_srcpad_event); return pad; } @@ -721,6 +735,7 @@ case 'S': case 'R': case 'D': + case 'Q': valid = TRUE; break; default: @@ -744,14 +759,12 @@ } if (gst_nuv_demux_validate_header (&nuv->priv->fh) == TRUE) - valid = TRUE; + valid = TRUE; else - g_debug ("Invalid frame header"); + g_debug ("Invalid frame header"); } while (valid == FALSE); -// } while (gst_nuv_demux_validate_header (nuv->priv->fh)); - nuv->priv->state = GST_NUV_DEMUX_MOVI; return ret; } @@ -871,11 +884,6 @@ } if ((buf != NULL) && (pad != NULL)) { - /* update average time */ - nuv->priv->streamer_offset += h.i_length; - gst_segment_set_last_stop (&nuv->priv->segment, GST_FORMAT_TIME, timestamp); - gst_nuv_demux_update_duration (nuv, timestamp); - /* pushing the buffer */ gst_buffer_set_caps (buf, GST_PAD_CAPS (pad)); ret = gst_pad_push (pad, buf); @@ -971,7 +979,7 @@ return ret; gst_nuv_demux_create_pads (nuv); - nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; + nuv->priv->state = GST_NUV_DEMUX_INDEX_CREATE; return ret; } @@ -1012,6 +1020,38 @@ return res; } +static void +gst_nuv_demux_create_seek_index (GstNuvDemux * nuv) +{ + GstMessage *msg; + nuv_frame_header h; + g_debug ("CREATING INDEX"); + + while (gst_nuv_demux_frame_header_load (nuv, &h) == GST_FLOW_OK) { + if (h.i_keyframe == 0) { + //if (h.i_type == 'V') { + frame_index_data *f = g_new0 (frame_index_data, 1); + g_debug ("KEY FRAME AT %lld", nuv->priv->offset); + + f->offset = nuv->priv->offset - 12; + f->timecode = h.i_timecode * GST_MSECOND; + + nuv->priv->index = g_slist_append (nuv->priv->index, f); + } + if (h.i_type != 'R') { + nuv->priv->offset += h.i_length; + if (h.i_type == 'A' || h.i_type == 'V') + nuv->priv->duration_time = h.i_timecode * GST_MSECOND; + } + } + g_debug ("CREATING INDEX: DONE : DURATION Bytes/Sec: %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, nuv->priv->offset, nuv->priv->duration_time); + nuv->priv->duration_bytes = nuv->priv->offset; + nuv->priv->offset = nuv->priv->header_lengh; + + msg = gst_message_new_duration (GST_OBJECT (nuv), GST_FORMAT_TIME, nuv->priv->duration_time); + gst_element_post_message (GST_ELEMENT (nuv), msg); +} + static GstFlowReturn gst_nuv_demux_play (GstPad * pad) @@ -1060,6 +1100,15 @@ if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } + //store file header size + nuv->priv->header_lengh = nuv->priv->offset; + break; + + case GST_NUV_DEMUX_INDEX_CREATE: + if (nuv->priv->mode = NUV_PULL_MODE) { + gst_nuv_demux_create_seek_index (nuv); + } + nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; break; case GST_NUV_DEMUX_FRAME_HEADER: @@ -1123,15 +1172,13 @@ if (nuv->priv->mode == NUV_PULL_MODE) { ret = gst_pad_pull_range (nuv->priv->sinkpad, nuv->priv->offset, size, buffer); if (ret == GST_FLOW_OK) { - if (move) { - nuv->priv->offset += size; - } + if (move) { + nuv->priv->offset += size; + } /* got eos */ } else if (ret == GST_FLOW_UNEXPECTED) { - if (buffer != NULL) - gst_buffer_unref (buffer); - - gst_nuv_demux_send_eos (nuv); + g_debug ("GOT EOS"); + //gst_nuv_demux_send_eos (nuv); return GST_FLOW_WRONG_STATE; } } else { @@ -1213,6 +1260,41 @@ return TRUE; } +static frame_index_data * +gst_nuv_demux_do_seek_index (GstNuvDemux *nuv, gint64 seek_pos, + gint64 segment_stop, GstFormat format) +{ + GSList *l; + frame_index_data *ret = NULL; + + if (nuv->priv->index == NULL) { + return NULL; + } + + /* find keyframe closest to the requested position */ + for (l = nuv->priv->index; l != NULL; l = l->next) { + frame_index_data *f = (frame_index_data *) l->data; + gint64 pos = 0; + + if (format == GST_FORMAT_BYTES) { + pos = f->offset; + } else if (format == GST_FORMAT_TIME) { + pos = f->timecode; + } else { + return NULL; + } + + if (pos >= seek_pos) { + ret = f; + break; + } + if ((segment_stop != -1) && (pos > segment_stop)) + break; + } + + return ret; +} + static gboolean gst_nuv_demux_do_seek (GstNuvDemux *nuv, GstEvent * event) { @@ -1224,57 +1306,150 @@ GstSeekType stop_type; gint64 stop; gboolean flush; + frame_index_data *entry; + gint64 segment_start; + gint64 segment_stop; + GstEvent *newsegment_event; + g_debug ("DEMUX SEEK"); gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); - if (format == GST_FORMAT_BYTES && - nuv->priv->state > GST_NUV_DEMUX_EXTEND_HEADER_DATA && - gst_pad_is_linked (nuv->priv->sinkpad)) { +/* + if (format == GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (nuv, "Can only seek on BYTES"); + return FALSE; + } +*/ - flush = flags & GST_SEEK_FLAG_FLUSH; - if (flush) { - gst_pad_push_event (nuv->priv->sinkpad, gst_event_new_flush_start ()); - - if (nuv->priv->src_video_pad != NULL) { - gst_pad_push_event (nuv->priv->src_video_pad, gst_event_new_flush_start ()); - } - - if (nuv->priv->src_audio_pad != NULL) { - gst_pad_push_event (nuv->priv->src_audio_pad, gst_event_new_flush_start ()); - } + if (rate <= 0.0) { + GST_DEBUG_OBJECT (nuv, "Can only seek with positive rate"); + return FALSE; + } + + if (cur_type == GST_SEEK_TYPE_SET) { + GST_OBJECT_LOCK (nuv); + if (gst_nuv_demux_do_seek_index (nuv, cur, -1, format) == NULL) { + GST_DEBUG_OBJECT (nuv, "No matching seek entry in index"); + GST_OBJECT_UNLOCK (nuv); + return FALSE; } - else { - gst_pad_pause_task (nuv->priv->sinkpad); + GST_OBJECT_UNLOCK (nuv); + } + flush = !!(flags & GST_SEEK_FLAG_FLUSH); + + if (flush) { + gst_pad_push_event (nuv->priv->sinkpad, gst_event_new_flush_start ()); + if (nuv->priv->src_video_pad != NULL) { + gst_pad_push_event (nuv->priv->src_video_pad, gst_event_new_flush_start ()); } - GST_PAD_STREAM_LOCK (nuv->priv->sinkpad); + if (nuv->priv->src_audio_pad != NULL) { + gst_pad_push_event (nuv->priv->src_audio_pad, gst_event_new_flush_start ()); + } + } + else { + gst_pad_pause_task (nuv->priv->sinkpad); + } - g_debug ("offset %ld, cur %ld", nuv->priv->offset, cur); + GST_PAD_STREAM_LOCK (nuv->priv->sinkpad); + GST_OBJECT_LOCK (nuv); - nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; - if (flush) { - gst_pad_push_event (nuv->priv->sinkpad, gst_event_new_flush_stop ()); + if (cur == GST_CLOCK_TIME_NONE) + cur = 0; - if (nuv->priv->src_video_pad != NULL) { - gst_pad_push_event (nuv->priv->src_video_pad, gst_event_new_flush_stop ()); - } - if (nuv->priv->src_audio_pad != NULL) { - gst_pad_push_event (nuv->priv->src_audio_pad, gst_event_new_flush_stop ()); - } +// if (stop == GST_CLOCK_TIME_NONE) +// stop = nuv->priv->duration_time; + + if (cur_type == GST_SEEK_TYPE_SET) + segment_start = cur; + else if (cur_type == GST_SEEK_TYPE_CUR) + segment_start = nuv->priv->segment_start + cur; + else + segment_start = nuv->priv->segment_start; + + if (stop_type == GST_SEEK_TYPE_SET) + segment_stop = stop; + else if (stop_type == GST_SEEK_TYPE_CUR) + segment_stop = nuv->priv->segment_stop + stop; + else + segment_stop = nuv->priv->segment_stop; + +// segment_start = CLAMP (segment_start, 0, nuv->priv->duration_bytes); +// segment_stop = CLAMP (segment_stop, 0, nuv->priv->duration_bytes); + + entry = gst_nuv_demux_do_seek_index (nuv, segment_start, + segment_stop, format); + + if (entry == NULL) { + GST_DEBUG_OBJECT (nuv, "No matching seek entry in index"); + goto seek_error; + } + + g_debug ("found frame at %lld", entry->offset); + nuv->priv->offset = entry->offset; + + segment_start = entry->timecode; + + nuv->priv->segment_start = segment_start; + nuv->priv->segment_stop = segment_stop; + + GST_OBJECT_UNLOCK (nuv); + +/* + { + GstMessage *msg; + msg = gst_message_new_segment_start (GST_OBJECT (nuv), GST_FORMAT_TIME, + nuv->priv->segment_start); + + gst_element_post_message (GST_ELEMENT (nuv), msg); + } +*/ + + +// newsegment_event = gst_event_new_new_segment (FALSE, rate, +// GST_FORMAT_TIME, segment_start, segment_stop, segment_start); + + + if (flush) { + gst_pad_push_event (nuv->priv->sinkpad, gst_event_new_flush_stop ()); + if (nuv->priv->src_video_pad != NULL) { + gst_pad_push_event (nuv->priv->src_video_pad, gst_event_new_flush_stop ()); } - g_debug ("STARTING TASK AGAIN"); - gst_pad_start_task (nuv->priv->sinkpad, (GstTaskFunction) gst_nuv_demux_loop, - nuv->priv->sinkpad); + if (nuv->priv->src_audio_pad != NULL) { + gst_pad_push_event (nuv->priv->src_audio_pad, gst_event_new_flush_stop ()); + } + } - GST_PAD_STREAM_UNLOCK (nuv->priv->sinkpad); - return TRUE; +/* + + if (nuv->priv->src_video_pad != NULL) { + gst_pad_push_event (nuv->priv->src_video_pad, newsegment_event); } + if (nuv->priv->src_audio_pad != NULL) { + gst_pad_push_event (nuv->priv->src_audio_pad, newsegment_event); + } + +*/ + + g_debug ("STARTING TASK AGAIN"); + nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; + gst_pad_start_task (nuv->priv->sinkpad, (GstTaskFunction) gst_nuv_demux_loop, + nuv->priv->sinkpad); + + GST_PAD_STREAM_UNLOCK (nuv->priv->sinkpad); + return TRUE; + +seek_error: + GST_DEBUG_OBJECT (nuv, "Got a seek error"); + GST_OBJECT_UNLOCK (nuv); + GST_PAD_STREAM_UNLOCK (nuv->priv->sinkpad); return FALSE; + } static gboolean @@ -1287,60 +1462,14 @@ switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: + g_debug ("SEEK"); res = gst_nuv_demux_do_seek (nuv, event); break; default: res = FALSE; break; } - return res; -} - -static gboolean -gst_nuv_demux_sink_event (GstPad *pad, GstEvent *event) -{ - gboolean res = FALSE; - GstNuvDemux *nuv; - - nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - gdouble rate; - GstFormat format; - GstSeekFlags flags; - GstSeekType cur_type; - gint64 cur; - GstSeekType stop_type; - gint64 stop; - - gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); - GST_DEBUG_OBJECT (nuv, "got seek, start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, cur, stop); - g_debug ("got seek, start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, cur, stop); - res = gst_pad_event_default (pad, event); - break; - } - case GST_EVENT_NEWSEGMENT: - { - gint64 start, stop, time; - gdouble rate; - GstFormat format; - gboolean update; - - gst_event_parse_new_segment (event, &update, &rate, &format, &start, &stop, &time); - GST_DEBUG_OBJECT (nuv, "got newsegment, start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, start, stop); - g_debug ("got newsegment, start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, start, stop); - //nuv->priv->duration_bytes = stop - start; - res = gst_pad_event_default (pad, event); - break; - } - case GST_EVENT_EOS: - default: - res = gst_pad_event_default (pad, event); - break; - } - + gst_object_unref (nuv); return res; } @@ -1377,19 +1506,33 @@ } static void +gst_nuv_demux_index_free (gpointer data, gpointer user_data) +{ + g_free (data); +} + +static void gst_nuv_demux_reset (GstNuvDemux * nuv) { + g_debug ("RESET"); nuv->priv->more_data = FALSE; nuv->priv->state = GST_NUV_DEMUX_START; nuv->priv->mode = NUV_PUSH_MODE; nuv->priv->offset = 0; - nuv->priv->streamer_offset = 0; nuv->priv->time_start = 0; nuv->priv->time_qos = GST_CLOCK_TIME_NONE; nuv->priv->duration_bytes = GST_CLOCK_TIME_NONE; nuv->priv->duration_time = GST_CLOCK_TIME_NONE; - nuv->priv->duration_average = GST_CLOCK_TIME_NONE; - gst_segment_init (&nuv->priv->segment, GST_FORMAT_TIME); + nuv->priv->last_video_return = GST_FLOW_OK; + nuv->priv->last_audio_return = GST_FLOW_OK; + nuv->priv->header_lengh = 0; + nuv->priv->segment_stop = GST_CLOCK_TIME_NONE; + nuv->priv->segment_start = GST_CLOCK_TIME_NONE; + + //clear index list + g_slist_foreach (nuv->priv->index, gst_nuv_demux_index_free, NULL); + g_slist_free (nuv->priv->index); + nuv->priv->index = NULL; gst_adapter_clear (nuv->priv->adapter); @@ -1435,7 +1578,7 @@ switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - //case GST_STATE_CHANGE_READY_TO_NULL: + case GST_STATE_CHANGE_READY_TO_NULL: g_debug ("PAUSED_TO_READY"); gst_nuv_demux_reset (GST_NUV_DEMUX (element)); gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (element)); @@ -1448,76 +1591,6 @@ return ret; } -static void -gst_nuv_demux_update_duration (GstNuvDemux *nuv, guint64 current_timestamp) -{ - guint64 interval = 0; - - if (gst_nuv_demux_get_time_duration (nuv) != GST_CLOCK_TIME_NONE) - return; - - interval = current_timestamp - nuv->priv->last_update; - - if (interval > (10 * GST_SECOND)) { - GstMessage* msg = NULL; - gint64 average = 0; - gint64 duration_bytes = gst_nuv_demux_get_bytes_duration (nuv); - - if (duration_bytes == GST_CLOCK_TIME_NONE) { - return; - } - - interval = gst_util_uint64_scale (1, current_timestamp - nuv->priv->time_start, GST_SECOND); - if (interval > 0) { - average = gst_util_uint64_scale (1 , nuv->priv->streamer_offset, interval); - } - if (average > 0) { - nuv->priv->duration_average = gst_util_uint64_scale (GST_SECOND, duration_bytes, average); - } - - nuv->priv->last_update = current_timestamp; - msg = gst_message_new_duration (GST_OBJECT (nuv), GST_FORMAT_TIME, nuv->priv->duration_average); - gst_element_post_message (GST_ELEMENT (nuv), msg); - GST_DEBUG_OBJECT (nuv, "New Duration Average %"G_GUINT64_FORMAT, nuv->priv->duration_average); - g_debug ("New Duration Average %"G_GUINT64_FORMAT, nuv->priv->duration_average); - } -} - -static gint64 -gst_nuv_demux_get_bytes_duration (GstNuvDemux *nuv) -{ - if (nuv->priv->duration_bytes == GST_CLOCK_TIME_NONE) { - GstPad *peer = gst_pad_get_peer (nuv->priv->sinkpad); - GstQuery *query = gst_query_new_duration (GST_FORMAT_BYTES); - if (gst_pad_query (peer, query)) { - gint64 duration; - - gst_query_parse_duration (query, NULL, &duration); - nuv->priv->duration_bytes = duration; - } - gst_object_unref (peer); - gst_query_unref (query); - } - return nuv->priv->duration_bytes; -} - -static gint64 -gst_nuv_demux_get_time_duration (GstNuvDemux *nuv) -{ - if (nuv->priv->duration_time == GST_CLOCK_TIME_NONE) { - GstPad *peer = gst_pad_get_peer (nuv->priv->sinkpad); - GstQuery *query = gst_query_new_duration (GST_FORMAT_TIME); - if (gst_pad_query (peer, query)) { - gint64 duration; - gst_query_parse_duration (query, NULL, &duration); - nuv->priv->duration_time = duration; - } - gst_object_unref (peer); - gst_query_unref (query); - } - return nuv->priv->duration_time; -} - #if (GST_VERSION_MINOR == 10) && (GST_VERSION_MICRO < 6) GstBuffer * gst_adapter_take_buffer (GstAdapter * adapter, guint nbytes)