33243039626970e54fc461fb6835dc9a9c42cffc
[platform/upstream/gst-plugins-good.git] / tests / check / elements / rtph264.c
1 /* GStreamer RTP H.264 unit test
2  *
3  * Copyright (C) 2017 Centricular Ltd
4  *   @author: Tim-Philipp Müller <tim@centricular.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
22 #include <gst/check/check.h>
23 #include <gst/app/app.h>
24 #include <gst/rtp/gstrtpbuffer.h>
25
26 #define ALLOCATOR_CUSTOM_SYSMEM "CustomSysMem"
27
28 static GstAllocator *custom_sysmem_allocator;   /* NULL */
29
30 /* Custom memory */
31
32 typedef struct
33 {
34   GstMemory mem;
35   guint8 *data;
36   guint8 *allocdata;
37 } CustomSysmem;
38
39 static CustomSysmem *
40 custom_sysmem_new (GstMemoryFlags flags, gsize maxsize, gsize align,
41     gsize offset, gsize size)
42 {
43   gsize aoffset, padding;
44   CustomSysmem *mem;
45
46   /* ensure configured alignment */
47   align |= gst_memory_alignment;
48   /* allocate more to compensate for alignment */
49   maxsize += align;
50
51   mem = g_new0 (CustomSysmem, 1);
52
53   mem->allocdata = g_malloc (maxsize);
54
55   mem->data = mem->allocdata;
56
57   /* do alignment */
58   if ((aoffset = ((guintptr) mem->data & align))) {
59     aoffset = (align + 1) - aoffset;
60     mem->data += aoffset;
61     maxsize -= aoffset;
62   }
63
64   if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
65     memset (mem->data, 0, offset);
66
67   padding = maxsize - (offset + size);
68   if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
69     memset (mem->data + offset + size, 0, padding);
70
71   gst_memory_init (GST_MEMORY_CAST (mem), flags, custom_sysmem_allocator,
72       NULL, maxsize, align, offset, size);
73
74   return mem;
75 }
76
77 static gpointer
78 custom_sysmem_map (CustomSysmem * mem, gsize maxsize, GstMapFlags flags)
79 {
80   return mem->data;
81 }
82
83 static gboolean
84 custom_sysmem_unmap (CustomSysmem * mem)
85 {
86   return TRUE;
87 }
88
89 static CustomSysmem *
90 custom_sysmem_copy (CustomSysmem * mem, gssize offset, gsize size)
91 {
92   g_return_val_if_reached (NULL);
93 }
94
95 static CustomSysmem *
96 custom_sysmem_share (CustomSysmem * mem, gssize offset, gsize size)
97 {
98   g_return_val_if_reached (NULL);
99 }
100
101 static gboolean
102 custom_sysmem_is_span (CustomSysmem * mem1, CustomSysmem * mem2, gsize * offset)
103 {
104   g_return_val_if_reached (FALSE);
105 }
106
107 /* Custom allocator */
108
109 typedef struct
110 {
111   GstAllocator allocator;
112 } CustomSysmemAllocator;
113
114 typedef struct
115 {
116   GstAllocatorClass allocator_class;
117 } CustomSysmemAllocatorClass;
118
119 GType custom_sysmem_allocator_get_type (void);
120 G_DEFINE_TYPE (CustomSysmemAllocator, custom_sysmem_allocator,
121     GST_TYPE_ALLOCATOR);
122
123 static GstMemory *
124 custom_sysmem_allocator_alloc (GstAllocator * allocator, gsize size,
125     GstAllocationParams * params)
126 {
127   gsize maxsize = size + params->prefix + params->padding;
128
129   return (GstMemory *) custom_sysmem_new (params->flags,
130       maxsize, params->align, params->prefix, size);
131 }
132
133 static void
134 custom_sysmem_allocator_free (GstAllocator * allocator, GstMemory * mem)
135 {
136   CustomSysmem *csmem = (CustomSysmem *) mem;
137
138   g_free (csmem->allocdata);
139   g_free (csmem);
140 }
141
142 static void
143 custom_sysmem_allocator_class_init (CustomSysmemAllocatorClass * klass)
144 {
145   GstAllocatorClass *allocator_class = (GstAllocatorClass *) klass;
146
147   allocator_class->alloc = custom_sysmem_allocator_alloc;
148   allocator_class->free = custom_sysmem_allocator_free;
149 }
150
151 static void
152 custom_sysmem_allocator_init (CustomSysmemAllocator * allocator)
153 {
154   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
155
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;
162 }
163
164 /* AppSink subclass proposing our custom allocator to upstream */
165
166 typedef struct
167 {
168   GstAppSink appsink;
169 } CMemAppSink;
170
171 typedef struct
172 {
173   GstAppSinkClass appsink;
174 } CMemAppSinkClass;
175
176 GType c_mem_app_sink_get_type (void);
177
178 G_DEFINE_TYPE (CMemAppSink, c_mem_app_sink, GST_TYPE_APP_SINK);
179
180 static void
181 c_mem_app_sink_init (CMemAppSink * cmemsink)
182 {
183 }
184
185 static gboolean
186 c_mem_app_sink_propose_allocation (GstBaseSink * sink, GstQuery * query)
187 {
188   gst_query_add_allocation_param (query, custom_sysmem_allocator, NULL);
189   return TRUE;
190 }
191
192 static void
193 c_mem_app_sink_class_init (CMemAppSinkClass * klass)
194 {
195   GstBaseSinkClass *basesink_class = (GstBaseSinkClass *) klass;
196
197   basesink_class->propose_allocation = c_mem_app_sink_propose_allocation;
198 }
199
200 #define RTP_H264_FILE GST_TEST_FILES_PATH G_DIR_SEPARATOR_S "h264.rtp"
201
202 GST_START_TEST (test_rtph264depay_with_downstream_allocator)
203 {
204   GstElement *pipeline, *src, *depay, *sink;
205   GstMemory *mem;
206   GstSample *sample;
207   GstBuffer *buf;
208   GstCaps *caps;
209
210   custom_sysmem_allocator =
211       g_object_new (custom_sysmem_allocator_get_type (), NULL);
212
213   pipeline = gst_pipeline_new ("pipeline");
214
215   src = gst_element_factory_make ("appsrc", NULL);
216
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);
229
230   depay = gst_element_factory_make ("rtph264depay", NULL);
231   gst_bin_add (GST_BIN (pipeline), depay);
232
233   sink = g_object_new (c_mem_app_sink_get_type (), NULL);
234   gst_bin_add (GST_BIN (pipeline), sink);
235
236   gst_element_link_many (src, depay, sink, NULL);
237
238   gst_element_set_state (pipeline, GST_STATE_PAUSED);
239
240   {
241     gchar *data, *pdata;
242     gsize len;
243
244     fail_unless (g_file_get_contents (RTP_H264_FILE, &data, &len, NULL));
245     fail_unless (len > 2);
246
247     pdata = data;
248     while (len > 2) {
249       GstFlowReturn flow;
250       guint16 packet_len;
251
252       packet_len = GST_READ_UINT16_BE (pdata);
253       GST_INFO ("rtp packet length: %u (bytes left: %u)", packet_len,
254           (guint) len);
255       fail_unless (len >= 2 + packet_len);
256
257       flow = gst_app_src_push_buffer (GST_APP_SRC (src),
258           gst_buffer_new_wrapped (g_memdup (pdata + 2, packet_len),
259               packet_len));
260
261       fail_unless_equals_int (flow, GST_FLOW_OK);
262
263       pdata += 2 + packet_len;
264       len -= 2 + packet_len;
265     }
266
267     g_free (data);
268   }
269
270   gst_app_src_end_of_stream (GST_APP_SRC (src));
271
272   sample = gst_app_sink_pull_preroll (GST_APP_SINK (sink));
273   fail_unless (sample != NULL);
274
275   buf = gst_sample_get_buffer (sample);
276
277   GST_LOG ("buffer has %u memories", gst_buffer_n_memory (buf));
278   GST_LOG ("buffer size: %u", (guint) gst_buffer_get_size (buf));
279
280   fail_unless (gst_buffer_n_memory (buf) > 0);
281   mem = gst_buffer_peek_memory (buf, 0);
282   fail_unless (mem != NULL);
283
284   GST_LOG ("buffer memory type: %s", mem->allocator->mem_type);
285   fail_unless (gst_memory_is_type (mem, ALLOCATOR_CUSTOM_SYSMEM));
286
287   gst_sample_unref (sample);
288
289   gst_element_set_state (pipeline, GST_STATE_NULL);
290
291   gst_object_unref (pipeline);
292
293   g_object_unref (custom_sysmem_allocator);
294   custom_sysmem_allocator = NULL;
295 }
296
297 GST_END_TEST;
298
299 /* AUD */
300 static guint8 h264_aud[] = {
301   0x00, 0x00, 0x00, 0x01, 0x09, 0xf0
302 };
303
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 \
308  *     ! fakesink dump=1
309  */
310
311 /* SPS */
312 static guint8 h264_sps[] = {
313   0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x29,
314   0x8c, 0x8d, 0x41, 0x02, 0x24, 0x03, 0xc2, 0x21,
315   0x1a, 0x80
316 };
317
318 /* PPS */
319 static guint8 h264_pps[] = {
320   0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80
321 };
322
323 /* IDR Slice 1 */
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
332 };
333
334 /* IDR Slice 2 */
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
343 };
344
345 static GstBuffer *
346 wrap_static_buffer_with_pts (guint8 * buf, gsize size, GstClockTime pts)
347 {
348   GstBuffer *buffer;
349
350   buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
351       buf, size, 0, size, NULL, NULL);
352   GST_BUFFER_PTS (buffer) = pts;
353
354   return buffer;
355 }
356
357 static GstBuffer *
358 wrap_static_buffer (guint8 * buf, gsize size)
359 {
360   return wrap_static_buffer_with_pts (buf, size, GST_CLOCK_TIME_NONE);
361 }
362
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)
366 {
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)];
373   GstFlowReturn ret;
374
375   gst_harness_set_src_caps_str (h,
376       "video/x-h264,alignment=nal,stream-format=byte-stream");
377
378   ret = gst_harness_push (h, wrap_static_buffer (h264_sps, sizeof (h264_sps)));
379   fail_unless_equals_int (ret, GST_FLOW_OK);
380
381   ret = gst_harness_push (h, wrap_static_buffer (h264_pps, sizeof (h264_pps)));
382   fail_unless_equals_int (ret, GST_FLOW_OK);
383
384   memcpy (nal_24, h264_aud, sizeof (h264_aud));
385   nal_24[4] = 24;
386   ret = gst_harness_push (h, wrap_static_buffer (nal_24, sizeof (nal_24)));
387   fail_unless_equals_int (ret, GST_FLOW_OK);
388
389   memcpy (nal_25, h264_aud, sizeof (h264_aud));
390   nal_25[4] = 25;
391   ret = gst_harness_push (h, wrap_static_buffer (nal_25, sizeof (nal_25)));
392   fail_unless_equals_int (ret, GST_FLOW_OK);
393
394
395   memcpy (nal_26, h264_aud, sizeof (h264_aud));
396   nal_26[4] = 26;
397   ret = gst_harness_push (h, wrap_static_buffer (nal_26, sizeof (nal_26)));
398   fail_unless_equals_int (ret, GST_FLOW_OK);
399
400
401   memcpy (nal_27, h264_aud, sizeof (h264_aud));
402   nal_27[4] = 27;
403   ret = gst_harness_push (h, wrap_static_buffer (nal_27, sizeof (nal_27)));
404   fail_unless_equals_int (ret, GST_FLOW_OK);
405
406   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2);
407   gst_harness_teardown (h);
408 }
409
410 GST_END_TEST;
411
412
413 GST_START_TEST (test_rtph264pay_two_slices_timestamp)
414 {
415   GstHarness *h = gst_harness_new_parse ("rtph264pay timestamp-offset=123");
416   GstFlowReturn ret;
417   GstBuffer *buffer;
418   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
419
420   gst_harness_set_src_caps_str (h,
421       "video/x-h264,alignment=nal,stream-format=byte-stream");
422
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);
426
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);
430
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);
434
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);
438
439
440   fail_unless_equals_int (gst_harness_buffers_in_queue (h), 4);
441
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);
448
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);
455
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);
462
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);
469
470   gst_harness_teardown (h);
471 }
472
473 GST_END_TEST;
474
475 static Suite *
476 rtph264_suite (void)
477 {
478   Suite *s = suite_create ("rtph264");
479   TCase *tc_chain;
480
481   tc_chain = tcase_create ("rtph264depay");
482   suite_add_tcase (s, tc_chain);
483   tcase_add_test (tc_chain, test_rtph264depay_with_downstream_allocator);
484
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);
489
490   return s;
491 }
492
493 GST_CHECK_MAIN (rtph264);