1 /* GStreamer RTP H.265 unit test
3 * Copyright (C) 2017 Centricular Ltd
4 * @author: Tim-Philipp Müller <tim@centricular.com>
5 * Copyright (C) 2018 Collabora Ltd
6 * @author: Nicolas Dufresne <nicolas.dufresne@collabora.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include <gst/check/check.h>
25 #include <gst/app/app.h>
26 #include <gst/rtp/gstrtpbuffer.h>
28 #define ALLOCATOR_CUSTOM_SYSMEM "CustomSysMem"
30 static GstAllocator *custom_sysmem_allocator; /* NULL */
42 custom_sysmem_new (GstMemoryFlags flags, gsize maxsize, gsize align,
43 gsize offset, gsize size)
45 gsize aoffset, padding;
48 /* ensure configured alignment */
49 align |= gst_memory_alignment;
50 /* allocate more to compensate for alignment */
53 mem = g_new0 (CustomSysmem, 1);
55 mem->allocdata = g_malloc (maxsize);
57 mem->data = mem->allocdata;
60 if ((aoffset = ((guintptr) mem->data & align))) {
61 aoffset = (align + 1) - aoffset;
66 if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
67 memset (mem->data, 0, offset);
69 padding = maxsize - (offset + size);
70 if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
71 memset (mem->data + offset + size, 0, padding);
73 gst_memory_init (GST_MEMORY_CAST (mem), flags, custom_sysmem_allocator,
74 NULL, maxsize, align, offset, size);
80 custom_sysmem_map (CustomSysmem * mem, gsize maxsize, GstMapFlags flags)
86 custom_sysmem_unmap (CustomSysmem * mem)
92 custom_sysmem_copy (CustomSysmem * mem, gssize offset, gsize size)
94 g_return_val_if_reached (NULL);
98 custom_sysmem_share (CustomSysmem * mem, gssize offset, gsize size)
100 g_return_val_if_reached (NULL);
104 custom_sysmem_is_span (CustomSysmem * mem1, CustomSysmem * mem2, gsize * offset)
106 g_return_val_if_reached (FALSE);
109 /* Custom allocator */
113 GstAllocator allocator;
114 } CustomSysmemAllocator;
118 GstAllocatorClass allocator_class;
119 } CustomSysmemAllocatorClass;
121 GType custom_sysmem_allocator_get_type (void);
122 G_DEFINE_TYPE (CustomSysmemAllocator, custom_sysmem_allocator,
126 custom_sysmem_allocator_alloc (GstAllocator * allocator, gsize size,
127 GstAllocationParams * params)
129 gsize maxsize = size + params->prefix + params->padding;
131 return (GstMemory *) custom_sysmem_new (params->flags,
132 maxsize, params->align, params->prefix, size);
136 custom_sysmem_allocator_free (GstAllocator * allocator, GstMemory * mem)
138 CustomSysmem *csmem = (CustomSysmem *) mem;
140 g_free (csmem->allocdata);
145 custom_sysmem_allocator_class_init (CustomSysmemAllocatorClass * klass)
147 GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
149 allocator_class->alloc = custom_sysmem_allocator_alloc;
150 allocator_class->free = custom_sysmem_allocator_free;
154 custom_sysmem_allocator_init (CustomSysmemAllocator * allocator)
156 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
158 alloc->mem_type = ALLOCATOR_CUSTOM_SYSMEM;
159 alloc->mem_map = (GstMemoryMapFunction) custom_sysmem_map;
160 alloc->mem_unmap = (GstMemoryUnmapFunction) custom_sysmem_unmap;
161 alloc->mem_copy = (GstMemoryCopyFunction) custom_sysmem_copy;
162 alloc->mem_share = (GstMemoryShareFunction) custom_sysmem_share;
163 alloc->mem_is_span = (GstMemoryIsSpanFunction) custom_sysmem_is_span;
166 /* AppSink subclass proposing our custom allocator to upstream */
175 GstAppSinkClass appsink;
178 GType c_mem_app_sink_get_type (void);
180 G_DEFINE_TYPE (CMemAppSink, c_mem_app_sink, GST_TYPE_APP_SINK);
183 c_mem_app_sink_init (CMemAppSink * cmemsink)
188 c_mem_app_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
190 gst_query_add_allocation_param (query, custom_sysmem_allocator, NULL);
195 c_mem_app_sink_class_init (CMemAppSinkClass * klass)
197 GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
199 basesink_class->propose_allocation = c_mem_app_sink_propose_allocation;
202 #define RTP_H265_FILE GST_TEST_FILES_PATH G_DIR_SEPARATOR_S "h265.rtp"
204 GST_START_TEST (test_rtph265depay_with_downstream_allocator)
206 GstElement *pipeline, *src, *depay, *sink;
212 custom_sysmem_allocator =
213 g_object_new (custom_sysmem_allocator_get_type (), NULL);
215 pipeline = gst_pipeline_new ("pipeline");
217 src = gst_element_factory_make ("appsrc", NULL);
219 caps = gst_caps_new_simple ("application/x-rtp",
220 "media", G_TYPE_STRING, "video",
221 "payload", G_TYPE_INT, 96,
222 "clock-rate", G_TYPE_INT, 90000,
223 "encoding-name", G_TYPE_STRING, "H265",
224 "ssrc", G_TYPE_UINT, 1990683810,
225 "timestamp-offset", G_TYPE_UINT, 3697583446,
226 "seqnum-offset", G_TYPE_UINT, 15568,
227 "a-framerate", G_TYPE_STRING, "30", NULL);
228 g_object_set (src, "format", GST_FORMAT_TIME, "caps", caps, NULL);
229 gst_bin_add (GST_BIN (pipeline), src);
230 gst_caps_unref (caps);
232 depay = gst_element_factory_make ("rtph265depay", NULL);
233 gst_bin_add (GST_BIN (pipeline), depay);
235 sink = g_object_new (c_mem_app_sink_get_type (), NULL);
236 gst_bin_add (GST_BIN (pipeline), sink);
238 gst_element_link_many (src, depay, sink, NULL);
240 gst_element_set_state (pipeline, GST_STATE_PAUSED);
246 fail_unless (g_file_get_contents (RTP_H265_FILE, &data, &len, NULL));
247 fail_unless (len > 2);
254 packet_len = GST_READ_UINT16_BE (pdata);
255 GST_INFO ("rtp packet length: %u (bytes left: %u)", packet_len,
257 fail_unless (len >= 2 + packet_len);
259 flow = gst_app_src_push_buffer (GST_APP_SRC (src),
260 gst_buffer_new_wrapped (g_memdup (pdata + 2, packet_len),
263 fail_unless_equals_int (flow, GST_FLOW_OK);
265 pdata += 2 + packet_len;
266 len -= 2 + packet_len;
272 gst_app_src_end_of_stream (GST_APP_SRC (src));
274 sample = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
275 fail_unless (sample != NULL);
277 buf = gst_sample_get_buffer (sample);
279 GST_LOG ("buffer has %u memories", gst_buffer_n_memory (buf));
280 GST_LOG ("buffer size: %u", (guint) gst_buffer_get_size (buf));
282 fail_unless (gst_buffer_n_memory (buf) > 0);
283 mem = gst_buffer_peek_memory (buf, 0);
284 fail_unless (mem != NULL);
286 GST_LOG ("buffer memory type: %s", mem->allocator->mem_type);
287 fail_unless (gst_memory_is_type (mem, ALLOCATOR_CUSTOM_SYSMEM));
289 gst_sample_unref (sample);
291 gst_element_set_state (pipeline, GST_STATE_NULL);
293 gst_object_unref (pipeline);
295 g_object_unref (custom_sysmem_allocator);
296 custom_sysmem_allocator = NULL;
303 wrap_static_buffer_with_pts (guint8 * buf, gsize size, GstClockTime pts)
307 buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
308 buf, size, 0, size, NULL, NULL);
309 GST_BUFFER_PTS (buffer) = pts;
315 wrap_static_buffer (guint8 * buf, gsize size)
317 return wrap_static_buffer_with_pts (buf, size, GST_CLOCK_TIME_NONE);
320 /* This was generated using pipeline:
321 * gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
322 * ! video/x-raw,width=64,height=64 ! x265enc ! h265parse \
323 * ! rtph265pay ! fakesink dump=1
325 /* RTP h265_idr + marker */
326 static guint8 rtp_h265_idr[] = {
327 0x80, 0xe0, 0x2c, 0x6a, 0xab, 0x7f, 0x71, 0xc0,
328 0x8d, 0x11, 0x33, 0x07, 0x28, 0x01, 0xaf, 0x05,
329 0x38, 0x4a, 0x03, 0x06, 0x7c, 0x7a, 0xb1, 0x8b,
330 0xff, 0xfe, 0xfd, 0xb7, 0xff, 0xff, 0xd1, 0xff,
331 0x40, 0x06, 0xd8, 0xd3, 0xb2, 0xf8
334 GST_START_TEST (test_rtph265depay_eos)
336 GstHarness *h = gst_harness_new ("rtph265depay");
338 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
341 gst_harness_set_caps_str (h,
342 "application/x-rtp,media=video,clock-rate=90000,encoding-name=H265",
343 "video/x-h265,alignment=au,stream-format=byte-stream");
345 buffer = wrap_static_buffer (rtp_h265_idr, sizeof (rtp_h265_idr));
346 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
347 gst_rtp_buffer_set_marker (&rtp, FALSE);
348 gst_rtp_buffer_unmap (&rtp);
350 ret = gst_harness_push (h, buffer);
351 fail_unless_equals_int (ret, GST_FLOW_OK);
352 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
354 fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
355 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
357 gst_harness_teardown (h);
363 GST_START_TEST (test_rtph265depay_marker_to_flag)
365 GstHarness *h = gst_harness_new ("rtph265depay");
367 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
371 gst_harness_set_caps_str (h,
372 "application/x-rtp,media=video,clock-rate=90000,encoding-name=H265",
373 "video/x-h265,alignment=au,stream-format=byte-stream");
375 buffer = wrap_static_buffer (rtp_h265_idr, sizeof (rtp_h265_idr));
376 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
377 fail_unless (gst_rtp_buffer_get_marker (&rtp));
378 seq = gst_rtp_buffer_get_seq (&rtp);
379 gst_rtp_buffer_unmap (&rtp);
381 ret = gst_harness_push (h, buffer);
382 fail_unless_equals_int (ret, GST_FLOW_OK);
383 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
385 buffer = wrap_static_buffer (rtp_h265_idr, sizeof (rtp_h265_idr));
386 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_WRITE, &rtp));
387 gst_rtp_buffer_set_marker (&rtp, FALSE);
388 gst_rtp_buffer_set_seq (&rtp, ++seq);
389 gst_rtp_buffer_unmap (&rtp);
391 ret = gst_harness_push (h, buffer);
392 fail_unless_equals_int (ret, GST_FLOW_OK);
394 /* the second NAL is blocked as there is no marker to let the payloader
395 * know it's a complete AU, we'll use an EOS to unblock it */
396 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1);
397 fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
398 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
400 buffer = gst_harness_pull (h);
401 fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER));
402 gst_buffer_unref (buffer);
404 buffer = gst_harness_pull (h);
405 fail_if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_MARKER));
406 gst_buffer_unref (buffer);
408 gst_harness_teardown (h);
413 /* These were generated using pipeline:
414 * gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
415 * ! video/x-raw,width=256,height=256 \
416 * ! x265enc option-string="slices=2" \
421 static guint8 h265_idr_slice_1[] = {
422 0x00, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, 0x08,
423 0xa2, 0xe6, 0xa3, 0xc6, 0x53, 0x90, 0xea, 0xc8,
424 0x3f, 0xfe, 0xfa, 0xf9, 0x3f, 0xf2, 0x61, 0x98,
425 0xef, 0xf4, 0xe9, 0x97, 0xe7, 0xc2, 0x74, 0x78,
426 0x98, 0x10, 0x01, 0x21, 0xa4, 0x3c, 0x4c, 0x08,
427 0x00, 0x3e, 0x40, 0x92, 0x0c, 0x78
431 static guint8 h265_idr_slice_2[] = {
432 0x00, 0x00, 0x01, 0x28, 0x01, 0x30, 0xf0, 0x8a,
433 0x2e, 0x60, 0xa3, 0xc6, 0x53, 0x90, 0xea, 0xc8,
434 0x3f, 0xfe, 0xfa, 0xf9, 0x3f, 0xf2, 0x61, 0x98,
435 0xef, 0xf4, 0xe9, 0x97, 0xe7, 0xc2, 0x74, 0x78,
436 0x98, 0x10, 0x01, 0x21, 0xa4, 0x3c, 0x4c, 0x08,
437 0x00, 0x3e, 0x40, 0x92, 0x0c, 0x78
440 GST_START_TEST (test_rtph265pay_two_slices_timestamp)
442 GstHarness *h = gst_harness_new_parse ("rtph265pay timestamp-offset=123");
445 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
447 gst_harness_set_src_caps_str (h,
448 "video/x-h265,alignment=nal,stream-format=byte-stream");
450 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h265_idr_slice_1,
451 sizeof (h265_idr_slice_1), 0));
452 fail_unless_equals_int (ret, GST_FLOW_OK);
454 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h265_idr_slice_2,
455 sizeof (h265_idr_slice_2), 0));
456 fail_unless_equals_int (ret, GST_FLOW_OK);
458 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h265_idr_slice_1,
459 sizeof (h265_idr_slice_1), GST_SECOND));
460 fail_unless_equals_int (ret, GST_FLOW_OK);
462 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h265_idr_slice_2,
463 sizeof (h265_idr_slice_2), GST_SECOND));
464 fail_unless_equals_int (ret, GST_FLOW_OK);
466 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
468 buffer = gst_harness_pull (h);
469 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
470 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
471 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
472 gst_rtp_buffer_unmap (&rtp);
473 gst_buffer_unref (buffer);
475 buffer = gst_harness_pull (h);
476 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
477 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
478 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
479 gst_rtp_buffer_unmap (&rtp);
480 gst_buffer_unref (buffer);
482 buffer = gst_harness_pull (h);
483 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
484 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
485 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
486 gst_rtp_buffer_unmap (&rtp);
487 gst_buffer_unref (buffer);
489 buffer = gst_harness_pull (h);
490 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
491 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
492 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
493 gst_rtp_buffer_unmap (&rtp);
494 gst_buffer_unref (buffer);
496 gst_harness_teardown (h);
501 GST_START_TEST (test_rtph265pay_marker_for_flag)
503 GstHarness *h = gst_harness_new_parse ("rtph265pay timestamp-offset=123");
506 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
508 gst_harness_set_src_caps_str (h,
509 "video/x-h265,alignment=nal,stream-format=byte-stream");
511 ret = gst_harness_push (h, wrap_static_buffer (h265_idr_slice_1,
512 sizeof (h265_idr_slice_1)));
513 fail_unless_equals_int (ret, GST_FLOW_OK);
515 buffer = wrap_static_buffer (h265_idr_slice_2, sizeof (h265_idr_slice_2));
516 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_MARKER);
517 ret = gst_harness_push (h, buffer);
518 fail_unless_equals_int (ret, GST_FLOW_OK);
520 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
522 buffer = gst_harness_pull (h);
523 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
524 fail_if (gst_rtp_buffer_get_marker (&rtp));
525 gst_rtp_buffer_unmap (&rtp);
526 gst_buffer_unref (buffer);
528 buffer = gst_harness_pull (h);
529 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
530 fail_unless (gst_rtp_buffer_get_marker (&rtp));
531 gst_rtp_buffer_unmap (&rtp);
532 gst_buffer_unref (buffer);
534 gst_harness_teardown (h);
540 GST_START_TEST (test_rtph265pay_marker_for_au)
542 GstHarness *h = gst_harness_new_parse ("rtph265pay timestamp-offset=123");
544 GstBuffer *slice1, *slice2, *buffer;
545 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
547 gst_harness_set_src_caps_str (h,
548 "video/x-h265,alignment=au,stream-format=byte-stream");
550 slice1 = wrap_static_buffer (h265_idr_slice_1, sizeof (h265_idr_slice_1));
551 slice2 = wrap_static_buffer (h265_idr_slice_2, sizeof (h265_idr_slice_2));
552 buffer = gst_buffer_append (slice1, slice2);
554 ret = gst_harness_push (h, buffer);
555 fail_unless_equals_int (ret, GST_FLOW_OK);
557 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
559 buffer = gst_harness_pull (h);
560 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
561 fail_if (gst_rtp_buffer_get_marker (&rtp));
562 gst_rtp_buffer_unmap (&rtp);
563 gst_buffer_unref (buffer);
565 buffer = gst_harness_pull (h);
566 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
567 fail_unless (gst_rtp_buffer_get_marker (&rtp));
568 gst_rtp_buffer_unmap (&rtp);
569 gst_buffer_unref (buffer);
571 gst_harness_teardown (h);
577 GST_START_TEST (test_rtph265pay_marker_for_fragmented_au)
580 gst_harness_new_parse ("rtph265pay timestamp-offset=123 mtu=40");
582 GstBuffer *slice1, *slice2, *buffer;
583 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
586 gst_harness_set_src_caps_str (h,
587 "video/x-h265,alignment=au,stream-format=byte-stream");
589 slice1 = wrap_static_buffer (h265_idr_slice_1, sizeof (h265_idr_slice_1));
590 slice2 = wrap_static_buffer (h265_idr_slice_2, sizeof (h265_idr_slice_2));
591 buffer = gst_buffer_append (slice1, slice2);
593 ret = gst_harness_push (h, buffer);
594 fail_unless_equals_int (ret, GST_FLOW_OK);
596 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
598 for (i = 0; i < 3; i++) {
599 buffer = gst_harness_pull (h);
600 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
601 fail_if (gst_rtp_buffer_get_marker (&rtp));
602 gst_rtp_buffer_unmap (&rtp);
603 gst_buffer_unref (buffer);
606 buffer = gst_harness_pull (h);
607 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
608 fail_unless (gst_rtp_buffer_get_marker (&rtp));
609 gst_rtp_buffer_unmap (&rtp);
610 gst_buffer_unref (buffer);
612 gst_harness_teardown (h);
621 Suite *s = suite_create ("rtph265");
624 tc_chain = tcase_create ("rtph265depay");
625 suite_add_tcase (s, tc_chain);
626 tcase_add_test (tc_chain, test_rtph265depay_with_downstream_allocator);
627 tcase_add_test (tc_chain, test_rtph265depay_eos);
628 tcase_add_test (tc_chain, test_rtph265depay_marker_to_flag);
630 tc_chain = tcase_create ("rtph265pay");
631 suite_add_tcase (s, tc_chain);
632 tcase_add_test (tc_chain, test_rtph265pay_two_slices_timestamp);
633 tcase_add_test (tc_chain, test_rtph265pay_marker_for_flag);
634 tcase_add_test (tc_chain, test_rtph265pay_marker_for_au);
635 tcase_add_test (tc_chain, test_rtph265pay_marker_for_fragmented_au);
640 GST_CHECK_MAIN (rtph265);