1 /* GStreamer
2 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include <gst/check/gstcheck.h>
21 #include <gst/check/gstharness.h>
22
23 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
24 GST_PAD_SINK,
25 GST_PAD_ALWAYS,
26 GST_STATIC_CAPS ("video/x-h264, "
27 "width = (int) [1, MAX], "
28 "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
29
30 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
31 GST_PAD_SRC,
32 GST_PAD_ALWAYS,
33 GST_STATIC_CAPS ("video/x-raw, "
34 "format = (string) NV12, "
35 "width = (int) [1, MAX], "
36 "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
37
38 static GstPad *sinkpad, *srcpad;
39
40 static GstElement *
setup_nvenc(const gchar * src_caps_str,GstCaps ** srccaps)41 setup_nvenc (const gchar * src_caps_str, GstCaps ** srccaps)
42 {
43 GstElement *nvenc;
44 GstCaps *caps = NULL;
45 GstBus *bus;
46
47 caps = gst_caps_from_string (src_caps_str);
48 fail_unless (caps != NULL);
49
50 nvenc = gst_check_setup_element ("nvh264enc");
51 fail_unless (nvenc != NULL);
52 srcpad = gst_check_setup_src_pad (nvenc, &srctemplate);
53 sinkpad = gst_check_setup_sink_pad (nvenc, &sinktemplate);
54 gst_pad_set_active (srcpad, TRUE);
55 gst_pad_set_active (sinkpad, TRUE);
56
57 bus = gst_bus_new ();
58 gst_element_set_bus (nvenc, bus);
59
60 *srccaps = caps;
61
62 buffers = NULL;
63 return nvenc;
64 }
65
66 static void
cleanup_nvenc(GstElement * nvenc)67 cleanup_nvenc (GstElement * nvenc)
68 {
69 GstBus *bus;
70
71 /* Free parsed buffers */
72 gst_check_drop_buffers ();
73
74 bus = GST_ELEMENT_BUS (nvenc);
75 gst_bus_set_flushing (bus, TRUE);
76 gst_object_unref (bus);
77
78 gst_pad_set_active (srcpad, FALSE);
79 gst_pad_set_active (sinkpad, FALSE);
80 gst_check_teardown_src_pad (nvenc);
81 gst_check_teardown_sink_pad (nvenc);
82 gst_check_teardown_element (nvenc);
83 }
84
GST_START_TEST(test_encode_simple)85 GST_START_TEST (test_encode_simple)
86 {
87 GstElement *nvenc;
88 GstBuffer *buffer;
89 gint i;
90 GList *iter;
91 GstCaps *outcaps, *sinkcaps, *srccaps;
92 GstSegment seg;
93
94 nvenc =
95 setup_nvenc
96 ("video/x-raw,format=(string)NV12,width=(int)320,height=(int)240,"
97 "framerate=(fraction)25/1,interlace-mode=(string)progressive", &srccaps);
98
99 ASSERT_SET_STATE (nvenc, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
100
101 gst_segment_init (&seg, GST_FORMAT_TIME);
102 seg.stop = gst_util_uint64_scale (10, GST_SECOND, 25);
103
104 gst_check_setup_events (srcpad, nvenc, srccaps, GST_FORMAT_TIME);
105 fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
106
107 buffer = gst_buffer_new_allocate (NULL, 320 * 240 + 2 * 160 * 120, NULL);
108 gst_buffer_memset (buffer, 0, 0, -1);
109
110 for (i = 0; i < 10; i++) {
111 GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (i, GST_SECOND, 25);
112 GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
113 fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
114 }
115
116 gst_buffer_unref (buffer);
117
118 fail_unless (gst_pad_push_event (srcpad, gst_event_new_eos ()));
119
120 /* All buffers must be there now */
121 fail_unless_equals_int (g_list_length (buffers), 10);
122
123 outcaps =
124 gst_caps_from_string
125 ("video/x-h264,width=(int)320,height=(int)240,framerate=(fraction)25/1");
126
127 for (iter = buffers; iter; iter = g_list_next (iter)) {
128 buffer = iter->data;
129
130 fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
131 gst_util_uint64_scale (1, GST_SECOND, 25));
132
133 sinkcaps = gst_pad_get_current_caps (sinkpad);
134 fail_unless (gst_caps_can_intersect (sinkcaps, outcaps));
135 gst_caps_unref (sinkcaps);
136 }
137
138 gst_caps_unref (outcaps);
139 gst_caps_unref (srccaps);
140
141 cleanup_nvenc (nvenc);
142 }
143
144 GST_END_TEST;
145
146
GST_START_TEST(test_reuse)147 GST_START_TEST (test_reuse)
148 {
149 GstElement *nvenc;
150 GstBuffer *buffer;
151 gint i, loop;
152 GList *iter;
153 GstCaps *outcaps, *sinkcaps;
154 GstSegment seg;
155 GstCaps *srccaps;
156
157 nvenc =
158 setup_nvenc
159 ("video/x-raw,format=(string)NV12,width=(int)320,height=(int)240,"
160 "framerate=(fraction)25/1,interlace-mode=(string)progressive", &srccaps);
161
162 for (loop = 0; loop < 2; loop++) {
163 ASSERT_SET_STATE (nvenc, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
164
165 gst_segment_init (&seg, GST_FORMAT_TIME);
166 seg.stop = gst_util_uint64_scale (10, GST_SECOND, 25);
167
168 gst_check_setup_events (srcpad, nvenc, srccaps, GST_FORMAT_TIME);
169 fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
170
171 gst_segment_init (&seg, GST_FORMAT_TIME);
172 seg.stop = gst_util_uint64_scale (10, GST_SECOND, 25);
173
174 fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
175
176 buffer = gst_buffer_new_allocate (NULL, 320 * 240 + 2 * 160 * 120, NULL);
177 gst_buffer_memset (buffer, 0, 0, -1);
178
179 for (i = 0; i < 10; i++) {
180 GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (i, GST_SECOND, 25);
181 GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
182 fail_unless (gst_pad_push (srcpad,
183 gst_buffer_ref (buffer)) == GST_FLOW_OK);
184 }
185
186 gst_buffer_unref (buffer);
187
188 fail_unless (gst_pad_push_event (srcpad, gst_event_new_eos ()));
189
190 /* All buffers must be there now */
191 fail_unless_equals_int (g_list_length (buffers), 10);
192
193 outcaps =
194 gst_caps_from_string
195 ("video/x-h264,width=(int)320,height=(int)240,framerate=(fraction)25/1");
196
197 for (iter = buffers; iter; iter = g_list_next (iter)) {
198 buffer = iter->data;
199
200 fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
201 gst_util_uint64_scale (1, GST_SECOND, 25));
202
203 sinkcaps = gst_pad_get_current_caps (sinkpad);
204 fail_unless (gst_caps_can_intersect (sinkcaps, outcaps));
205 gst_caps_unref (sinkcaps);
206 }
207 gst_check_drop_buffers ();
208 gst_caps_unref (outcaps);
209
210 ASSERT_SET_STATE (nvenc, GST_STATE_READY, GST_STATE_CHANGE_SUCCESS);
211 }
212
213 gst_caps_unref (srccaps);
214
215 cleanup_nvenc (nvenc);
216 }
217
218 GST_END_TEST;
219
GST_START_TEST(test_caps_interlace_mode)220 GST_START_TEST (test_caps_interlace_mode)
221 {
222 GstElement *nvenc;
223 GstBuffer *buffer;
224 GstCaps *caps, *srccaps;
225 GstSegment seg;
226
227 nvenc =
228 setup_nvenc
229 ("video/x-raw,format=(string)NV12,width=(int)320,height=(int)240,"
230 "framerate=(fraction)25/1", &srccaps);
231
232 ASSERT_SET_STATE (nvenc, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
233
234 gst_check_setup_events (srcpad, nvenc, srccaps, GST_FORMAT_TIME);
235 gst_segment_init (&seg, GST_FORMAT_TIME);
236 fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
237
238 buffer = gst_buffer_new_allocate (NULL, 320 * 240 + 2 * 160 * 120, NULL);
239 gst_buffer_memset (buffer, 0, 0, -1);
240
241 /* empty interlace-mode */
242 GST_BUFFER_TIMESTAMP (buffer) = 0;
243 GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
244 fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
245
246 /* always valid interlace mode */
247 caps =
248 gst_caps_from_string
249 ("video/x-raw,format=(string)NV12,width=(int)320,height=(int)240,"
250 "framerate=(fraction)25/1,interlace-mode=(string)progressive");
251 fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps)));
252 gst_caps_unref (caps);
253
254 GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
255 GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
256 fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
257
258 /* not-supported interlace mode */
259 caps =
260 gst_caps_from_string
261 ("video/x-raw,format=(string)NV12,width=(int)320,height=(int)240,"
262 "framerate=(fraction)25/1,interlace-mode=(string)alternate");
263 fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps)));
264 gst_caps_unref (caps);
265
266 GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (2, GST_SECOND, 25);
267 GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
268 fail_if (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
269 gst_buffer_unref (buffer);
270 gst_caps_unref (srccaps);
271
272 cleanup_nvenc (nvenc);
273 }
274
275 GST_END_TEST;
276
277 #define MAX_PUSH_BUFFER 64
278
279 static void
resolution_change_common(gint from_width,gint from_height,gint to_width,gint to_height)280 resolution_change_common (gint from_width, gint from_height, gint to_width,
281 gint to_height)
282 {
283 GstHarness *h;
284 GstCaps *caps;
285 GstEvent *event;
286 GstBuffer *in_buf, *out_buf = NULL;
287 GstFlowReturn ret;
288 gint i = 0;
289
290 h = gst_harness_new_parse ("nvh264enc ! h264parse");
291 fail_unless (h != NULL);
292
293 gst_harness_play (h);
294
295 caps = gst_caps_from_string ("video/x-raw,format=NV12");
296 gst_caps_set_simple (caps, "width", G_TYPE_INT, from_width,
297 "height", G_TYPE_INT, from_height, NULL);
298 gst_harness_set_src_caps (h, caps);
299
300 in_buf = gst_buffer_new_and_alloc ((from_width * from_height) * 3 / 2);
301 gst_buffer_memset (in_buf, 0, 0, -1);
302
303 GST_BUFFER_DURATION (in_buf) = GST_SECOND;
304 GST_BUFFER_DTS (in_buf) = GST_CLOCK_TIME_NONE;
305
306 /* Push buffers until get encoder output */
307 do {
308 fail_if (i > MAX_PUSH_BUFFER);
309
310 GST_BUFFER_PTS (in_buf) = i * GST_SECOND;
311
312 ret = gst_harness_push (h, gst_buffer_ref (in_buf));
313 fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
314 gst_flow_get_name (ret));
315
316 out_buf = gst_harness_try_pull (h);
317 i++;
318 } while (out_buf == NULL);
319 gst_buffer_unref (out_buf);
320 gst_buffer_unref (in_buf);
321
322 /* change resolution */
323 caps = gst_caps_from_string ("video/x-raw,format=NV12");
324 gst_caps_set_simple (caps, "width", G_TYPE_INT, to_width,
325 "height", G_TYPE_INT, to_width, NULL);
326
327 GST_DEBUG ("Set new resolution %dx%d", to_width, to_height);
328 gst_harness_set_src_caps (h, caps);
329 in_buf = gst_buffer_new_and_alloc ((to_width * to_height) * 3 / 2);
330 gst_buffer_memset (in_buf, 0, 0, -1);
331
332 GST_BUFFER_PTS (in_buf) = i * GST_SECOND;
333
334 ret = gst_harness_push (h, gst_buffer_ref (in_buf));
335 fail_unless (ret == GST_FLOW_OK, "GstFlowReturn was %s",
336 gst_flow_get_name (ret));
337
338 /* push EOS to drain all buffers */
339 fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
340
341 do {
342 gboolean term = FALSE;
343 event = gst_harness_pull_event (h);
344 /* wait until get EOS event */
345 if (event) {
346 if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
347 term = TRUE;
348
349 gst_event_unref (event);
350
351 if (term)
352 break;
353 }
354 } while (out_buf == NULL);
355
356 /* check the last resolution from caps */
357 caps = gst_pad_get_current_caps (h->sinkpad);
358 fail_unless (caps != NULL);
359
360 GST_DEBUG ("last encoder src caps %" GST_PTR_FORMAT, caps);
361 {
362 gint val;
363 GstStructure *s;
364
365 s = gst_caps_get_structure (caps, 0);
366 fail_unless_equals_string (gst_structure_get_name (s), "video/x-h264");
367 fail_unless (gst_structure_get_int (s, "width", &val));
368 fail_unless_equals_int (val, to_width);
369 fail_unless (gst_structure_get_int (s, "height", &val));
370 fail_unless_equals_int (val, to_height);
371 }
372 gst_caps_unref (caps);
373
374 gst_harness_teardown (h);
375 }
376
GST_START_TEST(test_resolution_change_to_larger)377 GST_START_TEST (test_resolution_change_to_larger)
378 {
379 resolution_change_common (64, 64, 128, 128);
380 }
381
382 GST_END_TEST;
383
GST_START_TEST(test_resolution_change_to_smaller)384 GST_START_TEST (test_resolution_change_to_smaller)
385 {
386 resolution_change_common (128, 128, 64, 64);
387 }
388
389 GST_END_TEST;
390
391 static gboolean
check_nvenc_available(void)392 check_nvenc_available (void)
393 {
394 gboolean ret = TRUE;
395 GstElement *nvenc;
396
397 nvenc = gst_element_factory_make ("nvh264enc", NULL);
398 if (!nvenc) {
399 GST_WARNING ("nvh264enc is not available, possibly driver load failure");
400 return FALSE;
401 }
402
403 /* GST_STATE_READY is meaning that driver could be loaded */
404 if (gst_element_set_state (nvenc,
405 GST_STATE_PAUSED) != GST_STATE_CHANGE_SUCCESS) {
406 GST_WARNING ("cannot open device");
407 ret = FALSE;
408 }
409
410 gst_element_set_state (nvenc, GST_STATE_NULL);
411 gst_object_unref (nvenc);
412
413 return ret;
414 }
415
416 static Suite *
nvenc_suite(void)417 nvenc_suite (void)
418 {
419 Suite *s;
420 TCase *tc_chain;
421
422 /* HACK: cuda device init/deinit with fork seems to problematic */
423 g_setenv ("CK_FORK", "no", TRUE);
424
425 s = suite_create ("nvenc");
426 tc_chain = tcase_create ("general");
427
428 suite_add_tcase (s, tc_chain);
429
430 if (!check_nvenc_available ()) {
431 GST_DEBUG ("Skip nvenc test since cannot open device");
432 goto end;
433 }
434
435 tcase_add_test (tc_chain, test_encode_simple);
436 tcase_add_test (tc_chain, test_reuse);
437 tcase_add_test (tc_chain, test_caps_interlace_mode);
438 tcase_add_test (tc_chain, test_resolution_change_to_larger);
439 tcase_add_test (tc_chain, test_resolution_change_to_smaller);
440
441 end:
442 return s;
443 }
444
445 GST_CHECK_MAIN (nvenc);
446