• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/gst.h>
26 #include <gst/check/gstcheck.h>
27 #include <string.h>
28 
29 typedef struct
30 {
31   GMainLoop *loop;
32   GstElement *pipeline;
33   guint n_buffers;
34   guint restart_count;
35   GstState reuse_state;
36 } SrcReuseTestData;
37 
38 static gboolean
src_reuse_bus_handler(GstBus * bus,GstMessage * message,SrcReuseTestData * data)39 src_reuse_bus_handler (GstBus * bus, GstMessage * message,
40     SrcReuseTestData * data)
41 {
42   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
43     GST_ERROR ("Got error message from pipeline");
44     g_main_loop_quit (data->loop);
45   }
46 
47   return TRUE;
48 }
49 
50 static void
start_pipeline(SrcReuseTestData * data)51 start_pipeline (SrcReuseTestData * data)
52 {
53   GstStateChangeReturn ret;
54 
55   GST_INFO ("Start pipeline");
56   ret = gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
57   fail_unless (ret != GST_STATE_CHANGE_FAILURE);
58 }
59 
60 static gboolean
restart_pipeline(SrcReuseTestData * data)61 restart_pipeline (SrcReuseTestData * data)
62 {
63   data->restart_count++;
64   start_pipeline (data);
65 
66   return G_SOURCE_REMOVE;
67 }
68 
69 static gboolean
handle_handoff(SrcReuseTestData * data)70 handle_handoff (SrcReuseTestData * data)
71 {
72   data->n_buffers++;
73 
74   /* Restart every 10 packets */
75   if (data->n_buffers > 10) {
76     GstStateChangeReturn ret;
77     data->n_buffers = 0;
78 
79     ret = gst_element_set_state (data->pipeline, data->reuse_state);
80     fail_unless (ret != GST_STATE_CHANGE_FAILURE);
81 
82     if (data->restart_count < 2) {
83       GST_INFO ("Restart pipeline, current restart count %d",
84           data->restart_count);
85       g_timeout_add_seconds (1, (GSourceFunc) restart_pipeline, data);
86     } else {
87       GST_INFO ("Finish test");
88       g_main_loop_quit (data->loop);
89     }
90   }
91 
92   return G_SOURCE_REMOVE;
93 }
94 
95 static void
on_sink_handoff(GstElement * element,GstBuffer * buffer,GstPad * pad,SrcReuseTestData * data)96 on_sink_handoff (GstElement * element, GstBuffer * buffer, GstPad * pad,
97     SrcReuseTestData * data)
98 {
99   g_idle_add ((GSourceFunc) handle_handoff, data);
100 }
101 
102 static void
wasapi2src_reuse(GstState reuse_state)103 wasapi2src_reuse (GstState reuse_state)
104 {
105   GstBus *bus;
106   GstElement *sink;
107   SrcReuseTestData data;
108 
109   memset (&data, 0, sizeof (SrcReuseTestData));
110 
111   data.loop = g_main_loop_new (NULL, FALSE);
112 
113   data.pipeline = gst_parse_launch ("wasapi2src provide-clock=false ! queue ! "
114       "fakesink name=sink async=false", NULL);
115   fail_unless (data.pipeline != NULL);
116   data.reuse_state = reuse_state;
117 
118   sink = gst_bin_get_by_name (GST_BIN (data.pipeline), "sink");
119   fail_unless (sink);
120 
121   g_object_set (G_OBJECT (sink), "signal-handoffs", TRUE, NULL);
122   g_signal_connect (sink, "handoff", G_CALLBACK (on_sink_handoff), &data);
123 
124   bus = gst_element_get_bus (GST_ELEMENT (data.pipeline));
125   fail_unless (bus != NULL);
126 
127   gst_bus_add_watch (bus, (GstBusFunc) src_reuse_bus_handler, &data);
128   start_pipeline (&data);
129   g_main_loop_run (data.loop);
130 
131   fail_unless (data.restart_count == 2);
132 
133   gst_element_set_start_time (data.pipeline, GST_STATE_NULL);
134   gst_bus_remove_watch (bus);
135   gst_object_unref (bus);
136 
137   gst_object_unref (data.pipeline);
138   g_main_loop_unref (data.loop);
139 }
140 
141 /* https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1110 */
GST_START_TEST(test_wasapi2src_reuse_null)142 GST_START_TEST (test_wasapi2src_reuse_null)
143 {
144   wasapi2src_reuse (GST_STATE_NULL);
145 }
146 
147 GST_END_TEST;
148 
GST_START_TEST(test_wasapi2src_reuse_ready)149 GST_START_TEST (test_wasapi2src_reuse_ready)
150 {
151   wasapi2src_reuse (GST_STATE_READY);
152 }
153 
154 GST_END_TEST;
155 
156 typedef struct
157 {
158   GMainLoop *loop;
159   GstElement *pipe;
160   guint rem_st_changes;
161   GstState reuse_state;
162 } SinkPlayReadyTData;
163 
164 static gboolean
sink_reuse_bus_watch_cb(GstBus * bus,GstMessage * message,gpointer user_data)165 sink_reuse_bus_watch_cb (GstBus * bus, GstMessage * message, gpointer user_data)
166 {
167   fail_unless (message->type != GST_MESSAGE_ERROR);
168 
169   return G_SOURCE_CONTINUE;
170 }
171 
172 static gboolean
state_timer_cb(gpointer user_data)173 state_timer_cb (gpointer user_data)
174 {
175   SinkPlayReadyTData *tdata = user_data;
176   GstState nxt_st = tdata->rem_st_changes % 2 == 1 ?
177       tdata->reuse_state : GST_STATE_PLAYING;
178 
179   ASSERT_SET_STATE (tdata->pipe, nxt_st, GST_STATE_CHANGE_SUCCESS);
180   tdata->rem_st_changes--;
181 
182   if (tdata->rem_st_changes == 0) {
183     g_main_loop_quit (tdata->loop);
184     return G_SOURCE_REMOVE;
185   }
186   return G_SOURCE_CONTINUE;
187 }
188 
189 /* Test that the wasapisink can survive the state change
190  * from PLAYING to READY and then back to PLAYING */
191 static void
wasapi2sink_reuse(GstState reuse_state)192 wasapi2sink_reuse (GstState reuse_state)
193 {
194   SinkPlayReadyTData tdata;
195   GstBus *bus;
196 
197   tdata.pipe =
198       gst_parse_launch ("audiotestsrc ! wasapi2sink async=false", NULL);
199   fail_unless (tdata.pipe != NULL);
200   bus = gst_element_get_bus (tdata.pipe);
201   fail_unless (bus != NULL);
202   gst_bus_add_watch (bus, sink_reuse_bus_watch_cb, NULL);
203 
204   tdata.reuse_state = reuse_state;
205 
206   ASSERT_SET_STATE (tdata.pipe, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
207   tdata.rem_st_changes = 3;     /* -> READY -> PLAYING -> QUIT */
208   g_timeout_add_seconds (1, state_timer_cb, &tdata);
209 
210   tdata.loop = g_main_loop_new (NULL, FALSE);
211   g_main_loop_run (tdata.loop);
212 
213   g_main_loop_unref (tdata.loop);
214   gst_bus_remove_watch (bus);
215   gst_object_unref (bus);
216   gst_object_unref (tdata.pipe);
217 }
218 
GST_START_TEST(test_wasapi2sink_reuse_null)219 GST_START_TEST (test_wasapi2sink_reuse_null)
220 {
221   wasapi2sink_reuse (GST_STATE_NULL);
222 }
223 
224 GST_END_TEST;
225 
GST_START_TEST(test_wasapi2sink_reuse_ready)226 GST_START_TEST (test_wasapi2sink_reuse_ready)
227 {
228   wasapi2sink_reuse (GST_STATE_READY);
229 }
230 
231 GST_END_TEST;
232 
233 static gboolean
check_wasapi2_element(gboolean is_src)234 check_wasapi2_element (gboolean is_src)
235 {
236   gboolean ret = TRUE;
237   GstElement *elem;
238   const gchar *elem_name;
239 
240   if (is_src)
241     elem_name = "wasapi2src";
242   else
243     elem_name = "wasapi2sink";
244 
245   elem = gst_element_factory_make (elem_name, NULL);
246   if (!elem) {
247     GST_INFO ("%s is not available", elem_name);
248     return FALSE;
249   }
250 
251   /* GST_STATE_READY is meaning that camera is available */
252   if (gst_element_set_state (elem, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) {
253     GST_INFO ("cannot open device");
254     ret = FALSE;
255   }
256 
257   gst_element_set_state (elem, GST_STATE_NULL);
258   gst_object_unref (elem);
259 
260   return ret;
261 }
262 
263 static Suite *
wasapi2_suite(void)264 wasapi2_suite (void)
265 {
266   Suite *s = suite_create ("wasapi2");
267   TCase *tc_basic = tcase_create ("general");
268   gboolean have_src = FALSE;
269   gboolean have_sink = FALSE;
270 
271   suite_add_tcase (s, tc_basic);
272 
273   have_src = check_wasapi2_element (TRUE);
274   have_sink = check_wasapi2_element (FALSE);
275 
276   if (!have_src && !have_sink) {
277     GST_INFO ("Skipping tests, wasapi2src/wasapi2sink are unavailable");
278   } else {
279     if (have_src) {
280       tcase_add_test (tc_basic, test_wasapi2src_reuse_null);
281       tcase_add_test (tc_basic, test_wasapi2src_reuse_ready);
282     }
283 
284     if (have_sink) {
285       tcase_add_test (tc_basic, test_wasapi2sink_reuse_null);
286       tcase_add_test (tc_basic, test_wasapi2sink_reuse_ready);
287     }
288   }
289 
290   return s;
291 }
292 
293 GST_CHECK_MAIN (wasapi2);
294