• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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