diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c index 1ab39d7dff..989ce4c52e 100644 --- a/ext/flac/gstflacenc.c +++ b/ext/flac/gstflacenc.c @@ -462,6 +462,8 @@ gst_flac_enc_stop (GstAudioEncoder * enc) g_list_free (flacenc->headers); flacenc->headers = NULL; + gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); + return TRUE; } diff --git a/ext/speex/gstspeexenc.c b/ext/speex/gstspeexenc.c index 0b124e39cf..f427f08826 100644 --- a/ext/speex/gstspeexenc.c +++ b/ext/speex/gstspeexenc.c @@ -270,6 +270,8 @@ gst_speex_enc_stop (GstAudioEncoder * benc) g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); enc->headers = NULL; + gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); + return TRUE; } @@ -530,7 +532,7 @@ gst_speex_enc_encode (GstSpeexEnc * enc, GstBuffer * buf) gint frame_size = enc->frame_size; gint bytes = frame_size * 2 * enc->channels, samples; gint outsize, written, dtx_ret = 0; - guint8 *data, *bdata, *outdata; + guint8 *data, *data0 = NULL, *bdata, *outdata; gsize bsize, size; GstBuffer *outbuf; GstFlowReturn ret = GST_FLOW_OK; @@ -542,7 +544,7 @@ gst_speex_enc_encode (GstSpeexEnc * enc, GstBuffer * buf) GST_DEBUG_OBJECT (enc, "draining; adding silence samples"); size = ((bsize / bytes) + 1) * bytes; - data = g_malloc0 (size); + data0 = data = g_malloc0 (size); memcpy (data, bdata, bsize); gst_buffer_unmap (buf, bdata, bsize); bdata = NULL; @@ -606,6 +608,7 @@ gst_speex_enc_encode (GstSpeexEnc * enc, GstBuffer * buf) outbuf, samples); done: + g_free (data0); return ret; } diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c index 4308610dc6..3935169ce8 100644 --- a/gst/audioparsers/gstflacparse.c +++ b/gst/audioparsers/gstflacparse.c @@ -394,6 +394,8 @@ gst_flac_parse_frame_header_is_valid (GstFlacParse * flacparse, /* 0 == fixed block size, 1 == variable block size */ blocking_strategy = gst_bit_reader_get_bits_uint8_unchecked (&reader, 1); + if (flacparse->force_variable_block_size) + blocking_strategy = 1; /* block size index, calculation of the real blocksize below */ block_size = gst_bit_reader_get_bits_uint16_unchecked (&reader, 4); @@ -527,6 +529,16 @@ gst_flac_parse_frame_header_is_valid (GstFlacParse * flacparse, if (actual_crc != expected_crc) goto error; + /* Sanity check sample number against blocking strategy, as it seems + some files claim fixed block size but supply sample numbers, + rather than block numbers. */ + if (set && blocking_strategy == 0 && block_size == sample_number) { + GST_WARNING_OBJECT (flacparse, "This file claims fixed block size, " + "but seems to be lying: assuming variable block size"); + flacparse->force_variable_block_size = TRUE; + blocking_strategy = 1; + } + /* The FLAC format documentation says: The "blocking strategy" bit determines how to calculate the sample number diff --git a/gst/audioparsers/gstflacparse.h b/gst/audioparsers/gstflacparse.h index 1c6db0e581..282755244c 100644 --- a/gst/audioparsers/gstflacparse.h +++ b/gst/audioparsers/gstflacparse.h @@ -79,6 +79,8 @@ struct _GstFlacParse { GList *headers; GstBuffer *seektable; + + gboolean force_variable_block_size; }; struct _GstFlacParseClass { diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c index 90a4c33875..5e507d40ca 100644 --- a/gst/rtpmanager/gstrtpsession.c +++ b/gst/rtpmanager/gstrtpsession.c @@ -1375,30 +1375,34 @@ gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstObject * parent, static gboolean gst_rtp_session_request_remote_key_unit (GstRtpSession * rtpsession, - guint32 ssrc, guint payload, gboolean all_headers) + guint32 ssrc, guint payload, gboolean all_headers, gint count) { GstCaps *caps; - gboolean requested = FALSE; caps = gst_rtp_session_get_caps_for_pt (rtpsession, payload); if (caps) { const GstStructure *s = gst_caps_get_structure (caps, 0); gboolean pli; + gboolean fir; pli = gst_structure_has_field (s, "rtcp-fb-nack-pli"); + fir = gst_structure_has_field (s, "rtcp-fb-ccm-fir") && all_headers; + + /* Google Talk uses FIR for repair, so send it even if we just want a + * regular PLI */ + if (!pli && + gst_structure_has_field (s, "rtcp-fb-x-gstreamer-fir-as-repair")) + fir = TRUE; gst_caps_unref (caps); - if (pli) { - rtp_session_request_key_unit (rtpsession->priv->session, ssrc); - rtp_session_request_early_rtcp (rtpsession->priv->session, - gst_clock_get_time (rtpsession->priv->sysclock), 200 * GST_MSECOND); - requested = TRUE; - } + if (pli || fir) + return rtp_session_request_key_unit (rtpsession->priv->session, ssrc, + gst_clock_get_time (rtpsession->priv->sysclock), fir, count); } - return requested; + return FALSE; } static gboolean @@ -1421,10 +1425,13 @@ gst_rtp_session_event_recv_rtp_src (GstPad * pad, GstObject * parent, gst_structure_get_uint (s, "ssrc", &ssrc) && gst_structure_get_uint (s, "payload", &pt)) { gboolean all_headers = FALSE; + gint count = -1; gst_structure_get_boolean (s, "all-headers", &all_headers); + if (gst_structure_get_int (s, "count", &count) && count < 0) + count += G_MAXINT; /* Make sure count is positive if present */ if (gst_rtp_session_request_remote_key_unit (rtpsession, ssrc, pt, - all_headers)) + all_headers, count)) forward = FALSE; } break; diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 930dc31a89..bd57df4785 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -567,7 +567,6 @@ rtp_session_init (RTPSession * sess) sess->rtcp_immediate_feedback_threshold = 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); @@ -590,8 +589,6 @@ rtp_session_finalize (GObject * object) g_hash_table_destroy (sess->cnames); g_object_unref (sess->source); - g_array_free (sess->rtcp_pli_requests, TRUE); - G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object); } @@ -2169,45 +2166,112 @@ rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet, GST_DEBUG ("received APP"); } +static gboolean +rtp_session_request_local_key_unit (RTPSession * sess, RTPSource * src, + gboolean fir, GstClockTime current_time) +{ + guint32 round_trip = 0; + + rtp_source_get_last_rb (src, NULL, NULL, NULL, NULL, NULL, NULL, &round_trip); + + if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && round_trip) { + GstClockTime round_trip_in_ns = gst_util_uint64_scale (round_trip, + GST_SECOND, 65536); + + if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && + current_time - sess->last_keyframe_request < 2 * round_trip_in_ns) { + GST_DEBUG ("Ignoring %s request because one was send without one " + "RTT (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", + fir ? "FIR" : "PLI", + GST_TIME_ARGS (current_time - sess->last_keyframe_request), + GST_TIME_ARGS (round_trip_in_ns));; + return FALSE; + } + } + + sess->last_keyframe_request = current_time; + + GST_LOG ("received %s request from %X %p(%p)", fir ? "FIR" : "PLI", + rtp_source_get_ssrc (src), sess->callbacks.process_rtp, + sess->callbacks.request_key_unit); + + RTP_SESSION_UNLOCK (sess); + sess->callbacks.request_key_unit (sess, fir, + sess->request_key_unit_user_data); + RTP_SESSION_LOCK (sess); + + return TRUE; +} + static void rtp_session_process_pli (RTPSession * sess, guint32 sender_ssrc, guint32 media_ssrc, GstClockTime current_time) { RTPSource *src; - guint32 round_trip = 0; if (!sess->callbacks.request_key_unit) return; src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (sender_ssrc)); + if (!src) + return; + + rtp_session_request_local_key_unit (sess, src, FALSE, current_time); +} + +static void +rtp_session_process_fir (RTPSession * sess, guint32 sender_ssrc, + guint8 * fci_data, guint fci_length, GstClockTime current_time) +{ + RTPSource *src; + guint32 ssrc; + guint position = 0; + gboolean our_request = FALSE; + + if (!sess->callbacks.request_key_unit) + return; + + if (fci_length < 8) + return; + + src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], + GINT_TO_POINTER (sender_ssrc)); + + /* Hack because Google fails to set the sender_ssrc correctly */ + if (!src && sender_ssrc == 1) { + GHashTableIter iter; + + if (sess->stats.sender_sources > + RTP_SOURCE_IS_SENDER (sess->source) ? 2 : 1) + return; + + g_hash_table_iter_init (&iter, sess->ssrcs[sess->mask_idx]); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) & src)) { + if (src != sess->source && rtp_source_is_sender (src)) + break; + src = NULL; + } + } if (!src) return; - if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && - rtp_source_get_last_rb (src, NULL, NULL, NULL, NULL, NULL, NULL, - &round_trip)) { - GstClockTime round_trip_in_ns = gst_util_uint64_scale (round_trip, - GST_SECOND, 65536); + for (position = 0; position < fci_length; position += 8) { + guint8 *data = fci_data + position; - if (sess->last_keyframe_request != GST_CLOCK_TIME_NONE && - current_time - sess->last_keyframe_request < round_trip_in_ns) { - GST_DEBUG ("Ignoring PLI because one was send without one RTT (%" - GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")", - GST_TIME_ARGS (current_time - sess->last_keyframe_request), - GST_TIME_ARGS (round_trip_in_ns));; - return; + ssrc = GST_READ_UINT32_BE (data); + + if (ssrc == rtp_source_get_ssrc (sess->source)) { + our_request = TRUE; + break; } } + if (!our_request) + return; - sess->last_keyframe_request = current_time; - - GST_LOG ("received PLI from %X %p(%p)", sender_ssrc, - sess->callbacks.process_rtp, sess->callbacks.request_key_unit); - - sess->callbacks.request_key_unit (sess, FALSE, - sess->request_key_unit_user_data); + rtp_session_request_local_key_unit (sess, src, TRUE, current_time); } static void @@ -2251,7 +2315,9 @@ rtp_session_process_feedback (RTPSession * sess, GstRTCPPacket * packet, rtp_source_retain_rtcp_packet (src, packet, arrival->running_time); } - if (rtp_source_get_ssrc (sess->source) == media_ssrc) { + if (rtp_source_get_ssrc (sess->source) == media_ssrc || + /* PSFB FIR puts the media ssrc inside the FCI */ + (type == GST_RTCP_TYPE_PSFB && fbtype == GST_RTCP_PSFB_TYPE_FIR)) { switch (type) { case GST_RTCP_TYPE_PSFB: switch (fbtype) { @@ -2259,6 +2325,10 @@ rtp_session_process_feedback (RTPSession * sess, GstRTCPPacket * packet, rtp_session_process_pli (sess, sender_ssrc, media_ssrc, current_time); break; + case GST_RTCP_PSFB_TYPE_FIR: + rtp_session_process_fir (sess, sender_ssrc, fci_data, fci_length, + current_time); + break; default: break; } @@ -3262,19 +3332,32 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time, dont_send: RTP_SESSION_UNLOCK (sess); - } -void -rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc) +gboolean +rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc, GstClockTime now, + gboolean fir, gint count) { - guint i; + RTPSource *src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], + GUINT_TO_POINTER (ssrc)); - for (i = 0; i < sess->rtcp_pli_requests->len; i++) - if (ssrc == g_array_index (sess->rtcp_pli_requests, guint32, i)) - return; + if (!src) + return FALSE; - g_array_append_val (sess->rtcp_pli_requests, ssrc); + if (fir) { + src->send_pli = FALSE; + src->send_fir = TRUE; + + if (count == -1 || count != src->last_fir_count) + src->current_send_fir_seqnum++; + src->last_fir_count = count; + } else if (!src->send_fir) { + src->send_pli = TRUE; + } + + rtp_session_request_early_rtcp (sess, now, 200 * GST_MSECOND); + + return TRUE; } static gboolean @@ -3302,39 +3385,78 @@ rtp_session_on_sending_rtcp (RTPSession * sess, GstBuffer * buffer, gboolean early) { gboolean ret = FALSE; + GHashTableIter iter; + gpointer key, value; + gboolean started_fir = FALSE; + GstRTCPPacket fir_rtcppacket; + GstRTCPBuffer rtcp; RTP_SESSION_LOCK (sess); - while (sess->rtcp_pli_requests->len) { - GstRTCPPacket rtcppacket; - guint media_ssrc = g_array_index (sess->rtcp_pli_requests, guint32, 0); - RTPSource *media_src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx], - GUINT_TO_POINTER (media_ssrc)); + gst_rtcp_buffer_map (buffer, GST_MAP_WRITE, &rtcp); - if (media_src && !rtp_source_has_retained (media_src, - has_pli_compare_func, NULL)) { - GstRTCPBuffer rtcp; + g_hash_table_iter_init (&iter, sess->ssrcs[sess->mask_idx]); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint media_ssrc = GPOINTER_TO_UINT (key); + RTPSource *media_src = value; + guint8 *fci_data; - gst_rtcp_buffer_map (buffer, GST_MAP_WRITE, &rtcp); - if (gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_PSFB, &rtcppacket)) { - gst_rtcp_packet_fb_set_type (&rtcppacket, GST_RTCP_PSFB_TYPE_PLI); - gst_rtcp_packet_fb_set_sender_ssrc (&rtcppacket, + if (media_src->send_fir) { + if (!started_fir) { + if (!gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_PSFB, + &fir_rtcppacket)) + break; + gst_rtcp_packet_fb_set_type (&fir_rtcppacket, GST_RTCP_PSFB_TYPE_FIR); + gst_rtcp_packet_fb_set_sender_ssrc (&fir_rtcppacket, rtp_source_get_ssrc (sess->source)); - gst_rtcp_packet_fb_set_media_ssrc (&rtcppacket, media_ssrc); - ret = TRUE; - gst_rtcp_buffer_unmap (&rtcp); - } else { - /* Break because the packet is full, will put next request in a - * further packet - */ - gst_rtcp_buffer_unmap (&rtcp); - break; - } - } + gst_rtcp_packet_fb_set_media_ssrc (&fir_rtcppacket, 0); - g_array_remove_index (sess->rtcp_pli_requests, 0); + if (!gst_rtcp_packet_fb_set_fci_length (&fir_rtcppacket, 2)) { + gst_rtcp_packet_remove (&fir_rtcppacket); + break; + } + ret = TRUE; + started_fir = TRUE; + } else { + if (!gst_rtcp_packet_fb_set_fci_length (&fir_rtcppacket, + !gst_rtcp_packet_fb_get_fci_length (&fir_rtcppacket) + 2)) + break; + } + + fci_data = gst_rtcp_packet_fb_get_fci (&fir_rtcppacket) - + ((gst_rtcp_packet_fb_get_fci_length (&fir_rtcppacket) - 2) * 4); + + GST_WRITE_UINT32_BE (fci_data, media_ssrc); + fci_data += 4; + fci_data[0] = media_src->current_send_fir_seqnum; + fci_data[1] = fci_data[2] = fci_data[3] = 0; + media_src->send_fir = FALSE; + } } + g_hash_table_iter_init (&iter, sess->ssrcs[sess->mask_idx]); + while (g_hash_table_iter_next (&iter, &key, &value)) { + guint media_ssrc = GPOINTER_TO_UINT (key); + RTPSource *media_src = value; + GstRTCPPacket pli_rtcppacket; + + if (media_src->send_pli && !rtp_source_has_retained (media_src, + has_pli_compare_func, NULL)) { + if (!gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_PSFB, + &pli_rtcppacket)) + /* Break because the packet is full, will put next request in a + * further packet */ + break; + gst_rtcp_packet_fb_set_type (&pli_rtcppacket, GST_RTCP_PSFB_TYPE_PLI); + gst_rtcp_packet_fb_set_sender_ssrc (&pli_rtcppacket, + rtp_source_get_ssrc (sess->source)); + gst_rtcp_packet_fb_set_media_ssrc (&pli_rtcppacket, media_ssrc); + ret = TRUE; + } + media_src->send_pli = FALSE; + } + gst_rtcp_buffer_unmap (&rtcp); + RTP_SESSION_UNLOCK (sess); return ret; diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h index 4d43079da3..929b6d494d 100644 --- a/gst/rtpmanager/rtpsession.h +++ b/gst/rtpmanager/rtpsession.h @@ -234,8 +234,8 @@ struct _RTPSession { GstClockTime rtcp_feedback_retention_window; guint rtcp_immediate_feedback_threshold; - GArray *rtcp_pli_requests; GstClockTime last_keyframe_request; + gboolean last_keyframe_all_headers; }; /** @@ -348,7 +348,10 @@ void rtp_session_request_early_rtcp (RTPSession * sess, GstClockT GstClockTimeDiff max_delay); /* Notify session of a request for a new key unit */ -void rtp_session_request_key_unit (RTPSession * sess, - guint32 ssrc); +gboolean rtp_session_request_key_unit (RTPSession * sess, + guint32 ssrc, + GstClockTime now, + gboolean fir, + gint count); #endif /* __RTP_SESSION_H__ */ diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h index 398634c231..ca95dff25f 100644 --- a/gst/rtpmanager/rtpsource.h +++ b/gst/rtpmanager/rtpsource.h @@ -172,6 +172,11 @@ struct _RTPSource { GList *conflicting_addresses; GQueue *retained_feedback; + + gboolean send_pli; + gboolean send_fir; + guint8 current_send_fir_seqnum; + gint last_fir_count; }; struct _RTPSourceClass { diff --git a/sys/v4l2/gstv4l2object.c b/sys/v4l2/gstv4l2object.c index 1fa8e7c9bb..24ab3c2ee8 100644 --- a/sys/v4l2/gstv4l2object.c +++ b/sys/v4l2/gstv4l2object.c @@ -1729,7 +1729,8 @@ return_data: s = gst_structure_copy (template); gst_structure_set (s, "width", G_TYPE_INT, (gint) width, "height", G_TYPE_INT, (gint) height, - "interlaced", G_TYPE_BOOLEAN, interlaced, NULL); + "interlaced", G_TYPE_BOOLEAN, interlaced, + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL); if (G_IS_VALUE (&rates)) { /* only change the framerate on the template when we have a valid probed new @@ -1990,6 +1991,8 @@ default_frame_sizes: gst_structure_set (tmp, "height", GST_TYPE_INT_RANGE, min_h, max_h, NULL); gst_structure_set (tmp, "interlaced", G_TYPE_BOOLEAN, interlaced, NULL); + gst_structure_set (tmp, "pixel-aspect-ratio", + GST_TYPE_FRACTION, 1, 1, NULL); gst_caps_append_structure (ret, tmp);