diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index ad5ab26ed8..6596c15241 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -461,6 +461,9 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, cret = GST_FLOW_OK; + GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p", + ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length), + ogg->push_disable_seeking, ogg->building_chain); GST_PUSH_LOCK (ogg); if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING && ogg->push_time_length == GST_CLOCK_TIME_NONE @@ -1056,6 +1059,8 @@ gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets) break; case 1: GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes); + if (packet.bytes > ogg->max_packet_size) + ogg->max_packet_size = packet.bytes; result = gst_ogg_pad_submit_packet (pad, &packet); /* not linked is not a problem, it's possible that we are still * collecting headers and that we don't have exposed the pads yet */ @@ -1097,19 +1102,22 @@ gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg) GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target)); ogg->push_offset1 = ogg->push_last_seek_offset; ogg->push_time1 = ogg->push_last_seek_time; + ogg->seek_undershot = FALSE; } else { GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time)); ogg->push_offset0 = ogg->push_last_seek_offset; ogg->push_time0 = ogg->push_last_seek_time; + ogg->seek_undershot = TRUE; } } static gint64 -gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg) +gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality) { gint64 best; gint64 segment_bitrate; + gint64 skew; /* we might not know the length of the stream in time, so push_time1 might not be set */ @@ -1136,6 +1144,7 @@ gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg) ogg->push_offset0 + gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0, segment_bitrate, 8 * GST_SECOND); + ogg->seek_secant = TRUE; } else { GST_DEBUG_OBJECT (ogg, "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT @@ -1152,20 +1161,63 @@ gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg) "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1), segment_bitrate); + best = ogg->push_offset0 + gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0, segment_bitrate, 8 * GST_SECOND); + if (seek_quality < 0.5f && ogg->seek_secant) { + gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2; + /* if dire result, give as much as 25% weight to a dumb bisection guess */ + float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25; + new_best = (best * secant_weight + best2 * (1.0f - secant_weight)); + GST_DEBUG_OBJECT (ogg, + "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT + ", new best %" G_GINT64_FORMAT " with secant_weight %f", best, + best2, new_best, secant_weight); + best = new_best; + ogg->seek_secant = FALSE; + } else { + ogg->seek_secant = TRUE; + } } } - /* offset by typical page size */ - best -= CHUNKSIZE; + GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best); + + /* offset the guess down as we need to capture the start of the + page we are targetting - but only do so if we did not undershoot + last time, as we're likely to still do this time */ + if (!ogg->seek_undershot) { + /* very small packets are packed on pages, so offset by at least + a value which is likely to get us at least one page where the + packet starts */ + skew = + ogg->max_packet_size > + ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size; + GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew); + best -= skew; + } + + /* do not seek too close to the bounds, as we stop seeking + when we get to within max_packet_size before the target */ + if (best > ogg->push_offset1 - ogg->max_packet_size) { + best = ogg->push_offset1 - ogg->max_packet_size; + GST_DEBUG_OBJECT (ogg, + "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best); + } else if (best < ogg->push_offset0 + ogg->max_packet_size) { + best = ogg->push_offset0 + ogg->max_packet_size; + GST_DEBUG_OBJECT (ogg, + "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best); + } + + /* keep within bounds */ + if (best > ogg->push_offset1) + best = ogg->push_offset1; if (best < ogg->push_offset0) best = ogg->push_offset0; - if (best < 0) - best = 0; + GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best); return best; } @@ -1242,6 +1294,60 @@ gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg) return GST_FLOW_OK; } +static float +gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg) +{ + gint64 diff; /* how far from the goal we ended up */ + gint64 dist; /* how far we moved this iteration */ + float seek_quality; + + if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) { + /* for the first seek, we pretend we got a good seek, + as we don't have a previous seek yet */ + return 1.0f; + } + + /* We take a guess at how good the last seek was at guessing + the byte target by comparing the amplitude of the last + seek to the error */ + diff = ogg->push_seek_time_target - ogg->push_last_seek_time; + if (diff < 0) + diff = -diff; + dist = ogg->push_last_seek_time - ogg->push_prev_seek_time; + if (dist < 0) + dist = -dist; + + seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist); + + GST_DEBUG_OBJECT (ogg, + "We moved %" GST_TIME_FORMAT ", we're off by %" GST_TIME_FORMAT + ", seek quality %f", GST_TIME_ARGS (dist), GST_TIME_ARGS (diff), + seek_quality); + return seek_quality; +} + +static void +gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg) +{ + int n; + + GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps", + ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]); + + for (n = 0; n < 2; ++n) { + ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n]; + if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n]) + ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n]; + } + ogg->stats_nbisections++; + + GST_INFO_OBJECT (ogg, + "So far, %.2f + %.2f bisections needed per seek (max %d + %d)", + ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections, + ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections, + ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]); +} + static gboolean gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) { @@ -1317,6 +1423,7 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) GstEvent *sevent; int res; gboolean close_enough; + float seek_quality; /* ignore -1 granpos when seeking, we want to sync on a real granpos */ if (granpos < 0) { @@ -1354,14 +1461,18 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) GST_TIME_ARGS (ogg->push_seek_time_target)); if (ogg->push_time1 != GST_CLOCK_TIME_NONE) { + seek_quality = gst_ogg_demux_estimate_seek_quality (ogg); GST_DEBUG_OBJECT (ogg, "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT - " (%" GST_TIME_FORMAT ")", ogg->push_offset0, ogg->push_offset1, - ogg->push_offset1 - ogg->push_offset0, + " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0, + ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0, GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1), - GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0)); + GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality); } else { + /* in a open ended seek, we can't do bisection, so we pretend + we like our result so far */ + seek_quality = 1.0f; GST_DEBUG_OBJECT (ogg, "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown", @@ -1369,30 +1480,39 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) ogg->push_offset1 - ogg->push_offset0, GST_TIME_ARGS (ogg->push_time0)); } + ogg->push_prev_seek_time = ogg->push_last_seek_time; gst_ogg_demux_setup_bisection_bounds (ogg); - best = gst_ogg_demux_estimate_bisection_target (ogg); + best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality); if (ogg->push_seek_time_target == 0) { GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough"); close_enough = (ogg->push_last_seek_time == 0); } else { /* TODO: make this dependent on framerate ? */ - GstClockTime threshold = GST_SECOND / 2; + GstClockTime time_threshold = GST_SECOND / 2; + guint64 byte_threshold = + (ogg->max_packet_size > + 64 * 1024 ? ogg->max_packet_size : 64 * 1024); - /* We want to be within half a second before the target */ - if (threshold > ogg->push_seek_time_target) - threshold = ogg->push_seek_time_target; + /* We want to be within half a second before the target, + or before the target and half less or equal to the max + packet size left to search in */ + if (time_threshold > ogg->push_seek_time_target) + time_threshold = ogg->push_seek_time_target; close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target - && ogg->push_last_seek_time >= - ogg->push_seek_time_target - threshold; + && (ogg->push_last_seek_time >= + ogg->push_seek_time_target - time_threshold + || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold); GST_DEBUG_OBJECT (ogg, "testing if we're close enough: %" GST_TIME_FORMAT " <= %" - GST_TIME_FORMAT " < %" GST_TIME_FORMAT " ? %s", - GST_TIME_ARGS (ogg->push_seek_time_target - threshold), + GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT + " <= %" G_GUINT64_FORMAT " ? %s", + GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold), GST_TIME_ARGS (ogg->push_last_seek_time), GST_TIME_ARGS (ogg->push_seek_time_target), + ogg->push_offset1 - ogg->push_offset0, byte_threshold, close_enough ? "Yes" : "No"); } @@ -1422,14 +1542,13 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) GST_TIME_ARGS (ogg->push_seek_time_original_target)); ogg->push_state = PUSH_LINEAR2; } else { - GST_DEBUG_OBJECT (ogg, "Seek to keyframe done, playing"); + GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing"); /* we're synced to the seek target, so flush stream and stuff any queued pages into the stream so we start decoding there */ ogg->push_state = PUSH_PLAYING; } - GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps", - ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]); + gst_ogg_demux_update_bisection_stats (ogg); } } } else if (ogg->push_state == PUSH_LINEAR1) { @@ -1443,14 +1562,30 @@ gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page) GST_TIME_ARGS (pad->push_kf_time)); earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg); if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (ogg, - "All non sparse streams now have a previous keyframe time," - "bisecting again to %" GST_TIME_FORMAT, - GST_TIME_ARGS (earliest_keyframe_time)); - ogg->push_seek_time_target = earliest_keyframe_time; + if (earliest_keyframe_time > ogg->push_last_seek_time) { + GST_INFO_OBJECT (ogg, + "All non sparse streams now have a previous keyframe time, " + "and we already decoded it, switching to playing"); + ogg->push_state = PUSH_PLAYING; + gst_ogg_demux_update_bisection_stats (ogg); + } else { + GST_INFO_OBJECT (ogg, + "All non sparse streams now have a previous keyframe time, " + "bisecting again to %" GST_TIME_FORMAT, + GST_TIME_ARGS (earliest_keyframe_time)); - ogg->push_state = PUSH_BISECT2; - best = gst_ogg_demux_estimate_bisection_target (ogg); + ogg->push_seek_time_target = earliest_keyframe_time; + ogg->push_offset0 = 0; + ogg->push_time0 = ogg->push_start_time; + ogg->push_offset1 = ogg->push_last_seek_offset; + ogg->push_time1 = ogg->push_last_seek_time; + ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE; + ogg->seek_secant = FALSE; + ogg->seek_undershot = FALSE; + + ogg->push_state = PUSH_BISECT2; + best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f); + } } } } @@ -1555,6 +1690,9 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) return result; } + if (page->header_len + page->body_len > ogg->max_page_size) + ogg->max_page_size = page->header_len + page->body_len; + if (ogg_stream_pagein (&pad->map.stream, page) != 0) goto choked; @@ -1837,6 +1975,12 @@ gst_ogg_demux_init (GstOggDemux * ogg) ogg->push_lock = g_mutex_new (); ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *)); + ogg->stats_nbisections = 0; + ogg->stats_bisection_steps[0] = 0; + ogg->stats_bisection_steps[1] = 0; + ogg->stats_bisection_max_steps[0] = 0; + ogg->stats_bisection_max_steps[1] = 0; + ogg->newsegment = NULL; } @@ -3233,8 +3377,11 @@ gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event) ogg->push_time0 = ogg->push_start_time; ogg->push_time1 = ogg->push_time_length; ogg->push_seek_time_target = start; + ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE; ogg->push_seek_time_original_target = start; ogg->push_state = PUSH_BISECT1; + ogg->seek_secant = FALSE; + ogg->seek_undershot = FALSE; /* reset pad push mode seeking state */ for (i = 0; i < chain->streams->len; i++) { diff --git a/ext/ogg/gstoggdemux.h b/ext/ogg/gstoggdemux.h index 050caf20de..62e678c28e 100644 --- a/ext/ogg/gstoggdemux.h +++ b/ext/ogg/gstoggdemux.h @@ -145,6 +145,10 @@ struct _GstOggDemux gboolean need_chains; gboolean resync; + /* keep track of how large pages and packets are, + useful for skewing when seeking */ + guint64 max_packet_size, max_page_size; + /* state */ GMutex *chain_lock; /* we need the lock to protect the chains */ GArray *chains; /* list of chains we know */ @@ -185,8 +189,14 @@ struct _GstOggDemux GstSeekFlags push_seek_flags; GstEvent *push_mode_seek_delayed_event; gboolean push_disable_seeking; + gboolean seek_secant; + gboolean seek_undershot; + GstClockTime push_prev_seek_time; gint push_bisection_steps[2]; + gint stats_bisection_steps[2]; + gint stats_bisection_max_steps[2]; + gint stats_nbisections; /* ogg stuff */ ogg_sync_state sync; diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 922409b9cd..ebdcd3159d 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -448,7 +448,7 @@ gst_ogg_mux_request_new_pad (GstElement * element, oggpad = (GstOggPadData *) gst_collect_pads2_add_pad_full (ogg_mux->collect, newpad, - sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify, TRUE); + sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify, FALSE); ogg_mux->active_pads++; oggpad->map.serialno = serial; @@ -968,6 +968,12 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped) } else { GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT, pad->map.caps); + + if (pad->map.is_sparse) { + GST_DEBUG_OBJECT (pad, "Pad is sparse, marking as such"); + gst_collect_pads2_set_waiting (ogg_mux->collect, + (GstCollectData2 *) pad, FALSE); + } } if (caps) gst_caps_unref (caps); @@ -1325,8 +1331,8 @@ gst_ogg_mux_send_headers (GstOggMux * mux) GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad)); - /* if the pad has no buffer, we don't care */ - if (pad->buffer == NULL) + /* if the pad has no buffer and is not sparse, we don't care */ + if (pad->buffer == NULL && !pad->map.is_sparse) continue; /* now figure out the headers */ diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 9678a31b9e..81799bd607 100644 --- a/ext/vorbis/Makefile.am +++ b/ext/vorbis/Makefile.am @@ -11,7 +11,7 @@ libgstvorbis_la_SOURCES = gstvorbis.c \ gstvorbistag.c \ gstvorbiscommon.c -libgstvorbis_la_CFLAGS = -DGST_USE_UNSTABLE_API \ +libgstvorbis_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(VORBIS_CFLAGS) ## AM_PATH_VORBIS also sets VORBISENC_LIBS libgstvorbis_la_LIBADD = \ @@ -28,7 +28,7 @@ plugin_LTLIBRARIES += libgstivorbisdec.la libgstivorbisdec_la_SOURCES = gstivorbisdec.c \ gstvorbisdec.c gstvorbisdeclib.c gstvorbiscommon.c -libgstivorbisdec_la_CFLAGS = -DGST_USE_UNSTABLE_API \ +libgstivorbisdec_la_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ -DTREMOR $(IVORBIS_CFLAGS) libgstivorbisdec_la_LIBADD = \ diff --git a/ext/vorbis/gstvorbisenc.c b/ext/vorbis/gstvorbisenc.c index 5a13ffd9e9..c699d5f0e6 100644 --- a/ext/vorbis/gstvorbisenc.c +++ b/ext/vorbis/gstvorbisenc.c @@ -65,7 +65,7 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " GST_AUDIO_NE (F32) ", " - "rate = (int) [ 1, 200000 ], " "channels = (int) [ 1, 256 ]") + "rate = (int) [ 1, 200000 ], " "channels = (int) [ 1, 255 ]") ); static GstStaticPadTemplate vorbis_enc_src_factory = @@ -295,7 +295,7 @@ gst_vorbis_enc_generate_sink_caps (void) gst_caps_append_structure (caps, gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "rate", GST_TYPE_INT_RANGE, 1, 200000, - "channels", GST_TYPE_INT_RANGE, 9, 256, NULL)); + "channels", GST_TYPE_INT_RANGE, 9, 255, NULL)); return caps; } diff --git a/gst-libs/gst/audio/Makefile.am b/gst-libs/gst/audio/Makefile.am index 7dc94d5219..f49d4059f4 100644 --- a/gst-libs/gst/audio/Makefile.am +++ b/gst-libs/gst/audio/Makefile.am @@ -80,7 +80,6 @@ GstAudio-@GST_MAJORMINOR@.gir: $(INTROSPECTION_SCANNER) libgstaudio-@GST_MAJORMI --nsversion=@GST_MAJORMINOR@ \ --warn-all \ --strip-prefix=Gst \ - -DGST_USE_UNSTABLE_API \ -I$(top_srcdir)/gst-libs \ -I$(top_builddir)/gst-libs \ $(gir_cincludes) \ diff --git a/gst-libs/gst/audio/gstaudiodecoder.h b/gst-libs/gst/audio/gstaudiodecoder.h index fe3290b264..1e9bf4532e 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.h +++ b/gst-libs/gst/audio/gstaudiodecoder.h @@ -23,11 +23,6 @@ #ifndef _GST_AUDIO_DECODER_H_ #define _GST_AUDIO_DECODER_H_ -#ifndef GST_USE_UNSTABLE_API -#warning "GstAudioDecoder is unstable API and may change in future." -#warning "You can define GST_USE_UNSTABLE_API to avoid this warning." -#endif - #include #include #include diff --git a/gst-libs/gst/audio/gstaudioencoder.h b/gst-libs/gst/audio/gstaudioencoder.h index 6b02024d00..94bc89e163 100644 --- a/gst-libs/gst/audio/gstaudioencoder.h +++ b/gst-libs/gst/audio/gstaudioencoder.h @@ -22,11 +22,6 @@ #ifndef __GST_AUDIO_ENCODER_H__ #define __GST_AUDIO_ENCODER_H__ -#ifndef GST_USE_UNSTABLE_API -#warning "GstAudioEncoder is unstable API and may change in future." -#warning "You can define GST_USE_UNSTABLE_API to avoid this warning." -#endif - #include #include diff --git a/gst-libs/gst/riff/riff-ids.h b/gst-libs/gst/riff/riff-ids.h index 8771d2eb6d..106f71aa7b 100644 --- a/gst-libs/gst/riff/riff-ids.h +++ b/gst-libs/gst/riff/riff-ids.h @@ -46,6 +46,8 @@ G_BEGIN_DECLS #define GST_RIFF_TAG_JUNQ GST_MAKE_FOURCC ('J','U','N','Q') #define GST_RIFF_TAG_idx1 GST_MAKE_FOURCC ('i','d','x','1') #define GST_RIFF_TAG_dmlh GST_MAKE_FOURCC ('d','m','l','h') +#define GST_RIFF_TAG_ID32 GST_MAKE_FOURCC ('I','D','3','2') +#define GST_RIFF_TAG_IDVX GST_MAKE_FOURCC ('I','D','V','X') /* WAV stuff */ #define GST_RIFF_TAG_fmt GST_MAKE_FOURCC ('f','m','t',' ') #define GST_RIFF_TAG_data GST_MAKE_FOURCC ('d','a','t','a') diff --git a/gst/playback/gstdecodebin2.c b/gst/playback/gstdecodebin2.c index 0caa18453c..75de91959b 100644 --- a/gst/playback/gstdecodebin2.c +++ b/gst/playback/gstdecodebin2.c @@ -173,8 +173,6 @@ struct _GstDecodeBin gboolean expose_allstreams; /* Whether to expose unknow type streams or not */ - gboolean upstream_seekable; /* if upstream is seekable */ - GList *filtered; /* elements for which error messages are filtered */ }; @@ -268,7 +266,7 @@ static void type_found (GstElement * typefind, guint probability, GstCaps * caps, GstDecodeBin * decode_bin); static void decodebin_set_queue_size (GstDecodeBin * dbin, - GstElement * multiqueue, gboolean preroll); + GstElement * multiqueue, gboolean preroll, gboolean seekable); static gboolean gst_decode_bin_autoplug_continue (GstElement * element, GstPad * pad, GstCaps * caps); @@ -293,6 +291,8 @@ static GstStateChangeReturn gst_decode_bin_change_state (GstElement * element, GstStateChange transition); static void gst_decode_bin_handle_message (GstBin * bin, GstMessage * message); +static gboolean check_upstream_seekable (GstDecodeBin * dbin, GstPad * pad); + #define EXPOSE_LOCK(dbin) G_STMT_START { \ GST_LOG_OBJECT (dbin, \ "expose locking from thread %p", \ @@ -393,6 +393,7 @@ struct _GstDecodeChain GstPad *pad; /* srcpad that caused creation of this chain */ gboolean demuxer; /* TRUE if elements->data is a demuxer */ + gboolean seekable; /* TRUE if this chain ends on a demuxer and is seekable */ GList *elements; /* All elements in this group, first is the latest and most downstream element */ @@ -2203,40 +2204,39 @@ beach: * * Check if upstream is seekable. */ -static void +static gboolean check_upstream_seekable (GstDecodeBin * dbin, GstPad * pad) { GstQuery *query; gint64 start = -1, stop = -1; - - dbin->upstream_seekable = FALSE; + gboolean seekable = FALSE; query = gst_query_new_seeking (GST_FORMAT_BYTES); if (!gst_pad_peer_query (pad, query)) { GST_DEBUG_OBJECT (dbin, "seeking query failed"); gst_query_unref (query); - return; + return FALSE; } - gst_query_parse_seeking (query, NULL, &dbin->upstream_seekable, - &start, &stop); + gst_query_parse_seeking (query, NULL, &seekable, &start, &stop); gst_query_unref (query); /* try harder to query upstream size if we didn't get it the first time */ - if (dbin->upstream_seekable && stop == -1) { + if (seekable && stop == -1) { GST_DEBUG_OBJECT (dbin, "doing duration query to fix up unset stop"); gst_pad_peer_query_duration (pad, GST_FORMAT_BYTES, &stop); } /* if upstream doesn't know the size, it's likely that it's not seekable in * practice even if it technically may be seekable */ - if (dbin->upstream_seekable && (start != 0 || stop <= start)) { + if (seekable && (start != 0 || stop <= start)) { GST_DEBUG_OBJECT (dbin, "seekable but unknown start/stop -> disable"); - dbin->upstream_seekable = FALSE; + return FALSE; } - GST_DEBUG_OBJECT (dbin, "upstream seekable: %d", dbin->upstream_seekable); + GST_DEBUG_OBJECT (dbin, "upstream seekable: %d", seekable); + return seekable; } static void @@ -2267,10 +2267,6 @@ type_found (GstElement * typefind, guint probability, pad = gst_element_get_static_pad (typefind, "src"); sink_pad = gst_element_get_static_pad (typefind, "sink"); - /* if upstream is seekable we can safely set a limit in time to the queues so - * that streams at low bitrates can preroll */ - check_upstream_seekable (decode_bin, sink_pad); - /* need some lock here to prevent race with shutdown state change * which might yank away e.g. decode_chain while building stuff here. * In typical cases, STREAM_LOCK is held and handles that, it need not @@ -2404,7 +2400,8 @@ no_more_pads_cb (GstElement * element, GstDecodeChain * chain) * we can probably set its buffering state to playing now */ GST_DEBUG_OBJECT (group->dbin, "Setting group %p multiqueue to " "'playing' buffering mode", group); - decodebin_set_queue_size (group->dbin, group->multiqueue, FALSE); + decodebin_set_queue_size (group->dbin, group->multiqueue, FALSE, + (group->parent ? group->parent->seekable : TRUE)); CHAIN_MUTEX_UNLOCK (chain); EXPOSE_LOCK (chain->dbin); @@ -2753,6 +2750,12 @@ multi_queue_overrun_cb (GstElement * queue, GstDecodeGroup * group) queue); group->overrun = TRUE; + /* this group has prerolled enough to not need more pads, + * we can probably set its buffering state to playing now */ + GST_DEBUG_OBJECT (group->dbin, "Setting group %p multiqueue to " + "'playing' buffering mode", group); + decodebin_set_queue_size (group->dbin, group->multiqueue, FALSE, + (group->parent ? group->parent->seekable : TRUE)); /* FIXME: We should make sure that everything gets exposed now * even if child chains are not complete because the will never @@ -2858,7 +2861,7 @@ gst_decode_group_hide (GstDecodeGroup * group) * playing or prerolling. */ static void decodebin_set_queue_size (GstDecodeBin * dbin, GstElement * multiqueue, - gboolean preroll) + gboolean preroll, gboolean seekable) { guint max_bytes, max_buffers; guint64 max_time; @@ -2871,7 +2874,7 @@ decodebin_set_queue_size (GstDecodeBin * dbin, GstElement * multiqueue, if ((max_buffers = dbin->max_size_buffers) == 0) max_buffers = AUTO_PREROLL_SIZE_BUFFERS; if ((max_time = dbin->max_size_time) == 0) - max_time = dbin->upstream_seekable ? AUTO_PREROLL_SEEKABLE_SIZE_TIME : + max_time = seekable ? AUTO_PREROLL_SEEKABLE_SIZE_TIME : AUTO_PREROLL_NOT_SEEKABLE_SIZE_TIME; } else { /* update runtime limits. At runtime, we try to keep the amount of buffers @@ -2901,6 +2904,7 @@ gst_decode_group_new (GstDecodeBin * dbin, GstDecodeChain * parent) { GstDecodeGroup *group = g_slice_new0 (GstDecodeGroup); GstElement *mq; + gboolean seekable; GST_DEBUG_OBJECT (dbin, "Creating new group %p with parent chain %p", group, parent); @@ -2921,7 +2925,17 @@ gst_decode_group_new (GstDecodeBin * dbin, GstDecodeChain * parent) } /* configure queue sizes for preroll */ - decodebin_set_queue_size (dbin, mq, TRUE); + seekable = FALSE; + if (parent && parent->demuxer) { + GstElement *element = + ((GstDecodeElement *) parent->elements->data)->element; + GstPad *pad = gst_element_get_static_pad (element, "sink"); + if (pad) { + seekable = parent->seekable = check_upstream_seekable (dbin, pad); + gst_object_unref (pad); + } + } + decodebin_set_queue_size (dbin, mq, TRUE, seekable); group->overrunsig = g_signal_connect (G_OBJECT (mq), "overrun", G_CALLBACK (multi_queue_overrun_cb), group); @@ -3682,7 +3696,7 @@ gst_decode_chain_expose (GstDecodeChain * chain, GList ** endpads, dbin = group->dbin; /* configure queues for playback */ - decodebin_set_queue_size (dbin, group->multiqueue, FALSE); + decodebin_set_queue_size (dbin, group->multiqueue, FALSE, TRUE); /* we can now disconnect any overrun signal, which is used to expose the * group. */ diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index 6d0ed79b6f..dcdcb02ac1 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -825,6 +825,8 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * * This signal is emitted when the current uri is about to finish. You can * set the uri and suburi to make sure that playback continues. + * + * This signal is emitted from the context of a GStreamer streaming thread. */ gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH] = g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass), @@ -839,7 +841,12 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the number or order of the video * streams has changed. The application will most likely want to select * a new video stream. + * + * This signal is usually emitted from the context of a GStreamer streaming + * thread. You can use gst_message_new_application() and + * gst_element_post_message() to notify your application's main thread. */ + /* FIXME 0.11: turn video-changed signal into message? */ gst_play_bin_signals[SIGNAL_VIDEO_CHANGED] = g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -852,7 +859,12 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the number or order of the audio * streams has changed. The application will most likely want to select * a new audio stream. + * + * This signal may be emitted from the context of a GStreamer streaming thread. + * You can use gst_message_new_application() and gst_element_post_message() + * to notify your application's main thread. */ + /* FIXME 0.11: turn audio-changed signal into message? */ gst_play_bin_signals[SIGNAL_AUDIO_CHANGED] = g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -865,7 +877,12 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the number or order of the text * streams has changed. The application will most likely want to select * a new text stream. + * + * This signal may be emitted from the context of a GStreamer streaming thread. + * You can use gst_message_new_application() and gst_element_post_message() + * to notify your application's main thread. */ + /* FIXME 0.11: turn text-changed signal into message? */ gst_play_bin_signals[SIGNAL_TEXT_CHANGED] = g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -880,6 +897,10 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the tags of a video stream have changed. * The application will most likely want to get the new tags. * + * This signal may be emitted from the context of a GStreamer streaming thread. + * You can use gst_message_new_application() and gst_element_post_message() + * to notify your application's main thread. + * * Since: 0.10.24 */ gst_play_bin_signals[SIGNAL_VIDEO_TAGS_CHANGED] = @@ -896,6 +917,10 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the tags of an audio stream have changed. * The application will most likely want to get the new tags. * + * This signal may be emitted from the context of a GStreamer streaming thread. + * You can use gst_message_new_application() and gst_element_post_message() + * to notify your application's main thread. + * * Since: 0.10.24 */ gst_play_bin_signals[SIGNAL_AUDIO_TAGS_CHANGED] = @@ -912,6 +937,10 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * This signal is emitted whenever the tags of a text stream have changed. * The application will most likely want to get the new tags. * + * This signal may be emitted from the context of a GStreamer streaming thread. + * You can use gst_message_new_application() and gst_element_post_message() + * to notify your application's main thread. + * * Since: 0.10.24 */ gst_play_bin_signals[SIGNAL_TEXT_TAGS_CHANGED] = @@ -931,6 +960,9 @@ gst_play_bin_class_init (GstPlayBinClass * klass) * an audio cd source). This is functionally equivalent to connecting to * the notify::source signal, but more convenient. * + * This signal is usually emitted from the context of a GStreamer streaming + * thread. + * * Since: 0.10.33 */ gst_play_bin_signals[SIGNAL_SOURCE_SETUP] = diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index f8783d067b..a0756bc476 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -179,12 +179,12 @@ gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass) g_object_class_install_property (gobject_class, PROP_USE_CONVERTERS, g_param_spec_boolean ("use-converters", "Use converters", "Whether to use conversion elements", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_USE_VOLUME, g_param_spec_boolean ("use-volume", "Use volume", "Whether to use a volume element", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_set_details_simple (gstelement_class, "Player Sink Audio Converter", "Audio/Bin/Converter", diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index ba5efaa463..d5479e605c 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -137,7 +137,8 @@ gst_play_sink_convert_bin_add_identity (GstPlaySinkConvertBin * self) ); } else { - g_object_set (self->identity, "silent", TRUE, NULL); + g_object_set (self->identity, "silent", TRUE, "signal-handoffs", FALSE, + NULL); gst_bin_add (GST_BIN_CAST (self), self->identity); } } @@ -335,11 +336,23 @@ gst_play_sink_convert_bin_sink_setcaps (GstPlaySinkConvertBin * self, GST_DEBUG_OBJECT (self, "raw %d, self->raw %d, blocked %d", raw, self->raw, gst_pad_is_blocked (self->sink_proxypad)); + if (raw) { - if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { - GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); - reconfigure = TRUE; - block_proxypad (self); + if (!gst_pad_is_blocked (self->sink_proxypad)) { + GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (self->sinkpad)); + + if (!self->raw || (target && !gst_pad_query_accept_caps (target, caps))) { + if (!self->raw) + GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw"); + else + GST_DEBUG_OBJECT (self, "Changing caps in an incompatible way"); + + reconfigure = TRUE; + block_proxypad (self); + } + + if (target) + gst_object_unref (target); } } else { if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) { @@ -402,6 +415,22 @@ gst_play_sink_convert_bin_getcaps (GstPad * pad, GstCaps * filter) gst_object_unref (self); + GST_DEBUG_OBJECT (pad, "Returning caps %" GST_PTR_FORMAT, ret); + + return ret; +} + +static gboolean +gst_play_sink_convert_bin_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstCaps *allowed_caps; + gboolean ret; + + allowed_caps = gst_pad_query_caps (pad, NULL); + /* FIXME 0.11: Should be a subset check now */ + ret = gst_caps_can_intersect (caps, allowed_caps); + gst_caps_unref (allowed_caps); + return ret; } @@ -412,6 +441,16 @@ gst_play_sink_convert_bin_query (GstPad * pad, GstObject * parent, gboolean res = FALSE; switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ACCEPT_CAPS: + { + GstCaps *caps; + + gst_query_parse_accept_caps (query, &caps); + gst_query_set_accept_caps_result (query, + gst_play_sink_convert_bin_acceptcaps (pad, caps)); + res = TRUE; + break; + } case GST_QUERY_CAPS: { GstCaps *filter, *caps; diff --git a/gst/videorate/gstvideorate.c b/gst/videorate/gstvideorate.c index 3317af2521..8313eb69d8 100644 --- a/gst/videorate/gstvideorate.c +++ b/gst/videorate/gstvideorate.c @@ -92,18 +92,17 @@ enum enum { - ARG_0, - ARG_IN, - ARG_OUT, - ARG_DUP, - ARG_DROP, - ARG_SILENT, - ARG_NEW_PREF, - ARG_SKIP_TO_FIRST, - ARG_DROP_ONLY, - ARG_AVERAGE_PERIOD, - ARG_MAX_RATE - /* FILL ME */ + PROP_0, + PROP_IN, + PROP_OUT, + PROP_DUP, + PROP_DROP, + PROP_SILENT, + PROP_NEW_PREF, + PROP_SKIP_TO_FIRST, + PROP_DROP_ONLY, + PROP_AVERAGE_PERIOD, + PROP_MAX_RATE }; static GstStaticPadTemplate gst_video_rate_src_template = @@ -172,25 +171,25 @@ gst_video_rate_class_init (GstVideoRateClass * klass) base_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_video_rate_fixate_caps); base_class->query = GST_DEBUG_FUNCPTR (gst_video_rate_query); - g_object_class_install_property (object_class, ARG_IN, + g_object_class_install_property (object_class, PROP_IN, g_param_spec_uint64 ("in", "In", "Number of input frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, ARG_OUT, + g_object_class_install_property (object_class, PROP_OUT, g_param_spec_uint64 ("out", "Out", "Number of output frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); pspec_duplicate = g_param_spec_uint64 ("duplicate", "Duplicate", "Number of duplicated frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, ARG_DUP, pspec_duplicate); + g_object_class_install_property (object_class, PROP_DUP, pspec_duplicate); pspec_drop = g_param_spec_uint64 ("drop", "Drop", "Number of dropped frames", 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, ARG_DROP, pspec_drop); - g_object_class_install_property (object_class, ARG_SILENT, + g_object_class_install_property (object_class, PROP_DROP, pspec_drop); + g_object_class_install_property (object_class, PROP_SILENT, g_param_spec_boolean ("silent", "silent", "Don't emit notify for dropped and duplicated frames", DEFAULT_SILENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, ARG_NEW_PREF, + g_object_class_install_property (object_class, PROP_NEW_PREF, g_param_spec_double ("new-pref", "New Pref", "Value indicating how much to prefer new frames (unused)", 0.0, 1.0, DEFAULT_NEW_PREF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -202,7 +201,7 @@ gst_video_rate_class_init (GstVideoRateClass * klass) * * Since: 0.10.25 */ - g_object_class_install_property (object_class, ARG_SKIP_TO_FIRST, + g_object_class_install_property (object_class, PROP_SKIP_TO_FIRST, g_param_spec_boolean ("skip-to-first", "Skip to first buffer", "Don't produce buffers before the first one we receive", DEFAULT_SKIP_TO_FIRST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -214,7 +213,7 @@ gst_video_rate_class_init (GstVideoRateClass * klass) * * Since: 0.10.36 */ - g_object_class_install_property (object_class, ARG_DROP_ONLY, + g_object_class_install_property (object_class, PROP_DROP_ONLY, g_param_spec_boolean ("drop-only", "Only Drop", "Only drop frames, no duplicates are produced", DEFAULT_DROP_ONLY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -228,7 +227,7 @@ gst_video_rate_class_init (GstVideoRateClass * klass) * * Since: 0.10.36 */ - g_object_class_install_property (object_class, ARG_AVERAGE_PERIOD, + g_object_class_install_property (object_class, PROP_AVERAGE_PERIOD, g_param_spec_uint64 ("average-period", "Period over which to average", "Period over which to average the framerate (in ns) (0 = disabled)", 0, G_MAXINT64, DEFAULT_AVERAGE_PERIOD, @@ -241,7 +240,7 @@ gst_video_rate_class_init (GstVideoRateClass * klass) * * Since: 0.10.36 */ - g_object_class_install_property (object_class, ARG_MAX_RATE, + g_object_class_install_property (object_class, PROP_MAX_RATE, g_param_spec_int ("max-rate", "maximum framerate", "Maximum framerate allowed to pass through " "(in frames per second, implies drop-only)", @@ -1123,23 +1122,23 @@ gst_video_rate_set_property (GObject * object, GST_OBJECT_LOCK (videorate); switch (prop_id) { - case ARG_SILENT: + case PROP_SILENT: videorate->silent = g_value_get_boolean (value); break; - case ARG_NEW_PREF: + case PROP_NEW_PREF: videorate->new_pref = g_value_get_double (value); break; - case ARG_SKIP_TO_FIRST: + case PROP_SKIP_TO_FIRST: videorate->skip_to_first = g_value_get_boolean (value); break; - case ARG_DROP_ONLY: + case PROP_DROP_ONLY: videorate->drop_only = g_value_get_boolean (value); goto reconfigure; break; - case ARG_AVERAGE_PERIOD: + case PROP_AVERAGE_PERIOD: videorate->average_period_set = g_value_get_uint64 (value); break; - case ARG_MAX_RATE: + case PROP_MAX_RATE: g_atomic_int_set (&videorate->max_rate, g_value_get_int (value)); goto reconfigure; break; @@ -1163,34 +1162,34 @@ gst_video_rate_get_property (GObject * object, GST_OBJECT_LOCK (videorate); switch (prop_id) { - case ARG_IN: + case PROP_IN: g_value_set_uint64 (value, videorate->in); break; - case ARG_OUT: + case PROP_OUT: g_value_set_uint64 (value, videorate->out); break; - case ARG_DUP: + case PROP_DUP: g_value_set_uint64 (value, videorate->dup); break; - case ARG_DROP: + case PROP_DROP: g_value_set_uint64 (value, videorate->drop); break; - case ARG_SILENT: + case PROP_SILENT: g_value_set_boolean (value, videorate->silent); break; - case ARG_NEW_PREF: + case PROP_NEW_PREF: g_value_set_double (value, videorate->new_pref); break; - case ARG_SKIP_TO_FIRST: + case PROP_SKIP_TO_FIRST: g_value_set_boolean (value, videorate->skip_to_first); break; - case ARG_DROP_ONLY: + case PROP_DROP_ONLY: g_value_set_boolean (value, videorate->drop_only); break; - case ARG_AVERAGE_PERIOD: + case PROP_AVERAGE_PERIOD: g_value_set_uint64 (value, videorate->average_period_set); break; - case ARG_MAX_RATE: + case PROP_MAX_RATE: g_value_set_int (value, g_atomic_int_get (&videorate->max_rate)); break; default: