1 /* GStreamer
2 *
3 * Copyright (C) 2015 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
22 #include <gst/check/check.h>
23 #include <gst/check/gstharness.h>
24 #include <gst/rtp/gstrtpbuffer.h>
25
26 #define RTP_H263_CAPS_STR(p) \
27 "application/x-rtp,media=video,encoding-name=H263,clock-rate=90000," \
28 "payload=" G_STRINGIFY(p)
29
30 #define H263P_RTP_CAPS_STR(p) \
31 "application/x-rtp,media=video,encoding-name=H263-1998,clock-rate=90000," \
32 "payload="G_STRINGIFY(p)
33
34 static gboolean
have_element(const gchar * element_name)35 have_element (const gchar * element_name)
36 {
37 GstElement *element;
38 gboolean ret;
39
40 element = gst_element_factory_make (element_name, NULL);
41 ret = element != NULL;
42
43 if (element)
44 gst_object_unref (element);
45
46 return ret;
47 }
48
49 static GstBuffer *
create_rtp_buffer(guint8 * data,gsize size,guint ts,gint seqnum)50 create_rtp_buffer (guint8 * data, gsize size, guint ts, gint seqnum)
51 {
52 GstBuffer *buf = gst_rtp_buffer_new_copy_data (data, size);
53 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
54
55 GST_BUFFER_PTS (buf) = (ts) * (GST_SECOND / 30);
56
57 gst_rtp_buffer_map (buf, GST_MAP_WRITE, &rtp);
58 gst_rtp_buffer_set_seq (&rtp, seqnum);
59 gst_rtp_buffer_unmap (&rtp);
60
61 return buf;
62 }
63
GST_START_TEST(test_h263depay_start_packet_too_small_mode_a)64 GST_START_TEST (test_h263depay_start_packet_too_small_mode_a)
65 {
66 GstHarness *h = gst_harness_new ("rtph263depay");
67 guint8 packet[] = {
68 0x80, 0xa2, 0x17, 0x62, 0x57, 0xbb, 0x48, 0x98, 0x4a, 0x59, 0xe8, 0xdc,
69 0x00, 0x00, 0x80, 0x00
70 };
71
72 gst_harness_set_src_caps_str (h, RTP_H263_CAPS_STR (34));
73 fail_unless_equals_int (GST_FLOW_OK,
74 gst_harness_push (h, create_rtp_buffer (packet, sizeof (packet), 0, 0)));
75
76 /* Packet should be dropped and depayloader not crash */
77 fail_unless_equals_int (0, gst_harness_buffers_received (h));
78
79 gst_harness_teardown (h);
80 }
81
82 GST_END_TEST;
83
GST_START_TEST(test_h263depay_start_packet_too_small_mode_b)84 GST_START_TEST (test_h263depay_start_packet_too_small_mode_b)
85 {
86 GstHarness *h = gst_harness_new ("rtph263depay");
87 guint8 packet[] = {
88 0x80, 0xa2, 0x17, 0x62, 0x57, 0xbb, 0x48, 0x98, 0x4a, 0x59, 0xe8, 0xdc,
89 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
90 };
91
92 gst_harness_set_src_caps_str (h, RTP_H263_CAPS_STR (34));
93 fail_unless_equals_int (GST_FLOW_OK,
94 gst_harness_push (h, create_rtp_buffer (packet, sizeof (packet), 0, 0)));
95
96 /* Packet should be dropped and depayloader not crash */
97 fail_unless_equals_int (0, gst_harness_buffers_received (h));
98
99 gst_harness_teardown (h);
100 }
101
102 GST_END_TEST;
103
GST_START_TEST(test_h263depay_start_packet_too_small_mode_c)104 GST_START_TEST (test_h263depay_start_packet_too_small_mode_c)
105 {
106 GstHarness *h = gst_harness_new ("rtph263depay");
107 guint8 packet[] = {
108 0x80, 0xa2, 0x17, 0x62, 0x57, 0xbb, 0x48, 0x98, 0x4a, 0x59, 0xe8, 0xdc,
109 0xc0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
110 };
111
112 gst_harness_set_src_caps_str (h, RTP_H263_CAPS_STR (34));
113 fail_unless_equals_int (GST_FLOW_OK,
114 gst_harness_push (h, create_rtp_buffer (packet, sizeof (packet), 0, 0)));
115
116 /* Packet should be dropped and depayloader not crash */
117 fail_unless_equals_int (0, gst_harness_buffers_received (h));
118
119 gst_harness_teardown (h);
120 }
121
122 GST_END_TEST;
123
GST_START_TEST(test_h263pay_mode_b_snow)124 GST_START_TEST (test_h263pay_mode_b_snow)
125 {
126 /* Payloading one large frame (like snow) is more likely to use mode b and
127 * trigger issues in valgrind seen previously like double free, invalid read
128 * etc. */
129 GstHarness *h;
130 guint frames = 1;
131 guint i;
132
133 if (!have_element ("avenc_h263"))
134 return;
135
136 h = gst_harness_new_parse
137 ("avenc_h263 rtp-payload-size=1 ! rtph263pay mtu=1350 ");
138 gst_harness_add_src_parse (h,
139 "videotestsrc pattern=snow is-live=1 ! "
140 "capsfilter caps=\"video/x-raw,format=I420,width=176,height=144\"", TRUE);
141
142 for (i = 0; i < frames; i++)
143 gst_harness_push_from_src (h);
144 fail_unless (gst_harness_buffers_received (h) >= frames);
145
146 gst_harness_teardown (h);
147 }
148
149 GST_END_TEST;
150
151 /* gst_rtp_buffer_get_payload() may return a copy of the payload. This test
152 * makes sure that the rtph263pdepay also produces the correct output in this
153 * case. */
GST_START_TEST(test_h263pdepay_fragmented_memory_non_writable_buffer)154 GST_START_TEST (test_h263pdepay_fragmented_memory_non_writable_buffer)
155 {
156 GstHarness *h;
157 GstBuffer *header_buf, *payload_buf, *buf;
158 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
159 guint8 header[] = {
160 0x04, 0x00
161 };
162 guint8 payload[] = {
163 0x80, 0x02, 0x1c, 0xb8, 0x01, 0x00, 0x11, 0xe0, 0x44, 0xc4
164 };
165 guint8 frame[] = {
166 0x00, 0x00, 0x80, 0x02, 0x1c, 0xb8, 0x01, 0x00, 0x11, 0xe0, 0x44, 0xc4
167 };
168
169 h = gst_harness_new ("rtph263pdepay");
170 gst_harness_set_src_caps_str (h, "application/x-rtp, media=video, "
171 "clock-rate=90000, encoding-name=H263-1998");
172
173 /* Packet with M=1, P=1 */
174 header_buf = gst_rtp_buffer_new_allocate (sizeof (header), 0, 0);
175 gst_rtp_buffer_map (header_buf, GST_MAP_WRITE, &rtp);
176 gst_rtp_buffer_set_marker (&rtp, TRUE);
177 memcpy (gst_rtp_buffer_get_payload (&rtp), header, sizeof (header));
178 gst_rtp_buffer_unmap (&rtp);
179
180 payload_buf = gst_buffer_new_allocate (NULL, sizeof (payload), NULL);
181 gst_buffer_fill (payload_buf, 0, payload, sizeof (payload));
182 buf = gst_buffer_append (header_buf, payload_buf);
183
184 gst_harness_push (h, gst_buffer_ref (buf));
185 gst_buffer_unref (buf);
186
187 buf = gst_harness_pull (h);
188 fail_unless (gst_buffer_memcmp (buf, 0, frame, sizeof (frame)) == 0);
189 gst_buffer_unref (buf);
190
191 gst_harness_teardown (h);
192 }
193
194 GST_END_TEST;
195
196 /* gst_rtp_buffer_get_payload() may return a copy of the payload. This test
197 * makes sure that the rtph263pdepay also produces the correct output in this
198 * case. */
GST_START_TEST(test_h263pdepay_fragmented_memory_non_writable_buffer_split_frame)199 GST_START_TEST
200 (test_h263pdepay_fragmented_memory_non_writable_buffer_split_frame) {
201 GstHarness *h;
202 GstBuffer *header_buf, *payload_buf, *buf;
203 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
204 guint8 header[] = {
205 0x04, 0x00
206 };
207 guint8 payload[] = {
208 0x80, 0x02, 0x1c, 0xb8, 0x01, 0x00, 0x11, 0xe0, 0x44, 0xc4
209 };
210 guint8 frame[] = {
211 0x00, 0x00, 0x80, 0x02, 0x1c, 0xb8, 0x01, 0x00, 0x11, 0xe0, 0x44, 0xc4
212 };
213
214 h = gst_harness_new ("rtph263pdepay");
215 gst_harness_set_src_caps_str (h, "application/x-rtp, media=video, "
216 "clock-rate=90000, encoding-name=H263-1998");
217
218 /* First packet, M=0, P=1 */
219 header_buf = gst_rtp_buffer_new_allocate (sizeof (header), 0, 0);
220 gst_rtp_buffer_map (header_buf, GST_MAP_WRITE, &rtp);
221 gst_rtp_buffer_set_marker (&rtp, FALSE);
222 gst_rtp_buffer_set_seq (&rtp, 0);
223 memcpy (gst_rtp_buffer_get_payload (&rtp), header, sizeof (header));
224 gst_rtp_buffer_unmap (&rtp);
225
226 payload_buf = gst_buffer_new_allocate (NULL, sizeof (payload), NULL);
227 gst_buffer_fill (payload_buf, 0, payload, sizeof (payload));
228 buf = gst_buffer_append (header_buf, payload_buf);
229
230 gst_harness_push (h, gst_buffer_ref (buf));
231 gst_buffer_unref (buf);
232 fail_unless_equals_int (gst_harness_buffers_received (h), 0);
233
234 /* Second packet, M=1, P=1 */
235 header_buf = gst_rtp_buffer_new_allocate (sizeof (header), 0, 0);
236 gst_rtp_buffer_map (header_buf, GST_MAP_WRITE, &rtp);
237 gst_rtp_buffer_set_marker (&rtp, TRUE);
238 gst_rtp_buffer_set_seq (&rtp, 1);
239 memcpy (gst_rtp_buffer_get_payload (&rtp), header, sizeof (header));
240 gst_rtp_buffer_unmap (&rtp);
241
242 payload_buf = gst_buffer_new_allocate (NULL, sizeof (payload), NULL);
243 gst_buffer_memset (payload_buf, 0, 0, 10);
244 buf = gst_buffer_append (header_buf, payload_buf);
245
246 gst_harness_push (h, gst_buffer_ref (buf));
247 gst_buffer_unref (buf);
248 fail_unless_equals_int (gst_harness_buffers_received (h), 1);
249
250 buf = gst_harness_pull (h);
251 fail_unless (gst_buffer_memcmp (buf, 0, frame, sizeof (frame)) == 0);
252 gst_buffer_unref (buf);
253
254 gst_harness_teardown (h);
255 }
256
257 GST_END_TEST;
258
GST_START_TEST(test_h263pdepay_dont_push_empty_frame)259 GST_START_TEST (test_h263pdepay_dont_push_empty_frame)
260 {
261 GstHarness *h = gst_harness_new ("rtph263pdepay");
262
263 /* Packet that only contains header information and an extra picture header
264 * (PLEN > 0). Partly handcrafted packet. Originally this packet did not
265 * have P=1 (hence it was not start of a picture). With both P=1 and M=1 we
266 * only need one packet to reproduce the issue where trying to push an empty
267 * frame when PLEN is set */
268 guint8 packet[] = {
269 0x80, 0xe8, 0xbc, 0xaa, 0x14, 0x12, 0x16, 0x5c, 0xb8, 0x4e, 0x39, 0x04,
270 0x25, 0x00, 0x54, 0x39, 0xd0, 0x12, 0x06, 0x9e, 0xb5, 0x0a, 0xf5, 0xe8,
271 0x32, 0xeb, 0xd0, 0x6b, 0xd6, 0xa2, 0xfa, 0xd4, 0x3d, 0xd7, 0xa0, 0x2b,
272 0x24, 0x97, 0xc3, 0xbf, 0xc0, 0xbb, 0xd7, 0xa0,
273 };
274
275 gst_harness_set_src_caps_str (h, H263P_RTP_CAPS_STR (100));
276
277 fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
278 create_rtp_buffer (packet, sizeof (packet), 0, 0)));
279
280 fail_unless_equals_int (gst_harness_buffers_received (h), 0);
281
282 gst_harness_teardown (h);
283 }
284
285 GST_END_TEST;
286
GST_START_TEST(test_h263ppay_non_fixed_caps)287 GST_START_TEST (test_h263ppay_non_fixed_caps)
288 {
289 GstHarness *h;
290 guint8 frame[] = {
291 0x00, 0x00, 0x80, 0x06, 0x1c, 0xa8, 0x01, 0x04, 0x91, 0xe0, 0x37, 0xff,
292 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
293 };
294
295 h = gst_harness_new_parse ("rtph263ppay");
296
297 /* Set non-fixed caps after payloader */
298 gst_harness_set_caps_str (h, "video/x-h263, variant=(string)itu",
299 "application/x-rtp, clock-rate=[1, MAX]");
300
301 gst_harness_push (h, gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
302 frame, sizeof (frame), 0, sizeof (frame), NULL, NULL));
303
304 fail_unless_equals_int (1, gst_harness_buffers_received (h));
305
306 gst_harness_teardown (h);
307 }
308
309 GST_END_TEST;
310
311 static Suite *
rtph263_suite(void)312 rtph263_suite (void)
313 {
314 Suite *s = suite_create ("rtph263");
315 TCase *tc_chain;
316
317 suite_add_tcase (s, (tc_chain = tcase_create ("h263depay")));
318 tcase_add_test (tc_chain, test_h263depay_start_packet_too_small_mode_a);
319 tcase_add_test (tc_chain, test_h263depay_start_packet_too_small_mode_b);
320 tcase_add_test (tc_chain, test_h263depay_start_packet_too_small_mode_c);
321
322 suite_add_tcase (s, (tc_chain = tcase_create ("h263pay")));
323 tcase_add_test (tc_chain, test_h263pay_mode_b_snow);
324
325 suite_add_tcase (s, (tc_chain = tcase_create ("h263pdepay")));
326 tcase_add_test (tc_chain,
327 test_h263pdepay_fragmented_memory_non_writable_buffer);
328 tcase_add_test (tc_chain,
329 test_h263pdepay_fragmented_memory_non_writable_buffer_split_frame);
330 tcase_add_test (tc_chain, test_h263pdepay_dont_push_empty_frame);
331
332 suite_add_tcase (s, (tc_chain = tcase_create ("h263ppay")));
333 tcase_add_test (tc_chain, test_h263ppay_non_fixed_caps);
334
335 return s;
336 }
337
338 GST_CHECK_MAIN (rtph263);
339