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