diff --git a/ChangeLog b/ChangeLog index d5db267917..487364c70f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2006-05-03 Wim Taymans + + * ext/ogg/gstoggdemux.c: (gst_ogg_demux_chain_peer), + (gst_ogg_chain_mark_discont), (gst_ogg_chain_new_stream), + (gst_ogg_demux_activate_chain), (gst_ogg_demux_perform_seek): + Mark buffers with DISCONT after seek and after activating new + chains. + + * ext/theora/gsttheoradec.h: + * ext/theora/theoradec.c: (gst_theora_dec_reset), + (theora_get_query_types), (theora_dec_sink_event), + (theora_dec_push), (theora_handle_data_packet), (theora_dec_chain), + (theora_dec_change_state): + Fix frame counter. + Detect and mark DISCONT buffers. + + * ext/vorbis/vorbisdec.c: (vorbis_dec_src_query), + (vorbis_dec_sink_event), (vorbis_dec_push), (vorbis_dec_chain), + (vorbis_dec_change_state): + * ext/vorbis/vorbisdec.h: + Use GstSegment. + Detect and mark DISCONT buffers. + Don't crash on 0 sized buffers. + 2006-05-03 Wim Taymans * gst/volume/gstvolume.c: (volume_funcfind), (volume_set_caps), diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index dbdc887fb2..69c354eb2b 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -137,6 +137,8 @@ struct _GstOggPad ogg_stream_state stream; + gboolean discont; + gboolean dynamic; /* True if the internal element had dynamic pads */ guint padaddedid; /* The signal id for element::pad-added */ }; @@ -949,6 +951,12 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) GST_BUFFER_OFFSET (buf) = -1; GST_BUFFER_OFFSET_END (buf) = packet->granulepos; + /* Mark discont on the buffer */ + if (pad->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + pad->discont = FALSE; + } + ret = gst_pad_push (GST_PAD (pad), buf); /* ignore not linked */ if (ret == GST_FLOW_NOT_LINKED) @@ -1204,6 +1212,18 @@ gst_ogg_chain_free (GstOggChain * chain) g_free (chain); } +static void +gst_ogg_chain_mark_discont (GstOggChain * chain) +{ + gint i; + + for (i = 0; i < chain->streams->len; i++) { + GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i); + + pad->discont = TRUE; + } +} + static GstOggPad * gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) { @@ -1225,6 +1245,7 @@ gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) GST_PAD_DIRECTION (ret) = GST_PAD_SRC; ret->chain = chain; ret->ogg = chain->ogg; + ret->discont = TRUE; gst_object_set_name (GST_OBJECT (ret), name); g_free (name); @@ -1643,6 +1664,9 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, pad = g_array_index (chain->streams, GstOggPad *, i); GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad); + /* mark discont */ + pad->discont = TRUE; + /* activate first */ gst_pad_set_active (GST_PAD_CAST (pad), TRUE); @@ -1667,6 +1691,11 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, for (headers = pad->headers; headers; headers = g_list_next (headers)) { GstBuffer *buffer = GST_BUFFER (headers->data); + if (pad->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + pad->discont = FALSE; + } + /* we don't care about the return value here */ gst_pad_push (GST_PAD_CAST (pad), buffer); } @@ -1991,7 +2020,8 @@ gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event) /* switch to different chain, send segment on new chain */ gst_ogg_demux_activate_chain (ogg, chain, event); } else { - /* send segment on current chain */ + /* mark discont and send segment on current chain */ + gst_ogg_chain_mark_discont (chain); gst_ogg_demux_send_event (ogg, event); } diff --git a/ext/theora/gsttheoradec.h b/ext/theora/gsttheoradec.h index 5222781a38..a117926ced 100644 --- a/ext/theora/gsttheoradec.h +++ b/ext/theora/gsttheoradec.h @@ -78,6 +78,7 @@ struct _GstTheoraDec /* segment info */ /* with STREAM_LOCK */ GstSegment segment; + gboolean discont; /* QoS stuff */ /* with LOCK*/ gdouble proportion; diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index c60fa342e5..cb41860e9b 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -172,6 +172,8 @@ gst_theora_dec_reset (GstTheoraDec * dec) dec->need_keyframe = TRUE; dec->last_timestamp = -1; dec->granulepos = -1; + dec->discont = TRUE; + dec->frame_nr = -1; gst_segment_init (&dec->segment, GST_FORMAT_TIME); GST_OBJECT_LOCK (dec); @@ -180,7 +182,6 @@ gst_theora_dec_reset (GstTheoraDec * dec) GST_OBJECT_UNLOCK (dec); } -/* FIXME: copy from libtheora, theora should somehow make this available for seeking */ static int _theora_ilog (unsigned int v) { @@ -281,6 +282,8 @@ theora_get_query_types (GstPad * pad) { static const GstQueryType theora_src_query_types[] = { GST_QUERY_POSITION, + GST_QUERY_DURATION, + GST_QUERY_CONVERT, 0 }; @@ -641,7 +644,6 @@ theora_dec_sink_event (GstPad * pad, GstEvent * event) ret = gst_pad_push_event (dec->srcpad, event); break; case GST_EVENT_FLUSH_STOP: - /* FIXME, makes us waiting for keyframes */ gst_theora_dec_reset (dec); ret = gst_pad_push_event (dec->srcpad, event); break; @@ -876,6 +878,11 @@ theora_dec_push (GstTheoraDec * dec, GstBuffer * buf) GST_DEBUG_OBJECT (dec, "patch buffer %lld %lld", size, time); GST_BUFFER_TIMESTAMP (buffer) = time; + + if (dec->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + dec->discont = FALSE; + } /* ignore the result.. */ gst_pad_push (dec->srcpad, buffer); size--; @@ -883,6 +890,10 @@ theora_dec_push (GstTheoraDec * dec, GstBuffer * buf) g_list_free (dec->queued); dec->queued = NULL; } + if (dec->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + dec->discont = FALSE; + } result = gst_pad_push (dec->srcpad, buf); } @@ -904,20 +915,23 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, gint cwidth, cheight; GstFlowReturn result; - if (!dec->have_header) + if (G_UNLIKELY (!dec->have_header)) goto not_initialized; /* the second most significant bit of the first data byte is cleared * for keyframes */ keyframe = (packet->packet[0] & 0x40) == 0; if (keyframe) { + GST_DEBUG_OBJECT (dec, "we have a keyframe"); dec->need_keyframe = FALSE; } else if (dec->need_keyframe) { goto dropping; } + GST_DEBUG_OBJECT (dec, "parsing data packet"); + /* this does the decoding */ - if (theora_decode_packetin (&dec->state, packet)) + if (G_UNLIKELY (theora_decode_packetin (&dec->state, packet))) goto decode_error; if (outtime != -1) { @@ -942,10 +956,11 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, /* this does postprocessing and set up the decoded frame * pointers in our yuv variable */ - if (theora_decode_YUVout (&dec->state, &yuv) < 0) + if (G_UNLIKELY (theora_decode_YUVout (&dec->state, &yuv) < 0)) goto no_yuv; - if ((yuv.y_width != dec->info.width) || (yuv.y_height != dec->info.height)) + if (G_UNLIKELY ((yuv.y_width != dec->info.width) + || (yuv.y_height != dec->info.height))) goto wrong_dimensions; width = dec->width; @@ -966,7 +981,7 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, result = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, GST_BUFFER_OFFSET_NONE, out_size, GST_PAD_CAPS (dec->srcpad), &out); - if (result != GST_FLOW_OK) + if (G_UNLIKELY (result != GST_FLOW_OK)) goto no_buffer; /* copy the visible region to the destination. This is actually pretty @@ -1011,9 +1026,9 @@ theora_handle_data_packet (GstTheoraDec * dec, ogg_packet * packet, } } - /* FIXME, frame_nr not correct */ GST_BUFFER_OFFSET (out) = dec->frame_nr; - dec->frame_nr++; + if (dec->frame_nr != -1) + dec->frame_nr++; GST_BUFFER_OFFSET_END (out) = dec->frame_nr; GST_BUFFER_DURATION (out) = gst_util_uint64_scale_int (GST_SECOND, dec->info.fps_denominator, @@ -1034,11 +1049,14 @@ not_initialized: dropping: { GST_WARNING_OBJECT (dec, "dropping frame because we need a keyframe"); + dec->discont = TRUE; return GST_FLOW_OK; } dropping_qos: { - dec->frame_nr++; + if (dec->frame_nr != -1) + dec->frame_nr++; + dec->discont = TRUE; GST_WARNING_OBJECT (dec, "dropping frame because of QoS"); return GST_FLOW_OK; } @@ -1079,9 +1097,11 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf) /* resync on DISCONT */ if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT))) { + GST_DEBUG_OBJECT (dec, "received DISCONT buffer"); dec->need_keyframe = TRUE; dec->last_timestamp = -1; dec->granulepos = -1; + dec->discont = TRUE; } /* make ogg_packet out of the buffer */ @@ -1107,7 +1127,7 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf) if (packet.bytes < 1) goto wrong_size; - GST_DEBUG_OBJECT (dec, "header=%d packetno=%lld, outtime=%" GST_TIME_FORMAT, + GST_DEBUG_OBJECT (dec, "header=%02x packetno=%lld, outtime=%" GST_TIME_FORMAT, packet.packet[0], packet.packetno, GST_TIME_ARGS (dec->last_timestamp)); /* switch depending on packet type */ @@ -1122,7 +1142,7 @@ theora_dec_chain (GstPad * pad, GstBuffer * buf) } done: - /* interpollate granule pos */ + /* interpolate granule pos */ dec->granulepos = _inc_granulepos (dec, dec->granulepos); gst_object_unref (dec); @@ -1135,6 +1155,7 @@ done: wrong_size: { GST_WARNING_OBJECT (dec, "received empty packet"); + dec->discont = TRUE; result = GST_FLOW_OK; goto done; } @@ -1171,6 +1192,7 @@ theora_dec_change_state (GstElement * element, GstStateChange transition) theora_clear (&dec->state); theora_comment_clear (&dec->comment); theora_info_clear (&dec->info); + gst_theora_dec_reset (dec); break; case GST_STATE_CHANGE_READY_TO_NULL: break; diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c index e6bd56aef7..af28a6d0a5 100644 --- a/ext/vorbis/vorbisdec.c +++ b/ext/vorbis/vorbisdec.c @@ -275,7 +275,7 @@ vorbis_dec_src_query (GstPad * pad, GstQuery * query) &value))) goto error; - value = (value - dec->segment_start) + dec->segment_time; + value = (value - dec->segment.start) + dec->segment.time; gst_query_set_position (query, format, value); @@ -426,25 +426,18 @@ vorbis_dec_sink_event (GstPad * pad, GstEvent * event) gst_event_parse_new_segment (event, &update, &rate, &format, &start, &stop, &time); + /* we need time and a positive rate for now */ if (format != GST_FORMAT_TIME) goto newseg_wrong_format; if (rate <= 0.0) goto newseg_wrong_rate; - /* now copy over the values */ - dec->segment_rate = rate; - dec->segment_start = start; - dec->segment_stop = stop; - dec->segment_time = time; + /* now configure the values */ + gst_segment_set_newsegment (&dec->segment, update, + rate, format, start, stop, time); - dec->granulepos = -1; - dec->cur_timestamp = GST_CLOCK_TIME_NONE; - dec->prev_timestamp = GST_CLOCK_TIME_NONE; - -#ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&dec->vd); -#endif + /* and forward */ ret = gst_pad_push_event (dec->srcpad, event); break; } @@ -659,6 +652,8 @@ header_read_error: } } +/* These samples can be outside of the float -1.0 -- 1.0 range, this + * is allowed, downstream elements are supposed to clip */ static void copy_samples (float *out, float **in, guint samples, gint channels) { @@ -714,11 +709,19 @@ vorbis_dec_push (GstVorbisDec * dec, GstBuffer * buf) GstBuffer *buffer = GST_BUFFER (walk->data); /* ignore the result */ + if (dec->discont) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); + dec->discont = FALSE; + } gst_pad_push (dec->srcpad, buffer); } g_list_free (dec->queued); dec->queued = NULL; } + if (dec->discont) { + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + dec->discont = FALSE; + } result = gst_pad_push (dec->srcpad, buf); } @@ -841,12 +844,18 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) ogg_packet packet; GstFlowReturn result = GST_FLOW_OK; - vd = GST_VORBIS_DEC (GST_PAD_PARENT (pad)); + vd = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - if (GST_BUFFER_SIZE (buffer) == 0) { - gst_buffer_unref (buffer); - GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), ("empty buffer received")); - return GST_FLOW_ERROR; + /* resync on DISCONT */ + if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) { + GST_DEBUG_OBJECT (vd, "received DISCONT buffer"); + vd->granulepos = -1; + vd->cur_timestamp = GST_CLOCK_TIME_NONE; + vd->prev_timestamp = GST_CLOCK_TIME_NONE; +#ifdef HAVE_VORBIS_SYNTHESIS_RESTART + vorbis_synthesis_restart (&vd->vd); +#endif + vd->discont = TRUE; } /* only ogg has granulepos, demuxers of other container formats @@ -876,6 +885,9 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) */ packet.e_o_s = 0; + if (packet.bytes < 1) + goto wrong_size; + GST_DEBUG_OBJECT (vd, "vorbis granule: %" G_GINT64_FORMAT, (gint64) packet.granulepos); @@ -895,8 +907,18 @@ vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) done: gst_buffer_unref (buffer); + gst_object_unref (vd); return result; + + /* ERRORS */ +wrong_size: + { + GST_WARNING_OBJECT (vd, "received empty packet"); + result = GST_FLOW_OK; + vd->discont = TRUE; + goto done; + } } static GstStateChangeReturn @@ -917,6 +939,7 @@ vorbis_dec_change_state (GstElement * element, GstStateChange transition) vd->prev_timestamp = GST_CLOCK_TIME_NONE; vd->granulepos = -1; vd->packetno = 0; + vd->discont = TRUE; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; diff --git a/ext/vorbis/vorbisdec.h b/ext/vorbis/vorbisdec.h index 1922f9b471..ec904e4f04 100644 --- a/ext/vorbis/vorbisdec.h +++ b/ext/vorbis/vorbisdec.h @@ -64,10 +64,8 @@ struct _GstVorbisDec { GList *queued; - gdouble segment_rate; - gint64 segment_start; - gint64 segment_stop; - gint64 segment_time; + GstSegment segment; + gboolean discont; GstClockTime cur_timestamp; /* only used with non-ogg container formats */ GstClockTime prev_timestamp; /* only used with non-ogg container formats */