diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c index 31745aafcb..8a828dc56e 100644 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -1370,7 +1370,7 @@ gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment) return FALSE; } - if (segment->rate != 1.0 || segment->format != GST_FORMAT_BYTES) { + if (segment->rate < 0.0 || segment->format != GST_FORMAT_BYTES) { GST_WARNING_OBJECT (src, "Invalid seek segment"); return FALSE; } diff --git a/gst/audioparsers/gstamrparse.c b/gst/audioparsers/gstamrparse.c index f0b54eac72..c5ad92046f 100644 --- a/gst/audioparsers/gstamrparse.c +++ b/gst/audioparsers/gstamrparse.c @@ -296,12 +296,28 @@ gst_amr_parse_check_valid_frame (GstBaseParse * parse, * to contain a valid header as well (and there is enough data to * perform this check) */ - if (fsize && - (!GST_BASE_PARSE_LOST_SYNC (parse) || GST_BASE_PARSE_DRAINING (parse) - || (dsize > fsize && (data[fsize] & 0x83) == 0))) { - *framesize = fsize; - ret = TRUE; - goto done; + if (fsize) { + gboolean found = FALSE; + + /* in sync, no further check */ + if (!GST_BASE_PARSE_LOST_SYNC (parse)) { + found = TRUE; + } else if (dsize > fsize) { + /* enough data, check for next sync */ + if ((data[fsize] & 0x83) == 0) + found = TRUE; + } else if (GST_BASE_PARSE_DRAINING (parse)) { + /* not enough, but draining, so ok */ + found = TRUE; + } else { + /* indicate we need not skip, but need more data */ + *skipsize = 0; + *framesize = fsize + 1; + } + if (found) { + *framesize = fsize; + return TRUE; + } } } GST_LOG ("sync lost"); diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c index d81c2dcb2a..23adee23f4 100644 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -318,7 +318,7 @@ struct _QtDemuxStream guint32 stts_samples; guint32 n_sample_times; guint32 stts_sample_index; - guint32 stts_time; + guint64 stts_time; guint32 stts_duration; /* stss */ gboolean stss_present; @@ -1311,7 +1311,10 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT, GST_TIME_ARGS (desired_offset)); - if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) { + /* may not have enough fragmented info to do this adjustment, + * and we can't scan (and probably should not) at this time with + * possibly flushing upstream */ + if ((segment->flags & GST_SEEK_FLAG_KEY_UNIT) && !qtdemux->fragmented) { gint64 min_offset; gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL); @@ -1864,6 +1867,8 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE; qtdemux->seek_offset = 0; + qtdemux->upstream_seekable = FALSE; + qtdemux->upstream_size = 0; break; } default: @@ -2103,6 +2108,14 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, stream->track_id, d_sample_duration, d_sample_size, d_sample_flags, *base_offset); + /* presence of stss or not can't really tell us much, + * and flags and so on tend to be marginally reliable in these files */ + if (stream->subtype == FOURCC_soun) { + GST_DEBUG_OBJECT (qtdemux, + "sound track in fragmented file; marking all keyframes"); + stream->all_keyframe = TRUE; + } + if (!gst_byte_reader_skip (trun, 1) || !gst_byte_reader_get_uint24_be (trun, &flags)) goto fail; @@ -3971,6 +3984,47 @@ qtdemux_seek_offset (GstQTDemux * demux, guint64 offset) return res; } +/* check for seekable upstream, above and beyond a mere query */ +static void +gst_qtdemux_check_seekability (GstQTDemux * demux) +{ + GstQuery *query; + gboolean seekable = FALSE; + gint64 start = -1, stop = -1; + + if (demux->upstream_size) + return; + + query = gst_query_new_seeking (GST_FORMAT_BYTES); + if (!gst_pad_peer_query (demux->sinkpad, query)) { + GST_DEBUG_OBJECT (demux, "seeking query failed"); + goto done; + } + + gst_query_parse_seeking (query, NULL, &seekable, &start, &stop); + + /* try harder to query upstream size if we didn't get it the first time */ + if (seekable && stop == -1) { + GST_DEBUG_OBJECT (demux, "doing duration query to fix up unset stop"); + gst_pad_query_peer_duration (demux->sinkpad, 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 (seekable && (start != 0 || stop <= start)) { + GST_DEBUG_OBJECT (demux, "seekable but unknown start/stop -> disable"); + seekable = FALSE; + } + +done: + gst_query_unref (query); + + GST_DEBUG_OBJECT (demux, "seekable: %d (%" G_GUINT64_FORMAT " - %" + G_GUINT64_FORMAT ")", seekable, start, stop); + demux->upstream_seekable = seekable; + demux->upstream_size = seekable ? stop : -1; +} + /* FIXME, unverified after edit list updates */ static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) @@ -4002,6 +4056,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) guint32 fourcc; guint64 size; + gst_qtdemux_check_seekability (demux); + data = gst_adapter_map (demux->adapter, demux->neededbytes); /* get fourcc/length, set neededbytes */ @@ -4040,7 +4096,15 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) target = old + size; /* try to jump over the atom with a seek */ - res = qtdemux_seek_offset (demux, target); + /* only bother if it seems worth doing so, + * and avoids possible upstream/server problems */ + if (demux->upstream_seekable && + demux->upstream_size > 4 * (1 << 20)) { + res = qtdemux_seek_offset (demux, target); + } else { + GST_DEBUG_OBJECT (demux, "skipping seek"); + res = FALSE; + } if (res) { GST_DEBUG_OBJECT (demux, "seek success"); @@ -4058,7 +4122,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) } else { /* seek failed, need to buffer */ demux->offset = old; - GST_DEBUG_OBJECT (demux, "seek failed"); + GST_DEBUG_OBJECT (demux, "seek failed/skipped"); /* there may be multiple mdat (or alike) buffers */ /* sanity check */ if (demux->mdatbuffer) @@ -4153,6 +4217,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) * put preceding buffered data ahead of current moov data. * This should also handle evil mdat, moov, mdat cases and alike */ gst_adapter_clear (demux->adapter); + gst_adapter_push (demux->adapter, demux->mdatbuffer); demux->mdatbuffer = NULL; demux->offset = demux->mdatoffset; demux->neededbytes = next_entry_size (demux); @@ -5640,8 +5705,8 @@ done2: for (i = stream->stts_index; i < n_sample_times; i++) { guint32 stts_samples; - guint32 stts_duration; - guint32 stts_time; + gint32 stts_duration; + gint64 stts_time; if (stream->stts_sample_index >= stream->stts_samples || !stream->stts_sample_index) { @@ -5671,7 +5736,9 @@ done2: cur->timestamp = stts_time; cur->duration = stts_duration; - stts_time += stts_duration; + /* avoid 32-bit wrap-around, + * but still mind possible 'negative' duration */ + stts_time += (gint64) stts_duration; cur++; if (G_UNLIKELY (cur > last)) { diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h index 6967cfbddb..17fe8817be 100644 --- a/gst/isomp4/qtdemux.h +++ b/gst/isomp4/qtdemux.h @@ -108,6 +108,9 @@ struct _GstQTDemux { gint64 requested_seek_time; guint64 seek_offset; + + gboolean upstream_seekable; + gboolean upstream_size; }; struct _GstQTDemuxClass { diff --git a/gst/rtp/gstrtph263ppay.c b/gst/rtp/gstrtph263ppay.c index 52a3fb314d..df77fae70f 100644 --- a/gst/rtp/gstrtph263ppay.c +++ b/gst/rtp/gstrtph263ppay.c @@ -157,9 +157,32 @@ static gboolean gst_rtp_h263p_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) { gboolean res; + GstCaps *peercaps; + gchar *encoding_name = NULL; - gst_basertppayload_set_options (payload, "video", TRUE, "H263-1998", 90000); + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + peercaps = + gst_pad_peer_get_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload), NULL); + if (peercaps) { + GstCaps *intersect = gst_caps_intersect (peercaps, + gst_pad_get_pad_template_caps (GST_BASE_RTP_PAYLOAD_SRCPAD (payload))); + + gst_caps_unref (peercaps); + if (!gst_caps_is_empty (intersect)) { + GstStructure *s = gst_caps_get_structure (intersect, 0); + encoding_name = g_strdup (gst_structure_get_string (s, "encoding-name")); + } + gst_caps_unref (intersect); + } + + if (!encoding_name) + encoding_name = g_strdup ("H263-1998"); + + gst_basertppayload_set_options (payload, "video", TRUE, + (gchar *) encoding_name, 90000); res = gst_basertppayload_set_outcaps (payload, NULL); + g_free (encoding_name); return res; } diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 41784128f0..9e4163cb95 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -567,6 +567,7 @@ rtp_session_init (RTPSession * sess) DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD; sess->rtcp_pli_requests = g_array_new (FALSE, FALSE, sizeof (guint32)); + sess->last_keyframe_request = GST_CLOCK_TIME_NONE; GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc); } diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 37600f2711..dec7c2518f 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -173,6 +173,7 @@ gst_rtsp_src_buffer_mode_get_type (void) #define DEFAULT_USER_PW NULL #define DEFAULT_BUFFER_MODE BUFFER_MODE_AUTO #define DEFAULT_PORT_RANGE NULL +#define DEFAULT_SHORT_HEADER FALSE enum { @@ -194,6 +195,7 @@ enum PROP_BUFFER_MODE, PROP_PORT_RANGE, PROP_UDP_BUFFER_SIZE, + PROP_SHORT_HEADER, PROP_LAST }; @@ -444,6 +446,18 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass) 0, G_MAXINT, DEFAULT_UDP_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstRTSPSrc::short-header: + * + * Only send the basic RTSP headers for broken encoders. + * + * Since: 0.10.31 + */ + g_object_class_install_property (gobject_class, PROP_SHORT_HEADER, + g_param_spec_boolean ("short-header", "Short Header", + "Only send the basic RTSP headers for broken encoders", + DEFAULT_SHORT_HEADER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gstelement_class->send_event = gst_rtspsrc_send_event; gstelement_class->change_state = gst_rtspsrc_change_state; @@ -492,6 +506,7 @@ gst_rtspsrc_init (GstRTSPSrc * src) src->client_port_range.min = 0; src->client_port_range.max = 0; src->udp_buffer_size = DEFAULT_UDP_BUFFER_SIZE; + src->short_header = DEFAULT_SHORT_HEADER; /* get a list of all extensions */ src->extensions = gst_rtsp_ext_list_get (); @@ -685,6 +700,9 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value, case PROP_UDP_BUFFER_SIZE: rtspsrc->udp_buffer_size = g_value_get_int (value); break; + case PROP_SHORT_HEADER: + rtspsrc->short_header = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -777,6 +795,9 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value, case PROP_UDP_BUFFER_SIZE: g_value_set_int (value, rtspsrc->udp_buffer_size); break; + case PROP_SHORT_HEADER: + g_value_set_boolean (value, rtspsrc->short_header); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4399,7 +4420,8 @@ gst_rtspsrc_try_send (GstRTSPSrc * src, GstRTSPConnection * conn, gint try = 0; again: - gst_rtsp_ext_list_before_send (src->extensions, request); + if (!src->short_header) + gst_rtsp_ext_list_before_send (src->extensions, request); GST_DEBUG_OBJECT (src, "sending message"); diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index 1ad9ec8ece..aa06ae7bab 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -214,6 +214,7 @@ struct _GstRTSPSrc { gint buffer_mode; GstRTSPRange client_port_range; gint udp_buffer_size; + gboolean short_header; /* state */ GstRTSPState state; diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c index 85bb47acec..5ee6d516ce 100644 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -174,7 +174,8 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) * of destinations. */ gst_multiudpsink_signals[SIGNAL_ADD] = - g_signal_new ("add", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + g_signal_new ("add", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiUDPSinkClass, add), NULL, NULL, gst_udp_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); @@ -188,7 +189,8 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) * clients. */ gst_multiudpsink_signals[SIGNAL_REMOVE] = - g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + g_signal_new ("remove", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiUDPSinkClass, remove), NULL, NULL, gst_udp_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); @@ -199,7 +201,8 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) * Clear the list of clients. */ gst_multiudpsink_signals[SIGNAL_CLEAR] = - g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiUDPSinkClass, clear), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** @@ -214,7 +217,8 @@ gst_multiudpsink_class_init (GstMultiUDPSinkClass * klass) * connect_time (in epoch seconds), disconnect_time (in epoch seconds) */ gst_multiudpsink_signals[SIGNAL_GET_STATS] = - g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, + g_signal_new ("get-stats", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstMultiUDPSinkClass, get_stats), NULL, NULL, gst_udp_marshal_BOXED__STRING_INT, G_TYPE_VALUE_ARRAY, 2, G_TYPE_STRING, G_TYPE_INT); diff --git a/tests/check/elements/parser.c b/tests/check/elements/parser.c index 806b63f236..165dc00135 100644 --- a/tests/check/elements/parser.c +++ b/tests/check/elements/parser.c @@ -339,7 +339,9 @@ gst_parser_test_drain_garbage (guint8 * data, guint size, guint8 * garbage, { GstParserTest ptest; - gst_parser_test_init (&ptest, data, size, 1); + /* provide enough initial frames since it may take some parsers some + * time to be convinced of proper sync */ + gst_parser_test_init (&ptest, data, size, 10); ptest.series[1].data = garbage; ptest.series[1].size = gsize; ptest.series[1].num = 1;