1 /* GStreamer RTP H.264 unit test
3 * Copyright (C) 2017 Centricular Ltd
4 * @author: Tim-Philipp Müller <tim@centricular.com>
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.
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.
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.
22 #include <gst/check/check.h>
23 #include <gst/app/app.h>
24 #include <gst/rtp/gstrtpbuffer.h>
26 #define ALLOCATOR_CUSTOM_SYSMEM "CustomSysMem"
28 static GstAllocator *custom_sysmem_allocator; /* NULL */
40 custom_sysmem_new (GstMemoryFlags flags, gsize maxsize, gsize align,
41 gsize offset, gsize size)
43 gsize aoffset, padding;
46 /* ensure configured alignment */
47 align |= gst_memory_alignment;
48 /* allocate more to compensate for alignment */
51 mem = g_new0 (CustomSysmem, 1);
53 mem->allocdata = g_malloc (maxsize);
55 mem->data = mem->allocdata;
58 if ((aoffset = ((guintptr) mem->data & align))) {
59 aoffset = (align + 1) - aoffset;
64 if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
65 memset (mem->data, 0, offset);
67 padding = maxsize - (offset + size);
68 if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
69 memset (mem->data + offset + size, 0, padding);
71 gst_memory_init (GST_MEMORY_CAST (mem), flags, custom_sysmem_allocator,
72 NULL, maxsize, align, offset, size);
78 custom_sysmem_map (CustomSysmem * mem, gsize maxsize, GstMapFlags flags)
84 custom_sysmem_unmap (CustomSysmem * mem)
90 custom_sysmem_copy (CustomSysmem * mem, gssize offset, gsize size)
92 g_return_val_if_reached (NULL);
96 custom_sysmem_share (CustomSysmem * mem, gssize offset, gsize size)
98 g_return_val_if_reached (NULL);
102 custom_sysmem_is_span (CustomSysmem * mem1, CustomSysmem * mem2, gsize * offset)
104 g_return_val_if_reached (FALSE);
107 /* Custom allocator */
111 GstAllocator allocator;
112 } CustomSysmemAllocator;
116 GstAllocatorClass allocator_class;
117 } CustomSysmemAllocatorClass;
119 GType custom_sysmem_allocator_get_type (void);
120 G_DEFINE_TYPE (CustomSysmemAllocator, custom_sysmem_allocator,
124 custom_sysmem_allocator_alloc (GstAllocator * allocator, gsize size,
125 GstAllocationParams * params)
127 gsize maxsize = size + params->prefix + params->padding;
129 return (GstMemory *) custom_sysmem_new (params->flags,
130 maxsize, params->align, params->prefix, size);
134 custom_sysmem_allocator_free (GstAllocator * allocator, GstMemory * mem)
136 CustomSysmem *csmem = (CustomSysmem *) mem;
138 g_free (csmem->allocdata);
143 custom_sysmem_allocator_class_init (CustomSysmemAllocatorClass * klass)
145 GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
147 allocator_class->alloc = custom_sysmem_allocator_alloc;
148 allocator_class->free = custom_sysmem_allocator_free;
152 custom_sysmem_allocator_init (CustomSysmemAllocator * allocator)
154 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
156 alloc->mem_type = ALLOCATOR_CUSTOM_SYSMEM;
157 alloc->mem_map = (GstMemoryMapFunction) custom_sysmem_map;
158 alloc->mem_unmap = (GstMemoryUnmapFunction) custom_sysmem_unmap;
159 alloc->mem_copy = (GstMemoryCopyFunction) custom_sysmem_copy;
160 alloc->mem_share = (GstMemoryShareFunction) custom_sysmem_share;
161 alloc->mem_is_span = (GstMemoryIsSpanFunction) custom_sysmem_is_span;
164 /* AppSink subclass proposing our custom allocator to upstream */
173 GstAppSinkClass appsink;
176 GType c_mem_app_sink_get_type (void);
178 G_DEFINE_TYPE (CMemAppSink, c_mem_app_sink, GST_TYPE_APP_SINK);
181 c_mem_app_sink_init (CMemAppSink * cmemsink)
186 c_mem_app_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
188 gst_query_add_allocation_param (query, custom_sysmem_allocator, NULL);
193 c_mem_app_sink_class_init (CMemAppSinkClass * klass)
195 GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
197 basesink_class->propose_allocation = c_mem_app_sink_propose_allocation;
200 #define RTP_H264_FILE GST_TEST_FILES_PATH G_DIR_SEPARATOR_S "h264.rtp"
202 GST_START_TEST (test_rtph264depay_with_downstream_allocator)
204 GstElement *pipeline, *src, *depay, *sink;
210 custom_sysmem_allocator =
211 g_object_new (custom_sysmem_allocator_get_type (), NULL);
213 pipeline = gst_pipeline_new ("pipeline");
215 src = gst_element_factory_make ("appsrc", NULL);
217 caps = gst_caps_new_simple ("application/x-rtp",
218 "media", G_TYPE_STRING, "video",
219 "payload", G_TYPE_INT, 96,
220 "clock-rate", G_TYPE_INT, 90000,
221 "encoding-name", G_TYPE_STRING, "H264",
222 "ssrc", G_TYPE_UINT, 1990683810,
223 "timestamp-offset", G_TYPE_UINT, 3697583446,
224 "seqnum-offset", G_TYPE_UINT, 15568,
225 "a-framerate", G_TYPE_STRING, "30", NULL);
226 g_object_set (src, "format", GST_FORMAT_TIME, "caps", caps, NULL);
227 gst_bin_add (GST_BIN (pipeline), src);
228 gst_caps_unref (caps);
230 depay = gst_element_factory_make ("rtph264depay", NULL);
231 gst_bin_add (GST_BIN (pipeline), depay);
233 sink = g_object_new (c_mem_app_sink_get_type (), NULL);
234 gst_bin_add (GST_BIN (pipeline), sink);
236 gst_element_link_many (src, depay, sink, NULL);
238 gst_element_set_state (pipeline, GST_STATE_PAUSED);
244 fail_unless (g_file_get_contents (RTP_H264_FILE, &data, &len, NULL));
245 fail_unless (len > 2);
252 packet_len = GST_READ_UINT16_BE (pdata);
253 GST_INFO ("rtp packet length: %u (bytes left: %u)", packet_len,
255 fail_unless (len >= 2 + packet_len);
257 flow = gst_app_src_push_buffer (GST_APP_SRC (src),
258 gst_buffer_new_wrapped (g_memdup (pdata + 2, packet_len),
261 fail_unless_equals_int (flow, GST_FLOW_OK);
263 pdata += 2 + packet_len;
264 len -= 2 + packet_len;
270 gst_app_src_end_of_stream (GST_APP_SRC (src));
272 sample = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
273 fail_unless (sample != NULL);
275 buf = gst_sample_get_buffer (sample);
277 GST_LOG ("buffer has %u memories", gst_buffer_n_memory (buf));
278 GST_LOG ("buffer size: %u", (guint) gst_buffer_get_size (buf));
280 fail_unless (gst_buffer_n_memory (buf) > 0);
281 mem = gst_buffer_peek_memory (buf, 0);
282 fail_unless (mem != NULL);
284 GST_LOG ("buffer memory type: %s", mem->allocator->mem_type);
285 fail_unless (gst_memory_is_type (mem, ALLOCATOR_CUSTOM_SYSMEM));
287 gst_sample_unref (sample);
289 gst_element_set_state (pipeline, GST_STATE_NULL);
291 gst_object_unref (pipeline);
293 g_object_unref (custom_sysmem_allocator);
294 custom_sysmem_allocator = NULL;
300 static guint8 h264_aud[] = {
301 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0
304 /* These were generated using pipeline:
305 * gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \
306 * ! video/x-raw,width=128,height=128 \
307 * ! openh264enc num-slices=2 \
312 static guint8 h264_sps[] = {
313 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x29,
314 0x8c, 0x8d, 0x41, 0x02, 0x24, 0x03, 0xc2, 0x21,
319 static guint8 h264_pps[] = {
320 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80
324 static guint8 h264_idr_slice_1[] = {
325 0x00, 0x00, 0x00, 0x01, 0x65, 0xb8, 0x00, 0x04,
326 0x00, 0x00, 0x11, 0xff, 0xff, 0xf8, 0x22, 0x8a,
327 0x1f, 0x1c, 0x00, 0x04, 0x0a, 0x63, 0x80, 0x00,
328 0x81, 0xec, 0x9a, 0x93, 0x93, 0x93, 0x93, 0x93,
329 0x93, 0xad, 0x57, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
330 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d,
331 0x75, 0xd7, 0x5d, 0x78
335 static guint8 h264_idr_slice_2[] = {
336 0x00, 0x00, 0x00, 0x01, 0x65, 0x04, 0x2e, 0x00,
337 0x01, 0x00, 0x00, 0x04, 0x7f, 0xff, 0xfe, 0x08,
338 0xa2, 0x87, 0xc7, 0x00, 0x01, 0x02, 0x98, 0xe0,
339 0x00, 0x20, 0x7b, 0x26, 0xa4, 0xe4, 0xe4, 0xe4,
340 0xe4, 0xe4, 0xeb, 0x55, 0xd7, 0x5d, 0x75, 0xd7,
341 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75,
342 0xd7, 0x5d, 0x75, 0xd7, 0x5e
346 wrap_static_buffer_with_pts (guint8 * buf, gsize size, GstClockTime pts)
350 buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
351 buf, size, 0, size, NULL, NULL);
352 GST_BUFFER_PTS (buffer) = pts;
358 wrap_static_buffer (guint8 * buf, gsize size)
360 return wrap_static_buffer_with_pts (buf, size, GST_CLOCK_TIME_NONE);
363 /* The RFC makes special use of NAL type 24 to 27, this test makes sure that
364 * such a NAL from the outside gets ignored properly. */
365 GST_START_TEST (test_rtph264pay_reserved_nals)
367 GstHarness *h = gst_harness_new ("rtph264pay");
368 /* we simply hack an AUD with the reserved nal types */
369 guint8 nal_24[sizeof (h264_aud)];
370 guint8 nal_25[sizeof (h264_aud)];
371 guint8 nal_26[sizeof (h264_aud)];
372 guint8 nal_27[sizeof (h264_aud)];
375 gst_harness_set_src_caps_str (h,
376 "video/x-h264,alignment=nal,stream-format=byte-stream");
378 ret = gst_harness_push (h, wrap_static_buffer (h264_sps, sizeof (h264_sps)));
379 fail_unless_equals_int (ret, GST_FLOW_OK);
381 ret = gst_harness_push (h, wrap_static_buffer (h264_pps, sizeof (h264_pps)));
382 fail_unless_equals_int (ret, GST_FLOW_OK);
384 memcpy (nal_24, h264_aud, sizeof (h264_aud));
386 ret = gst_harness_push (h, wrap_static_buffer (nal_24, sizeof (nal_24)));
387 fail_unless_equals_int (ret, GST_FLOW_OK);
389 memcpy (nal_25, h264_aud, sizeof (h264_aud));
391 ret = gst_harness_push (h, wrap_static_buffer (nal_25, sizeof (nal_25)));
392 fail_unless_equals_int (ret, GST_FLOW_OK);
395 memcpy (nal_26, h264_aud, sizeof (h264_aud));
397 ret = gst_harness_push (h, wrap_static_buffer (nal_26, sizeof (nal_26)));
398 fail_unless_equals_int (ret, GST_FLOW_OK);
401 memcpy (nal_27, h264_aud, sizeof (h264_aud));
403 ret = gst_harness_push (h, wrap_static_buffer (nal_27, sizeof (nal_27)));
404 fail_unless_equals_int (ret, GST_FLOW_OK);
406 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
407 gst_harness_teardown (h);
413 GST_START_TEST (test_rtph264pay_two_slices_timestamp)
415 GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123");
418 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
420 gst_harness_set_src_caps_str (h,
421 "video/x-h264,alignment=nal,stream-format=byte-stream");
423 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_1,
424 sizeof (h264_idr_slice_1), 0));
425 fail_unless_equals_int (ret, GST_FLOW_OK);
427 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_2,
428 sizeof (h264_idr_slice_2), 0));
429 fail_unless_equals_int (ret, GST_FLOW_OK);
431 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_1,
432 sizeof (h264_idr_slice_1), GST_SECOND));
433 fail_unless_equals_int (ret, GST_FLOW_OK);
435 ret = gst_harness_push (h, wrap_static_buffer_with_pts (h264_idr_slice_2,
436 sizeof (h264_idr_slice_2), GST_SECOND));
437 fail_unless_equals_int (ret, GST_FLOW_OK);
440 fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
442 buffer = gst_harness_pull (h);
443 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
444 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
445 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
446 gst_rtp_buffer_unmap (&rtp);
447 gst_buffer_unref (buffer);
449 buffer = gst_harness_pull (h);
450 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
451 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0);
452 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123);
453 gst_rtp_buffer_unmap (&rtp);
454 gst_buffer_unref (buffer);
456 buffer = gst_harness_pull (h);
457 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
458 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
459 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
460 gst_rtp_buffer_unmap (&rtp);
461 gst_buffer_unref (buffer);
463 buffer = gst_harness_pull (h);
464 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp));
465 fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), GST_SECOND);
466 fail_unless_equals_uint64 (gst_rtp_buffer_get_timestamp (&rtp), 123 + 90000);
467 gst_rtp_buffer_unmap (&rtp);
468 gst_buffer_unref (buffer);
470 gst_harness_teardown (h);
478 Suite *s = suite_create ("rtph264");
481 tc_chain = tcase_create ("rtph264depay");
482 suite_add_tcase (s, tc_chain);
483 tcase_add_test (tc_chain, test_rtph264depay_with_downstream_allocator);
485 tc_chain = tcase_create ("rtph264pay");
486 suite_add_tcase (s, tc_chain);
487 tcase_add_test (tc_chain, test_rtph264pay_reserved_nals);
488 tcase_add_test (tc_chain, test_rtph264pay_two_slices_timestamp);
493 GST_CHECK_MAIN (rtph264);