gstreamer/tests/check/elements/rtpvp8.c
Mikhail Fludkov 346b077ae0 rtpvp*depay: possibly forward might-have-been-fec PacketLost events
This is ad adaptation of a Pexip patch for dealing with spurious
GstRTPPacketLost events caused by lost ulpfec packets: as FEC packets
under that scheme are spliced in the same sequence domain as the media
packets, it is not generally possible to determine whether a lost packet
was a FEC packet or a media packet.

When upstreaming pexip's ulpfec patches, we decided to drop all lost
events at the base depayloader level, and where the original patch
from pexip was making use of picture ids and marker bits to determine
whether a packet should be forwarded, this patch makes use of those
to determine whether they should be dropped instead (by removing their
might-have-been-fec field).

Spurious lost events coming out of the depayloader can cause the
decoder to stop decoding until the next keyframe and / or request a new
keyframe, and while this is not desirable it makes sense to forward
that information when we have other means to determine whether a lost
packet was indeed a FEC packet, as is the case with VP8 / VP9 payloads
when they carry a picture id.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/769>
2020-10-29 19:56:07 +01:00

624 lines
23 KiB
C

/* GStreamer
*
* Copyright (C) 2016 Pexip AS
* @author Stian Selnes <stian@pexip.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/check/check.h>
#include <gst/check/gstharness.h>
#define RTP_VP8_CAPS_STR \
"application/x-rtp,media=video,encoding-name=VP8,clock-rate=90000,payload=96"
#define gst_buffer_new_from_array(array) gst_buffer_new_wrapped ( \
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)
{
GstCustomMeta *meta;
GstStructure *s;
meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
fail_unless (meta != NULL);
s = gst_custom_meta_get_structure (meta);
gst_structure_set (s,
"use-temporal-scaling", G_TYPE_BOOLEAN, use_temporal_scaling,
"layer-sync", G_TYPE_BOOLEAN, layer_sync,
"layer-id", G_TYPE_UINT, layer_id,
"tl0picidx", G_TYPE_UINT, tl0picidx, NULL);
}
/* PictureID emum is not exported */
enum PictureID
{
VP8_PAY_NO_PICTURE_ID = 0,
VP8_PAY_PICTURE_ID_7BITS = 1,
VP8_PAY_PICTURE_ID_15BITS = 2,
};
static const struct no_meta_test_data
{
/* control inputs */
enum PictureID pid; /* picture ID type of test */
gboolean vp8_payload_header_m_flag;
/* expected outputs */
guint vp8_payload_header_size;
guint vp8_payload_control_value;
} no_meta_test_data[] = {
{
VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x10}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0x90}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0x90}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
/* repeated with non reference frame */
{
VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x30}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0xB0}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, 4, 0xB0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
};
GST_START_TEST (test_pay_no_meta)
{
guint8 vp8_bitstream_payload[] = {
0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
};
const struct no_meta_test_data *test_data = &no_meta_test_data[__i__];
GstBuffer *buffer;
GstMapInfo map = GST_MAP_INFO_INIT;
GstHarness *h = gst_harness_new ("rtpvp8pay");
gst_harness_set_src_caps_str (h, "video/x-vp8");
/* check unknown picture id enum value */
fail_unless (test_data->pid <= VP8_PAY_PICTURE_ID_15BITS);
g_object_set (h->element, "picture-id-mode", test_data->pid,
"picture-id-offset", 0x5A5A, NULL);
buffer = gst_buffer_new_wrapped (g_memdup (vp8_bitstream_payload,
sizeof (vp8_bitstream_payload)), sizeof (vp8_bitstream_payload));
/* set droppable if N flag set */
if ((test_data->vp8_payload_control_value & 0x20) != 0) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DROPPABLE);
}
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless (map.data != NULL);
/* check buffer size and content */
fail_unless_equals_int (map.size,
12 + test_data->vp8_payload_header_size + sizeof (vp8_bitstream_payload));
fail_unless_equals_int (test_data->vp8_payload_control_value, map.data[12]);
if (test_data->vp8_payload_header_size > 2) {
/* vp8 header extension byte must have I set */
fail_unless_equals_int (0x80, map.data[13]);
/* check picture id */
if (test_data->pid == VP8_PAY_PICTURE_ID_7BITS) {
fail_unless_equals_int (0x5a, map.data[14]);
} else if (test_data->pid == VP8_PAY_PICTURE_ID_15BITS) {
fail_unless_equals_int (0xDA, map.data[14]);
fail_unless_equals_int (0x5A, map.data[15]);
}
}
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
static const struct with_meta_test_data
{
/* control inputs */
enum PictureID pid; /* picture ID type of test */
gboolean vp8_payload_header_m_flag;
gboolean use_temporal_scaling;
gboolean y_flag;
/* expected outputs */
guint vp8_payload_header_size;
guint vp8_payload_control_value;
guint vp8_payload_extended_value;
} with_meta_test_data[] = {
{
VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x10, 0x80}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0x90, 0x80}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0x90, 0x80}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
{
VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0x90, 0x60}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0x90, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0x90, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
{
VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0x90, 0x60}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0x90, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0x90, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
/* repeated with non reference frame */
{
VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x30, 0x80}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, FALSE, FALSE, 3, 0xB0, 0x80}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, FALSE, FALSE, 4, 0xB0, 0x80}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
{
VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0xB0, 0x60}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, FALSE, 5, 0xB0, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, FALSE, 6, 0xB0, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
{
VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0xB0, 0x60}, /* no picture ID single byte header, S set */
{
VP8_PAY_PICTURE_ID_7BITS, FALSE, TRUE, TRUE, 5, 0xB0, 0xE0}, /* X bit to allow for I bit means header is three bytes, S and X set */
{
VP8_PAY_PICTURE_ID_15BITS, TRUE, TRUE, TRUE, 6, 0xB0, 0xE0}, /* X bit to allow for I bit with M bit means header is four bytes, S, X and M set */
};
GST_START_TEST (test_pay_with_meta)
{
guint8 vp8_bitstream_payload[] = {
0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
};
const struct with_meta_test_data *test_data = &with_meta_test_data[__i__];
GstBuffer *buffer;
GstCustomMeta *meta;
GstMapInfo map = GST_MAP_INFO_INIT;
GstHarness *h = gst_harness_new ("rtpvp8pay");
gst_harness_set_src_caps_str (h, "video/x-vp8");
/* check for unknown picture id enum value */
fail_unless (test_data->pid <= VP8_PAY_PICTURE_ID_15BITS);
g_object_set (h->element, "picture-id-mode", test_data->pid,
"picture-id-offset", 0x5A5A, NULL);
/* Push a buffer in */
buffer = gst_buffer_new_wrapped (g_memdup (vp8_bitstream_payload,
sizeof (vp8_bitstream_payload)), sizeof (vp8_bitstream_payload));
add_vp8_meta (buffer, test_data->use_temporal_scaling, test_data->y_flag,
2, 255);
/* set droppable if N flag set */
if ((test_data->vp8_payload_control_value & 0x20) != 0) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DROPPABLE);
}
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless (map.data != NULL);
meta = gst_buffer_get_custom_meta (buffer, "GstVP8Meta");
fail_unless (meta == NULL);
/* check buffer size and content */
fail_unless_equals_int (map.size,
12 + test_data->vp8_payload_header_size + sizeof (vp8_bitstream_payload));
fail_unless_equals_int (test_data->vp8_payload_control_value, map.data[12]);
if (test_data->vp8_payload_header_size > 1) {
int hdridx = 13;
fail_unless_equals_int (test_data->vp8_payload_extended_value,
map.data[hdridx++]);
/* check picture ID */
if (test_data->pid == VP8_PAY_PICTURE_ID_7BITS) {
fail_unless_equals_int (0x5A, map.data[hdridx++]);
} else if (test_data->pid == VP8_PAY_PICTURE_ID_15BITS) {
fail_unless_equals_int (0xDA, map.data[hdridx++]);
fail_unless_equals_int (0x5A, map.data[hdridx++]);
}
if (test_data->use_temporal_scaling) {
/* check temporal layer 0 picture ID value */
fail_unless_equals_int (255, map.data[hdridx++]);
/* check temporal layer ID value */
fail_unless_equals_int (2, (map.data[hdridx] >> 6) & 0x3);
if (test_data->y_flag) {
fail_unless_equals_int (1, (map.data[hdridx] >> 5) & 1);
} else {
fail_unless_equals_int (0, (map.data[hdridx] >> 5) & 1);
}
}
}
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_pay_continuous_picture_id_and_tl0picidx)
{
guint8 vp8_bitstream_payload[] = {
0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
};
GstHarness *h = gst_harness_new ("rtpvp8pay");
const gint header_len_without_tl0picidx = 3;
const gint header_len_with_tl0picidx = 5;
const gint packet_len_without_tl0picidx = 12 + header_len_without_tl0picidx +
sizeof (vp8_bitstream_payload);
const gint packet_len_with_tl0picidx = 12 + header_len_with_tl0picidx +
sizeof (vp8_bitstream_payload);
const gint picid_offset = 14;
const gint tl0picidx_offset = 15;
GstBuffer *buffer;
GstMapInfo map;
g_object_set (h->element, "picture-id-mode", VP8_PAY_PICTURE_ID_7BITS,
"picture-id-offset", 0, NULL);
gst_harness_set_src_caps_str (h, "video/x-vp8");
/* First, push a frame without temporal scalability meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_without_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x00);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
/* Push a frame for temporal layer 0 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x01);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
/* Push a frame for temporal layer 1 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 1, 0);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x02);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
/* Push next frame for temporal layer 0 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 0, 1);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x03);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x01);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
/* Another frame for temporal layer 0, but now the meta->tl0picidx has been
* reset to 0 (simulating an encoder reset). Payload must ensure tl0picidx
* is increasing. */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x04);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x02);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
/* If we receive a frame without meta, we should continue to increase and
* add tl0picidx (assuming TID=0) in order to maximize interop. */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
buffer = gst_harness_push_and_pull (h, buffer);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
fail_unless_equals_int (map.data[picid_offset], 0x05);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x03);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (test_pay_tl0picidx_split_buffer)
{
guint8 vp8_bitstream_payload[] = {
0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
};
GstHarness *h =
gst_harness_new_parse
("rtpvp8pay mtu=28 picture-id-mode=1 picture-id-offset=0");
const gint header_len = 12 + 5; /* RTP + VP8 payload header */
const gint picid_offset = 14;
const gint tl0picidx_offset = 15;
guint output_bytes_left;
GstBuffer *buffer;
GstMapInfo map;
gst_harness_set_src_caps_str (h, "video/x-vp8");
/* Push a frame for temporal layer 0 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
gst_harness_push (h, buffer);
/* Expect it to be split into multiple buffers to fit the MTU */
output_bytes_left = sizeof (vp8_bitstream_payload);
while (output_bytes_left > 0) {
const gint expected = MIN (output_bytes_left, 28 - header_len);
const gint packet_len = header_len + expected;
output_bytes_left -= expected;
buffer = gst_harness_pull (h);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len);
fail_unless_equals_int (map.data[picid_offset], 0x00);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
}
/* Push a frame for temporal layer 1 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 1, 0);
gst_harness_push (h, buffer);
/* Expect it to be split into multiple buffers to fit the MTU */
output_bytes_left = sizeof (vp8_bitstream_payload);
while (output_bytes_left > 0) {
const gint expected = MIN (output_bytes_left, 28 - header_len);
const gint packet_len = header_len + expected;
output_bytes_left -= expected;
buffer = gst_harness_pull (h);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len);
fail_unless_equals_int (map.data[picid_offset], 0x01);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
}
/* Push another frame for temporal layer 0 with meta */
buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
gst_harness_push (h, buffer);
/* Expect it to be split into multiple buffers to fit the MTU */
output_bytes_left = sizeof (vp8_bitstream_payload);
while (output_bytes_left > 0) {
const gint expected = MIN (output_bytes_left, 28 - header_len);
const gint packet_len = header_len + expected;
output_bytes_left -= expected;
buffer = gst_harness_pull (h);
fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
fail_unless_equals_int (map.size, packet_len);
fail_unless_equals_int (map.data[picid_offset], 0x02);
fail_unless_equals_int (map.data[tl0picidx_offset], 0x01);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
}
gst_harness_teardown (h);
}
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)
{
Suite *s = suite_create ("rtpvp8");
TCase *tc_chain;
static const gchar *tags[] = { NULL };
/* Register custom GstVP8Meta manually */
gst_meta_register_custom ("GstVP8Meta", tags, NULL, NULL, NULL);
suite_add_tcase (s, (tc_chain = tcase_create ("vp8pay")));
tcase_add_loop_test (tc_chain, test_pay_no_meta, 0,
G_N_ELEMENTS (no_meta_test_data));
tcase_add_loop_test (tc_chain, test_pay_with_meta, 0,
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;
}
GST_CHECK_MAIN (rtpvp8);