• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * Copyright (C) 2016 Pexip AS
4  *   @author Stian Selnes <stian@pexip.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/check/check.h>
26 #include <gst/check/gstharness.h>
27 
28 #define RTP_VP8_CAPS_STR \
29   "application/x-rtp,media=video,encoding-name=VP8,clock-rate=90000,payload=96"
30 
31 /* FIXME: array argument is unused! */
32 #define gst_buffer_new_from_array(array) \
33   gst_buffer_new_memdup (vp8_bitstream_payload, sizeof (vp8_bitstream_payload))
34 
35 static guint8 intra_picid6336_seqnum0[] = {
36   0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
37   0x90, 0x80, 0x98, 0xc0, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
38   0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
39 };
40 
41 static guint8 intra_picid24_seqnum0[] = {
42   0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
43   0x90, 0x80, 0x18, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
44   0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
45 };
46 
47 static guint8 intra_nopicid_seqnum0[] = {
48   0x80, 0xe0, 0x00, 0x00, 0x9a, 0xbb, 0xe3, 0xb3, 0x8b, 0xe9, 0x1d, 0x61,
49   0x90, 0x00, 0xf0, 0x07, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00,
50   0x90, 0x00, 0x06, 0x47, 0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21,
51 };
52 
53 static GstBuffer *
create_rtp_vp8_buffer_full(guint seqnum,guint picid,gint picid_bits,GstClockTime buf_pts,gboolean s_bit,gboolean marker_bit)54 create_rtp_vp8_buffer_full (guint seqnum, guint picid, gint picid_bits,
55     GstClockTime buf_pts, gboolean s_bit, gboolean marker_bit)
56 {
57   GstBuffer *ret;
58   guint8 *packet;
59   gsize size;
60 
61   g_assert (picid_bits == 0 || picid_bits == 7 || picid_bits == 15);
62 
63   if (picid_bits == 0) {
64     size = sizeof (intra_nopicid_seqnum0);
65     packet = g_memdup2 (intra_nopicid_seqnum0, size);
66   } else if (picid_bits == 7) {
67     size = sizeof (intra_picid24_seqnum0);
68     packet = g_memdup2 (intra_picid24_seqnum0, size);
69     packet[14] = picid & 0x7f;
70   } else {
71     size = sizeof (intra_picid6336_seqnum0);
72     packet = g_memdup2 (intra_picid6336_seqnum0, size);
73     packet[14] = ((picid >> 8) & 0xff) | 0x80;
74     packet[15] = (picid >> 0) & 0xff;
75   }
76 
77   packet[2] = (seqnum >> 8) & 0xff;
78   packet[3] = (seqnum >> 0) & 0xff;
79 
80   if (marker_bit)
81     packet[1] |= 0x80;
82   else
83     packet[1] &= ~0x80;
84 
85   if (s_bit)
86     packet[12] |= 0x10;
87   else
88     packet[12] &= ~0x10;
89 
90   ret = gst_buffer_new_wrapped (packet, size);
91   GST_BUFFER_PTS (ret) = buf_pts;
92   return ret;
93 }
94 
95 static GstBuffer *
create_rtp_vp8_buffer(guint seqnum,guint picid,guint picid_bits,GstClockTime buf_pts)96 create_rtp_vp8_buffer (guint seqnum, guint picid, guint picid_bits,
97     GstClockTime buf_pts)
98 {
99   return create_rtp_vp8_buffer_full (seqnum, picid, picid_bits, buf_pts, TRUE,
100       TRUE);
101 }
102 
103 static void
add_vp8_meta(GstBuffer * buffer,gboolean use_temporal_scaling,gboolean layer_sync,guint layer_id,guint tl0picidx)104 add_vp8_meta (GstBuffer * buffer, gboolean use_temporal_scaling,
105     gboolean layer_sync, guint layer_id, guint tl0picidx)
106 {
107   GstCustomMeta *meta;
108   GstStructure *s;
109 
110   meta = gst_buffer_add_custom_meta (buffer, "GstVP8Meta");
111   fail_unless (meta != NULL);
112   s = gst_custom_meta_get_structure (meta);
113   gst_structure_set (s,
114       "use-temporal-scaling", G_TYPE_BOOLEAN, use_temporal_scaling,
115       "layer-sync", G_TYPE_BOOLEAN, layer_sync,
116       "layer-id", G_TYPE_UINT, layer_id,
117       "tl0picidx", G_TYPE_UINT, tl0picidx, NULL);
118 }
119 
120 /* PictureID emum is not exported */
121 enum PictureID
122 {
123   VP8_PAY_NO_PICTURE_ID = 0,
124   VP8_PAY_PICTURE_ID_7BITS = 1,
125   VP8_PAY_PICTURE_ID_15BITS = 2,
126 };
127 
128 static const struct no_meta_test_data
129 {
130   /* control inputs */
131   enum PictureID pid;           /* picture ID type of test */
132   gboolean vp8_payload_header_m_flag;
133 
134   /* expected outputs */
135   guint vp8_payload_header_size;
136   guint vp8_payload_control_value;
137 } no_meta_test_data[] = {
138   {
139   VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x10},       /* no picture ID single byte header, S set */
140   {
141   VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0x90},    /* X bit to allow for I bit means header is three bytes, S and X set */
142   {
143   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 */
144       /* repeated with non reference frame */
145   {
146   VP8_PAY_NO_PICTURE_ID, FALSE, 1, 0x30},       /* no picture ID single byte header, S set */
147   {
148   VP8_PAY_PICTURE_ID_7BITS, FALSE, 3, 0xB0},    /* X bit to allow for I bit means header is three bytes, S and X set */
149   {
150   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 */
151 };
152 
GST_START_TEST(test_pay_no_meta)153 GST_START_TEST (test_pay_no_meta)
154 {
155   guint8 vp8_bitstream_payload[] = {
156     0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
157     0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
158   };
159   const struct no_meta_test_data *test_data = &no_meta_test_data[__i__];
160   GstBuffer *buffer;
161   GstMapInfo map = GST_MAP_INFO_INIT;
162   GstHarness *h = gst_harness_new ("rtpvp8pay");
163   gst_harness_set_src_caps_str (h, "video/x-vp8");
164 
165   /* check unknown picture id enum value */
166   fail_unless (test_data->pid <= VP8_PAY_PICTURE_ID_15BITS);
167 
168   g_object_set (h->element, "picture-id-mode", test_data->pid,
169       "picture-id-offset", 0x5A5A, NULL);
170 
171   buffer = gst_buffer_new_memdup (vp8_bitstream_payload,
172       sizeof (vp8_bitstream_payload));
173 
174   /* set droppable if N flag set */
175   if ((test_data->vp8_payload_control_value & 0x20) != 0) {
176     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DROPPABLE);
177   }
178 
179   buffer = gst_harness_push_and_pull (h, buffer);
180 
181   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
182   fail_unless (map.data != NULL);
183 
184   /* check buffer size and content */
185   fail_unless_equals_int (map.size,
186       12 + test_data->vp8_payload_header_size + sizeof (vp8_bitstream_payload));
187 
188   fail_unless_equals_int (test_data->vp8_payload_control_value, map.data[12]);
189 
190   if (test_data->vp8_payload_header_size > 2) {
191     /* vp8 header extension byte must have I set */
192     fail_unless_equals_int (0x80, map.data[13]);
193     /* check picture id */
194     if (test_data->pid == VP8_PAY_PICTURE_ID_7BITS) {
195       fail_unless_equals_int (0x5a, map.data[14]);
196     } else if (test_data->pid == VP8_PAY_PICTURE_ID_15BITS) {
197       fail_unless_equals_int (0xDA, map.data[14]);
198       fail_unless_equals_int (0x5A, map.data[15]);
199     }
200   }
201 
202   gst_buffer_unmap (buffer, &map);
203   gst_buffer_unref (buffer);
204 
205   gst_harness_teardown (h);
206 }
207 
208 GST_END_TEST;
209 
210 static const struct with_meta_test_data
211 {
212   /* control inputs */
213   enum PictureID pid;           /* picture ID type of test */
214   gboolean vp8_payload_header_m_flag;
215   gboolean use_temporal_scaling;
216   gboolean y_flag;
217 
218   /* expected outputs */
219   guint vp8_payload_header_size;
220   guint vp8_payload_control_value;
221   guint vp8_payload_extended_value;
222 } with_meta_test_data[] = {
223   {
224   VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x10, 0x80},   /* no picture ID single byte header, S set */
225   {
226   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 */
227   {
228   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 */
229   {
230   VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0x90, 0x60},    /* no picture ID single byte header, S set */
231   {
232   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 */
233   {
234   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 */
235   {
236   VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0x90, 0x60},     /* no picture ID single byte header, S set */
237   {
238   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 */
239   {
240   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 */
241       /* repeated with non reference frame */
242   {
243   VP8_PAY_NO_PICTURE_ID, FALSE, FALSE, FALSE, 1, 0x30, 0x80},   /* no picture ID single byte header, S set */
244   {
245   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 */
246   {
247   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 */
248   {
249   VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, FALSE, 4, 0xB0, 0x60},    /* no picture ID single byte header, S set */
250   {
251   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 */
252   {
253   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 */
254   {
255   VP8_PAY_NO_PICTURE_ID, FALSE, TRUE, TRUE, 4, 0xB0, 0x60},     /* no picture ID single byte header, S set */
256   {
257   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 */
258   {
259   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 */
260 };
261 
GST_START_TEST(test_pay_with_meta)262 GST_START_TEST (test_pay_with_meta)
263 {
264   guint8 vp8_bitstream_payload[] = {
265     0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
266     0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
267   };
268   const struct with_meta_test_data *test_data = &with_meta_test_data[__i__];
269   GstBuffer *buffer;
270   GstCustomMeta *meta;
271   GstMapInfo map = GST_MAP_INFO_INIT;
272   GstHarness *h = gst_harness_new ("rtpvp8pay");
273   gst_harness_set_src_caps_str (h, "video/x-vp8");
274 
275   /* check for unknown picture id enum value */
276   fail_unless (test_data->pid <= VP8_PAY_PICTURE_ID_15BITS);
277 
278   g_object_set (h->element, "picture-id-mode", test_data->pid,
279       "picture-id-offset", 0x5A5A, NULL);
280 
281   /* Push a buffer in */
282   buffer = gst_buffer_new_memdup (vp8_bitstream_payload,
283       sizeof (vp8_bitstream_payload));
284   add_vp8_meta (buffer, test_data->use_temporal_scaling, test_data->y_flag,
285       2, 255);
286   /* set droppable if N flag set */
287   if ((test_data->vp8_payload_control_value & 0x20) != 0) {
288     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DROPPABLE);
289   }
290 
291   buffer = gst_harness_push_and_pull (h, buffer);
292 
293   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
294   fail_unless (map.data != NULL);
295 
296   meta = gst_buffer_get_custom_meta (buffer, "GstVP8Meta");
297   fail_unless (meta == NULL);
298 
299   /* check buffer size and content */
300   fail_unless_equals_int (map.size,
301       12 + test_data->vp8_payload_header_size + sizeof (vp8_bitstream_payload));
302   fail_unless_equals_int (test_data->vp8_payload_control_value, map.data[12]);
303 
304   if (test_data->vp8_payload_header_size > 1) {
305     int hdridx = 13;
306     fail_unless_equals_int (test_data->vp8_payload_extended_value,
307         map.data[hdridx++]);
308 
309     /* check picture ID */
310     if (test_data->pid == VP8_PAY_PICTURE_ID_7BITS) {
311       fail_unless_equals_int (0x5A, map.data[hdridx++]);
312     } else if (test_data->pid == VP8_PAY_PICTURE_ID_15BITS) {
313       fail_unless_equals_int (0xDA, map.data[hdridx++]);
314       fail_unless_equals_int (0x5A, map.data[hdridx++]);
315     }
316 
317     if (test_data->use_temporal_scaling) {
318       /* check temporal layer 0 picture ID value */
319       fail_unless_equals_int (255, map.data[hdridx++]);
320       /* check temporal layer ID value */
321       fail_unless_equals_int (2, (map.data[hdridx] >> 6) & 0x3);
322 
323       if (test_data->y_flag) {
324         fail_unless_equals_int (1, (map.data[hdridx] >> 5) & 1);
325       } else {
326         fail_unless_equals_int (0, (map.data[hdridx] >> 5) & 1);
327       }
328     }
329   }
330 
331   gst_buffer_unmap (buffer, &map);
332   gst_buffer_unref (buffer);
333 
334   gst_harness_teardown (h);
335 }
336 
337 GST_END_TEST;
338 
GST_START_TEST(test_pay_continuous_picture_id_and_tl0picidx)339 GST_START_TEST (test_pay_continuous_picture_id_and_tl0picidx)
340 {
341   guint8 vp8_bitstream_payload[] = {
342     0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
343     0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
344   };
345   GstHarness *h = gst_harness_new ("rtpvp8pay");
346   const gint header_len_without_tl0picidx = 3;
347   const gint header_len_with_tl0picidx = 5;
348   const gint packet_len_without_tl0picidx = 12 + header_len_without_tl0picidx +
349       sizeof (vp8_bitstream_payload);
350   const gint packet_len_with_tl0picidx = 12 + header_len_with_tl0picidx +
351       sizeof (vp8_bitstream_payload);
352   const gint picid_offset = 14;
353   const gint tl0picidx_offset = 15;
354   GstBuffer *buffer;
355   GstMapInfo map;
356 
357   g_object_set (h->element, "picture-id-mode", VP8_PAY_PICTURE_ID_7BITS,
358       "picture-id-offset", 0, NULL);
359   gst_harness_set_src_caps_str (h, "video/x-vp8");
360 
361   /* First, push a frame without temporal scalability meta */
362   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
363   buffer = gst_harness_push_and_pull (h, buffer);
364   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
365   fail_unless_equals_int (map.size, packet_len_without_tl0picidx);
366   fail_unless_equals_int (map.data[picid_offset], 0x00);
367   gst_buffer_unmap (buffer, &map);
368   gst_buffer_unref (buffer);
369 
370   /* Push a frame for temporal layer 0 with meta */
371   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
372   add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
373 
374   buffer = gst_harness_push_and_pull (h, buffer);
375   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
376   fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
377   fail_unless_equals_int (map.data[picid_offset], 0x01);
378   fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
379   gst_buffer_unmap (buffer, &map);
380   gst_buffer_unref (buffer);
381 
382   /* Push a frame for temporal layer 1 with meta */
383   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
384   add_vp8_meta (buffer, TRUE, TRUE, 1, 0);
385   buffer = gst_harness_push_and_pull (h, buffer);
386   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
387   fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
388   fail_unless_equals_int (map.data[picid_offset], 0x02);
389   fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
390   gst_buffer_unmap (buffer, &map);
391   gst_buffer_unref (buffer);
392 
393   /* Push next frame for temporal layer 0 with meta */
394   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
395   add_vp8_meta (buffer, TRUE, TRUE, 0, 1);
396   buffer = gst_harness_push_and_pull (h, buffer);
397   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
398   fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
399   fail_unless_equals_int (map.data[picid_offset], 0x03);
400   fail_unless_equals_int (map.data[tl0picidx_offset], 0x01);
401   gst_buffer_unmap (buffer, &map);
402   gst_buffer_unref (buffer);
403 
404   /* Another frame for temporal layer 0, but now the meta->tl0picidx has been
405    * reset to 0 (simulating an encoder reset). Payload must ensure tl0picidx
406    * is increasing. */
407   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
408   add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
409   buffer = gst_harness_push_and_pull (h, buffer);
410   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
411   fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
412   fail_unless_equals_int (map.data[picid_offset], 0x04);
413   fail_unless_equals_int (map.data[tl0picidx_offset], 0x02);
414   gst_buffer_unmap (buffer, &map);
415   gst_buffer_unref (buffer);
416 
417   /* If we receive a frame without meta, we should continue to increase and
418    * add tl0picidx (assuming TID=0) in order to maximize interop. */
419   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
420   buffer = gst_harness_push_and_pull (h, buffer);
421   fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
422   fail_unless_equals_int (map.size, packet_len_with_tl0picidx);
423   fail_unless_equals_int (map.data[picid_offset], 0x05);
424   fail_unless_equals_int (map.data[tl0picidx_offset], 0x03);
425   gst_buffer_unmap (buffer, &map);
426   gst_buffer_unref (buffer);
427 
428   gst_harness_teardown (h);
429 }
430 
431 GST_END_TEST;
432 
GST_START_TEST(test_pay_tl0picidx_split_buffer)433 GST_START_TEST (test_pay_tl0picidx_split_buffer)
434 {
435   guint8 vp8_bitstream_payload[] = {
436     0x30, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0xb0, 0x00, 0x90, 0x00, 0x06, 0x47,
437     0x08, 0x85, 0x85, 0x88, 0x99, 0x84, 0x88, 0x21, 0x00
438   };
439   GstHarness *h =
440       gst_harness_new_parse
441       ("rtpvp8pay mtu=28 picture-id-mode=1 picture-id-offset=0");
442   const gint header_len = 12 + 5;       /* RTP + VP8 payload header */
443   const gint picid_offset = 14;
444   const gint tl0picidx_offset = 15;
445   guint output_bytes_left;
446   GstBuffer *buffer;
447   GstMapInfo map;
448 
449   gst_harness_set_src_caps_str (h, "video/x-vp8");
450 
451   /* Push a frame for temporal layer 0 with meta */
452   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
453   add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
454   gst_harness_push (h, buffer);
455 
456   /* Expect it to be split into multiple buffers to fit the MTU */
457   output_bytes_left = sizeof (vp8_bitstream_payload);
458   while (output_bytes_left > 0) {
459     const gint expected = MIN (output_bytes_left, 28 - header_len);
460     const gint packet_len = header_len + expected;
461     output_bytes_left -= expected;
462 
463     buffer = gst_harness_pull (h);
464     fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
465     fail_unless_equals_int (map.size, packet_len);
466     fail_unless_equals_int (map.data[picid_offset], 0x00);
467     fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
468     gst_buffer_unmap (buffer, &map);
469     gst_buffer_unref (buffer);
470   }
471 
472   /* Push a frame for temporal layer 1 with meta */
473   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
474   add_vp8_meta (buffer, TRUE, TRUE, 1, 0);
475   gst_harness_push (h, buffer);
476 
477   /* Expect it to be split into multiple buffers to fit the MTU */
478   output_bytes_left = sizeof (vp8_bitstream_payload);
479   while (output_bytes_left > 0) {
480     const gint expected = MIN (output_bytes_left, 28 - header_len);
481     const gint packet_len = header_len + expected;
482     output_bytes_left -= expected;
483 
484     buffer = gst_harness_pull (h);
485     fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
486     fail_unless_equals_int (map.size, packet_len);
487     fail_unless_equals_int (map.data[picid_offset], 0x01);
488     fail_unless_equals_int (map.data[tl0picidx_offset], 0x00);
489     gst_buffer_unmap (buffer, &map);
490     gst_buffer_unref (buffer);
491   }
492 
493   /* Push another frame for temporal layer 0 with meta */
494   buffer = gst_buffer_new_from_array (vp8_bitstream_payload);
495   add_vp8_meta (buffer, TRUE, TRUE, 0, 0);
496   gst_harness_push (h, buffer);
497 
498   /* Expect it to be split into multiple buffers to fit the MTU */
499   output_bytes_left = sizeof (vp8_bitstream_payload);
500   while (output_bytes_left > 0) {
501     const gint expected = MIN (output_bytes_left, 28 - header_len);
502     const gint packet_len = header_len + expected;
503     output_bytes_left -= expected;
504 
505     buffer = gst_harness_pull (h);
506     fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
507     fail_unless_equals_int (map.size, packet_len);
508     fail_unless_equals_int (map.data[picid_offset], 0x02);
509     fail_unless_equals_int (map.data[tl0picidx_offset], 0x01);
510     gst_buffer_unmap (buffer, &map);
511     gst_buffer_unref (buffer);
512   }
513 
514   gst_harness_teardown (h);
515 }
516 
517 GST_END_TEST;
518 
519 typedef struct _DepayGapEventTestData
520 {
521   gint seq_num;
522   gint picid;
523   gint picid_bits;
524 } DepayGapEventTestData;
525 
526 typedef struct
527 {
528   gint seq_num;
529   gint picid;
530   guint buffer_type;
531   gboolean s_bit;
532   gboolean marker_bit;
533 } DepayGapEventTestDataFull;
534 
535 static void
test_depay_gap_event_base(const DepayGapEventTestData * data,gboolean send_lost_event,gboolean expect_gap_event)536 test_depay_gap_event_base (const DepayGapEventTestData * data,
537     gboolean send_lost_event, gboolean expect_gap_event)
538 {
539   GstEvent *event;
540   GstClockTime pts = 0;
541   GstHarness *h = gst_harness_new ("rtpvp8depay");
542   gst_harness_set_src_caps_str (h, RTP_VP8_CAPS_STR);
543 
544   gst_harness_push (h, create_rtp_vp8_buffer (data[0].seq_num, data[0].picid,
545           data[0].picid_bits, pts));
546   pts += 33 * GST_MSECOND;
547 
548   /* Preparation before pushing gap event. Getting rid of all events which
549    * came by this point - segment, caps, etc */
550   for (gint i = 0; i < 3; i++)
551     gst_event_unref (gst_harness_pull_event (h));
552   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
553 
554   if (send_lost_event) {
555     gst_harness_push_event (h,
556         gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
557             gst_structure_new ("GstRTPPacketLost", "timestamp", G_TYPE_UINT64,
558                 pts, "duration", G_TYPE_UINT64, 33 * GST_MSECOND,
559                 "might-have-been-fec", G_TYPE_BOOLEAN, TRUE, NULL)));
560     pts += 33 * GST_MSECOND;
561   }
562 
563   gst_harness_push (h, create_rtp_vp8_buffer (data[1].seq_num, data[1].picid,
564           data[1].picid_bits, pts));
565   fail_unless_equals_int (2, gst_harness_buffers_received (h));
566 
567   if (expect_gap_event) {
568     /* Making sure the GAP event was pushed downstream */
569     event = gst_harness_pull_event (h);
570     fail_unless_equals_string ("gap",
571         gst_event_type_get_name (GST_EVENT_TYPE (event)));
572     gst_event_unref (event);
573   }
574   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
575 
576   gst_harness_teardown (h);
577 }
578 
579 /* Packet loss + no loss in picture ids */
580 static const DepayGapEventTestData stop_gap_events_test_data[][2] = {
581   /* 7bit picture ids */
582   {{100, 24, 7}, {102, 25, 7}},
583 
584   /* 15bit picture ids */
585   {{100, 250, 15}, {102, 251, 15}},
586 
587   /* 7bit picture ids wrap */
588   {{100, 127, 7}, {102, 0, 7}},
589 
590   /* 15bit picture ids wrap */
591   {{100, 32767, 15}, {102, 0, 15}},
592 
593   /* 7bit to 15bit picture id */
594   {{100, 127, 7}, {102, 128, 15}},
595 };
596 
GST_START_TEST(test_depay_stop_gap_events)597 GST_START_TEST (test_depay_stop_gap_events)
598 {
599   test_depay_gap_event_base (&stop_gap_events_test_data[__i__][0], TRUE, FALSE);
600 }
601 
602 GST_END_TEST;
603 
GST_START_TEST(test_depay_send_gap_event_when_marker_bit_missing_and_picid_gap)604 GST_START_TEST (test_depay_send_gap_event_when_marker_bit_missing_and_picid_gap)
605 {
606   gboolean send_lost_event = __i__ == 0;
607   GstClockTime pts = 0;
608   gint seqnum = 100;
609   gint i;
610   GstEvent *event;
611 
612   GstHarness *h = gst_harness_new_parse ("rtpvp8depay");
613   gst_harness_set_src_caps_str (h, RTP_VP8_CAPS_STR);
614 
615   /* Push a complete frame to avoid depayloader to suppress gap events */
616   fail_unless_equals_int (GST_FLOW_OK,
617       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 23,
618               7, pts, TRUE, TRUE)));
619   pts += 33 * GST_MSECOND;
620   seqnum++;
621 
622   for (i = 0; i < 3; i++)
623     gst_event_unref (gst_harness_pull_event (h));
624   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
625 
626   /* Push packet with start bit set, but no marker bit */
627   fail_unless_equals_int (GST_FLOW_OK,
628       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 24,
629               7, pts, TRUE, FALSE)));
630   pts += 33 * GST_MSECOND;
631   seqnum++;
632 
633   if (send_lost_event) {
634     gst_harness_push_event (h,
635         gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
636             gst_structure_new ("GstRTPPacketLost", "timestamp", G_TYPE_UINT64,
637                 pts, "duration", G_TYPE_UINT64, 33 * GST_MSECOND, NULL)));
638     pts += 33 * GST_MSECOND;
639     seqnum++;
640   }
641 
642   /* Push packet with gap in picid */
643   fail_unless_equals_int (GST_FLOW_OK,
644       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 26,
645               7, pts, TRUE, TRUE)));
646 
647   /* Expect only 2 output frames since the one frame was incomplete */
648   fail_unless_equals_int (2, gst_harness_buffers_received (h));
649 
650   /* There should be a gap event, either triggered by the loss or the picid
651    * gap */
652 
653   /* Making sure the GAP event was pushed downstream */
654   event = gst_harness_pull_event (h);
655   fail_unless_equals_string ("gap",
656       gst_event_type_get_name (GST_EVENT_TYPE (event)));
657   gst_event_unref (event);
658 
659   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
660 
661   gst_harness_teardown (h);
662 }
663 
664 GST_END_TEST;
665 
GST_START_TEST(test_depay_send_gap_event_when_marker_bit_missing_and_no_picid_gap)666 GST_START_TEST
667     (test_depay_send_gap_event_when_marker_bit_missing_and_no_picid_gap) {
668   GstClockTime pts = 0;
669   gint seqnum = 100;
670   gint i;
671   GstEvent *event;
672 
673   GstHarness *h = gst_harness_new_parse ("rtpvp8depay");
674   gst_harness_set_src_caps_str (h, RTP_VP8_CAPS_STR);
675 
676   /* Push a complete frame to avoid depayloader to suppress gap events */
677   fail_unless_equals_int (GST_FLOW_OK,
678       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 23,
679               7, pts, TRUE, TRUE)));
680   pts += 33 * GST_MSECOND;
681   seqnum++;
682 
683   for (i = 0; i < 3; i++)
684     gst_event_unref (gst_harness_pull_event (h));
685   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
686 
687   /* Push packet with start bit set, but no marker bit */
688   fail_unless_equals_int (GST_FLOW_OK,
689       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 24,
690               7, pts, TRUE, FALSE)));
691   pts += 33 * GST_MSECOND;
692   seqnum++;
693 
694   /* Push packet for next picid, without having sent a packet with marker bit
695    * foor the previous picid */
696   fail_unless_equals_int (GST_FLOW_OK,
697       gst_harness_push (h, create_rtp_vp8_buffer_full (seqnum, 25,
698               7, pts, TRUE, TRUE)));
699 
700   /* Expect only 2 output frames since one was incomplete */
701   fail_unless_equals_int (2, gst_harness_buffers_received (h));
702 
703   /* Making sure the GAP event was pushed downstream */
704   event = gst_harness_pull_event (h);
705   gst_event_unref (event);
706 
707   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
708 
709   gst_harness_teardown (h);
710 }
711 
712 GST_END_TEST;
713 
GST_START_TEST(test_depay_no_gap_event_when_partial_frames_with_no_picid_gap)714 GST_START_TEST (test_depay_no_gap_event_when_partial_frames_with_no_picid_gap)
715 {
716   gint i;
717   GstClockTime pts = 0;
718   GstHarness *h = gst_harness_new ("rtpvp8depay");
719   gst_harness_set_src_caps_str (h, RTP_VP8_CAPS_STR);
720 
721   /* start with complete frame to make sure depayloader will not drop
722    * potential gap events */
723   fail_unless_equals_int (GST_FLOW_OK,
724       gst_harness_push (h, create_rtp_vp8_buffer_full (100, 24,
725               7, pts, TRUE, TRUE)));
726   fail_unless_equals_int (1, gst_harness_buffers_received (h));
727 
728   /* drop setup events to more easily check for gap events */
729   for (i = 0; i < 3; i++)
730     gst_event_unref (gst_harness_pull_event (h));
731   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
732 
733   /* Next frame is split in two packets */
734   pts += 33 * GST_MSECOND;
735   fail_unless_equals_int (GST_FLOW_OK,
736       gst_harness_push (h, create_rtp_vp8_buffer_full (101, 25,
737               7, pts, TRUE, FALSE)));
738   fail_unless_equals_int (GST_FLOW_OK,
739       gst_harness_push (h, create_rtp_vp8_buffer_full (102, 25,
740               7, pts, FALSE, TRUE)));
741   fail_unless_equals_int (2, gst_harness_buffers_received (h));
742 
743   /* there must be no gap events */
744   fail_unless_equals_int (gst_harness_events_in_queue (h), 0);
745 
746   gst_harness_teardown (h);
747 }
748 
749 GST_END_TEST;
750 
751 /* Packet loss + lost picture ids */
752 static const DepayGapEventTestData resend_gap_event_test_data[][2] = {
753   /* 7bit picture ids */
754   {{100, 24, 7}, {102, 26, 7}},
755 
756   /* 15bit picture ids */
757   {{100, 250, 15}, {102, 252, 15}},
758 
759   /* 7bit picture ids wrap */
760   {{100, 127, 7}, {102, 1, 7}},
761 
762   /* 15bit picture ids wrap */
763   {{100, 32767, 15}, {102, 1, 15}},
764 
765   /* 7bit to 15bit picture id */
766   {{100, 126, 7}, {102, 129, 15}},
767 };
768 
GST_START_TEST(test_depay_resend_gap_event)769 GST_START_TEST (test_depay_resend_gap_event)
770 {
771   test_depay_gap_event_base (&resend_gap_event_test_data[__i__][0], TRUE, TRUE);
772 }
773 
774 GST_END_TEST;
775 
776 static Suite *
rtpvp8_suite(void)777 rtpvp8_suite (void)
778 {
779   Suite *s = suite_create ("rtpvp8");
780   TCase *tc_chain;
781   static const gchar *tags[] = { NULL };
782 
783   /* Register custom GstVP8Meta manually */
784   gst_meta_register_custom ("GstVP8Meta", tags, NULL, NULL, NULL);
785 
786   suite_add_tcase (s, (tc_chain = tcase_create ("vp8pay")));
787   tcase_add_loop_test (tc_chain, test_pay_no_meta, 0,
788       G_N_ELEMENTS (no_meta_test_data));
789   tcase_add_loop_test (tc_chain, test_pay_with_meta, 0,
790       G_N_ELEMENTS (with_meta_test_data));
791   tcase_add_test (tc_chain, test_pay_continuous_picture_id_and_tl0picidx);
792   tcase_add_test (tc_chain, test_pay_tl0picidx_split_buffer);
793   tcase_add_loop_test (tc_chain, test_depay_stop_gap_events, 0,
794       G_N_ELEMENTS (stop_gap_events_test_data));
795   tcase_add_loop_test (tc_chain, test_depay_resend_gap_event, 0,
796       G_N_ELEMENTS (resend_gap_event_test_data));
797   tcase_add_loop_test (tc_chain,
798       test_depay_send_gap_event_when_marker_bit_missing_and_picid_gap, 0, 2);
799   tcase_add_test (tc_chain,
800       test_depay_send_gap_event_when_marker_bit_missing_and_no_picid_gap);
801   tcase_add_test (tc_chain,
802       test_depay_no_gap_event_when_partial_frames_with_no_picid_gap);
803 
804   return s;
805 }
806 
807 GST_CHECK_MAIN (rtpvp8);
808