1 /* GStreamer plugin for forward error correction
2  * Copyright (C) 2017 Pexip
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Author: Mikhail Fludkov <misha@pexip.com>
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24 #include "packets.h"
25 
26 #include <gst/check/gstharness.h>
27 #include <gst/rtp/gstrtpbuffer.h>
28 #include <gst/check/gstcheck.h>
29 
30 #define RTP_PACKET_DUR (10 * GST_MSECOND)
31 
32 typedef struct
33 {
34   guint8 pt;
35   guint32 ssrc;
36   guint16 seq;
37 } RecoveredPacketInfo;
38 
39 static void
check_rtpulpfecdec_stats(GstHarness * h,guint packets_recovered,guint packets_unrecovered)40 check_rtpulpfecdec_stats (GstHarness * h, guint packets_recovered,
41     guint packets_unrecovered)
42 {
43   guint packets_recovered_out;
44   guint packets_unrecovered_out;
45   gst_harness_get (h, "rtpulpfecdec",
46       "recovered", &packets_recovered_out,
47       "unrecovered", &packets_unrecovered_out, NULL);
48 
49   fail_unless_equals_int (packets_recovered, packets_recovered_out);
50   fail_unless_equals_int (packets_unrecovered, packets_unrecovered_out);
51 }
52 
53 static void
push_lost_event(GstHarness * h,guint32 seqnum,guint64 timestamp,guint64 duration,gboolean event_goes_through)54 push_lost_event (GstHarness * h, guint32 seqnum,
55     guint64 timestamp, guint64 duration, gboolean event_goes_through)
56 {
57   GstEvent *packet_loss_in = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
58       gst_structure_new ("GstRTPPacketLost",
59           "seqnum", G_TYPE_UINT, seqnum,
60           "timestamp", G_TYPE_UINT64, timestamp,
61           "duration", G_TYPE_UINT64, duration, NULL));
62   GstEvent *it, *packet_loss_out = NULL;
63 
64   fail_unless_equals_int (TRUE, gst_harness_push_event (h, packet_loss_in));
65 
66   while (NULL != (it = gst_harness_try_pull_event (h))) {
67     if (GST_EVENT_TYPE (it) != GST_EVENT_CUSTOM_DOWNSTREAM ||
68         !gst_event_has_name (it, "GstRTPPacketLost")) {
69       gst_event_unref (it);
70     } else {
71       packet_loss_out = it;
72       break;
73     }
74   }
75 
76   if (event_goes_through) {
77     const GstStructure *s = gst_event_get_structure (packet_loss_out);
78     guint64 tscopy, durcopy;
79     gboolean might_have_been_fec;
80 
81     fail_unless (gst_structure_has_name (s, "GstRTPPacketLost"));
82     fail_if (gst_structure_has_field (s, "seqnum"));
83     fail_unless (gst_structure_get_uint64 (s, "timestamp", &tscopy));
84     fail_unless (gst_structure_get_uint64 (s, "duration", &durcopy));
85     fail_unless (gst_structure_get_boolean (s, "might-have-been-fec",
86             &might_have_been_fec));
87 
88     fail_unless_equals_uint64 (timestamp, tscopy);
89     fail_unless_equals_uint64 (duration, durcopy);
90     fail_unless (might_have_been_fec == TRUE);
91     gst_event_unref (packet_loss_out);
92   } else {
93     fail_unless (NULL == packet_loss_out);
94   }
95 }
96 
97 static void
lose_and_recover_test(GstHarness * h,guint16 lost_seq,gconstpointer recbuf,gsize recbuf_size)98 lose_and_recover_test (GstHarness * h, guint16 lost_seq,
99     gconstpointer recbuf, gsize recbuf_size)
100 {
101   guint64 duration = 222222;
102   guint64 timestamp = 111111;
103   GstBuffer *bufout;
104   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
105   GstRTPBuffer rtpout = GST_RTP_BUFFER_INIT;
106   GstBuffer *wrap;
107   gpointer reccopy = g_malloc (recbuf_size);
108 
109   memcpy (reccopy, recbuf, recbuf_size);
110 
111   push_lost_event (h, lost_seq, timestamp, duration, FALSE);
112 
113   bufout = gst_harness_pull (h);
114   fail_unless_equals_int (gst_buffer_get_size (bufout), recbuf_size);
115   fail_unless_equals_int (GST_BUFFER_PTS (bufout), timestamp);
116   wrap = gst_buffer_new_wrapped (reccopy, recbuf_size);
117   gst_rtp_buffer_map (wrap, GST_MAP_WRITE, &rtp);
118   gst_rtp_buffer_map (bufout, GST_MAP_READ, &rtpout);
119   gst_rtp_buffer_set_seq (&rtp, gst_rtp_buffer_get_seq (&rtpout));
120   fail_unless (gst_buffer_memcmp (bufout, 0, reccopy, recbuf_size) == 0);
121   gst_rtp_buffer_unmap (&rtp);
122   gst_rtp_buffer_unmap (&rtpout);
123   fail_unless (!GST_BUFFER_FLAG_IS_SET (bufout, GST_RTP_BUFFER_FLAG_REDUNDANT));
124   gst_buffer_unref (bufout);
125   gst_buffer_unref (wrap);
126 
127   /* Pushing the next buffer with discont flag set */
128   bufout = gst_rtp_buffer_new_allocate (0, 0, 0);
129   GST_BUFFER_FLAG_SET (bufout, GST_BUFFER_FLAG_DISCONT);
130   bufout = gst_harness_push_and_pull (h, bufout);
131   /* Checking the flag was unset */
132   fail_unless (!GST_BUFFER_IS_DISCONT (bufout));
133   gst_buffer_unref (bufout);
134 }
135 
136 static void
push_data(GstHarness * h,gconstpointer rtp,gsize rtp_length)137 push_data (GstHarness * h, gconstpointer rtp, gsize rtp_length)
138 {
139   GstBuffer *buf = gst_rtp_buffer_new_copy_data (rtp, rtp_length);
140   GstBuffer *bufout;
141 
142   bufout = gst_harness_push_and_pull (h, buf);
143   if (bufout)
144     gst_buffer_unref (bufout);
145 }
146 
147 static GstHarness *
harness_rtpulpfecdec(guint32 ssrc,guint8 lost_pt,guint8 fec_pt)148 harness_rtpulpfecdec (guint32 ssrc, guint8 lost_pt, guint8 fec_pt)
149 {
150   GstHarness *h =
151       gst_harness_new_parse ("rtpstorage ! rtpulpfecdec ! identity");
152   GObject *internal_storage;
153   gchar *caps_str =
154       g_strdup_printf ("application/x-rtp,ssrc=(uint)%u,payload=(int)%u",
155       ssrc, lost_pt);
156 
157   gst_harness_set (h, "rtpstorage", "size-time", (guint64) 200 * RTP_PACKET_DUR,
158       NULL);
159   gst_harness_get (h, "rtpstorage", "internal-storage", &internal_storage,
160       NULL);
161   gst_harness_set (h, "rtpulpfecdec", "storage", internal_storage, "pt", fec_pt,
162       NULL);
163   g_object_unref (internal_storage);
164 
165   gst_harness_set_src_caps_str (h, caps_str);
166   g_free (caps_str);
167 
168   return h;
169 }
170 
171 static void
packet_recovered_cb(GObject * internal_storage,GstBuffer * buffer,GList * infos)172 packet_recovered_cb (GObject * internal_storage, GstBuffer * buffer,
173     GList * infos)
174 {
175   gboolean found = FALSE;
176   GstRTPBuffer rtp = { NULL };
177   GList *it;
178 
179   fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
180 
181   for (it = infos; it; it = it->next) {
182     RecoveredPacketInfo *info = it->data;
183     if (gst_rtp_buffer_get_seq (&rtp) == info->seq) {
184       fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp), info->pt);
185       fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), info->ssrc);
186       found = TRUE;
187       break;
188     }
189   }
190 
191   gst_rtp_buffer_unmap (&rtp);
192   fail_unless (found);
193 }
194 
195 static GList *
expect_recovered_packets(GstHarness * h,RecoveredPacketInfo * infos,guint infos_len)196 expect_recovered_packets (GstHarness * h, RecoveredPacketInfo * infos,
197     guint infos_len)
198 {
199   GObject *internal_storage;
200   guint i = 0;
201   GList *res = NULL;
202 
203   for (i = 0; i < infos_len; i++)
204     res = g_list_prepend (res, &infos[i]);
205 
206   gst_harness_get (h, "rtpstorage", "internal-storage", &internal_storage,
207       NULL);
208   g_signal_connect (internal_storage, "packet-recovered",
209       G_CALLBACK (packet_recovered_cb), res);
210 
211   g_object_unref (internal_storage);
212 
213   return res;
214 }
215 
GST_START_TEST(rtpulpfecdec_up_and_down)216 GST_START_TEST (rtpulpfecdec_up_and_down)
217 {
218   GstHarness *h = harness_rtpulpfecdec (0, 0, 0);
219   gst_harness_set_src_caps_str (h, "application/x-rtp");
220   check_rtpulpfecdec_stats (h, 0, 0);
221   gst_harness_teardown (h);
222 }
223 
224 GST_END_TEST;
225 
226 static void
_recovered_from_fec_base(guint32 ssrc,guint8 fec_pt,guint8 lost_pt,guint16 lost_seq,gconstpointer fec_packet,gsize fec_packet_size,gconstpointer lost_packet,gsize lost_packet_size)227 _recovered_from_fec_base (guint32 ssrc, guint8 fec_pt,
228     guint8 lost_pt, guint16 lost_seq,
229     gconstpointer fec_packet, gsize fec_packet_size,
230     gconstpointer lost_packet, gsize lost_packet_size)
231 {
232   GstHarness *h = harness_rtpulpfecdec (ssrc, lost_pt, 123);
233   RecoveredPacketInfo info = {.pt = lost_pt,.ssrc = ssrc,.seq = lost_seq };
234   GList *expected = expect_recovered_packets (h, &info, 1);
235 
236   push_data (h, fec_packet, fec_packet_size);
237   lose_and_recover_test (h, lost_seq, lost_packet, lost_packet_size);
238 
239   check_rtpulpfecdec_stats (h, 1, 0);
240 
241   g_list_free (expected);
242   gst_harness_teardown (h);
243 }
244 
GST_START_TEST(rtpulpfecdec_recovered_from_fec)245 GST_START_TEST (rtpulpfecdec_recovered_from_fec)
246 {
247   _recovered_from_fec_base (3536077562UL, 123, 100, 36921,
248       SAMPLE_ULPFEC0_FEC, sizeof (SAMPLE_ULPFEC0_FEC) - 1,
249       SAMPLE_ULPFEC0_MEDIA, sizeof (SAMPLE_ULPFEC0_MEDIA) - 1);
250 }
251 
252 GST_END_TEST;
253 
GST_START_TEST(rtpulpfecdec_recovered_from_fec_long)254 GST_START_TEST (rtpulpfecdec_recovered_from_fec_long)
255 {
256   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
257   RecoveredPacketInfo info = {.pt = 100,.ssrc = 3536077562UL,.seq = 36921 };
258   const guint8 fec[] = SAMPLE_ULPFEC0_FEC;
259   guint8 *feclongmask = NULL;
260   GList *expected = expect_recovered_packets (h, &info, 1);
261 
262   /* Long FEC mask needs 4 more bytes */
263   feclongmask = g_malloc (sizeof (fec) - 1 + 4);
264 
265   /* Copying the beginning of FEC packet:
266    * RTP header (12 bytes) + RtpUlpFecHeader (10 bytes) + RtpUlpFecLevelHeader (4 bytes) */
267   memcpy (feclongmask, fec, 26);
268 
269   // Changing L bit: 0 -> 1
270   feclongmask[12] |= 64;
271   // Changing fec_seq_base: 36921 -> 36874
272   feclongmask[14] = 0x90;
273   feclongmask[15] = 0x0a;
274   // Changing fec_mask: 0x800000000000 -> 0x000000000001
275   feclongmask[24] = 0;
276   feclongmask[25] = 0;
277   feclongmask[26] = 0;
278   feclongmask[27] = 0;
279   feclongmask[28] = 0;
280   feclongmask[29] = 1;
281   memcpy (feclongmask + 30, fec + 26, sizeof (fec) - 1 - 26);
282 
283   push_data (h, feclongmask, sizeof (fec) - 1 + 4);
284   lose_and_recover_test (h, 36921, SAMPLE_ULPFEC0_MEDIA,
285       sizeof (SAMPLE_ULPFEC0_MEDIA) - 1);
286 
287   g_free (feclongmask);
288   check_rtpulpfecdec_stats (h, 1, 0);
289   g_list_free (expected);
290   gst_harness_teardown (h);
291 }
292 
293 GST_END_TEST;
294 
GST_START_TEST(rtpulpfecdec_recovered_from_many)295 GST_START_TEST (rtpulpfecdec_recovered_from_many)
296 {
297   GstHarness *h = harness_rtpulpfecdec (578322839UL, 126, 22);
298   static const gchar *packets[] = {
299     SAMPLE_ULPFEC1_MEDIA0,
300     SAMPLE_ULPFEC1_MEDIA1,
301     SAMPLE_ULPFEC1_MEDIA2,
302     SAMPLE_ULPFEC1_MEDIA3,
303     SAMPLE_ULPFEC1_FEC0,
304     SAMPLE_ULPFEC1_FEC1,
305   };
306   static gsize packets_size[] = {
307     sizeof (SAMPLE_ULPFEC1_MEDIA0) - 1,
308     sizeof (SAMPLE_ULPFEC1_MEDIA1) - 1,
309     sizeof (SAMPLE_ULPFEC1_MEDIA2) - 1,
310     sizeof (SAMPLE_ULPFEC1_MEDIA3) - 1,
311     sizeof (SAMPLE_ULPFEC1_FEC0) - 1,
312     sizeof (SAMPLE_ULPFEC1_FEC1) - 1,
313   };
314   guint lost_seq = __i__ + 8476;
315   const gchar *lost_packet = packets[__i__];
316   gsize lost_packet_size = packets_size[__i__];
317   RecoveredPacketInfo info = {.pt = 126,.ssrc = 578322839UL,.seq = lost_seq };
318   GList *expected = expect_recovered_packets (h, &info, 1);
319   gsize i;
320 
321   for (i = 0; i < G_N_ELEMENTS (packets); ++i) {
322     if (i != (gsize) __i__)
323       push_data (h, packets[i], packets_size[i]);
324   }
325 
326   lose_and_recover_test (h, lost_seq, lost_packet, lost_packet_size);
327 
328   check_rtpulpfecdec_stats (h, 1, 0);
329   g_list_free (expected);
330   gst_harness_teardown (h);
331 }
332 
333 GST_END_TEST;
334 
335 typedef struct _SampleRTPPacket SampleRTPPacket;
336 struct _SampleRTPPacket
337 {
338   const gchar *data;
339   gsize len;
340 };
341 
342 SampleRTPPacket avmcu_media_packets[] = {
343   {SAMPLE_AVMCU2_MEDIA0, sizeof (SAMPLE_AVMCU2_MEDIA0) - 1}
344   ,
345   {SAMPLE_AVMCU2_MEDIA1, sizeof (SAMPLE_AVMCU2_MEDIA1) - 1}
346   ,
347   {SAMPLE_AVMCU2_MEDIA2, sizeof (SAMPLE_AVMCU2_MEDIA2) - 1}
348   ,
349   {SAMPLE_AVMCU2_MEDIA3, sizeof (SAMPLE_AVMCU2_MEDIA3) - 1}
350   ,
351   {SAMPLE_AVMCU2_MEDIA4, sizeof (SAMPLE_AVMCU2_MEDIA4) - 1}
352   ,
353   {SAMPLE_AVMCU2_MEDIA5, sizeof (SAMPLE_AVMCU2_MEDIA5) - 1}
354   ,
355   {SAMPLE_AVMCU2_MEDIA6, sizeof (SAMPLE_AVMCU2_MEDIA6) - 1}
356   ,
357   {SAMPLE_AVMCU2_MEDIA7, sizeof (SAMPLE_AVMCU2_MEDIA7) - 1}
358   ,
359   {SAMPLE_AVMCU2_MEDIA8, sizeof (SAMPLE_AVMCU2_MEDIA8) - 1}
360   ,
361   {SAMPLE_AVMCU2_MEDIA9, sizeof (SAMPLE_AVMCU2_MEDIA9) - 1}
362   ,
363   {SAMPLE_AVMCU2_MEDIA10, sizeof (SAMPLE_AVMCU2_MEDIA10) - 1}
364   ,
365   {SAMPLE_AVMCU2_MEDIA11, sizeof (SAMPLE_AVMCU2_MEDIA11) - 1}
366   ,
367   {SAMPLE_AVMCU2_MEDIA12, sizeof (SAMPLE_AVMCU2_MEDIA12) - 1}
368   ,
369   {SAMPLE_AVMCU2_MEDIA13, sizeof (SAMPLE_AVMCU2_MEDIA13) - 1}
370   ,
371   {SAMPLE_AVMCU2_MEDIA14, sizeof (SAMPLE_AVMCU2_MEDIA14) - 1}
372   ,
373   {SAMPLE_AVMCU2_MEDIA15, sizeof (SAMPLE_AVMCU2_MEDIA15) - 1}
374   ,
375   {SAMPLE_AVMCU2_MEDIA16, sizeof (SAMPLE_AVMCU2_MEDIA16) - 1}
376   ,
377   {SAMPLE_AVMCU2_MEDIA17, sizeof (SAMPLE_AVMCU2_MEDIA17) - 1}
378   ,
379   {SAMPLE_AVMCU2_MEDIA18, sizeof (SAMPLE_AVMCU2_MEDIA18) - 1}
380   ,
381   {SAMPLE_AVMCU2_MEDIA19, sizeof (SAMPLE_AVMCU2_MEDIA19) - 1}
382   ,
383   {SAMPLE_AVMCU2_MEDIA20, sizeof (SAMPLE_AVMCU2_MEDIA20) - 1}
384   ,
385   {SAMPLE_AVMCU2_MEDIA21, sizeof (SAMPLE_AVMCU2_MEDIA21) - 1}
386   ,
387   {SAMPLE_AVMCU2_MEDIA22, sizeof (SAMPLE_AVMCU2_MEDIA22) - 1}
388   ,
389   {SAMPLE_AVMCU2_MEDIA23, sizeof (SAMPLE_AVMCU2_MEDIA23) - 1}
390   ,
391   {SAMPLE_AVMCU2_MEDIA24, sizeof (SAMPLE_AVMCU2_MEDIA24) - 1}
392   ,
393   {SAMPLE_AVMCU2_MEDIA25, sizeof (SAMPLE_AVMCU2_MEDIA25) - 1}
394   ,
395   {SAMPLE_AVMCU2_MEDIA26, sizeof (SAMPLE_AVMCU2_MEDIA26) - 1}
396   ,
397   {SAMPLE_AVMCU2_MEDIA27, sizeof (SAMPLE_AVMCU2_MEDIA27) - 1}
398   ,
399   {SAMPLE_AVMCU2_MEDIA28, sizeof (SAMPLE_AVMCU2_MEDIA28) - 1}
400   ,
401   {SAMPLE_AVMCU2_MEDIA29, sizeof (SAMPLE_AVMCU2_MEDIA29) - 1}
402   ,
403   {SAMPLE_AVMCU2_MEDIA30, sizeof (SAMPLE_AVMCU2_MEDIA30) - 1}
404   ,
405   {SAMPLE_AVMCU2_MEDIA31, sizeof (SAMPLE_AVMCU2_MEDIA31) - 1}
406   ,
407   {SAMPLE_AVMCU2_MEDIA32, sizeof (SAMPLE_AVMCU2_MEDIA32) - 1}
408   ,
409   {SAMPLE_AVMCU2_MEDIA33, sizeof (SAMPLE_AVMCU2_MEDIA33) - 1}
410   ,
411   {SAMPLE_AVMCU2_MEDIA34, sizeof (SAMPLE_AVMCU2_MEDIA34) - 1}
412   ,
413   {SAMPLE_AVMCU2_MEDIA35, sizeof (SAMPLE_AVMCU2_MEDIA35) - 1}
414   ,
415   {SAMPLE_AVMCU2_MEDIA36, sizeof (SAMPLE_AVMCU2_MEDIA36) - 1}
416   ,
417   {SAMPLE_AVMCU2_MEDIA37, sizeof (SAMPLE_AVMCU2_MEDIA37) - 1}
418   ,
419   {SAMPLE_AVMCU2_MEDIA38, sizeof (SAMPLE_AVMCU2_MEDIA38) - 1}
420   ,
421   {SAMPLE_AVMCU2_MEDIA39, sizeof (SAMPLE_AVMCU2_MEDIA39) - 1}
422   ,
423   {SAMPLE_AVMCU2_MEDIA40, sizeof (SAMPLE_AVMCU2_MEDIA40) - 1}
424   ,
425   {SAMPLE_AVMCU2_MEDIA41, sizeof (SAMPLE_AVMCU2_MEDIA41) - 1}
426   ,
427   {SAMPLE_AVMCU2_MEDIA42, sizeof (SAMPLE_AVMCU2_MEDIA42) - 1}
428   ,
429   {SAMPLE_AVMCU2_MEDIA43, sizeof (SAMPLE_AVMCU2_MEDIA43) - 1}
430   ,
431   {SAMPLE_AVMCU2_MEDIA44, sizeof (SAMPLE_AVMCU2_MEDIA44) - 1}
432   ,
433   {SAMPLE_AVMCU2_MEDIA45, sizeof (SAMPLE_AVMCU2_MEDIA45) - 1}
434   ,
435   {SAMPLE_AVMCU2_MEDIA46, sizeof (SAMPLE_AVMCU2_MEDIA46) - 1}
436   ,
437   {SAMPLE_AVMCU2_MEDIA47, sizeof (SAMPLE_AVMCU2_MEDIA47) - 1}
438   ,
439   {SAMPLE_AVMCU2_MEDIA48, sizeof (SAMPLE_AVMCU2_MEDIA48) - 1}
440   ,
441   {SAMPLE_AVMCU2_MEDIA49, sizeof (SAMPLE_AVMCU2_MEDIA49) - 1}
442   ,
443 };
444 
445 SampleRTPPacket avmcu_fec_packets[] = {
446   {SAMPLE_AVMCU2_FEC0, sizeof (SAMPLE_AVMCU2_FEC0) - 1}
447   ,
448   {SAMPLE_AVMCU2_FEC1, sizeof (SAMPLE_AVMCU2_FEC1) - 1}
449   ,
450   {SAMPLE_AVMCU2_FEC2, sizeof (SAMPLE_AVMCU2_FEC2) - 1}
451   ,
452   {SAMPLE_AVMCU2_FEC3, sizeof (SAMPLE_AVMCU2_FEC3) - 1}
453   ,
454   {SAMPLE_AVMCU2_FEC4, sizeof (SAMPLE_AVMCU2_FEC4) - 1}
455   ,
456   {SAMPLE_AVMCU2_FEC5, sizeof (SAMPLE_AVMCU2_FEC5) - 1}
457   ,
458   {SAMPLE_AVMCU2_FEC6, sizeof (SAMPLE_AVMCU2_FEC6) - 1}
459   ,
460 };
461 
462 
GST_START_TEST(rtpulpfecdec_recovered_using_recovered_packet)463 GST_START_TEST (rtpulpfecdec_recovered_using_recovered_packet)
464 {
465   GstHarness *h = harness_rtpulpfecdec (578322839UL, 126, 22);
466   RecoveredPacketInfo info[3] = {
467     {.pt = 126,.ssrc = 578322839UL,.seq = 8477}
468     ,
469     {.pt = 126,.ssrc = 578322839UL,.seq = 8476}
470     ,
471     {.pt = 126,.ssrc = 578322839UL,.seq = 8479}
472   };
473   GList *expected = expect_recovered_packets (h, info, 3);
474 
475   // To recover the packet we want we need to recover 2 packets
476   push_data (h, SAMPLE_ULPFEC1_MEDIA2, sizeof (SAMPLE_ULPFEC1_MEDIA2) - 1);
477   push_data (h, SAMPLE_ULPFEC1_FEC0, sizeof (SAMPLE_ULPFEC1_FEC0) - 1);
478   push_data (h, SAMPLE_ULPFEC1_FEC1, sizeof (SAMPLE_ULPFEC1_FEC1) - 1);
479   push_data (h, SAMPLE_ULPFEC1_FEC2, sizeof (SAMPLE_ULPFEC1_FEC2) - 1);
480 
481   lose_and_recover_test (h, 8479, SAMPLE_ULPFEC1_MEDIA3,
482       sizeof (SAMPLE_ULPFEC1_MEDIA3) - 1);
483 
484   check_rtpulpfecdec_stats (h, 1, 0);
485   g_list_free (expected);
486   gst_harness_teardown (h);
487 }
488 
489 GST_END_TEST;
490 
GST_START_TEST(rtpulpfecdec_recovered_from_storage)491 GST_START_TEST (rtpulpfecdec_recovered_from_storage)
492 {
493   GstHarness *h = harness_rtpulpfecdec (578322839UL, 126, 22);
494 
495   // The packet we want to recover is already in the storage
496   push_data (h, SAMPLE_ULPFEC1_MEDIA0, sizeof (SAMPLE_ULPFEC1_MEDIA0) - 1);
497   push_data (h, SAMPLE_ULPFEC1_MEDIA1, sizeof (SAMPLE_ULPFEC1_MEDIA1) - 1);
498   push_data (h, SAMPLE_ULPFEC1_MEDIA2, sizeof (SAMPLE_ULPFEC1_MEDIA2) - 1);
499   push_data (h, SAMPLE_ULPFEC1_MEDIA3, sizeof (SAMPLE_ULPFEC1_MEDIA3) - 1);
500   push_data (h, SAMPLE_ULPFEC1_FEC0, sizeof (SAMPLE_ULPFEC1_FEC0) - 1);
501   push_data (h, SAMPLE_ULPFEC1_FEC1, sizeof (SAMPLE_ULPFEC1_FEC1) - 1);
502   push_data (h, SAMPLE_ULPFEC1_FEC2, sizeof (SAMPLE_ULPFEC1_FEC2) - 1);
503 
504   lose_and_recover_test (h, 8479, SAMPLE_ULPFEC1_MEDIA3,
505       sizeof (SAMPLE_ULPFEC1_MEDIA3) - 1);
506 
507   check_rtpulpfecdec_stats (h, 1, 0);
508   gst_harness_teardown (h);
509 }
510 
511 GST_END_TEST;
512 
GST_START_TEST(rtpulpfecdec_recovered_push_failed)513 GST_START_TEST (rtpulpfecdec_recovered_push_failed)
514 {
515   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
516   RecoveredPacketInfo info = {.pt = 100,.ssrc = 3536077562UL,.seq = 36921 };
517   GList *expected = expect_recovered_packets (h, &info, 1);
518 
519   // the harness is already PLAYING because there are no src pads, which
520   // means the error-after counter isn't set, so reset and start again.
521   gst_element_set_state (h->element, GST_STATE_NULL);
522   gst_harness_set (h, "identity", "error-after", 2, NULL);
523   gst_harness_play (h);
524 
525   push_data (h, SAMPLE_ULPFEC0_FEC, sizeof (SAMPLE_ULPFEC0_FEC) - 1);
526   push_lost_event (h, 36921, 1111, 2222, FALSE);
527 
528   /* gst_pad_push for the recovered packet has failed,
529    * making sure the error will be propagated from the chain function*/
530   fail_unless_equals_int (gst_harness_push (h, gst_buffer_new ()),
531       GST_FLOW_ERROR);
532 
533   g_list_free (expected);
534   gst_harness_teardown (h);
535 }
536 
537 GST_END_TEST;
538 
GST_START_TEST(rtpulpfecdec_invalid_fec_size_mismatch)539 GST_START_TEST (rtpulpfecdec_invalid_fec_size_mismatch)
540 {
541   static const guint packet_sizes[] = { 21, 25,
542     sizeof (SAMPLE_ULPFEC0_FEC) - 2, sizeof (SAMPLE_ULPFEC0_FEC)
543   };
544   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
545 
546   push_data (h, SAMPLE_ULPFEC0_FEC, packet_sizes[__i__]);
547   push_lost_event (h, 36921, 1111, 2222, TRUE);
548 
549   check_rtpulpfecdec_stats (h, 0, 1);
550   gst_harness_teardown (h);
551 }
552 
553 GST_END_TEST;
554 
GST_START_TEST(rtpulpfecdec_invalid_fec_ebit_not_zero)555 GST_START_TEST (rtpulpfecdec_invalid_fec_ebit_not_zero)
556 {
557   const guint8 fec[] = SAMPLE_ULPFEC0_FEC;
558   guint8 *fec_ebit_not_zero = NULL;
559   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
560 
561   // Changing E bit: 0 -> 1
562   fec_ebit_not_zero = g_malloc (sizeof (fec) - 1);
563   memcpy (fec_ebit_not_zero, fec, sizeof (fec) - 1);
564   fec_ebit_not_zero[12] |= 128;
565 
566   push_data (h, fec_ebit_not_zero, sizeof (fec) - 1);
567   push_lost_event (h, 36921, 1111, 2222, TRUE);
568 
569   g_free (fec_ebit_not_zero);
570   check_rtpulpfecdec_stats (h, 0, 1);
571   gst_harness_teardown (h);
572 }
573 
574 GST_END_TEST;
575 
GST_START_TEST(rtpulpfecdec_invalid_recovered)576 GST_START_TEST (rtpulpfecdec_invalid_recovered)
577 {
578   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 50, 22);
579 
580   push_data (h, SAMPLE_ISSUE_7049, sizeof (SAMPLE_ISSUE_7049) - 1);
581   push_lost_event (h, 36921, 1111, 2222, TRUE);
582 
583   check_rtpulpfecdec_stats (h, 0, 1);
584   gst_harness_teardown (h);
585 }
586 
587 GST_END_TEST;
588 
GST_START_TEST(rtpulpfecdec_invalid_recovered_pt_mismatch)589 GST_START_TEST (rtpulpfecdec_invalid_recovered_pt_mismatch)
590 {
591   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
592   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
593   GstBuffer *modified;
594   GstBuffer *bufout;
595 
596   gst_harness_set_src_caps_str (h, "application/x-rtp,ssrc=(uint)3536077562");
597 
598   /* Payload type can be derivered neither from the caps nor from the media packets */
599   push_data (h, SAMPLE_ULPFEC0_FEC, sizeof (SAMPLE_ULPFEC0_FEC) - 1);
600   push_lost_event (h, 36921, 1111, 2222, TRUE);
601   check_rtpulpfecdec_stats (h, 0, 1);
602 
603   /* Payload type can only be derivered from the caps (pt=50). Recovered packet
604    * with pt=100 should be ignored */
605   gst_harness_set_src_caps_str (h,
606       "application/x-rtp,ssrc=(uint)3536077562,payload=(int)50");
607   push_lost_event (h, 36921, 1111, 2222, TRUE);
608   check_rtpulpfecdec_stats (h, 0, 2);
609 
610   modified =
611       gst_rtp_buffer_new_copy_data (SAMPLE_ULPFEC0_MEDIA,
612       sizeof (SAMPLE_ULPFEC0_MEDIA));
613   fail_unless (gst_rtp_buffer_map (modified, GST_MAP_READ, &rtp));
614   gst_rtp_buffer_set_payload_type (&rtp, 50);
615   gst_rtp_buffer_set_seq (&rtp, 36920);
616   gst_rtp_buffer_unmap (&rtp);
617   /* Now we have media packet with pt=50 and caps with pt=50. */
618   bufout = gst_harness_push_and_pull (h, modified);
619   if (bufout)
620     gst_buffer_unref (bufout);
621   push_lost_event (h, 36921, 1111, 2222, TRUE);
622   check_rtpulpfecdec_stats (h, 0, 3);
623 
624   /* Removing payload type from the caps */
625   gst_harness_set_src_caps_str (h, "application/x-rtp,ssrc=(uint)3536077562");
626   push_lost_event (h, 36921, 1111, 2222, TRUE);
627   check_rtpulpfecdec_stats (h, 0, 4);
628 
629   gst_harness_teardown (h);
630 }
631 
632 GST_END_TEST;
633 
GST_START_TEST(rtpulpfecdec_fecstorage_gives_no_buffers)634 GST_START_TEST (rtpulpfecdec_fecstorage_gives_no_buffers)
635 {
636   GstHarness *h = harness_rtpulpfecdec (3536077562UL, 100, 123);
637 
638   push_lost_event (h, 36921, 1111, 2222, TRUE);
639 
640   check_rtpulpfecdec_stats (h, 0, 1);
641 
642   gst_harness_teardown (h);
643 }
644 
645 GST_END_TEST;
646 
647 static Suite *
rtpfec_suite(void)648 rtpfec_suite (void)
649 {
650   Suite *s = suite_create ("rtpfec");
651   TCase *tc_chain = tcase_create ("general");
652 
653   suite_add_tcase (s, tc_chain);
654   tcase_add_test (tc_chain, rtpulpfecdec_up_and_down);
655 
656   tcase_add_test (tc_chain, rtpulpfecdec_recovered_from_fec);
657   tcase_add_test (tc_chain, rtpulpfecdec_recovered_from_fec_long);
658   tcase_add_loop_test (tc_chain, rtpulpfecdec_recovered_from_many, 0, 4);
659   tcase_add_test (tc_chain, rtpulpfecdec_recovered_using_recovered_packet);
660   tcase_add_test (tc_chain, rtpulpfecdec_recovered_from_storage);
661   tcase_add_test (tc_chain, rtpulpfecdec_recovered_push_failed);
662 
663   tcase_add_loop_test (tc_chain, rtpulpfecdec_invalid_fec_size_mismatch, 0, 4);
664   tcase_add_test (tc_chain, rtpulpfecdec_invalid_fec_ebit_not_zero);
665   tcase_add_test (tc_chain, rtpulpfecdec_invalid_recovered);
666   tcase_add_test (tc_chain, rtpulpfecdec_invalid_recovered_pt_mismatch);
667   tcase_add_test (tc_chain, rtpulpfecdec_fecstorage_gives_no_buffers);
668   return s;
669 }
670 
671 GST_CHECK_MAIN (rtpfec)
672