meson: build tests/icles/
[platform/upstream/gst-plugins-good.git] / tests / icles / test-accurate-seek.c
1 /* GStreamer interactive test for accurate seeking
2  * Copyright (C) 2014 Tim-Philipp Müller <tim centricular 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  * Based on python script by Kibeom Kim <kkb110@gmail.com>
20  */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #define _GNU_SOURCE             /* for memmem */
26 #include <string.h>
27
28 #include <gst/gst.h>
29 #include <gst/base/base.h>
30 #include <gst/audio/audio.h>
31 #include <gst/app/app.h>
32
33 #define SAMPLE_FREQ 44100
34
35 static GstClockTime
36 sample_to_nanotime (guint sample)
37 {
38   return (guint64) ((1.0 * sample * GST_SECOND / SAMPLE_FREQ) + 0.5);
39 }
40
41 static guint
42 nanotime_to_sample (GstClockTime nanotime)
43 {
44   return gst_util_uint64_scale_round (nanotime, SAMPLE_FREQ, GST_SECOND);
45 }
46
47 static GstBuffer *
48 generate_test_data (guint N)
49 {
50   gint16 *left, *right, *stereo;
51   guint largeN, i, j;
52
53   /* 32767 = (2 ** 15) - 1 */
54   /* 32768 = (2 ** 15) */
55   largeN = ((N + 32767) / 32768) * 32768;
56   left = g_new0 (gint16, largeN);
57   right = g_new0 (gint16, largeN);
58   stereo = g_new0 (gint16, 2 * largeN);
59
60   for (i = 0; i < (largeN / 32768); ++i) {
61     gint c = 0;
62
63     for (j = i * 32768; j < ((i + 1) * 32768); ++j) {
64       left[j] = i;
65
66       if (i % 2 == 0) {
67         right[j] = c;
68       } else {
69         right[j] = 32767 - c;
70       }
71       ++c;
72     }
73   }
74
75   /* could just fill stereo directly from the start, but keeping original code for now */
76   for (i = 0; i < largeN; ++i) {
77     stereo[(2 * i) + 0] = left[i];
78     stereo[(2 * i) + 1] = right[i];
79   }
80   g_free (left);
81   g_free (right);
82
83   return gst_buffer_new_wrapped (stereo, 2 * largeN * sizeof (gint16));
84 }
85
86 static void
87 generate_test_sound (const gchar * fn, const gchar * launch_string,
88     guint num_samples)
89 {
90   GstElement *pipeline, *src, *parse, *enc_bin, *sink;
91   GstFlowReturn flow;
92   GstMessage *msg;
93   GstBuffer *buf;
94   GstCaps *caps;
95
96   pipeline = gst_pipeline_new (NULL);
97
98   src = gst_element_factory_make ("appsrc", NULL);
99
100   caps = gst_caps_new_simple ("audio/x-raw",
101       "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
102       "rate", G_TYPE_INT, SAMPLE_FREQ, "channels", G_TYPE_INT, 2,
103       "layout", G_TYPE_STRING, "interleaved",
104       "channel-mask", GST_TYPE_BITMASK, (guint64) 3, NULL);
105   g_object_set (src, "caps", caps, "format", GST_FORMAT_TIME, NULL);
106   gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
107   gst_caps_unref (caps);
108
109   /* audioparse to put proper timestamps on buffers for us, without which
110    * vorbisenc in particular is unhappy (or oggmux, rather) */
111   parse = gst_element_factory_make ("audioparse", NULL);
112   if (parse != NULL) {
113     g_object_set (parse, "use-sink-caps", TRUE, NULL);
114   } else {
115     parse = gst_element_factory_make ("identity", NULL);
116     g_warning ("audioparse element not available, vorbis/ogg might not work\n");
117   }
118
119   enc_bin = gst_parse_bin_from_description (launch_string, TRUE, NULL);
120
121   sink = gst_element_factory_make ("filesink", NULL);
122   g_object_set (sink, "location", fn, NULL);
123
124   gst_bin_add_many (GST_BIN (pipeline), src, parse, enc_bin, sink, NULL);
125
126   gst_element_link_many (src, parse, enc_bin, sink, NULL);
127
128   gst_element_set_state (pipeline, GST_STATE_PLAYING);
129
130   buf = generate_test_data (num_samples);
131   flow = gst_app_src_push_buffer (GST_APP_SRC (src), buf);
132   g_assert (flow == GST_FLOW_OK);
133
134   gst_app_src_end_of_stream (GST_APP_SRC (src));
135
136   /*g_print ("generating test sound %s, waiting for EOS..\n", fn); */
137
138   msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline),
139       GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS | GST_MESSAGE_ERROR);
140
141   g_assert (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
142   gst_message_unref (msg);
143
144   gst_element_set_state (pipeline, GST_STATE_NULL);
145   gst_object_unref (pipeline);
146
147   /* g_print ("Done %s\n", fn); */
148 }
149
150 static void
151 test_seek_FORMAT_TIME_by_sample (const gchar * fn, GList * seek_positions)
152 {
153   GstElement *pipeline, *src, *sink;
154   GstAdapter *adapter;
155   GstSample *sample;
156   GstCaps *caps;
157   gconstpointer answer;
158   guint answer_size;
159
160   pipeline = gst_parse_launch ("filesrc name=src ! decodebin ! "
161       "audioconvert dithering=0 ! appsink name=sink", NULL);
162
163   src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
164   g_object_set (src, "location", fn, NULL);
165   gst_object_unref (src);
166
167   sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
168   caps = gst_caps_new_simple ("audio/x-raw",
169       "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
170       "rate", G_TYPE_INT, SAMPLE_FREQ, "channels", G_TYPE_INT, 2, NULL);
171   g_object_set (sink, "caps", caps, "sync", FALSE, NULL);
172   gst_caps_unref (caps);
173
174   gst_element_set_state (pipeline, GST_STATE_PLAYING);
175
176   /* wait for preroll, so we can seek */
177   gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline), GST_CLOCK_TIME_NONE,
178       GST_MESSAGE_ASYNC_DONE);
179
180   /* first, read entire file to end */
181   adapter = gst_adapter_new ();
182   while ((sample = gst_app_sink_pull_sample (GST_APP_SINK (sink)))) {
183     gst_adapter_push (adapter, gst_buffer_ref (gst_sample_get_buffer (sample)));
184     gst_sample_unref (sample);
185   }
186   answer_size = gst_adapter_available (adapter);
187   answer = gst_adapter_map (adapter, answer_size);
188   /* g_print ("%s: read %u bytes\n", fn, answer_size); */
189
190   g_print ("%10s\t%10s\t%10s\n", "requested", "sample per ts", "actual(data)");
191
192   while (seek_positions != NULL) {
193     gconstpointer found;
194     GstMapInfo map;
195     GstBuffer *buf;
196     gboolean ret;
197     guint actual_position, buffer_timestamp_position;
198     guint seek_sample;
199
200     seek_sample = GPOINTER_TO_UINT (seek_positions->data);
201
202     ret = gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
203         GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
204         sample_to_nanotime (seek_sample));
205
206     g_assert (ret);
207
208     sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));
209
210     buf = gst_sample_get_buffer (sample);
211     gst_buffer_map (buf, &map, GST_MAP_READ);
212     found = memmem (answer, answer_size, map.data, map.size);
213     gst_buffer_unmap (buf, &map);
214
215     g_assert (found != NULL);
216     actual_position = ((goffset) ((guint8 *) found - (guint8 *) answer)) / 4;
217     buffer_timestamp_position = nanotime_to_sample (GST_BUFFER_PTS (buf));
218     g_print ("%10u\t%10u\t%10u\n", seek_sample, buffer_timestamp_position,
219         actual_position);
220     gst_sample_unref (sample);
221
222     seek_positions = seek_positions->next;
223   }
224
225   gst_element_set_state (pipeline, GST_STATE_NULL);
226   gst_object_unref (sink);
227   gst_object_unref (pipeline);
228   g_object_unref (adapter);
229 }
230
231 static GList *
232 create_test_samples (guint from, guint to, guint step)
233 {
234   GQueue q = G_QUEUE_INIT;
235   guint i;
236
237   for (i = from; i < to; i += step)
238     g_queue_push_tail (&q, GUINT_TO_POINTER (i));
239
240   return q.head;
241 }
242
243 #define SECS 10
244
245 int
246 main (int argc, char **argv)
247 {
248   GList *test_samples;
249
250   gst_init (&argc, &argv);
251
252   test_samples = create_test_samples (SAMPLE_FREQ, SAMPLE_FREQ * 2, 5000);
253
254   g_print ("\nwav:\n");
255   generate_test_sound ("test.wav", "wavenc", SAMPLE_FREQ * SECS);
256   test_seek_FORMAT_TIME_by_sample ("test.wav", test_samples);
257
258   g_print ("\nflac:\n");
259   generate_test_sound ("test.flac", "flacenc", SAMPLE_FREQ * SECS);
260   test_seek_FORMAT_TIME_by_sample ("test.flac", test_samples);
261
262   g_print ("\nogg:\n");
263   generate_test_sound ("test.ogg",
264       "audioconvert dithering=0 ! vorbisenc quality=1 ! oggmux",
265       SAMPLE_FREQ * SECS);
266   test_seek_FORMAT_TIME_by_sample ("test.ogg", test_samples);
267
268   g_print ("\nmp3:\n");
269   generate_test_sound ("test.mp3", "lamemp3enc bitrate=320",
270       SAMPLE_FREQ * SECS);
271   test_seek_FORMAT_TIME_by_sample ("test.mp3", test_samples);
272
273   g_list_free (test_samples);
274   return 0;
275 }