Merge remote-tracking branch 'origin/master' into 0.11

Conflicts:
	ext/vorbis/gstvorbisenc.c
	gst/playback/gstdecodebin2.c
	gst/playback/gstplaysinkconvertbin.c
	gst/videorate/gstvideorate.c
This commit is contained in:
Tim-Philipp Müller 2011-11-26 12:12:59 +00:00
commit 32b14c6ed3
14 changed files with 350 additions and 112 deletions

View file

@ -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++) {

View file

@ -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;

View file

@ -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 */

View file

@ -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 = \

View file

@ -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;
}

View file

@ -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) \

View file

@ -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 <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/base/gstadapter.h>

View file

@ -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 <gst/gst.h>
#include <gst/audio/audio.h>

View file

@ -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')

View file

@ -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. */

View file

@ -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] =

View file

@ -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",

View file

@ -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;

View file

@ -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: