1 /* GStreamer
2 * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
3 * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
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 #include <gst/check/gstcheck.h>
22 #include <glib/gstdio.h>
23
24 static GstTagList *received_tags = NULL;
25
26 static gboolean
bus_handler(GstBus * bus,GstMessage * message,gpointer data)27 bus_handler (GstBus * bus, GstMessage * message, gpointer data)
28 {
29 GMainLoop *loop = (GMainLoop *) data;
30
31 switch (message->type) {
32 case GST_MESSAGE_EOS:
33 g_main_loop_quit (loop);
34 break;
35 case GST_MESSAGE_WARNING:
36 case GST_MESSAGE_ERROR:{
37 GError *gerror;
38 gchar *debug;
39
40 if (message->type == GST_MESSAGE_WARNING) {
41 gst_message_parse_warning (message, &gerror, &debug);
42 } else {
43 gst_message_parse_error (message, &gerror, &debug);
44 }
45 gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
46 g_error_free (gerror);
47 g_free (debug);
48 g_main_loop_quit (loop);
49 break;
50 }
51 case GST_MESSAGE_TAG:{
52 if (received_tags == NULL) {
53 gst_message_parse_tag (message, &received_tags);
54 } else {
55 GstTagList *tl = NULL, *ntl = NULL;
56
57 gst_message_parse_tag (message, &tl);
58 if (tl) {
59 ntl = gst_tag_list_merge (received_tags, tl, GST_TAG_MERGE_PREPEND);
60 if (ntl) {
61 GST_LOG ("taglists merged: %" GST_PTR_FORMAT, ntl);
62 gst_tag_list_unref (received_tags);
63 received_tags = ntl;
64 }
65 gst_tag_list_unref (tl);
66 }
67 }
68 break;
69 }
70 default:
71 break;
72 }
73
74 return TRUE;
75 }
76
77 /*
78 * Creates a pipeline in the form:
79 * fakesrc num-buffers=1 ! caps ! muxer ! filesink location=file
80 *
81 * And sets the tags in tag_str into the muxer via tagsetter.
82 */
83 static void
test_mux_tags(const gchar * tag_str,const gchar * caps,const gchar * muxer,const gchar * file)84 test_mux_tags (const gchar * tag_str, const gchar * caps,
85 const gchar * muxer, const gchar * file)
86 {
87 GstElement *pipeline;
88 GstBus *bus;
89 GMainLoop *loop;
90 GstTagList *sent_tags;
91 GstElement *mux;
92 GstTagSetter *setter;
93 gchar *launch_str;
94 guint bus_watch = 0;
95
96 GST_DEBUG ("testing xmp muxing on : %s", muxer);
97
98 launch_str =
99 g_strdup_printf ("fakesrc num-buffers=1 format=time datarate=100 ! "
100 "%s ! %s name=mux ! filesink location=%s name=sink", caps, muxer, file);
101 pipeline = gst_parse_launch (launch_str, NULL);
102 g_free (launch_str);
103 fail_unless (pipeline != NULL);
104
105 mux = gst_bin_get_by_name (GST_BIN (pipeline), "mux");
106 fail_unless (mux != NULL);
107
108 loop = g_main_loop_new (NULL, TRUE);
109 fail_unless (loop != NULL);
110
111 bus = gst_element_get_bus (pipeline);
112 fail_unless (bus != NULL);
113 bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
114 gst_object_unref (bus);
115
116 gst_element_set_state (pipeline, GST_STATE_READY);
117
118 setter = GST_TAG_SETTER (mux);
119 fail_unless (setter != NULL);
120 sent_tags = gst_tag_list_new_from_string (tag_str);
121 fail_unless (sent_tags != NULL);
122 gst_tag_setter_merge_tags (setter, sent_tags, GST_TAG_MERGE_REPLACE);
123 gst_tag_list_unref (sent_tags);
124
125 gst_element_set_state (pipeline, GST_STATE_PLAYING);
126 g_main_loop_run (loop);
127
128 gst_element_set_state (pipeline, GST_STATE_NULL);
129
130 g_main_loop_unref (loop);
131 g_object_unref (mux);
132 g_object_unref (pipeline);
133 g_source_remove (bus_watch);
134 }
135
136 /*
137 * Makes a pipeline in the form:
138 * filesrc location=file ! demuxer ! fakesink
139 *
140 * And gets the tags that are posted on the bus to compare
141 * with the tags in 'tag_str'
142 */
143 static void
test_demux_tags(const gchar * tag_str,const gchar * demuxer,const gchar * file)144 test_demux_tags (const gchar * tag_str, const gchar * demuxer,
145 const gchar * file)
146 {
147 GstElement *pipeline;
148 GstBus *bus;
149 GMainLoop *loop;
150 GstTagList *sent_tags;
151 gint i, j, k, n_recv, n_sent;
152 const gchar *name_sent, *name_recv;
153 const GValue *value_sent, *value_recv;
154 gboolean found;
155 gint comparison;
156 GstElement *demux;
157 gchar *launch_str;
158 guint bus_watch = 0;
159
160 GST_DEBUG ("testing tags : %s", tag_str);
161
162 if (received_tags) {
163 gst_tag_list_unref (received_tags);
164 received_tags = NULL;
165 }
166
167 launch_str = g_strdup_printf ("filesrc location=%s ! %s name=demux ! "
168 "fakesink", file, demuxer);
169 pipeline = gst_parse_launch (launch_str, NULL);
170 g_free (launch_str);
171 fail_unless (pipeline != NULL);
172
173 demux = gst_bin_get_by_name (GST_BIN (pipeline), "demux");
174 fail_unless (demux != NULL);
175
176 loop = g_main_loop_new (NULL, TRUE);
177 fail_unless (loop != NULL);
178
179 bus = gst_element_get_bus (pipeline);
180 fail_unless (bus != NULL);
181 bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
182 gst_object_unref (bus);
183
184 sent_tags = gst_tag_list_new_from_string (tag_str);
185 fail_unless (sent_tags != NULL);
186
187 gst_element_set_state (pipeline, GST_STATE_PLAYING);
188 g_main_loop_run (loop);
189
190 GST_DEBUG ("mainloop done : %p", received_tags);
191
192 /* verify tags */
193 fail_unless (received_tags != NULL);
194 n_recv = gst_tag_list_n_tags (received_tags);
195 n_sent = gst_tag_list_n_tags (sent_tags);
196 fail_unless (n_recv >= n_sent);
197 /* FIXME: compare taglits values */
198 for (i = 0; i < n_sent; i++) {
199 name_sent = gst_tag_list_nth_tag_name (sent_tags, i);
200
201 found = FALSE;
202 for (j = 0; j < n_recv; j++) {
203 name_recv = gst_tag_list_nth_tag_name (received_tags, j);
204
205 if (!strcmp (name_sent, name_recv)) {
206 guint sent_len, recv_len;
207
208 sent_len = gst_tag_list_get_tag_size (sent_tags, name_sent);
209 recv_len = gst_tag_list_get_tag_size (received_tags, name_recv);
210
211 fail_unless (sent_len == recv_len,
212 "tag item %s has been received with different size", name_sent);
213
214 for (k = 0; k < sent_len; k++) {
215 value_sent = gst_tag_list_get_value_index (sent_tags, name_sent, k);
216 value_recv =
217 gst_tag_list_get_value_index (received_tags, name_recv, k);
218
219 comparison = gst_value_compare (value_sent, value_recv);
220 if (comparison != GST_VALUE_EQUAL) {
221 gchar *vs = g_strdup_value_contents (value_sent);
222 gchar *vr = g_strdup_value_contents (value_recv);
223 GST_DEBUG ("sent = %s:'%s', recv = %s:'%s'",
224 G_VALUE_TYPE_NAME (value_sent), vs,
225 G_VALUE_TYPE_NAME (value_recv), vr);
226 g_free (vs);
227 g_free (vr);
228 }
229 fail_unless (comparison == GST_VALUE_EQUAL,
230 "tag item %s has been received with different type or value",
231 name_sent);
232 found = TRUE;
233 break;
234 }
235 }
236 }
237 fail_unless (found, "tag item %s is lost", name_sent);
238 }
239
240 gst_tag_list_unref (received_tags);
241 received_tags = NULL;
242 gst_tag_list_unref (sent_tags);
243
244 gst_element_set_state (pipeline, GST_STATE_NULL);
245
246 g_main_loop_unref (loop);
247 g_object_unref (demux);
248 g_object_unref (pipeline);
249 g_source_remove (bus_watch);
250 }
251
252 /*
253 * Tests if the muxer/demuxer pair can serialize the tags in 'tag_str'
254 * to a file and recover them correctly.
255 *
256 * 'caps' are used to assure the muxer accepts the fake buffer fakesrc
257 * will send to it.
258 */
259 static void
test_tags(const gchar * tag_str,const gchar * caps,const gchar * muxer,const gchar * demuxer)260 test_tags (const gchar * tag_str, const gchar * caps, const gchar * muxer,
261 const gchar * demuxer)
262 {
263 gchar *tmpfile;
264 gchar *tmpdir;
265
266 tmpdir = g_dir_make_tmp ("gst-check-good-XXXXXX", NULL);
267 fail_unless (tmpdir != NULL);
268 tmpfile = g_build_filename (tmpdir, "tagschecking-xmp", NULL);
269
270 GST_DEBUG ("testing tags : %s", tag_str);
271 test_mux_tags (tag_str, caps, muxer, tmpfile);
272 test_demux_tags (tag_str, demuxer, tmpfile);
273 g_unlink (tmpfile);
274 g_rmdir (tmpdir);
275 g_free (tmpfile);
276 g_free (tmpdir);
277 }
278
279 #define H264_CAPS "video/x-h264, width=(int)320, height=(int)240," \
280 " framerate=(fraction)30/1, codec_data=(buffer)" \
281 "01401592ffe10017674d401592540a0fd8088000000300" \
282 "8000001e478b175001000468ee3c80, "\
283 "stream-format=(string)avc, alignment=(string)au"
284
285 #define COMMON_TAGS \
286 "taglist,title=test_title," \
287 "artist=test_artist," \
288 "keywords=\"key1,key2\"," \
289 "description=test_desc"
290
GST_START_TEST(test_common_tags)291 GST_START_TEST (test_common_tags)
292 {
293 if (!gst_registry_check_feature_version (gst_registry_get (), "qtdemux", 0,
294 10, 23)) {
295 GST_INFO ("Skipping test, qtdemux either not available or too old");
296 return;
297 }
298 test_tags (COMMON_TAGS, H264_CAPS, "qtmux", "qtdemux");
299 test_tags (COMMON_TAGS, H264_CAPS, "mp4mux", "qtdemux");
300 test_tags (COMMON_TAGS, H264_CAPS, "3gppmux", "qtdemux");
301 }
302
303 GST_END_TEST;
304
305 #define GEO_LOCATION_TAGS \
306 "taglist,geo-location-country=Brazil," \
307 "geo-location-city=\"Campina Grande\"," \
308 "geo-location-sublocation=Bodocongo," \
309 "geo-location-latitude=-12.125," \
310 "geo-location-longitude=56.75," \
311 "geo-location-elevation=327.5"
312
GST_START_TEST(test_geo_location_tags)313 GST_START_TEST (test_geo_location_tags)
314 {
315 if (!gst_registry_check_feature_version (gst_registry_get (), "qtdemux", 0,
316 10, 23)) {
317 GST_INFO ("Skipping test, qtdemux either not available or too old");
318 return;
319 }
320 test_tags (GEO_LOCATION_TAGS, H264_CAPS, "qtmux", "qtdemux");
321 test_tags (GEO_LOCATION_TAGS, H264_CAPS, "mp4mux", "qtdemux");
322 test_tags (GEO_LOCATION_TAGS, H264_CAPS, "3gppmux", "qtdemux");
323 }
324
325 GST_END_TEST;
326
327
328 #define USER_TAGS \
329 "taglist,user-rating=(uint)85"
330
GST_START_TEST(test_user_tags)331 GST_START_TEST (test_user_tags)
332 {
333 if (!gst_registry_check_feature_version (gst_registry_get (), "qtdemux", 0,
334 10, 23)) {
335 GST_INFO ("Skipping test, qtdemux either not available or too old");
336 return;
337 }
338
339 test_tags (USER_TAGS, H264_CAPS, "qtmux", "qtdemux");
340 test_tags (USER_TAGS, H264_CAPS, "mp4mux", "qtdemux");
341 test_tags (USER_TAGS, H264_CAPS, "3gppmux", "qtdemux");
342 }
343
344 GST_END_TEST;
345
346 static Suite *
metadata_suite(void)347 metadata_suite (void)
348 {
349 Suite *s = suite_create ("tagschecking");
350
351 TCase *tc_chain = tcase_create ("general");
352
353 /* time out after 60s, not the default 3 */
354 tcase_set_timeout (tc_chain, 60);
355
356 suite_add_tcase (s, tc_chain);
357 tcase_add_test (tc_chain, test_common_tags);
358 tcase_add_test (tc_chain, test_geo_location_tags);
359 tcase_add_test (tc_chain, test_user_tags);
360
361 return s;
362 }
363
364 GST_CHECK_MAIN (metadata);
365