# HG changeset patch # User renatofilho # Date 1163769528 0 # Node ID 6b1e210c250abbf41950faa4e8f9275a67cdeeeb # Parent f6a9705509a1381c4cb673505bdf67fc846bb2b3 [svn r92] code review diff -r f6a9705509a1 -r 6b1e210c250a gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c --- a/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c Fri Nov 17 13:09:12 2006 +0000 +++ b/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.c Fri Nov 17 13:18:48 2006 +0000 @@ -56,11 +56,12 @@ #include "glib/gi18n.h" #include "gstnuvdemux.h" +#define GST_NUV_DEMUX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_NUV_DEMUX, GstNuvDemuxPrivate)) + GST_DEBUG_CATEGORY_STATIC (nuvdemux_debug); #define GST_CAT_DEFAULT nuvdemux_debug +#define GST_FLOW_ERROR_NO_DATA -101 - -#define GST_FLOW_ERROR_NO_DATA -101 enum { NUV_PUSH_MODE = 0, @@ -76,6 +77,155 @@ "Renato Araujo Oliveira Filho ," "Rosfran Borges "); + +/* file header */ +typedef struct +{ + gchar id[12]; /* "NuppelVideo\0" or "MythTVVideo\0" */ + gchar version[5]; /* "x.xx\0" */ + + gint i_width; + gint i_height; + gint i_width_desired; + gint i_height_desired; + + gchar i_mode; /* P progressive, I interlaced */ + + gdouble d_aspect; /* 1.0 squared pixel */ + gdouble d_fps; + //fps num/denom + gint i_fpsn; + gint i_fpsd; + + gint i_video_blocks; /* 0 no video, -1 unknown */ + gint i_audio_blocks; + gint i_text_blocks; + + gint i_keyframe_distance; + +} nuv_header; + +/* frame header */ +typedef struct +{ + gchar i_type; /* A: audio, V: video, S: sync; T: test + R: Seekpoint (string:RTjjjjjjjj) + D: Extra data for codec */ + gchar i_compression; /* V: 0 uncompressed + 1 RTJpeg + 2 RTJpeg+lzo + N black frame + L copy last + A: 0 uncompressed (44100 1-bits, 2ch) + 1 lzo + 2 layer 2 + 3 layer 3 + F flac + S shorten + N null frame loudless + L copy last + S: B audio and vdeo sync point + A audio sync info (timecode == effective + dsp frequency*100) + V next video sync (timecode == next video + frame num) + S audio,video,text correlation */ + gchar i_keyframe; /* 0 keyframe, else no no key frame */ + guint8 i_filters; /* 0x01: gauss 5 pixel (8,2,2,2,2)/16 + 0x02: gauss 5 pixel (8,1,1,1,1)/12 + 0x04: cartoon filter */ + + gint32 i_timecode; /* ms */ + + gint i_length; /* V,A,T: length of following data + S: length of packet correl */ +} nuv_frame_header; + +/* FIXME Not sure of this one */ +typedef struct +{ + gint i_version; + guint32 i_video_fcc; + + guint32 i_audio_fcc; + gint i_audio_sample_rate; + gint i_audio_bits_per_sample; + gint i_audio_channels; + gint i_audio_compression_ratio; + gint i_audio_quality; + gint i_rtjpeg_quality; + gint i_rtjpeg_luma_filter; + gint i_rtjpeg_chroma_filter; + gint i_lavc_bitrate; + gint i_lavc_qmin; + gint i_lavc_qmax; + gint i_lavc_maxqdiff; + gint64 i_seekable_offset; + gint64 i_keyframe_adjust_offset; + +} nuv_extended_header; + +typedef enum { + GST_NUV_DEMUX_START, + GST_NUV_DEMUX_HEADER_DATA, + GST_NUV_DEMUX_EXTRA_DATA, + GST_NUV_DEMUX_MPEG_DATA, + GST_NUV_DEMUX_EXTEND_HEADER, + GST_NUV_DEMUX_EXTEND_HEADER_DATA, + GST_NUV_DEMUX_FRAME_HEADER, + GST_NUV_DEMUX_MOVI, + GST_NUV_DEMUX_INVALID_DATA +} GstNuvDemuxState; + +struct _GstNuvDemuxPrivate { + /* used for indicate the mode */ + guint mode; + + /* used on push mode */ + GstAdapter *adapter; + + /* pads */ + GstPad *sinkpad; + GstPad *src_video_pad; + GstPad *src_audio_pad; + + /* Flow control */ + GstFlowReturn last_video_return; + GstFlowReturn last_audio_return; + + /* 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; + + /* segment control info */ + gboolean new_audio_segment; + gboolean new_video_segment; + + /* Mpeg ExtraData */ + guint64 mpeg_data_size; + GstBuffer *mpeg_buffer; + + /* Headers */ + nuv_header *h; + nuv_extended_header *eh; + nuv_frame_header *fh; + + /* anothers info */ + gint64 time_start; + gint64 time_diff; + gint64 time_qos; + guint64 last_frame_time; +}; + + static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -93,6 +243,7 @@ GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY); +static void gst_nuv_demux_dispose (GObject * object); static void gst_nuv_demux_finalize (GObject * object); static GstStateChangeReturn gst_nuv_demux_change_state (GstElement * element, GstStateChange transition); @@ -101,18 +252,25 @@ static GstFlowReturn gst_nuv_demux_play (GstPad * pad); static gboolean gst_nuv_demux_sink_activate_pull (GstPad * sinkpad, gboolean active); -static gboolean gst_nuv_demux_sink_activate_push (GstPad * pad, +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 GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move, GstBuffer ** buffer); static void gst_nuv_demux_reset (GstNuvDemux * nuv); static void gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv); static void gst_nuv_demux_send_eos (GstNuvDemux * nuv); +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); -/* GObject methods */ + GST_BOILERPLATE (GstNuvDemux, gst_nuv_demux, GstElement, GST_TYPE_ELEMENT); +/****************************************************************************** + * Utils function + ******************************************************************************/ #if G_BYTE_ORDER == G_BIG_ENDIAN static inline gdouble _gdouble_swap_le_be (gdouble * d) @@ -133,7 +291,7 @@ #define READ_DOUBLE_FROM_LE(d) *((gdouble* ) (d)) #endif /* G_BYTE_ORDER != G_BIG_ENDIAN */ -static void +static void double2fraction (double in, int *num, int *denom) { if (in == 29.97) { @@ -152,6 +310,8 @@ } } +/* GObject Functions */ + static void gst_nuv_demux_base_init (gpointer klass) { @@ -179,54 +339,55 @@ parent_class = g_type_class_peek_parent (klass); + gobject_class->dispose = gst_nuv_demux_dispose; gobject_class->finalize = gst_nuv_demux_finalize; gstelement_class->change_state = gst_nuv_demux_change_state; + + g_type_class_add_private (gobject_class, sizeof (GstNuvDemuxPrivate)); } static void gst_nuv_demux_init (GstNuvDemux * nuv, GstNuvDemuxClass * nuv_class) { - nuv->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + nuv->priv = GST_NUV_DEMUX_GET_PRIVATE (nuv); + nuv->priv->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_activate_function (nuv->priv->sinkpad, gst_nuv_demux_sink_activate); + gst_pad_set_activatepull_function (nuv->priv->sinkpad, + gst_nuv_demux_sink_activate_pull); + gst_pad_set_activatepush_function (nuv->priv->sinkpad, + 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); - gst_pad_set_activate_function (nuv->sinkpad, gst_nuv_demux_sink_activate); + nuv->priv->new_audio_segment = TRUE; + nuv->priv->new_video_segment = TRUE; - gst_pad_set_activatepull_function (nuv->sinkpad, - gst_nuv_demux_sink_activate_pull); + gst_nuv_demux_reset (nuv); +} - gst_pad_set_activatepush_function (nuv->sinkpad, - gst_nuv_demux_sink_activate_push); +static void +gst_nuv_demux_dispose (GObject * object) +{ + GstNuvDemux *nuv = GST_NUV_DEMUX (object); - gst_pad_set_chain_function (nuv->sinkpad, - GST_DEBUG_FUNCPTR (gst_nuv_demux_chain)); + if (nuv->priv->mpeg_buffer != NULL) { + gst_buffer_unref (nuv->priv->mpeg_buffer); + } - gst_element_add_pad (GST_ELEMENT (nuv), nuv->sinkpad); - - nuv->adapter = NULL; - nuv->mpeg_buffer = NULL; - nuv->h = NULL; - nuv->eh = NULL; - nuv->fh = NULL; + gst_nuv_demux_reset (GST_NUV_DEMUX (object)); + gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (object)); - nuv->new_audio_segment = TRUE; - nuv->new_video_segment = TRUE; - - gst_nuv_demux_reset (nuv); + if (nuv->priv->adapter != NULL) { + gst_object_unref (nuv->priv->adapter); + } } static void gst_nuv_demux_finalize (GObject * object) { - GstNuvDemux *nuv = GST_NUV_DEMUX (object); - - if (nuv->mpeg_buffer != NULL) { - gst_buffer_unref (nuv->mpeg_buffer); - } - - gst_nuv_demux_destoy_src_pad (nuv); - gst_nuv_demux_reset (nuv); - if (nuv->adapter != NULL) { - gst_object_unref (nuv->adapter); - } G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -274,10 +435,11 @@ static GstFlowReturn gst_nuv_demux_stream_header_data (GstNuvDemux * nuv) { - GstFlowReturn res = gst_nuv_demux_header_load (nuv, &nuv->h); + GstFlowReturn res; + res = gst_nuv_demux_header_load (nuv, &nuv->priv->h); if (res == GST_FLOW_OK) - nuv->state = GST_NUV_DEMUX_EXTRA_DATA; + nuv->priv->state = GST_NUV_DEMUX_EXTRA_DATA; return res; } @@ -296,10 +458,10 @@ } else { if (strncmp ((gchar *) file_header->data, "MythTVVideo", 11) || strncmp ((gchar *) file_header->data, "NuppelVideo", 11)) { - nuv->state = GST_NUV_DEMUX_HEADER_DATA; + nuv->priv->state = GST_NUV_DEMUX_HEADER_DATA; } else { GST_DEBUG_OBJECT (nuv, "error parsing file header"); - nuv->state = GST_NUV_DEMUX_INVALID_DATA; + nuv->priv->state = GST_NUV_DEMUX_INVALID_DATA; res = GST_FLOW_ERROR; } } @@ -336,7 +498,7 @@ h->i_filters = GPOINTER_TO_INT (data[3]); h->i_timecode = 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=%d l=%d", h->i_type, h->i_compression ? h->i_compression : ' ', @@ -400,11 +562,15 @@ gst_buffer_unref (buff); return res; } + + +/* Query Functions */ static const GstQueryType * gst_nuv_demux_get_src_query_types (GstPad * pad) { static const GstQueryType src_types[] = { GST_QUERY_POSITION, + GST_QUERY_DURATION, 0 }; @@ -419,11 +585,23 @@ switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: - if (GST_CLOCK_TIME_IS_VALID (nuv->last_frame_time)) { - gst_query_set_position (query, GST_FORMAT_TIME, - nuv->last_frame_time); + 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; - GST_DEBUG_OBJECT (nuv, "POS %d", nuv->last_frame_time); + } + 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; + } } break; default: @@ -436,52 +614,48 @@ return res; } -//TODO: create a function to control events and send to src pads +static GstPad* +gst_nuv_demux_create_pad (GstNuvDemux *nuv, GstCaps *caps, GstStaticPadTemplate *template, const gchar* name) +{ + GstPad *pad = NULL; + pad = gst_pad_new_from_static_template (template, name); + gst_pad_use_fixed_caps (pad); + gst_pad_set_caps (pad, caps); + gst_pad_set_active (pad, TRUE); + gst_element_add_pad (GST_ELEMENT (nuv), pad); + + return pad; +} + static void gst_nuv_demux_create_pads (GstNuvDemux * nuv) { - if (nuv->h->i_video_blocks != 0) { + if (nuv->priv->h->i_video_blocks != 0) { GstCaps *video_caps = NULL; - nuv->src_video_pad = - gst_pad_new_from_static_template (&video_src_template, "video_src"); - video_caps = gst_caps_new_simple ("video/x-divx", "divxversion", G_TYPE_INT, 4, - "width", G_TYPE_INT, nuv->h->i_width, - "height", G_TYPE_INT, nuv->h->i_height, - "framerate", GST_TYPE_FRACTION, nuv->h->i_fpsn, nuv->h->i_fpsd, - "format", GST_TYPE_FOURCC, nuv->eh->i_video_fcc, + "width", G_TYPE_INT, nuv->priv->h->i_width, + "height", G_TYPE_INT, nuv->priv->h->i_height, + "framerate", GST_TYPE_FRACTION, nuv->priv->h->i_fpsn, nuv->priv->h->i_fpsd, + "format", GST_TYPE_FOURCC, nuv->priv->eh->i_video_fcc, "pixel-aspect-ratio", GST_TYPE_FRACTION, - (gint) (nuv->h->d_aspect * 1000.0f), 1000, NULL); + (gint) (nuv->priv->h->d_aspect * 1000.0f), 1000, NULL); - gst_pad_use_fixed_caps (nuv->src_video_pad); - gst_pad_set_caps (nuv->src_video_pad, video_caps); - gst_pad_set_active (nuv->src_video_pad, TRUE); - gst_pad_set_query_type_function (nuv->src_video_pad, gst_nuv_demux_get_src_query_types); - gst_pad_set_query_function (nuv->src_video_pad, gst_nuv_demux_handle_src_query); - gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_video_pad); + nuv->priv->src_video_pad = gst_nuv_demux_create_pad (nuv, video_caps, &video_src_template, "video_src"); gst_caps_unref (video_caps); } - if (nuv->h->i_audio_blocks != 0) { + if (nuv->priv->h->i_audio_blocks != 0) { GstCaps *audio_caps = NULL; - nuv->src_audio_pad = - gst_pad_new_from_static_template (&audio_src_template, "audio_src"); + audio_caps = gst_caps_new_simple ("audio/mpeg", + "rate", G_TYPE_INT, nuv->priv->eh->i_audio_sample_rate, + "format", GST_TYPE_FOURCC, nuv->priv->eh->i_audio_fcc, + "channels", G_TYPE_INT, nuv->priv->eh->i_audio_channels, + "mpegversion", G_TYPE_INT, nuv->priv->eh->i_version, NULL); - audio_caps = gst_caps_new_simple ("audio/mpeg", - "rate", G_TYPE_INT, nuv->eh->i_audio_sample_rate, - "format", GST_TYPE_FOURCC, nuv->eh->i_audio_fcc, - "channels", G_TYPE_INT, nuv->eh->i_audio_channels, - "mpegversion", G_TYPE_INT, nuv->eh->i_version, NULL); - - gst_pad_use_fixed_caps (nuv->src_audio_pad); - gst_pad_set_caps (nuv->src_audio_pad, audio_caps); - gst_pad_set_active (nuv->src_audio_pad, TRUE); - gst_pad_set_query_type_function (nuv->src_video_pad, gst_nuv_demux_get_src_query_types); - gst_pad_set_query_function (nuv->src_video_pad, gst_nuv_demux_handle_src_query); - gst_element_add_pad (GST_ELEMENT (nuv), nuv->src_audio_pad); + nuv->priv->src_audio_pad = gst_nuv_demux_create_pad (nuv, audio_caps, &audio_src_template, "audio_src"); gst_caps_unref (audio_caps); } @@ -493,49 +667,53 @@ { GstFlowReturn ret = GST_FLOW_OK; - if (nuv->fh != NULL) + if (nuv->priv->fh != NULL) { - g_free (nuv->fh); - nuv->fh = NULL; + g_free (nuv->priv->fh); + nuv->priv->fh = NULL; } - - ret = gst_nuv_demux_frame_header_load (nuv, &nuv->fh); + + ret = gst_nuv_demux_frame_header_load (nuv, &nuv->priv->fh); if (ret != GST_FLOW_OK) return ret; - nuv->state = GST_NUV_DEMUX_MOVI; + nuv->priv->state = GST_NUV_DEMUX_MOVI; return ret; } static gboolean gst_nuv_combine_flow (GstNuvDemux *nuv) { - GstFlowReturn ret_video = nuv->last_video_return; - GstFlowReturn ret_audio = nuv->last_audio_return; + GstFlowReturn ret_video = nuv->priv->last_video_return; + GstFlowReturn ret_audio = nuv->priv->last_audio_return; if ((ret_video != GST_FLOW_OK) && (ret_audio != GST_FLOW_OK)) return FALSE; - if (GST_FLOW_IS_FATAL (nuv->last_video_return)) + if (GST_FLOW_IS_FATAL (ret_video)) return FALSE; - if (GST_FLOW_IS_FATAL (nuv->last_audio_return)) + if (GST_FLOW_IS_FATAL (ret_audio)) return FALSE; - return TRUE; + return TRUE; } static GstFlowReturn gst_nuv_demux_stream_data (GstNuvDemux * nuv) { GstFlowReturn ret = GST_FLOW_OK; + GstPad *pad = NULL; + guint64 timestamp; GstBuffer *buf = NULL; - nuv_frame_header *h = nuv->fh; + nuv_frame_header *h = NULL; + + h = nuv->priv->fh; if (h->i_type == 'R') goto done; - + if (h->i_length > 0) { ret = gst_nuv_demux_read_bytes (nuv, h->i_length, TRUE, &buf); if ((ret != GST_FLOW_OK) || (buf == NULL)) @@ -543,81 +721,74 @@ if (h->i_timecode < 0) { h->i_timecode = h->i_timecode * -1; - nuv->time_offset = h->i_timecode; + nuv->priv->time_diff = h->i_timecode; } else - h->i_timecode += nuv->time_offset; - - GST_BUFFER_TIMESTAMP (buf) = h->i_timecode * GST_MSECOND; - nuv->last_frame_time = h->i_timecode * GST_MSECOND; + h->i_timecode += nuv->priv->time_diff; + + timestamp = h->i_timecode * GST_MSECOND; + + GST_BUFFER_SIZE (buf) = h->i_length; + GST_BUFFER_TIMESTAMP (buf) = timestamp; + + if ((h->i_type == 'V') || (h->i_type == 'A')) { + } } + else { + goto done; + } + switch (h->i_type) { case 'V': { - if (h->i_length == 0) - break; - - if (nuv->new_video_segment) { + pad = nuv->priv->src_video_pad; + + if (nuv->priv->new_video_segment) { /* send new segment event*/ - gst_pad_push_event (nuv->src_video_pad, - gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, + gst_pad_push_event (nuv->priv->src_video_pad, + gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0)); - nuv->new_video_segment = FALSE; + + if (nuv->priv->time_start == GST_CLOCK_TIME_NONE) { + nuv->priv->time_start = timestamp; + } + nuv->priv->new_video_segment = FALSE; } - - GST_BUFFER_SIZE (buf) = h->i_length; - gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_video_pad)); - nuv->last_video_return = gst_pad_push (nuv->src_video_pad, buf); - if (!gst_nuv_combine_flow (nuv)) { - ret = nuv->last_video_return; - GST_WARNING_OBJECT (nuv, "error: %d pushing on srcpad %s, is linked? = %d", - nuv->last_video_return, gst_pad_get_name (nuv->src_video_pad), gst_pad_is_linked (nuv->src_video_pad)); - } + break; } case 'A': { - if (h->i_length == 0) - break; - - if (nuv->new_audio_segment) { + pad = nuv->priv->src_audio_pad; + + if (nuv->priv->new_audio_segment) { /* send new segment event*/ - gst_pad_push_event (nuv->src_audio_pad, - gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, + gst_pad_push_event (nuv->priv->src_audio_pad, + gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0)); - nuv->new_audio_segment = FALSE; + + if (nuv->priv->time_start == GST_CLOCK_TIME_NONE) { + nuv->priv->time_start = timestamp; + } + nuv->priv->new_audio_segment = FALSE; } - - GST_BUFFER_SIZE (buf) = h->i_length; - gst_buffer_set_caps (buf, GST_PAD_CAPS (nuv->src_audio_pad)); - nuv->last_audio_return = gst_pad_push (nuv->src_audio_pad, buf); - if (!gst_nuv_combine_flow (nuv)) { - ret = nuv->last_audio_return; - GST_WARNING_OBJECT (nuv, "Error %d pushing on srcpad %s, is linked? = %d", - nuv->last_audio_return, gst_pad_get_name (nuv->src_audio_pad), gst_pad_is_linked (nuv->src_audio_pad)); - } + break; } case 'S': { switch (h->i_compression) { case 'V': - if ( !gst_pad_is_linked( nuv->src_video_pad ) ) - break; - GST_DEBUG_OBJECT (nuv, "sending new video segment: %d", h->i_timecode); - gst_pad_push_event (nuv->src_video_pad, - gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, h->i_timecode * GST_MSECOND, + gst_pad_push_event (nuv->priv->src_video_pad, + gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, h->i_timecode * GST_MSECOND, GST_CLOCK_TIME_NONE, 0)); break; - case 'A': - if ( !gst_pad_is_linked( nuv->src_audio_pad ) ) - break; - + case 'A': GST_DEBUG_OBJECT (nuv, "sending new audio segment: %d", h->i_timecode); - gst_pad_push_event (nuv->src_audio_pad, - gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, + gst_pad_push_event (nuv->priv->src_audio_pad, + gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0)); break; default: @@ -625,6 +796,8 @@ } if (buf != NULL) gst_buffer_unref (buf); + + goto done; } default: if (buf != NULL) @@ -633,10 +806,37 @@ break; } + 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); + + if (ret != GST_FLOW_OK) { + if (pad == nuv->priv->src_video_pad) { + nuv->priv->last_video_return = ret; + } + else if (pad == nuv->priv->src_audio_pad) { + nuv->priv->last_audio_return = ret; + } + + /* verify anothers flow if is necessary stop task */ + if (gst_nuv_combine_flow (nuv) != FALSE) { + ret = GST_FLOW_OK; + } + + GST_WARNING_OBJECT (nuv, "error: %d pushing on srcpad %s", ret, gst_pad_get_name (pad)); + } + } + done: - nuv->state = GST_NUV_DEMUX_FRAME_HEADER; - g_free (nuv->fh); - nuv->fh = NULL; + nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; + g_free (nuv->priv->fh); + nuv->priv->fh = NULL; return ret; } @@ -646,14 +846,14 @@ GstFlowReturn ret = GST_FLOW_OK; /* ffmpeg extra data */ - ret = - gst_nuv_demux_read_bytes (nuv, nuv->mpeg_data_size, TRUE, - &nuv->mpeg_buffer); - if ((ret != GST_FLOW_OK) || (nuv->mpeg_buffer == NULL)) { - return ret; + ret = gst_nuv_demux_read_bytes (nuv, nuv->priv->mpeg_data_size, TRUE, + &nuv->priv->mpeg_buffer); + if ((ret != GST_FLOW_OK) || (nuv->priv->mpeg_buffer == NULL)) { + return ret; } - GST_BUFFER_SIZE (nuv->mpeg_buffer) = nuv->mpeg_data_size; - nuv->state = GST_NUV_DEMUX_EXTEND_HEADER; + + GST_BUFFER_SIZE (nuv->priv->mpeg_buffer) = nuv->priv->mpeg_data_size; + nuv->priv->state = GST_NUV_DEMUX_EXTEND_HEADER; return ret; } @@ -676,13 +876,13 @@ if (h->i_length > 0) { if (h->i_compression == 'F') { - nuv->state = GST_NUV_DEMUX_MPEG_DATA; + nuv->priv->state = GST_NUV_DEMUX_MPEG_DATA; } else { g_free (h); return GST_FLOW_ERROR; } } else { - nuv->state = GST_NUV_DEMUX_EXTEND_HEADER; + nuv->priv->state = GST_NUV_DEMUX_EXTEND_HEADER; } g_free (h); @@ -695,12 +895,12 @@ { GstFlowReturn ret = GST_FLOW_OK; - ret = gst_nuv_demux_extended_header_load (nuv, &nuv->eh); + ret = gst_nuv_demux_extended_header_load (nuv, &nuv->priv->eh); if (ret != GST_FLOW_OK) return ret; gst_nuv_demux_create_pads (nuv); - nuv->state = GST_NUV_DEMUX_FRAME_HEADER; + nuv->priv->state = GST_NUV_DEMUX_FRAME_HEADER; return ret; } @@ -733,9 +933,9 @@ } g_free (h); h = NULL; - nuv->state = GST_NUV_DEMUX_EXTEND_HEADER_DATA; + nuv->priv->state = GST_NUV_DEMUX_EXTEND_HEADER_DATA; } else { - nuv->state = GST_NUV_DEMUX_INVALID_DATA; + nuv->priv->state = GST_NUV_DEMUX_INVALID_DATA; g_object_unref (buf); GST_ELEMENT_WARNING (nuv, STREAM, FAILED, (_("incomplete NUV support")), ("incomplete NUV support")); @@ -750,13 +950,13 @@ GstFlowReturn res = GST_FLOW_OK; GstNuvDemux *nuv = GST_NUV_DEMUX (GST_PAD_PARENT (pad)); - switch (nuv->state) { + switch (nuv->priv->state) { case GST_NUV_DEMUX_START: res = gst_nuv_demux_stream_file_header (nuv); if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } - if (nuv->state != GST_NUV_DEMUX_HEADER_DATA) + if (nuv->priv->state != GST_NUV_DEMUX_HEADER_DATA) break; case GST_NUV_DEMUX_HEADER_DATA: @@ -764,7 +964,7 @@ if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } - if (nuv->state != GST_NUV_DEMUX_EXTRA_DATA) + if (nuv->priv->state != GST_NUV_DEMUX_EXTRA_DATA) break; case GST_NUV_DEMUX_EXTRA_DATA: @@ -772,7 +972,7 @@ if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } - if (nuv->state != GST_NUV_DEMUX_MPEG_DATA) + if (nuv->priv->state != GST_NUV_DEMUX_MPEG_DATA) break; case GST_NUV_DEMUX_MPEG_DATA: @@ -781,7 +981,7 @@ goto pause; } - if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER) + if (nuv->priv->state != GST_NUV_DEMUX_EXTEND_HEADER) break; case GST_NUV_DEMUX_EXTEND_HEADER: @@ -789,7 +989,7 @@ if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } - if (nuv->state != GST_NUV_DEMUX_EXTEND_HEADER_DATA) + if (nuv->priv->state != GST_NUV_DEMUX_EXTEND_HEADER_DATA) break; case GST_NUV_DEMUX_EXTEND_HEADER_DATA: @@ -798,7 +998,7 @@ goto pause; } - if (nuv->state != GST_NUV_DEMUX_FRAME_HEADER) + if (nuv->priv->state != GST_NUV_DEMUX_FRAME_HEADER) break; case GST_NUV_DEMUX_FRAME_HEADER: @@ -806,7 +1006,7 @@ if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) { goto pause; } - if (nuv->state != GST_NUV_DEMUX_MOVI) + if (nuv->priv->state != GST_NUV_DEMUX_MOVI) break; case GST_NUV_DEMUX_MOVI: @@ -822,14 +1022,13 @@ g_assert_not_reached (); } - GST_DEBUG_OBJECT (nuv, "state: %d res:%s", nuv->state, - gst_flow_get_name (res)); + GST_DEBUG_OBJECT (nuv, "state: %d res:%s", nuv->priv->state, gst_flow_get_name (res)); return GST_FLOW_OK; pause: GST_LOG_OBJECT (nuv, "pausing task, reason %s", gst_flow_get_name (res)); - gst_pad_pause_task (nuv->sinkpad); + gst_pad_pause_task (nuv->priv->sinkpad); if (GST_FLOW_IS_FATAL (res)) { GST_ELEMENT_ERROR (nuv, STREAM, FAILED, (_("Internal data stream error.")), @@ -846,12 +1045,12 @@ gst_element_post_message (GST_ELEMENT (nuv), gst_message_new_segment_done (GST_OBJECT (nuv), GST_FORMAT_TIME, -1)); - if (nuv->src_video_pad) - gst_pad_push_event (nuv->src_video_pad, gst_event_new_eos ()); - if (nuv->src_audio_pad) - gst_pad_push_event (nuv->src_audio_pad, gst_event_new_eos ()); + if (nuv->priv->src_video_pad) + gst_pad_push_event (nuv->priv->src_video_pad, gst_event_new_eos ()); + if (nuv->priv->src_audio_pad) + gst_pad_push_event (nuv->priv->src_audio_pad, gst_event_new_eos ()); } - + static GstFlowReturn gst_nuv_demux_read_bytes (GstNuvDemux * nuv, guint64 size, gboolean move, GstBuffer ** buffer) @@ -861,33 +1060,33 @@ if (size == 0) { return ret; } - - if (nuv->mode == NUV_PULL_MODE) { - ret = gst_pad_pull_range (nuv->sinkpad, nuv->offset, size, buffer); + + 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->offset += size; + 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); return GST_FLOW_WRONG_STATE; } } else { - if (gst_adapter_available (nuv->adapter) < size) + if (gst_adapter_available (nuv->priv->adapter) < size) return GST_FLOW_ERROR_NO_DATA; if (move) { guint8 *data = NULL; - data = (guint8 *) gst_adapter_take (nuv->adapter, size); + data = (guint8 *) gst_adapter_take (nuv->priv->adapter, size); *buffer = gst_buffer_new (); gst_buffer_set_data (*buffer, data, size); } else { guint8 *data = NULL; - data = (guint8 *) gst_adapter_peek (nuv->adapter, size); + data = (guint8 *) gst_adapter_peek (nuv->priv->adapter, size); *buffer = gst_buffer_new (); gst_buffer_set_data (*buffer, data, size); } @@ -917,11 +1116,11 @@ if (active) { GST_DEBUG_OBJECT (nuv, "activating pull function"); - nuv->mode = NUV_PULL_MODE; - if (nuv->adapter) { - gst_adapter_clear (nuv->adapter); - g_object_unref (nuv->adapter); - nuv->adapter = NULL; + nuv->priv->mode = NUV_PULL_MODE; + if (nuv->priv->adapter) { + gst_adapter_clear (nuv->priv->adapter); + g_object_unref (nuv->priv->adapter); + nuv->priv->adapter = NULL; } gst_pad_start_task (sinkpad, (GstTaskFunction) gst_nuv_demux_loop, sinkpad); } else { @@ -939,11 +1138,11 @@ GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad)); if (active) { - nuv->mode = NUV_PUSH_MODE; - if (nuv->adapter) { - gst_adapter_clear (nuv->adapter); + nuv->priv->mode = NUV_PUSH_MODE; + if (nuv->priv->adapter) { + gst_adapter_clear (nuv->priv->adapter); } else { - nuv->adapter = gst_adapter_new (); + nuv->priv->adapter = gst_adapter_new (); } GST_DEBUG_OBJECT (nuv, "activating push/chain function"); } else { @@ -955,13 +1154,84 @@ return TRUE; } + +static gboolean +gst_nuv_demux_sink_event (GstPad *pad, GstEvent *event) +{ + gboolean res; + 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); + + break; + } + case GST_EVENT_NEWSEGMENT: + { + gint64 start, stop, time; + gdouble rate, arate; + GstFormat format; + gboolean update; + + GST_DEBUG_OBJECT (nuv, "got a new segment event"); + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &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; + gst_event_unref (event); + res = TRUE; + break; + } + case GST_EVENT_QOS: + { +/* + gdouble proportion; + GstClockTimeDiff diff; + GstClockTime timestamp; + + gst_event_parse_qos (event, &proportion, &diff, ×tamp); + if (diff > 0) + nuv->time_qos = timecode + diff; + else + nuv->time_qos = -1; +*/ + + break; + } + + case GST_EVENT_EOS: + default: + res = gst_pad_event_default (pad, event); + break; + } + + gst_object_unref (nuv); + return res; +} + static GstFlowReturn gst_nuv_demux_chain (GstPad * pad, GstBuffer * buf) { GstNuvDemux *nuv = GST_NUV_DEMUX (gst_pad_get_parent (pad)); GST_DEBUG_OBJECT (nuv, " gst_nuv_demux_chain function"); - gst_adapter_push (nuv->adapter, buf); + gst_adapter_push (nuv->priv->adapter, buf); gst_object_unref (nuv); @@ -977,40 +1247,46 @@ static void gst_nuv_demux_reset (GstNuvDemux * nuv) { - nuv->state = GST_NUV_DEMUX_START; - nuv->mode = 0; - nuv->offset = 0; - nuv->time_offset = 0; + nuv->priv->state = GST_NUV_DEMUX_START; + nuv->priv->mode = 0; + 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); - if (nuv->adapter != NULL) - gst_adapter_clear (nuv->adapter); + if (nuv->priv->adapter != NULL) + gst_adapter_clear (nuv->priv->adapter); - if (nuv->mpeg_buffer != NULL) { - gst_buffer_unref (nuv->mpeg_buffer); - nuv->mpeg_buffer = NULL; + if (nuv->priv->mpeg_buffer != NULL) { + gst_buffer_unref (nuv->priv->mpeg_buffer); + nuv->priv->mpeg_buffer = NULL; } - g_free (nuv->h); - nuv->h = NULL; + g_free (nuv->priv->h); + nuv->priv->h = NULL; - g_free (nuv->eh); - nuv->eh = NULL; + g_free (nuv->priv->eh); + nuv->priv->eh = NULL; - g_free (nuv->fh); - nuv->fh = NULL; + g_free (nuv->priv->fh); + nuv->priv->fh = NULL; } static void gst_nuv_demux_destoy_src_pad (GstNuvDemux * nuv) { - if (nuv->src_video_pad) { - gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_video_pad); - nuv->src_video_pad = NULL; + if (nuv->priv->src_video_pad) { + gst_element_remove_pad (GST_ELEMENT (nuv), nuv->priv->src_video_pad); + nuv->priv->src_video_pad = NULL; } - if (nuv->src_audio_pad) { - gst_element_remove_pad (GST_ELEMENT (nuv), nuv->src_audio_pad); - nuv->src_audio_pad = NULL; + if (nuv->priv->src_audio_pad) { + gst_element_remove_pad (GST_ELEMENT (nuv), nuv->priv->src_audio_pad); + nuv->priv->src_audio_pad = NULL; } } @@ -1021,6 +1297,7 @@ switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_nuv_demux_reset (GST_NUV_DEMUX (element)); break; default: break; @@ -1031,10 +1308,9 @@ goto done; switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG_OBJECT (element, "GST_STATE_CHANGE_PAUSED_TO_READY"); + case GST_STATE_CHANGE_READY_TO_NULL: + gst_nuv_demux_reset (GST_NUV_DEMUX (element)); gst_nuv_demux_destoy_src_pad (GST_NUV_DEMUX (element)); - gst_nuv_demux_reset (GST_NUV_DEMUX (element)); break; default: break; @@ -1044,6 +1320,70 @@ 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); + average = gst_util_uint64_scale (1 , nuv->priv->streamer_offset, interval); + 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); + } +} + +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; +} + + static gboolean plugin_init (GstPlugin * plugin) { diff -r f6a9705509a1 -r 6b1e210c250a gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.h --- a/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.h Fri Nov 17 13:09:12 2006 +0000 +++ b/gst-plugins-nuvdemux/nuvdemux/gstnuvdemux.h Fri Nov 17 13:18:48 2006 +0000 @@ -38,155 +38,10 @@ #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[DEMUX_INDEX_SIZE_MAX]; -} nuv_demux_index; - -/* */ -typedef struct -{ - gchar id[12]; /* "NuppelVideo\0" or "MythTVVideo\0" */ - gchar version[5]; /* "x.xx\0" */ - - gint i_width; - gint i_height; - gint i_width_desired; - gint i_height_desired; - - gchar i_mode; /* P progressive, I interlaced */ - - gdouble d_aspect; /* 1.0 squared pixel */ - gdouble d_fps; - //fps num/denom - gint i_fpsn; - gint i_fpsd; - - gint i_video_blocks; /* 0 no video, -1 unknown */ - gint i_audio_blocks; - gint i_text_blocks; - - gint i_keyframe_distance; - -} nuv_header; - -typedef struct -{ - gchar i_type; /* A: audio, V: video, S: sync; T: test - R: Seekpoint (string:RTjjjjjjjj) - D: Extra data for codec */ - gchar i_compression; /* V: 0 uncompressed - 1 RTJpeg - 2 RTJpeg+lzo - N black frame - L copy last - A: 0 uncompressed (44100 1-bits, 2ch) - 1 lzo - 2 layer 2 - 3 layer 3 - F flac - S shorten - N null frame loudless - L copy last - S: B audio and vdeo sync point - A audio sync info (timecode == effective - dsp frequency*100) - V next video sync (timecode == next video - frame num) - S audio,video,text correlation */ - gchar i_keyframe; /* 0 keyframe, else no no key frame */ - guint8 i_filters; /* 0x01: gauss 5 pixel (8,2,2,2,2)/16 - 0x02: gauss 5 pixel (8,1,1,1,1)/12 - 0x04: cartoon filter */ - - gint32 i_timecode; /* ms */ - - gint i_length; /* V,A,T: length of following data - S: length of packet correl */ -} nuv_frame_header; - -/* FIXME Not sure of this one */ -typedef struct -{ - gint i_version; - guint32 i_video_fcc; - - guint32 i_audio_fcc; - gint i_audio_sample_rate; - gint i_audio_bits_per_sample; - gint i_audio_channels; - gint i_audio_compression_ratio; - gint i_audio_quality; - gint i_rtjpeg_quality; - gint i_rtjpeg_luma_filter; - gint i_rtjpeg_chroma_filter; - gint i_lavc_bitrate; - gint i_lavc_qmin; - gint i_lavc_qmax; - gint i_lavc_maxqdiff; - gint64 i_seekable_offset; - gint64 i_keyframe_adjust_offset; - -} nuv_extended_header; - -typedef enum { - GST_NUV_DEMUX_START, - GST_NUV_DEMUX_HEADER_DATA, - GST_NUV_DEMUX_EXTRA_DATA, - GST_NUV_DEMUX_MPEG_DATA, - GST_NUV_DEMUX_EXTEND_HEADER, - GST_NUV_DEMUX_EXTEND_HEADER_DATA, - GST_NUV_DEMUX_FRAME_HEADER, - GST_NUV_DEMUX_MOVI, - GST_NUV_DEMUX_INVALID_DATA -} GstNuvDemuxState; - +typedef struct _GstNuvDemuxPrivate GstNuvDemuxPrivate; typedef struct _GstNuvDemux { - GstElement parent; - - guint mode; - GstAdapter *adapter; - - /* pads */ - GstPad *sinkpad; - GstPad *src_video_pad; - GstPad *src_audio_pad; - - GstFlowReturn last_video_return; - GstFlowReturn last_audio_return; - - /* NUV decoding state */ - GstNuvDemuxState state; - guint64 duration; - guint64 offset; - guint64 time_offset; - guint64 last_frame_time; - gboolean new_audio_segment; - gboolean new_video_segment; - - /* Mpeg ExtraData */ - guint64 mpeg_data_size; - GstBuffer *mpeg_buffer; - - nuv_header *h; - nuv_extended_header *eh; - nuv_frame_header *fh; - - /* FIXME: change these fields to private struct */ - /* first data buffer received, should sent new_segment */ + GstElement parent; + GstNuvDemuxPrivate *priv; } GstNuvDemux; typedef struct _GstNuvDemuxClass {