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