summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gst/rtp/gstrtpvp8depay.c161
-rw-r--r--gst/rtp/gstrtpvp8depay.h10
-rw-r--r--gst/rtp/gstrtpvp9depay.c146
-rw-r--r--gst/rtp/gstrtpvp9depay.h9
-rw-r--r--tests/check/elements/rtpvp8.c155
-rw-r--r--tests/check/elements/rtpvp9.c252
6 files changed, 703 insertions, 30 deletions
diff --git a/gst/rtp/gstrtpvp8depay.c b/gst/rtp/gstrtpvp8depay.c
index e71baf816..396aa7597 100644
--- a/gst/rtp/gstrtpvp8depay.c
+++ b/gst/rtp/gstrtpvp8depay.c
@@ -43,6 +43,8 @@ static GstStateChangeReturn gst_rtp_vp8_depay_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_rtp_vp8_depay_handle_event (GstRTPBaseDepayload * depay,
GstEvent * event);
+static gboolean gst_rtp_vp8_depay_packet_lost (GstRTPBaseDepayload * depay,
+ GstEvent * event);
G_DEFINE_TYPE (GstRtpVP8Depay, gst_rtp_vp8_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
@@ -69,6 +71,9 @@ enum
PROP_WAIT_FOR_KEYFRAME
};
+#define PICTURE_ID_NONE (UINT_MAX)
+#define IS_PICTURE_ID_15BITS(pid) (((guint)(pid) & 0x8000) != 0)
+
static void
gst_rtp_vp8_depay_init (GstRtpVP8Depay * self)
{
@@ -109,6 +114,7 @@ gst_rtp_vp8_depay_class_init (GstRtpVP8DepayClass * gst_rtp_vp8_depay_class)
depay_class->process_rtp_packet = gst_rtp_vp8_depay_process;
depay_class->handle_event = gst_rtp_vp8_depay_handle_event;
+ depay_class->packet_lost = gst_rtp_vp8_depay_packet_lost;
GST_DEBUG_CATEGORY_INIT (gst_rtp_vp8_depay_debug, "rtpvp8depay", 0,
"VP8 Video RTP Depayloader");
@@ -161,14 +167,75 @@ gst_rtp_vp8_depay_get_property (GObject * object, guint prop_id,
}
}
+static gint
+picture_id_compare (guint16 id0, guint16 id1)
+{
+ guint shift = 16 - (IS_PICTURE_ID_15BITS (id1) ? 15 : 7);
+ id0 = id0 << shift;
+ id1 = id1 << shift;
+ return ((gint16) (id1 - id0)) >> shift;
+}
+
+static void
+send_last_lost_event (GstRtpVP8Depay * self)
+{
+ if (self->last_lost_event) {
+ GST_ERROR_OBJECT (self,
+ "Sending the last stopped lost event: %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ GST_RTP_BASE_DEPAYLOAD_CLASS (gst_rtp_vp8_depay_parent_class)
+ ->packet_lost (GST_RTP_BASE_DEPAYLOAD_CAST (self),
+ self->last_lost_event);
+ gst_event_replace (&self->last_lost_event, NULL);
+ }
+}
+
+static void
+send_last_lost_event_if_needed (GstRtpVP8Depay * self, guint new_picture_id)
+{
+ if (self->last_picture_id == PICTURE_ID_NONE)
+ return;
+
+ if (self->last_lost_event) {
+ gboolean send_lost_event = FALSE;
+ if (new_picture_id == PICTURE_ID_NONE) {
+ GST_DEBUG_OBJECT (self, "Dropping the last stopped lost event "
+ "(picture id does not exist): %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ } else if (IS_PICTURE_ID_15BITS (self->last_picture_id) &&
+ !IS_PICTURE_ID_15BITS (new_picture_id)) {
+ GST_DEBUG_OBJECT (self, "Dropping the last stopped lost event "
+ "(picture id has less bits than before): %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ } else if (picture_id_compare (self->last_picture_id, new_picture_id) != 1) {
+ GstStructure *s = gst_event_writable_structure (self->last_lost_event);
+
+ GST_DEBUG_OBJECT (self, "Sending the last stopped lost event "
+ "(gap in picture id %u %u): %" GST_PTR_FORMAT,
+ self->last_picture_id, new_picture_id, self->last_lost_event);
+ send_lost_event = TRUE;
+ /* Prevent rtpbasedepayload from dropping the event now
+ * that we have made sure the lost packet was not FEC */
+ gst_structure_remove_field (s, "might-have-been-fec");
+ }
+ if (send_lost_event)
+ GST_RTP_BASE_DEPAYLOAD_CLASS (gst_rtp_vp8_depay_parent_class)
+ ->packet_lost (GST_RTP_BASE_DEPAYLOAD_CAST (self),
+ self->last_lost_event);
+
+ gst_event_replace (&self->last_lost_event, NULL);
+ }
+}
+
static GstBuffer *
gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
{
GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (depay);
GstBuffer *payload;
guint8 *data;
- guint hdrsize;
- guint size;
+ guint hdrsize = 1;
+ guint picture_id = PICTURE_ID_NONE;
+ guint size = gst_rtp_buffer_get_payload_len (rtp);
if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (rtp->buffer))) {
GST_LOG_OBJECT (self, "Discontinuity, flushing adapter");
@@ -179,24 +246,11 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
self->waiting_for_keyframe = TRUE;
}
- size = gst_rtp_buffer_get_payload_len (rtp);
-
/* At least one header and one vp8 byte */
if (G_UNLIKELY (size < 2))
goto too_small;
data = gst_rtp_buffer_get_payload (rtp);
-
- if (G_UNLIKELY (!self->started)) {
- /* Check if this is the start of a VP8 frame, otherwise bail */
- /* S=1 and PartID= 0 */
- if ((data[0] & 0x17) != 0x10)
- goto done;
-
- self->started = TRUE;
- }
-
- hdrsize = 1;
/* Check X optional header */
if ((data[0] & 0x80) != 0) {
hdrsize++;
@@ -206,8 +260,13 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
goto too_small;
hdrsize++;
/* Check for 16 bits PictureID */
- if ((data[2] & 0x80) != 0)
+ picture_id = data[2];
+ if ((data[2] & 0x80) != 0) {
+ if (G_UNLIKELY (size < 4))
+ goto too_small;
hdrsize++;
+ picture_id = (picture_id << 8) | data[3];
+ }
}
/* Check L optional header */
if ((data[1] & 0x40) != 0)
@@ -216,19 +275,46 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
if ((data[1] & 0x20) != 0 || (data[1] & 0x10) != 0)
hdrsize++;
}
- GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u", hdrsize, size);
-
+ GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u, picture id 0x%x", hdrsize,
+ size, picture_id);
if (G_UNLIKELY (hdrsize >= size))
goto too_small;
+ if (G_UNLIKELY (!self->started)) {
+ /* Check if this is the start of a VP8 frame, otherwise bail */
+ /* S=1 and PartID= 0 */
+ if ((data[0] & 0x17) != 0x10) {
+ GST_DEBUG_OBJECT (depay,
+ "The frame is missing the first packets, ignoring the packet");
+ if (self->stop_lost_events) {
+ send_last_lost_event (self);
+ self->stop_lost_events = FALSE;
+ }
+ goto done;
+ }
+
+ GST_DEBUG_OBJECT (depay, "Found the start of the frame");
+
+ if (self->stop_lost_events) {
+ send_last_lost_event_if_needed (self, picture_id);
+ self->stop_lost_events = FALSE;
+ }
+
+ self->started = TRUE;
+ }
+
payload = gst_rtp_buffer_get_payload_subbuffer (rtp, hdrsize, -1);
gst_adapter_push (self->adapter, payload);
+ self->last_picture_id = picture_id;
/* Marker indicates that it was the last rtp packet for this frame */
if (gst_rtp_buffer_get_marker (rtp)) {
GstBuffer *out;
guint8 header[10];
+ GST_DEBUG_OBJECT (depay,
+ "Found the end of the frame (%" G_GSIZE_FORMAT " bytes)",
+ gst_adapter_available (self->adapter));
if (gst_adapter_available (self->adapter) < 10)
goto too_small;
gst_adapter_copy (self->adapter, &header, 0, 10);
@@ -284,6 +370,8 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
self->waiting_for_keyframe = FALSE;
}
+ if (picture_id != PICTURE_ID_NONE)
+ self->stop_lost_events = TRUE;
return out;
}
@@ -309,6 +397,10 @@ gst_rtp_vp8_depay_change_state (GstElement * element, GstStateChange transition)
self->last_height = -1;
self->last_width = -1;
self->waiting_for_keyframe = TRUE;
+ self->caps_sent = FALSE;
+ self->last_picture_id = PICTURE_ID_NONE;
+ gst_event_replace (&self->last_lost_event, NULL);
+ self->stop_lost_events = FALSE;
break;
default:
break;
@@ -329,6 +421,9 @@ gst_rtp_vp8_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event)
self->last_profile = -1;
self->last_height = -1;
self->last_width = -1;
+ self->last_picture_id = PICTURE_ID_NONE;
+ gst_event_replace (&self->last_lost_event, NULL);
+ self->stop_lost_events = FALSE;
break;
default:
break;
@@ -339,6 +434,36 @@ gst_rtp_vp8_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event)
(gst_rtp_vp8_depay_parent_class)->handle_event (depay, event);
}
+static gboolean
+gst_rtp_vp8_depay_packet_lost (GstRTPBaseDepayload * depay, GstEvent * event)
+{
+ GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (depay);
+ const GstStructure *s;
+ gboolean might_have_been_fec;
+
+ s = gst_event_get_structure (event);
+
+ if (self->stop_lost_events) {
+ if (gst_structure_get_boolean (s, "might-have-been-fec",
+ &might_have_been_fec)
+ && might_have_been_fec) {
+ GST_DEBUG_OBJECT (depay, "Stopping lost event %" GST_PTR_FORMAT, event);
+ gst_event_replace (&self->last_lost_event, event);
+ return TRUE;
+ }
+ } else if (self->last_picture_id != PICTURE_ID_NONE) {
+ GstStructure *s = gst_event_writable_structure (self->last_lost_event);
+
+ /* We are currently processing a picture, let's make sure the
+ * base depayloader doesn't drop this lost event */
+ gst_structure_remove_field (s, "might-have-been-fec");
+ }
+
+ return
+ GST_RTP_BASE_DEPAYLOAD_CLASS
+ (gst_rtp_vp8_depay_parent_class)->packet_lost (depay, event);
+}
+
gboolean
gst_rtp_vp8_depay_plugin_init (GstPlugin * plugin)
{
diff --git a/gst/rtp/gstrtpvp8depay.h b/gst/rtp/gstrtpvp8depay.h
index cde8e5ea8..c53ccaa8e 100644
--- a/gst/rtp/gstrtpvp8depay.h
+++ b/gst/rtp/gstrtpvp8depay.h
@@ -54,10 +54,20 @@ struct _GstRtpVP8Depay
GstAdapter *adapter;
gboolean started;
+ gboolean caps_sent;
+ /* In between pictures, we might store GstRTPPacketLost events instead
+ * of forwarding them immediately, we check upon reception of a new
+ * picture id whether a gap was introduced, in which case we do forward
+ * the event. This is to avoid forwarding spurious lost events for FEC
+ * packets.
+ */
+ gboolean stop_lost_events;
+ GstEvent *last_lost_event;
gboolean waiting_for_keyframe;
gint last_profile;
gint last_width;
gint last_height;
+ guint last_picture_id;
gboolean wait_for_keyframe;
};
diff --git a/gst/rtp/gstrtpvp9depay.c b/gst/rtp/gstrtpvp9depay.c
index c61affca5..b95eb5d91 100644
--- a/gst/rtp/gstrtpvp9depay.c
+++ b/gst/rtp/gstrtpvp9depay.c
@@ -40,6 +40,8 @@ static GstStateChangeReturn gst_rtp_vp9_depay_change_state (GstElement *
element, GstStateChange transition);
static gboolean gst_rtp_vp9_depay_handle_event (GstRTPBaseDepayload * depay,
GstEvent * event);
+static gboolean gst_rtp_vp9_depay_packet_lost (GstRTPBaseDepayload * depay,
+ GstEvent * event);
G_DEFINE_TYPE (GstRtpVP9Depay, gst_rtp_vp9_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
@@ -58,6 +60,9 @@ GST_STATIC_PAD_TEMPLATE ("sink",
"media = (string) \"video\","
"encoding-name = (string) { \"VP9\", \"VP9-DRAFT-IETF-01\" }"));
+#define PICTURE_ID_NONE (UINT_MAX)
+#define IS_PICTURE_ID_15BITS(pid) (((guint)(pid) & 0x8000) != 0)
+
static void
gst_rtp_vp9_depay_init (GstRtpVP9Depay * self)
{
@@ -89,6 +94,7 @@ gst_rtp_vp9_depay_class_init (GstRtpVP9DepayClass * gst_rtp_vp9_depay_class)
depay_class->process_rtp_packet = gst_rtp_vp9_depay_process;
depay_class->handle_event = gst_rtp_vp9_depay_handle_event;
+ depay_class->packet_lost = gst_rtp_vp9_depay_packet_lost;
GST_DEBUG_CATEGORY_INIT (gst_rtp_vp9_depay_debug, "rtpvp9depay", 0,
"VP9 Video RTP Depayloader");
@@ -109,6 +115,67 @@ gst_rtp_vp9_depay_dispose (GObject * object)
G_OBJECT_CLASS (gst_rtp_vp9_depay_parent_class)->dispose (object);
}
+static gint
+picture_id_compare (guint16 id0, guint16 id1)
+{
+ guint shift = 16 - (IS_PICTURE_ID_15BITS (id1) ? 15 : 7);
+ id0 = id0 << shift;
+ id1 = id1 << shift;
+ return ((gint16) (id1 - id0)) >> shift;
+}
+
+static void
+send_last_lost_event (GstRtpVP9Depay * self)
+{
+ if (self->last_lost_event) {
+ GST_DEBUG_OBJECT (self,
+ "Sending the last stopped lost event: %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ GST_RTP_BASE_DEPAYLOAD_CLASS (gst_rtp_vp9_depay_parent_class)
+ ->packet_lost (GST_RTP_BASE_DEPAYLOAD_CAST (self),
+ self->last_lost_event);
+ gst_event_replace (&self->last_lost_event, NULL);
+ }
+}
+
+static void
+send_last_lost_event_if_needed (GstRtpVP9Depay * self, guint new_picture_id)
+{
+ if (self->last_picture_id == PICTURE_ID_NONE ||
+ self->last_picture_id == new_picture_id)
+ return;
+
+ if (self->last_lost_event) {
+ gboolean send_lost_event = FALSE;
+ if (new_picture_id == PICTURE_ID_NONE) {
+ GST_DEBUG_OBJECT (self, "Dropping the last stopped lost event "
+ "(picture id does not exist): %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ } else if (IS_PICTURE_ID_15BITS (self->last_picture_id) &&
+ !IS_PICTURE_ID_15BITS (new_picture_id)) {
+ GST_DEBUG_OBJECT (self, "Dropping the last stopped lost event "
+ "(picture id has less bits than before): %" GST_PTR_FORMAT,
+ self->last_lost_event);
+ } else if (picture_id_compare (self->last_picture_id, new_picture_id) != 1) {
+ GstStructure *s = gst_event_writable_structure (self->last_lost_event);
+
+ GST_DEBUG_OBJECT (self, "Sending the last stopped lost event "
+ "(gap in picture id %u %u): %" GST_PTR_FORMAT,
+ self->last_picture_id, new_picture_id, self->last_lost_event);
+ send_lost_event = TRUE;
+ /* Prevent rtpbasedepayload from dropping the event now
+ * that we have made sure the lost packet was not FEC */
+ gst_structure_remove_field (s, "might-have-been-fec");
+ }
+ if (send_lost_event)
+ GST_RTP_BASE_DEPAYLOAD_CLASS (gst_rtp_vp9_depay_parent_class)
+ ->packet_lost (GST_RTP_BASE_DEPAYLOAD_CAST (self),
+ self->last_lost_event);
+
+ gst_event_replace (&self->last_lost_event, NULL);
+ }
+}
+
static GstBuffer *
gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
{
@@ -118,6 +185,7 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
guint hdrsize = 1;
guint size;
gint spatial_layer = 0;
+ guint picture_id = PICTURE_ID_NONE;
gboolean i_bit, p_bit, l_bit, f_bit, b_bit, e_bit, v_bit;
if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (rtp->buffer))) {
@@ -141,14 +209,6 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
e_bit = (data[0] & 0x04) != 0;
v_bit = (data[0] & 0x02) != 0;
- if (G_UNLIKELY (!self->started)) {
- /* Check if this is the start of a VP9 layer frame, otherwise bail */
- if (!b_bit)
- goto done;
-
- self->started = TRUE;
- }
-
GST_TRACE_OBJECT (self, "IPLFBEV : %d%d%d%d%d%d%d", i_bit, p_bit, l_bit,
f_bit, b_bit, e_bit, v_bit);
@@ -157,11 +217,13 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
hdrsize++;
if (G_UNLIKELY (size < hdrsize + 1))
goto too_small;
+ picture_id = data[1];
/* Check M for 15 bits PictureID */
if ((data[1] & 0x80) != 0) {
hdrsize++;
if (G_UNLIKELY (size < hdrsize + 1))
goto too_small;
+ picture_id = (picture_id << 8) | data[2];
}
}
@@ -245,25 +307,51 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
hdrsize += sssize;
}
- GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u", hdrsize, size);
+ GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u, picture id 0x%x",
+ hdrsize, size, picture_id);
if (G_UNLIKELY (hdrsize >= size))
goto too_small;
+ if (G_UNLIKELY (!self->started)) {
+ /* Check if this is the start of a VP9 layer frame, otherwise bail */
+ if (!b_bit) {
+ GST_DEBUG_OBJECT (depay,
+ "The layer is missing the first packets, ignoring the packet");
+ if (self->stop_lost_events) {
+ send_last_lost_event (self);
+ self->stop_lost_events = FALSE;
+ }
+ goto done;
+ }
+
+ GST_DEBUG_OBJECT (depay, "Found the start of the layer");
+ if (self->stop_lost_events) {
+ send_last_lost_event_if_needed (self, picture_id);
+ self->stop_lost_events = FALSE;
+ }
+ self->started = TRUE;
+ }
+
payload = gst_rtp_buffer_get_payload_subbuffer (rtp, hdrsize, -1);
- {
+ if (GST_LEVEL_MEMDUMP <= gst_debug_category_get_threshold (GST_CAT_DEFAULT)) {
GstMapInfo map;
gst_buffer_map (payload, &map, GST_MAP_READ);
GST_MEMDUMP_OBJECT (self, "vp9 payload", map.data, 16);
gst_buffer_unmap (payload, &map);
}
gst_adapter_push (self->adapter, payload);
+ self->last_picture_id = picture_id;
/* Marker indicates that it was the last rtp packet for this frame */
if (gst_rtp_buffer_get_marker (rtp)) {
GstBuffer *out;
gboolean key_frame_first_layer = !p_bit && spatial_layer == 0;
+ GST_DEBUG_OBJECT (depay,
+ "Found the end of the frame (%" G_GSIZE_FORMAT " bytes)",
+ gst_adapter_available (self->adapter));
+
if (gst_adapter_available (self->adapter) < 10)
goto too_small;
@@ -317,6 +405,8 @@ gst_rtp_vp9_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
}
}
+ if (picture_id != PICTURE_ID_NONE)
+ self->stop_lost_events = TRUE;
return out;
}
@@ -341,6 +431,9 @@ gst_rtp_vp9_depay_change_state (GstElement * element, GstStateChange transition)
self->last_width = -1;
self->last_height = -1;
self->caps_sent = FALSE;
+ self->last_picture_id = PICTURE_ID_NONE;
+ gst_event_replace (&self->last_lost_event, NULL);
+ self->stop_lost_events = FALSE;
break;
default:
break;
@@ -360,6 +453,9 @@ gst_rtp_vp9_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event)
case GST_EVENT_FLUSH_STOP:
self->last_width = -1;
self->last_height = -1;
+ self->last_picture_id = PICTURE_ID_NONE;
+ gst_event_replace (&self->last_lost_event, NULL);
+ self->stop_lost_events = FALSE;
break;
default:
break;
@@ -370,6 +466,36 @@ gst_rtp_vp9_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event)
(gst_rtp_vp9_depay_parent_class)->handle_event (depay, event);
}
+static gboolean
+gst_rtp_vp9_depay_packet_lost (GstRTPBaseDepayload * depay, GstEvent * event)
+{
+ GstRtpVP9Depay *self = GST_RTP_VP9_DEPAY (depay);
+ const GstStructure *s;
+ gboolean might_have_been_fec;
+
+ s = gst_event_get_structure (event);
+
+ if (self->stop_lost_events) {
+ if (gst_structure_get_boolean (s, "might-have-been-fec",
+ &might_have_been_fec)
+ && might_have_been_fec) {
+ GST_DEBUG_OBJECT (depay, "Stopping lost event %" GST_PTR_FORMAT, event);
+ gst_event_replace (&self->last_lost_event, event);
+ return TRUE;
+ }
+ } else if (self->last_picture_id != PICTURE_ID_NONE) {
+ GstStructure *s = gst_event_writable_structure (self->last_lost_event);
+
+ /* We are currently processing a picture, let's make sure the
+ * base depayloader doesn't drop this lost event */
+ gst_structure_remove_field (s, "might-have-been-fec");
+ }
+
+ return
+ GST_RTP_BASE_DEPAYLOAD_CLASS
+ (gst_rtp_vp9_depay_parent_class)->packet_lost (depay, event);
+}
+
gboolean
gst_rtp_vp9_depay_plugin_init (GstPlugin * plugin)
{
diff --git a/gst/rtp/gstrtpvp9depay.h b/gst/rtp/gstrtpvp9depay.h
index 6f783ab65..2483068fe 100644
--- a/gst/rtp/gstrtpvp9depay.h
+++ b/gst/rtp/gstrtpvp9depay.h
@@ -59,7 +59,16 @@ struct _GstRtpVP9Depay
gint ss_height;
gint last_width;
gint last_height;
+ guint last_picture_id;
+ GstEvent *last_lost_event;
gboolean caps_sent;
+ /* In between pictures, we might store GstRTPPacketLost events instead
+ * of forwarding them immediately, we check upon reception of a new
+ * picture id whether a gap was introduced, in which case we do forward
+ * the event. This is to avoid forwarding spurious lost events for FEC
+ * packets.
+ */
+ gboolean stop_lost_events;
};
GType gst_rtp_vp9_depay_get_type (void);
diff --git a/tests/check/elements/rtpvp8.c b/tests/check/elements/rtpvp8.c
index 666bed5ed..44112898a 100644
--- a/tests/check/elements/rtpvp8.c
+++ b/tests/check/elements/rtpvp8.c
@@ -29,6 +29,56 @@
g_memdup (vp8_bitstream_payload, sizeof (vp8_bitstream_payload)), \
sizeof (vp8_bitstream_payload))
+static guint8 intra_picid6336_seqnum0[] = {
+ 0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
+ 0x90, 0x80, 0x98, 0xc0, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
+ 0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
+};
+
+static guint8 intra_picid24_seqnum0[] = {
+ 0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
+ 0x90, 0x80, 0x18, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
+ 0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
+};
+
+static guint8 intra_nopicid_seqnum0[] = {
+ 0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
+ 0x90, 0x00, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
+ 0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
+};
+
+static GstBuffer *
+create_rtp_vp8_buffer (guint seqnum, guint picid, gint picid_bits,
+ GstClockTime buf_pts)
+{
+ GstBuffer *ret;
+ guint8 *packet;
+ gsize size;
+
+ g_assert (picid_bits == 0 || picid_bits == 7 || picid_bits == 15);
+
+ if (picid_bits == 0) {
+ size = sizeof (intra_nopicid_seqnum0);
+ packet = g_memdup (intra_nopicid_seqnum0, size);
+ } else if (picid_bits == 7) {
+ size = sizeof (intra_picid24_seqnum0);
+ packet = g_memdup (intra_picid24_seqnum0, size);
+ packet[14] = picid & 0x7f;
+ } else {
+ size = sizeof (intra_picid6336_seqnum0);
+ packet = g_memdup (intra_picid6336_seqnum0, size);
+ packet[14] = ((picid >> 8) & 0xff) | 0x80;
+ packet[15] = (picid >> 0) & 0xff;
+ }
+
+ packet[2] = (seqnum >> 8) & 0xff;
+ packet[3] = (seqnum >> 0) & 0xff;
+
+ ret = gst_buffer_new_wrapped (packet, size);
+ GST_BUFFER_PTS (ret) = buf_pts;
+ return ret;
+}
+
static void
add_vp8_meta (GstBuffer * buffer, gboolean use_temporal_scaling,
gboolean layer_sync, guint layer_id, guint tl0picidx)
@@ -445,6 +495,107 @@ GST_START_TEST (test_pay_tl0picidx_split_buffer)
GST_END_TEST;
+typedef struct _DepayGapEventTestData
+{
+ gint seq_num;
+ gint picid;
+ gint picid_bits;
+} DepayGapEventTestData;
+
+static void
+test_depay_gap_event_base (const DepayGapEventTestData * data,
+ gboolean send_lost_event, gboolean expect_gap_event)
+{
+ GstEvent *event;
+ GstClockTime pts = 0;
+ GstHarness *h = gst_harness_new ("rtpvp8depay");
+ gst_harness_set_src_caps_str (h, RTP_VP8_CAPS_STR);
+
+ gst_harness_push (h, create_rtp_vp8_buffer (data[0].seq_num, data[0].picid,
+ data[0].picid_bits, pts));
+ pts += 33 * GST_MSECOND;
+
+ /* Preparation before pushing gap event. Getting rid of all events which
+ * came by this point - segment, caps, etc */
+ for (gint i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+ fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
+
+ if (send_lost_event) {
+ gst_harness_push_event (h,
+ gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+ gst_structure_new ("GstRTPPacketLost", "timestamp", G_TYPE_UINT64,
+ pts, "duration", G_TYPE_UINT64, 33 * GST_MSECOND,
+ "might-have-been-fec", G_TYPE_BOOLEAN, TRUE, NULL)));
+ pts += 33 * GST_MSECOND;
+ }
+
+ gst_harness_push (h, create_rtp_vp8_buffer (data[1].seq_num, data[1].picid,
+ data[1].picid_bits, pts));
+ fail_unless_equals_int (2, gst_harness_buffers_received (h));
+
+ if (expect_gap_event) {
+ /* Making sure the GAP event was pushed downstream */
+ event = gst_harness_pull_event (h);
+ fail_unless_equals_string ("gap",
+ gst_event_type_get_name (GST_EVENT_TYPE (event)));
+ gst_event_unref (event);
+ }
+ fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
+
+ gst_harness_teardown (h);
+}
+
+/* Packet loss + no loss in picture ids */
+static const DepayGapEventTestData stop_gap_events_test_data[][2] = {
+ /* 7bit picture ids */
+ {{100, 24, 7}, {102, 25, 7}},
+
+ /* 15bit picture ids */
+ {{100, 250, 15}, {102, 251, 15}},
+
+ /* 7bit picture ids wrap */
+ {{100, 127, 7}, {102, 0, 7}},
+
+ /* 15bit picture ids wrap */
+ {{100, 32767, 15}, {102, 0, 15}},
+
+ /* 7bit to 15bit picture id */
+ {{100, 127, 7}, {102, 128, 15}},
+};
+
+GST_START_TEST (test_depay_stop_gap_events)
+{
+ test_depay_gap_event_base (&stop_gap_events_test_data[__i__][0], TRUE, FALSE);
+}
+
+GST_END_TEST;
+
+/* Packet loss + lost picture ids */
+static const DepayGapEventTestData resend_gap_event_test_data[][2] = {
+ /* 7bit picture ids */
+ {{100, 24, 7}, {102, 26, 7}},
+
+ /* 15bit picture ids */
+ {{100, 250, 15}, {102, 252, 15}},
+
+ /* 7bit picture ids wrap */
+ {{100, 127, 7}, {102, 1, 7}},
+
+ /* 15bit picture ids wrap */
+ {{100, 32767, 15}, {102, 1, 15}},
+
+ /* 7bit to 15bit picture id */
+ {{100, 126, 7}, {102, 129, 15}},
+};
+
+GST_START_TEST (test_depay_resend_gap_event)
+{
+ test_depay_gap_event_base (&resend_gap_event_test_data[__i__][0], TRUE, TRUE);
+}
+
+GST_END_TEST;
+
static Suite *
rtpvp8_suite (void)
{
@@ -462,6 +613,10 @@ rtpvp8_suite (void)
G_N_ELEMENTS (with_meta_test_data));
tcase_add_test (tc_chain, test_pay_continuous_picture_id_and_tl0picidx);
tcase_add_test (tc_chain, test_pay_tl0picidx_split_buffer);
+ tcase_add_loop_test (tc_chain, test_depay_stop_gap_events, 0,
+ G_N_ELEMENTS (stop_gap_events_test_data));
+ tcase_add_loop_test (tc_chain, test_depay_resend_gap_event, 0,
+ G_N_ELEMENTS (resend_gap_event_test_data));
return s;
}
diff --git a/tests/check/elements/rtpvp9.c b/tests/check/elements/rtpvp9.c
index 123069e86..f50ce3ba0 100644
--- a/tests/check/elements/rtpvp9.c
+++ b/tests/check/elements/rtpvp9.c
@@ -25,7 +25,6 @@
#define RTP_VP9_CAPS_STR \
"application/x-rtp,media=video,encoding-name=VP9,clock-rate=90000,payload=96"
-
GST_START_TEST (test_depay_flexible_mode)
{
/* b-bit, e-bit, f-bit and marker bit set */
@@ -96,17 +95,266 @@ GST_START_TEST (test_depay_non_flexible_mode)
GST_END_TEST;
+static guint8 intra_picid6336_seqnum0[] = {
+ 0x80, 0xf4, 0x00, 0x00, 0x49, 0x88, 0xd9, 0xf8, 0xa0, 0x6c, 0x65, 0x6c,
+ 0x8c, 0x98, 0xc0, 0x87, 0x01, 0x02, 0x49, 0x3f, 0x1c, 0x12, 0x0e, 0x0c,
+ 0xd0, 0x1b, 0xb9, 0x80, 0x80, 0xb0, 0x18, 0x0f, 0xa6, 0x4d, 0x01, 0xa5
+};
+
+static guint8 intra_picid24_seqnum0[] = {
+ 0x80, 0xf4, 0x00, 0x00, 0x49, 0x88, 0xd9, 0xf8, 0xa0, 0x6c, 0x65, 0x6c,
+ 0x8c, 0x18, 0x87, 0x01, 0x02, 0x49, 0x3f, 0x1c, 0x12, 0x0e, 0x0c,
+ 0xd0, 0x1b, 0xb9, 0x80, 0x80, 0xb0, 0x18, 0x0f, 0xa6, 0x4d, 0x01, 0xa5
+};
+
+static guint8 intra_nopicid_seqnum0[] = {
+ 0x80, 0xf4, 0x00, 0x00, 0x49, 0x88, 0xd9, 0xf8, 0xa0, 0x6c, 0x65, 0x6c,
+ 0x0c, 0x87, 0x01, 0x02, 0x49, 0x3f, 0x1c, 0x12, 0x0e, 0x0c,
+ 0xd0, 0x1b, 0xb9, 0x80, 0x80, 0xb0, 0x18, 0x0f, 0xa6, 0x4d, 0x01, 0xa5
+};
+
+enum
+{
+ BT_PLAIN_PICID_NONE,
+ BT_PLAIN_PICID_7,
+ BT_PLAIN_PICID_15,
+ /* Commented out for now, until added VP9 equvivalents.
+ BT_TS_PICID_NONE,
+ BT_TS_PICID_7,
+ BT_TS_PICID_15,
+ BT_TS_PICID_7_NO_TLOPICIDX,
+ BT_TS_PICID_7_NO_TID_Y_KEYIDX
+ */
+};
+
+static GstBuffer *
+create_rtp_vp9_buffer_full (guint seqnum, guint picid, guint buffer_type,
+ GstClockTime buf_pts, gboolean B_bit_start_of_frame, gboolean marker_bit)
+{
+ static struct BufferTemplate
+ {
+ guint8 *template;
+ gsize size;
+ gint picid_bits;
+ } templates[] = {
+ {
+ intra_nopicid_seqnum0, sizeof (intra_nopicid_seqnum0), 0}
+ , {
+ intra_picid24_seqnum0, sizeof (intra_picid24_seqnum0), 7}
+ , {
+ intra_picid6336_seqnum0, sizeof (intra_picid6336_seqnum0), 15}
+ ,
+ /*
+ { intra_nopicid_seqnum0_tl1_sync_tl0picidx12,
+ sizeof (intra_nopicid_seqnum0_tl1_sync_tl0picidx12),
+ 0
+ },
+ { intra_picid24_seqnum0_tl1_sync_tl0picidx12,
+ sizeof (intra_picid24_seqnum0_tl1_sync_tl0picidx12),
+ 7
+ },
+ { intra_picid6336_seqnum0_tl1_sync_tl0picidx12,
+ sizeof (intra_picid6336_seqnum0_tl1_sync_tl0picidx12),
+ 15
+ },
+ { intra_picid24_seqnum0_tl1_sync_no_tl0picidx,
+ sizeof (intra_picid24_seqnum0_tl1_sync_no_tl0picidx),
+ 7
+ },
+ { intra_picid24_seqnum0_notyk_tl0picidx12,
+ sizeof (intra_picid24_seqnum0_notyk_tl0picidx12),
+ 7
+ }
+ */
+ };
+ struct BufferTemplate *template = &templates[buffer_type];
+ guint8 *packet = g_memdup (template->template, template->size);
+ GstBuffer *ret;
+
+ packet[2] = (seqnum >> 8) & 0xff;
+ packet[3] = (seqnum >> 0) & 0xff;
+
+ /* We're forcing the E-bit (EndOfFrame) together with the RTP marker bit here, which is a bit of a hack.
+ * If we're to enable spatial scalability tests, we need to take that into account when setting the E bit.
+ */
+ if (marker_bit) {
+ packet[1] |= 0x80;
+ packet[12] |= 0x4;
+ } else {
+ packet[1] &= ~0x80;
+ packet[12] &= ~0x4;
+ }
+
+ if (B_bit_start_of_frame)
+ packet[12] |= 0x8;
+ else
+ packet[12] &= ~0x8;
+
+ if (template->picid_bits == 7) {
+ /* Prerequisites for this to be correct:
+ ((packet[12] & 0x80) == 0x80); I bit set
+ */
+ g_assert ((packet[12] & 0x80) == 0x80);
+ packet[13] = picid & 0x7f;
+
+ } else if (template->picid_bits == 15) {
+ /* Prerequisites for this to be correct:
+ ((packet[12] & 0x80) == 0x80); I bit set
+ */
+ g_assert ((packet[12] & 0x80) == 0x80);
+ packet[13] = ((picid >> 8) & 0xff) | 0x80;
+ packet[14] = (picid >> 0) & 0xff;
+ }
+
+ ret = gst_buffer_new_wrapped (packet, template->size);
+ GST_BUFFER_PTS (ret) = buf_pts;
+ return ret;
+}
+
+static GstBuffer *
+create_rtp_vp9_buffer (guint seqnum, guint picid, guint buffer_type,
+ GstClockTime buf_pts)
+{
+ return create_rtp_vp9_buffer_full (seqnum, picid, buffer_type, buf_pts, TRUE,
+ TRUE);
+}
+
+typedef struct _DepayGapEventTestData
+{
+ gint seq_num;
+ gint picid;
+ guint buffer_type;
+} DepayGapEventTestData;
+
+typedef struct
+{
+ gint seq_num;
+ gint picid;
+ guint buffer_type;
+ gboolean s_bit;
+ gboolean marker_bit;
+} DepayGapEventTestDataFull;
+
+static void
+test_depay_gap_event_base (const DepayGapEventTestData * data,
+ gboolean send_lost_event, gboolean expect_gap_event, int iter)
+{
+ GstEvent *event;
+ GstClockTime pts = 0;
+ GstHarness *h = gst_harness_new ("rtpvp9depay");
+ if (send_lost_event == FALSE && expect_gap_event) {
+ /* Expect picture ID gaps to be concealed, so tell the element to do so. */
+ g_object_set (h->element, "hide-picture-id-gap", TRUE, NULL);
+ }
+ gst_harness_set_src_caps_str (h, RTP_VP9_CAPS_STR);
+
+ gst_harness_push (h, create_rtp_vp9_buffer (data[0].seq_num, data[0].picid,
+ data[0].buffer_type, pts));
+ pts += 33 * GST_MSECOND;
+
+ /* Preparation before pushing gap event. Getting rid of all events which
+ * came by this point - segment, caps, etc */
+ for (gint i = 0; i < 3; i++)
+ gst_event_unref (gst_harness_pull_event (h));
+ fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
+
+ if (send_lost_event) {
+ gst_harness_push_event (h,
+ gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+ gst_structure_new ("GstRTPPacketLost", "timestamp", G_TYPE_UINT64,
+ pts, "duration", G_TYPE_UINT64, 33 * GST_MSECOND,
+ "might-have-been-fec", G_TYPE_BOOLEAN, TRUE, NULL)));
+ pts += 33 * GST_MSECOND;
+ }
+
+ gst_harness_push (h, create_rtp_vp9_buffer (data[1].seq_num, data[1].picid,
+ data[1].buffer_type, pts));
+ fail_unless_equals_int (2, gst_harness_buffers_received (h));
+
+ if (expect_gap_event) {
+ gboolean noloss = FALSE;
+
+ /* Making sure the GAP event was pushed downstream */
+ event = gst_harness_pull_event (h);
+ fail_unless_equals_string ("gap",
+ gst_event_type_get_name (GST_EVENT_TYPE (event)));
+ gst_structure_get_boolean (gst_event_get_structure (event),
+ "no-packet-loss", &noloss);
+
+ /* If we didn't send GstRTPPacketLost event, the gap
+ * event should indicate that with 'no-packet-loss' parameter */
+ fail_unless_equals_int (noloss, !send_lost_event);
+ gst_event_unref (event);
+ }
+
+ fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
+
+ gst_harness_teardown (h);
+}
+
+static const DepayGapEventTestData stop_gap_events_test_data[][2] = {
+ /* 7bit picture ids */
+ {{100, 24, BT_PLAIN_PICID_7}, {102, 25, BT_PLAIN_PICID_7}},
+
+ /* 15bit picture ids */
+ {{100, 250, BT_PLAIN_PICID_15}, {102, 251, BT_PLAIN_PICID_15}},
+
+ /* 7bit picture ids wrap */
+ {{100, 127, BT_PLAIN_PICID_7}, {102, 0, BT_PLAIN_PICID_7}},
+
+ /* 15bit picture ids wrap */
+ {{100, 32767, BT_PLAIN_PICID_15}, {102, 0, BT_PLAIN_PICID_15}},
+
+ /* 7bit to 15bit picture id */
+ {{100, 127, BT_PLAIN_PICID_7}, {102, 128, BT_PLAIN_PICID_15}},
+};
+
+GST_START_TEST (test_depay_stop_gap_events)
+{
+ test_depay_gap_event_base (&stop_gap_events_test_data[__i__][0], TRUE, FALSE,
+ __i__);
+}
+
+GST_END_TEST;
+
+/* Packet loss + lost picture ids */
+static const DepayGapEventTestData resend_gap_event_test_data[][2] = {
+ /* 7bit picture ids */
+ {{100, 24, BT_PLAIN_PICID_7}, {102, 26, BT_PLAIN_PICID_7}},
+
+ /* 15bit picture ids */
+ {{100, 250, BT_PLAIN_PICID_15}, {102, 252, BT_PLAIN_PICID_15}},
+
+ /* 7bit picture ids wrap */
+ {{100, 127, BT_PLAIN_PICID_7}, {102, 1, BT_PLAIN_PICID_7}},
+ /* 15bit picture ids wrap */
+ {{100, 32767, BT_PLAIN_PICID_15}, {102, 1, BT_PLAIN_PICID_15}},
+
+ /* 7bit to 15bit picture id */
+ {{100, 126, BT_PLAIN_PICID_7}, {102, 129, BT_PLAIN_PICID_15}},
+};
+
+GST_START_TEST (test_depay_resend_gap_event)
+{
+ test_depay_gap_event_base (&resend_gap_event_test_data[__i__][0], TRUE, TRUE,
+ __i__);
+}
+
+GST_END_TEST;
static Suite *
rtpvp9_suite (void)
{
Suite *s = suite_create ("rtpvp9");
TCase *tc_chain;
-
suite_add_tcase (s, (tc_chain = tcase_create ("vp9depay")));
tcase_add_test (tc_chain, test_depay_flexible_mode);
tcase_add_test (tc_chain, test_depay_non_flexible_mode);
+ tcase_add_loop_test (tc_chain, test_depay_stop_gap_events, 0,
+ G_N_ELEMENTS (stop_gap_events_test_data));
+ tcase_add_loop_test (tc_chain, test_depay_resend_gap_event, 0,
+ G_N_ELEMENTS (resend_gap_event_test_data));
return s;
}