merge libOggFLAC into libFLAC and libOggFLAC++ into FLAC++; documentation still needs...
[platform/upstream/flac.git] / src / test_seeking / main.c
1 /* test_seeking - Seeking tester for libFLAC
2  * Copyright (C) 2004,2005,2006  Josh Coalson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #if defined _MSC_VER || defined __MINGW32__
28 #include <time.h>
29 #else
30 #include <sys/time.h>
31 #endif
32 #include <sys/stat.h> /* for stat() */
33 #include "FLAC/assert.h"
34 #include "FLAC/stream_decoder.h"
35
36 typedef struct {
37         FLAC__bool got_data;
38         FLAC__uint64 total_samples;
39         unsigned channels;
40         unsigned bits_per_sample;
41         FLAC__bool quiet;
42         FLAC__bool ignore_errors;
43         FLAC__bool error_occurred;
44 } DecoderClientData;
45
46 static FLAC__bool stop_signal_ = false;
47
48 static void our_sigint_handler_(int signal)
49 {
50         (void)signal;
51         printf("(caught SIGINT) ");
52         fflush(stdout);
53         stop_signal_ = true;
54 }
55
56 static FLAC__bool die_(const char *msg)
57 {
58         printf("ERROR: %s\n", msg);
59         return false;
60 }
61
62 static FLAC__bool die_s_(const char *msg, const FLAC__StreamDecoder *decoder)
63 {
64         FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
65
66         if(msg)
67                 printf("FAILED, %s", msg);
68         else
69                 printf("FAILED");
70
71         printf(", state = %u (%s)\n", (unsigned)state, FLAC__StreamDecoderStateString[state]);
72
73         return false;
74 }
75
76 static off_t get_filesize_(const char *srcpath)
77 {
78         struct stat srcstat;
79
80         if(0 == stat(srcpath, &srcstat))
81                 return srcstat.st_size;
82         else
83                 return -1;
84 }
85
86 static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
87 {
88         DecoderClientData *dcd = (DecoderClientData*)client_data;
89
90         (void)decoder, (void)buffer;
91
92         if(0 == dcd) {
93                 printf("ERROR: client_data in write callback is NULL\n");
94                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
95         }
96
97         if(dcd->error_occurred)
98                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
99
100         if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) {
101                 if (!dcd->quiet)
102                         printf("frame@%uf(%u)... ", frame->header.number.frame_number, frame->header.blocksize);
103         }
104         else if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) {
105                 if (!dcd->quiet)
106                         printf("frame@%llu(%u)... ", frame->header.number.sample_number, frame->header.blocksize);
107         }
108         else {
109                 FLAC__ASSERT(0);
110                 dcd->error_occurred = true;
111                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
112         }
113         fflush(stdout);
114
115         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
116 }
117
118 static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
119 {
120         DecoderClientData *dcd = (DecoderClientData*)client_data;
121
122         (void)decoder;
123
124         if(0 == dcd) {
125                 printf("ERROR: client_data in metadata callback is NULL\n");
126                 return;
127         }
128
129         if(dcd->error_occurred)
130                 return;
131
132         if (!dcd->got_data && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
133                 dcd->got_data = true;
134                 dcd->total_samples = metadata->data.stream_info.total_samples;
135                 dcd->channels = metadata->data.stream_info.channels;
136                 dcd->bits_per_sample = metadata->data.stream_info.bits_per_sample;
137         }
138 }
139
140 static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
141 {
142         DecoderClientData *dcd = (DecoderClientData*)client_data;
143
144         (void)decoder;
145
146         if(0 == dcd) {
147                 printf("ERROR: client_data in error callback is NULL\n");
148                 return;
149         }
150
151         if(!dcd->ignore_errors) {
152                 printf("ERROR: got error callback: err = %u (%s)\n", (unsigned)status, FLAC__StreamDecoderErrorStatusString[status]);
153                 dcd->error_occurred = true;
154         }
155 }
156
157 static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, off_t filesize, unsigned count)
158 {
159         FLAC__StreamDecoder *decoder;
160         DecoderClientData decoder_client_data;
161         unsigned i;
162         long int n;
163
164         decoder_client_data.got_data = false;
165         decoder_client_data.total_samples = 0;
166         decoder_client_data.quiet = false;
167         decoder_client_data.ignore_errors = false;
168         decoder_client_data.error_occurred = false;
169
170         printf("\n+++ seek test: FLAC__StreamDecoder (%s FLAC)\n\n", is_ogg? "Ogg":"native");
171
172         decoder = FLAC__stream_decoder_new();
173         if(0 == decoder)
174                 return die_("FLAC__stream_decoder_new() FAILED, returned NULL\n");
175
176         if(is_ogg) {
177                 if(FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
178                         return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
179         }
180         else {
181                 if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
182                         return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
183         }
184
185         if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
186                 return die_s_("FLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);
187
188         if(!is_ogg) { /* not necessary to do this for Ogg because of its seeking method */
189         /* process until end of stream to make sure we can still seek in that state */
190                 decoder_client_data.quiet = true;
191                 if(!FLAC__stream_decoder_process_until_end_of_stream(decoder))
192                         return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder);
193                 decoder_client_data.quiet = false;
194
195                 printf("stream decoder state is %s\n", FLAC__stream_decoder_get_resolved_state_string(decoder));
196                 if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM)
197                         return die_s_("expected FLAC__STREAM_DECODER_END_OF_STREAM", decoder);
198         }
199
200         printf("file's total_samples is %llu\n", decoder_client_data.total_samples);
201 #if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
202         if (decoder_client_data.total_samples > (FLAC__uint64)RAND_MAX) {
203                 printf("ERROR: must be total_samples < %u\n", (unsigned)RAND_MAX);
204                 return false;
205         }
206 #endif
207         n = (long int)decoder_client_data.total_samples;
208
209         /* if we don't have a total samples count, just guess based on the file size */
210         /* @@@ for is_ogg we should get it from last page's granulepos */
211         if(n == 0) {
212                 /* 8 would imply no compression, 9 guarantees that we will get some samples off the end of the stream to test that case */
213                 n = 9 * filesize / (decoder_client_data.channels * decoder_client_data.bits_per_sample);
214 #if !defined _MSC_VER && !defined __MINGW32__
215                 if(n > RAND_MAX)
216                         n = RAND_MAX;
217 #endif
218         }
219
220         printf("Begin seek barrage, count=%u\n", count);
221
222         for (i = 0; !stop_signal_ && (count == 0 || i < count); i++) {
223                 FLAC__uint64 pos;
224
225                 /* for the first 10, seek to the first 10 samples */
226                 if (n >= 10 && i < 10) {
227                         pos = i;
228                 }
229                 /* for the second 10, seek to the last 10 samples */
230                 else if (n >= 10 && i < 20) {
231                         pos = n - 1 - (i-10);
232                 }
233                 /* for the third 10, seek past the end and make sure we fail properly as expected */
234                 else if (i < 30) {
235                         pos = n + (i-20);
236                 }
237                 else {
238 #if !defined _MSC_VER && !defined __MINGW32__
239                         pos = (FLAC__uint64)(random() % n);
240 #else
241                         /* RAND_MAX is only 32767 in my MSVC */
242                         pos = (FLAC__uint64)((rand()<<15|rand()) % n);
243 #endif
244                 }
245
246                 printf("seek(%llu)... ", pos);
247                 fflush(stdout);
248                 if(!FLAC__stream_decoder_seek_absolute(decoder, pos)) {
249                         if(pos < (FLAC__uint64)n && decoder_client_data.total_samples != 0)
250                                 return die_s_("FLAC__stream_decoder_seek_absolute() FAILED", decoder);
251                         else if(decoder_client_data.total_samples == 0)
252                                 printf("seek failed, assuming it was past EOF... ");
253                         else
254                                 printf("seek past end failed as expected... ");
255                         if(!FLAC__stream_decoder_flush(decoder))
256                                 return die_s_("FLAC__stream_decoder_flush() FAILED", decoder);
257                 }
258                 else {
259                         printf("decode_frame... ");
260                         fflush(stdout);
261                         if(!FLAC__stream_decoder_process_single(decoder))
262                                 return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);
263
264                         printf("decode_frame... ");
265                         fflush(stdout);
266                         if(!FLAC__stream_decoder_process_single(decoder))
267                                 return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);
268                 }
269
270                 printf("OK\n");
271                 fflush(stdout);
272         }
273
274         if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) {
275                 if(!FLAC__stream_decoder_finish(decoder))
276                         return die_s_("FLAC__stream_decoder_finish() FAILED", decoder);
277         }
278
279         printf("\nPASSED!\n");
280
281         return true;
282 }
283
284 int main(int argc, char *argv[])
285 {
286         const char *filename;
287         unsigned count = 0;
288         off_t filesize;
289
290         static const char * const usage = "usage: test_seeking file.flac [#seeks]\n";
291
292         if (argc < 2 || argc > 3) {
293                 fprintf(stderr, usage);
294                 return 1;
295         }
296
297         filename = argv[1];
298
299         if (argc > 2)
300                 count = strtoul(argv[2], 0, 10);
301
302         if (count < 30)
303                 fprintf(stderr, "WARNING: random seeks don't kick in until after 30 preprogrammed ones\n");
304
305 #if !defined _MSC_VER && !defined __MINGW32__
306         {
307                 struct timeval tv;
308
309                 if (gettimeofday(&tv, 0) < 0) {
310                         fprintf(stderr, "WARNING: couldn't seed RNG with time\n");
311                         tv.tv_usec = 4321;
312                 }
313                 srandom(tv.tv_usec);
314         }
315 #else
316         srand(time(0));
317 #endif
318
319         filesize = get_filesize_(filename);
320         if (filesize < 0) {
321                 fprintf(stderr, "ERROR: can't determine filesize for %s\n", filename);
322                 return 1;
323         }
324
325         (void) signal(SIGINT, our_sigint_handler_);
326
327         {
328                 FLAC__bool ok;
329                 if (strlen(filename) > 4 && 0 == strcmp(filename+strlen(filename)-4, ".ogg")) {
330 #ifdef FLAC__HAS_OGG
331                         ok = seek_barrage(/*is_ogg=*/true, filename, filesize, count);
332 #else
333                         fprintf(stderr, "ERROR: Ogg FLAC not supported\n");
334                         ok = false;
335 #endif
336                 }
337                 else {
338                         ok = seek_barrage(/*is_ogg=*/false, filename, filesize, count);
339                 }
340                 return ok? 0 : 2;
341         }
342 }