1 /* GStreamer
2 * Copyright (C) <2020> Mathieu Duponchelle <mathieu@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include <gst/check/gstcheck.h>
21 #include <gst/check/gstharness.h>
22 #include <gst/rtp/gstrtpbuffer.h>
23 #include <gst/base/base.h>
24
25 typedef struct
26 {
27 guint16 seq;
28 guint16 len;
29 guint8 E;
30 guint8 pt;
31 guint32 mask;
32 guint32 timestamp;
33 guint8 N;
34 guint8 D;
35 guint8 type;
36 guint8 index;
37 guint8 offset;
38 guint8 NA;
39 guint8 seq_ext;
40 guint8 *payload;
41 guint payload_len;
42 } Rtp2DFecHeader;
43
44 static void
parse_header(Rtp2DFecHeader * fec,guint8 * data,guint len)45 parse_header (Rtp2DFecHeader * fec, guint8 * data, guint len)
46 {
47 GstBitReader bits;
48
49 fail_unless (len >= 16);
50
51 gst_bit_reader_init (&bits, data, len);
52
53 fec->seq = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16);
54 fec->len = gst_bit_reader_get_bits_uint16_unchecked (&bits, 16);
55 fec->E = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
56 fec->pt = gst_bit_reader_get_bits_uint8_unchecked (&bits, 7);
57 fec->mask = gst_bit_reader_get_bits_uint32_unchecked (&bits, 24);
58 fec->timestamp = gst_bit_reader_get_bits_uint32_unchecked (&bits, 32);
59 fec->N = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
60 fec->D = gst_bit_reader_get_bits_uint8_unchecked (&bits, 1);
61 fec->type = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3);
62 fec->index = gst_bit_reader_get_bits_uint8_unchecked (&bits, 3);
63 fec->offset = gst_bit_reader_get_bits_uint8_unchecked (&bits, 8);
64 fec->NA = gst_bit_reader_get_bits_uint8_unchecked (&bits, 8);
65 fec->seq_ext = gst_bit_reader_get_bits_uint8_unchecked (&bits, 8);
66 fec->payload = data + 16;
67 fec->payload_len = len - 16;
68 }
69
70 static GstBuffer *
make_media_sample(guint16 seq,guint32 ts,guint8 * payload,guint payload_len)71 make_media_sample (guint16 seq, guint32 ts, guint8 * payload, guint payload_len)
72 {
73 GstBuffer *ret;
74 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
75 guint8 *data;
76
77 ret = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
78
79 gst_rtp_buffer_map (ret, GST_MAP_WRITE, &rtp);
80 gst_rtp_buffer_set_payload_type (&rtp, 33);
81 gst_rtp_buffer_set_seq (&rtp, seq);
82 gst_rtp_buffer_set_timestamp (&rtp, ts);
83 data = gst_rtp_buffer_get_payload (&rtp);
84 memcpy (data, payload, payload_len);
85 gst_rtp_buffer_unmap (&rtp);
86
87 return ret;
88 }
89
90 static void
pull_and_check(GstHarness * h,guint n_packets,guint16 seq,guint length_recovery,guint8 pt_recovery,guint32 ts_recovery,gboolean row,guint8 offset,guint8 NA,guint8 * payload,guint payload_len)91 pull_and_check (GstHarness * h, guint n_packets, guint16 seq,
92 guint length_recovery, guint8 pt_recovery, guint32 ts_recovery,
93 gboolean row, guint8 offset, guint8 NA, guint8 * payload, guint payload_len)
94 {
95 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
96 GstBuffer *buffer;
97 Rtp2DFecHeader fec;
98
99 fail_unless_equals_int (gst_harness_buffers_in_queue (h), n_packets);
100 buffer = gst_harness_pull (h);
101 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
102
103 parse_header (&fec, gst_rtp_buffer_get_payload (&rtp),
104 gst_rtp_buffer_get_payload_len (&rtp));
105
106 fail_unless_equals_int (fec.seq, seq);
107 fail_unless_equals_int (fec.pt, pt_recovery);
108 fail_unless_equals_int (fec.timestamp, ts_recovery);
109 fail_unless_equals_int (fec.D, row ? 1 : 0);
110 fail_unless_equals_int (fec.offset, offset);
111 fail_unless_equals_int (fec.NA, NA);
112 fail_unless_equals_int (fec.payload_len, payload_len);
113 fail_unless_equals_int (memcmp (fec.payload, payload, fec.payload_len), 0);
114
115 gst_rtp_buffer_unmap (&rtp);
116 gst_buffer_unref (buffer);
117 }
118
GST_START_TEST(test_row)119 GST_START_TEST (test_row)
120 {
121 GstHarness *h, *h_fec_1;
122 guint8 payload;
123 GstElement *enc = gst_element_factory_make ("rtpst2022-1-fecenc", NULL);
124
125 g_object_set (enc, "columns", 3, "enable-column-fec", FALSE, NULL);
126 h = gst_harness_new_with_element (enc, "sink", "src");
127 h_fec_1 = gst_harness_new_with_element (h->element, NULL, "fec_1");
128
129 gst_harness_set_src_caps_str (h, "application/x-rtp");
130
131 payload = 0x37;
132 gst_harness_push (h, make_media_sample (0, 0, &payload, 1));
133 payload = 0x28;
134 gst_harness_push (h, make_media_sample (1, 0, &payload, 1));
135 payload = 0xff;
136 gst_harness_push (h, make_media_sample (2, 0, &payload, 1));
137
138 payload = 0x37 ^ 0x28 ^ 0xff;
139 pull_and_check (h_fec_1, 1, 0, 1, 33, 0, TRUE, 1, 3, &payload, 1);
140
141 gst_object_unref (enc);
142 gst_harness_teardown (h);
143 gst_harness_teardown (h_fec_1);
144 }
145
146 GST_END_TEST;
147
GST_START_TEST(test_columns)148 GST_START_TEST (test_columns)
149 {
150 GstHarness *h, *h_fec_0;
151 guint8 payload;
152 GstElement *enc = gst_element_factory_make ("rtpst2022-1-fecenc", NULL);
153
154 g_object_set (enc, "columns", 3, "rows", 3, "enable-row-fec", FALSE, NULL);
155 h = gst_harness_new_with_element (enc, "sink", "src");
156 h_fec_0 = gst_harness_new_with_element (h->element, NULL, "fec_0");
157
158 gst_harness_set_src_caps_str (h, "application/x-rtp");
159
160 payload = 0x37;
161 gst_harness_push (h, make_media_sample (0, 0, &payload, 1));
162 payload = 0x28;
163 gst_harness_push (h, make_media_sample (1, 0, &payload, 1));
164 payload = 0xff;
165 gst_harness_push (h, make_media_sample (2, 0, &payload, 1));
166 payload = 0xde;
167 gst_harness_push (h, make_media_sample (3, 0, &payload, 1));
168 payload = 0xad;
169 gst_harness_push (h, make_media_sample (4, 0, &payload, 1));
170 payload = 0xbe;
171 gst_harness_push (h, make_media_sample (5, 0, &payload, 1));
172 payload = 0xef;
173 gst_harness_push (h, make_media_sample (6, 0, &payload, 1));
174 payload = 0x58;
175 gst_harness_push (h, make_media_sample (7, 0, &payload, 1));
176 payload = 0x92;
177 gst_harness_push (h, make_media_sample (8, 0, &payload, 1));
178
179 /* Let's check distribution of the column FEC over the repair window
180 * We should receive column FEC packets upon pushing buffers with
181 * seqnums 9, 12 and 15
182 */
183
184 /* At this point no column FEC should have been put out */
185 fail_unless_equals_int (gst_harness_buffers_in_queue (h_fec_0), 0);
186
187 /* Now push the first buffer in the second 3 x 3 grid, it's at
188 * this point we expect to receive our first column FEC packet
189 */
190 gst_harness_push (h, make_media_sample (9, 0, &payload, 1));
191 payload = 0x37 ^ 0xde ^ 0xef;
192 pull_and_check (h_fec_0, 1, 0, 1, 33, 0, FALSE, 3, 3, &payload, 1);
193
194 gst_harness_push (h, make_media_sample (10, 0, &payload, 1));
195 gst_harness_push (h, make_media_sample (11, 0, &payload, 1));
196 fail_unless_equals_int (gst_harness_buffers_in_queue (h_fec_0), 0);
197 gst_harness_push (h, make_media_sample (12, 0, &payload, 1));
198 payload = 0x28 ^ 0xad ^ 0x58;
199 pull_and_check (h_fec_0, 1, 1, 1, 33, 0, FALSE, 3, 3, &payload, 1);
200
201 gst_harness_push (h, make_media_sample (13, 0, &payload, 1));
202 gst_harness_push (h, make_media_sample (14, 0, &payload, 1));
203 fail_unless_equals_int (gst_harness_buffers_in_queue (h_fec_0), 0);
204 gst_harness_push (h, make_media_sample (15, 0, &payload, 1));
205 payload = 0xff ^ 0xbe ^ 0x92;
206 pull_and_check (h_fec_0, 1, 2, 1, 33, 0, FALSE, 3, 3, &payload, 1);
207
208 gst_object_unref (enc);
209 gst_harness_teardown (h);
210 gst_harness_teardown (h_fec_0);
211 }
212
213 GST_END_TEST;
214
215 static Suite *
st2022_1_dec_suite(void)216 st2022_1_dec_suite (void)
217 {
218 Suite *s = suite_create ("rtpst2022-1-fecdec");
219 TCase *tc_chain = tcase_create ("general");
220
221 suite_add_tcase (s, tc_chain);
222
223 tcase_add_test (tc_chain, test_row);
224 tcase_add_test (tc_chain, test_columns);
225
226 return s;
227 }
228
229 GST_CHECK_MAIN (st2022_1_dec)
230