in FLAC tester, process til end-of-stream before seek barrage, to make sure the decod...
[platform/upstream/flac.git] / src / test_seeking / main.c
1 /* test_seeking - Seeking tester for libFLAC and libOggFLAC
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 #ifdef FLAC__HAS_OGG
36 #include "OggFLAC/stream_decoder.h"
37 #endif
38
39 typedef struct {
40         FLAC__bool got_data;
41         FLAC__uint64 total_samples;
42         unsigned channels;
43         unsigned bits_per_sample;
44         FLAC__bool quiet;
45         FLAC__bool ignore_errors;
46         FLAC__bool error_occurred;
47 } DecoderClientData;
48
49 static FLAC__bool stop_signal_ = false;
50
51 static void our_sigint_handler_(int signal)
52 {
53         (void)signal;
54         printf("(caught SIGINT) ");
55         fflush(stdout);
56         stop_signal_ = true;
57 }
58
59 static FLAC__bool die_(const char *msg)
60 {
61         printf("ERROR: %s\n", msg);
62         return false;
63 }
64
65 static FLAC__bool die_s_(const char *msg, const FLAC__StreamDecoder *decoder)
66 {
67         FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
68
69         if(msg)
70                 printf("FAILED, %s", msg);
71         else
72                 printf("FAILED");
73
74         printf(", state = %u (%s)\n", (unsigned)state, FLAC__StreamDecoderStateString[state]);
75
76         return false;
77 }
78
79 #ifdef FLAC__HAS_OGG
80 static FLAC__bool die_os_(const char *msg, const OggFLAC__StreamDecoder *decoder)
81 {
82         OggFLAC__StreamDecoderState state = OggFLAC__stream_decoder_get_state(decoder);
83
84         if(msg)
85                 printf("FAILED, %s", msg);
86         else
87                 printf("FAILED");
88
89         printf(", state = %u (%s)\n", (unsigned)state, OggFLAC__StreamDecoderStateString[state]);
90         if(state == OggFLAC__STREAM_DECODER_FLAC_STREAM_DECODER_ERROR) {
91                 FLAC__StreamDecoderState state_ = OggFLAC__stream_decoder_get_FLAC_stream_decoder_state(decoder);
92                 printf("      FLAC stream decoder state = %u (%s)\n", (unsigned)state_, FLAC__StreamDecoderStateString[state_]);
93         }
94
95         return false;
96 }
97 #endif
98
99 static off_t get_filesize_(const char *srcpath)
100 {
101         struct stat srcstat;
102
103         if(0 == stat(srcpath, &srcstat))
104                 return srcstat.st_size;
105         else
106                 return -1;
107 }
108
109 static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
110 {
111         DecoderClientData *dcd = (DecoderClientData*)client_data;
112
113         (void)decoder, (void)buffer;
114
115         if(0 == dcd) {
116                 printf("ERROR: client_data in write callback is NULL\n");
117                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
118         }
119
120         if(dcd->error_occurred)
121                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
122
123         if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) {
124                 if (!dcd->quiet)
125                         printf("frame@%uf(%u)... ", frame->header.number.frame_number, frame->header.blocksize);
126         }
127         else if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) {
128                 if (!dcd->quiet)
129                         printf("frame@%llu(%u)... ", frame->header.number.sample_number, frame->header.blocksize);
130         }
131         else {
132                 FLAC__ASSERT(0);
133                 dcd->error_occurred = true;
134                 return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
135         }
136         fflush(stdout);
137
138         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
139 }
140
141 static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
142 {
143         DecoderClientData *dcd = (DecoderClientData*)client_data;
144
145         (void)decoder;
146
147         if(0 == dcd) {
148                 printf("ERROR: client_data in metadata callback is NULL\n");
149                 return;
150         }
151
152         if(dcd->error_occurred)
153                 return;
154
155         if (!dcd->got_data && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
156                 dcd->got_data = true;
157                 dcd->total_samples = metadata->data.stream_info.total_samples;
158                 dcd->channels = metadata->data.stream_info.channels;
159                 dcd->bits_per_sample = metadata->data.stream_info.bits_per_sample;
160         }
161 }
162
163 static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
164 {
165         DecoderClientData *dcd = (DecoderClientData*)client_data;
166
167         (void)decoder;
168
169         if(0 == dcd) {
170                 printf("ERROR: client_data in error callback is NULL\n");
171                 return;
172         }
173
174         if(!dcd->ignore_errors) {
175                 printf("ERROR: got error callback: err = %u (%s)\n", (unsigned)status, FLAC__StreamDecoderErrorStatusString[status]);
176                 dcd->error_occurred = true;
177         }
178 }
179
180 static FLAC__bool seek_barrage_native_flac(const char *filename, off_t filesize, unsigned count)
181 {
182         FLAC__StreamDecoder *decoder;
183         DecoderClientData decoder_client_data;
184         unsigned i;
185         long int n;
186
187         decoder_client_data.got_data = false;
188         decoder_client_data.total_samples = 0;
189         decoder_client_data.quiet = false;
190         decoder_client_data.ignore_errors = false;
191         decoder_client_data.error_occurred = false;
192
193         printf("\n+++ seek test: FLAC__StreamDecoder\n\n");
194
195         decoder = FLAC__stream_decoder_new();
196         if(0 == decoder)
197                 return die_("FLAC__stream_decoder_new() FAILED, returned NULL\n");
198
199         if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
200                 return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
201
202         if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
203                 return die_s_("FLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);
204
205         /* process until end of stream to make sure we can still seek in that state */
206         decoder_client_data.quiet = true;
207         if(!FLAC__stream_decoder_process_until_end_of_stream(decoder))
208                 return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder);
209         decoder_client_data.quiet = false;
210
211         printf("stream decoder state is %s\n", FLAC__stream_decoder_get_resolved_state_string(decoder));
212         if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM)
213                 return die_s_("expected FLAC__STREAM_DECODER_END_OF_STREAM", decoder);
214
215         printf("file's total_samples is %llu\n", decoder_client_data.total_samples);
216 #if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
217         if (decoder_client_data.total_samples > (FLAC__uint64)RAND_MAX) {
218                 printf("ERROR: must be total_samples < %u\n", (unsigned)RAND_MAX);
219                 return false;
220         }
221 #endif
222         n = (long int)decoder_client_data.total_samples;
223
224         /* if we don't have a total samples count, just guess based on the file size */
225         if(n == 0) {
226                 /* 8 would imply no compression, 9 guarantees that we will get some samples off the end of the stream to test that case */
227                 n = 9 * filesize / (decoder_client_data.channels * decoder_client_data.bits_per_sample);
228 #if !defined _MSC_VER && !defined __MINGW32__
229                 if(n > RAND_MAX)
230                         n = RAND_MAX;
231 #endif
232         }
233
234         printf("Begin seek barrage, count=%u\n", count);
235
236         for (i = 0; !stop_signal_ && (count == 0 || i < count); i++) {
237                 FLAC__uint64 pos;
238
239                 /* for the first 10, seek to the first 10 samples */
240                 if (n >= 10 && i < 10) {
241                         pos = i;
242                 }
243                 /* for the second 10, seek to the last 10 samples */
244                 else if (n >= 10 && i < 20) {
245                         pos = n - 1 - (i-10);
246                 }
247                 /* for the third 10, seek past the end and make sure we fail properly as expected */
248                 else if (i < 30) {
249                         pos = n + (i-20);
250                 }
251                 else {
252 #if !defined _MSC_VER && !defined __MINGW32__
253                         pos = (FLAC__uint64)(random() % n);
254 #else
255                         /* RAND_MAX is only 32767 in my MSVC */
256                         pos = (FLAC__uint64)((rand()<<15|rand()) % n);
257 #endif
258                 }
259
260                 printf("seek(%llu)... ", pos);
261                 fflush(stdout);
262                 if(!FLAC__stream_decoder_seek_absolute(decoder, pos)) {
263                         if(pos < (FLAC__uint64)n && decoder_client_data.total_samples != 0)
264                                 return die_s_("FLAC__stream_decoder_seek_absolute() FAILED", decoder);
265                         else if(decoder_client_data.total_samples == 0)
266                                 printf("seek failed, assuming it was past EOF... ");
267                         else
268                                 printf("seek past end failed as expected... ");
269
270                         /* hack to work around a deficiency in the seek API's behavior */
271                         /* seeking past EOF sets the file decoder state to non-OK and there's no ..._flush() or ..._reset() call to reset it */
272                         /* @@@@@@ probably no longer true and we can remove this hack */
273                         if(!FLAC__stream_decoder_finish(decoder))
274                                 return die_s_("FLAC__stream_decoder_finish() FAILED", decoder);
275
276                         if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
277                                 return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
278
279                         if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
280                                 return die_s_("FLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);
281                 }
282                 else {
283                         printf("decode_frame... ");
284                         fflush(stdout);
285                         if(!FLAC__stream_decoder_process_single(decoder))
286                                 return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);
287
288                         printf("decode_frame... ");
289                         fflush(stdout);
290                         if(!FLAC__stream_decoder_process_single(decoder))
291                                 return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);
292                 }
293
294                 printf("OK\n");
295                 fflush(stdout);
296         }
297
298         if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) {
299                 if(!FLAC__stream_decoder_finish(decoder))
300                         return die_s_("FLAC__stream_decoder_finish() FAILED", decoder);
301         }
302
303         printf("\nPASSED!\n");
304
305         return true;
306 }
307
308 #ifdef FLAC__HAS_OGG
309 static FLAC__bool seek_barrage_ogg_flac(const char *filename, off_t filesize, unsigned count)
310 {
311         OggFLAC__StreamDecoder *decoder;
312         DecoderClientData decoder_client_data;
313         unsigned i;
314         long int n;
315
316         decoder_client_data.got_data = false;
317         decoder_client_data.total_samples = 0;
318         decoder_client_data.quiet = false;
319         decoder_client_data.ignore_errors = false;
320         decoder_client_data.error_occurred = false;
321
322         printf("\n+++ seek test: OggFLAC__StreamDecoder\n\n");
323
324         decoder = OggFLAC__stream_decoder_new();
325         if(0 == decoder)
326                 return die_("OggFLAC__stream_decoder_new() FAILED, returned NULL\n");
327
328         if(OggFLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
329                 return die_os_("OggFLAC__stream_decoder_init_file() FAILED", decoder);
330
331         if(!OggFLAC__stream_decoder_process_until_end_of_metadata(decoder))
332                 return die_os_("OggFLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);
333
334         /* process until end of stream to make sure we can still seek in that state */
335 #if 0
336         /* not necessary for the Ogg seeking method */
337         decoder_client_data.quiet = true;
338         if(!OggFLAC__stream_decoder_process_until_end_of_stream(decoder))
339                 return die_os_("OggFLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder);
340         decoder_client_data.quiet = false;
341
342         printf("stream decoder state is %s\n", OggFLAC__stream_decoder_get_resolved_state_string(decoder));
343         if(OggFLAC__stream_decoder_get_state(decoder) != OggFLAC__STREAM_DECODER_END_OF_STREAM)
344                 return die_os_("expected OggFLAC__STREAM_DECODER_END_OF_STREAM", decoder);
345 #endif
346
347         printf("file's total_samples is %llu\n", decoder_client_data.total_samples);
348 #if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
349         if (decoder_client_data.total_samples > (FLAC__uint64)RAND_MAX) {
350                 printf("ERROR: must be total_samples < %u\n", (unsigned)RAND_MAX);
351                 return false;
352         }
353 #endif
354         n = (long int)decoder_client_data.total_samples;
355
356         /* if we don't have a total samples count, just guess based on the file size */
357         /* @@@ should get it from last page's granulepos */
358         if(n == 0) {
359                 /* 8 would imply no compression, 9 guarantees that we will get some samples off the end of the stream to test that case */
360                 n = 9 * filesize / (decoder_client_data.channels * decoder_client_data.bits_per_sample);
361 #if !defined _MSC_VER && !defined __MINGW32__
362                 if(n > RAND_MAX)
363                         n = RAND_MAX;
364 #endif
365         }
366
367         printf("Begin seek barrage, count=%u\n", count);
368
369         for (i = 0; !stop_signal_ && (count == 0 || i < count); i++) {
370                 FLAC__uint64 pos;
371
372                 /* for the first 10, seek to the first 10 samples */
373                 if (n >= 10 && i < 10) {
374                         pos = i;
375                 }
376                 /* for the second 10, seek to the last 10 samples */
377                 else if (n >= 10 && i < 20) {
378                         pos = n - 1 - (i-10);
379                 }
380                 /* for the third 10, seek past the end and make sure we fail properly as expected */
381                 else if (i < 30) {
382                         pos = n + (i-20);
383                 }
384                 else {
385 #if !defined _MSC_VER && !defined __MINGW32__
386                         pos = (FLAC__uint64)(random() % n);
387 #else
388                         /* RAND_MAX is only 32767 in my MSVC */
389                         pos = (FLAC__uint64)((rand()<<15|rand()) % n);
390 #endif
391                 }
392
393                 printf("seek(%llu)... ", pos);
394                 fflush(stdout);
395                 if(!OggFLAC__stream_decoder_seek_absolute(decoder, pos)) {
396                         if(pos < (FLAC__uint64)n && decoder_client_data.total_samples != 0)
397                                 return die_os_("OggFLAC__stream_decoder_seek_absolute() FAILED", decoder);
398                         else if(decoder_client_data.total_samples == 0)
399                                 printf("seek failed, assuming it was past EOF... ");
400                         else
401                                 printf("seek past end failed as expected... ");
402
403                         /* hack to work around a deficiency in the seek API's behavior */
404                         /* seeking past EOF sets the file decoder state to non-OK and there's no ..._flush() or ..._reset() call to reset it */
405                         /* @@@@@@ probably no longer true and we can remove this hack */
406                         if(!OggFLAC__stream_decoder_finish(decoder))
407                                 return die_os_("OggFLAC__stream_decoder_finish() FAILED", decoder);
408
409                         if(OggFLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
410                                 return die_os_("OggFLAC__stream_decoder_init_file() FAILED", decoder);
411
412                         if(!OggFLAC__stream_decoder_process_until_end_of_metadata(decoder))
413                                 return die_os_("OggFLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);
414                 }
415                 else {
416                         printf("decode_frame... ");
417                         fflush(stdout);
418                         if(!OggFLAC__stream_decoder_process_single(decoder))
419                                 return die_os_("OggFLAC__stream_decoder_process_single() FAILED", decoder);
420
421                         printf("decode_frame... ");
422                         fflush(stdout);
423                         if(!OggFLAC__stream_decoder_process_single(decoder))
424                                 return die_os_("OggFLAC__stream_decoder_process_single() FAILED", decoder);
425                 }
426
427                 printf("OK\n");
428                 fflush(stdout);
429         }
430
431         if(OggFLAC__stream_decoder_get_state(decoder) != OggFLAC__STREAM_DECODER_UNINITIALIZED) {
432                 if(!OggFLAC__stream_decoder_finish(decoder))
433                         return die_os_("OggFLAC__stream_decoder_finish() FAILED", decoder);
434         }
435
436         printf("\nPASSED!\n");
437
438         return true;
439 }
440 #endif
441
442 int main(int argc, char *argv[])
443 {
444         const char *filename;
445         unsigned count = 0;
446         off_t filesize;
447
448         static const char * const usage = "usage: test_seeking file.flac [#seeks]\n";
449
450         if (argc < 1 || argc > 3) {
451                 fprintf(stderr, usage);
452                 return 1;
453         }
454
455         filename = argv[1];
456
457         if (argc > 2)
458                 count = strtoul(argv[2], 0, 10);
459
460         if (count < 30)
461                 fprintf(stderr, "WARNING: random seeks don't kick in until after 30 preprogrammed ones\n");
462
463 #if !defined _MSC_VER && !defined __MINGW32__
464         {
465                 struct timeval tv;
466
467                 if (gettimeofday(&tv, 0) < 0) {
468                         fprintf(stderr, "WARNING: couldn't seed RNG with time\n");
469                         tv.tv_usec = 4321;
470                 }
471                 srandom(tv.tv_usec);
472         }
473 #else
474         srand(time(0));
475 #endif
476
477         filesize = get_filesize_(filename);
478         if (filesize < 0) {
479                 fprintf(stderr, "ERROR: can't determine filesize for %s\n", filename);
480                 return 1;
481         }
482
483         (void) signal(SIGINT, our_sigint_handler_);
484
485         {
486                 FLAC__bool ok;
487                 if (strlen(filename) > 4 && 0 == strcmp(filename+strlen(filename)-4, ".ogg")) {
488 #ifdef FLAC__HAS_OGG
489                         ok = seek_barrage_ogg_flac(filename, filesize, count);
490 #else
491                         fprintf(stderr, "ERROR: Ogg FLAC not supported\n");
492                         ok = false;
493 #endif
494                 }
495                 else {
496                         ok = seek_barrage_native_flac(filename, filesize, count);
497                 }
498                 return ok? 0 : 2;
499         }
500 }