annotate src/opusfile-0.9/examples/seeking_example.c @ 69:7aeed7906520

Add Opus sources and macOS builds
author Chris Cannam
date Wed, 23 Jan 2019 13:48:08 +0000
parents
children
rev   line source
Chris@69 1 /********************************************************************
Chris@69 2 * *
Chris@69 3 * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
Chris@69 4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
Chris@69 5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
Chris@69 6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
Chris@69 7 * *
Chris@69 8 * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 *
Chris@69 9 * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
Chris@69 10 * *
Chris@69 11 ********************************************************************/
Chris@69 12 #ifdef HAVE_CONFIG_H
Chris@69 13 #include "config.h"
Chris@69 14 #endif
Chris@69 15
Chris@69 16 /*For fileno()*/
Chris@69 17 #if !defined(_POSIX_SOURCE)
Chris@69 18 # define _POSIX_SOURCE 1
Chris@69 19 #endif
Chris@69 20 #include <stdio.h>
Chris@69 21 #include <stdlib.h>
Chris@69 22 #include <errno.h>
Chris@69 23 #include <math.h>
Chris@69 24 #include <string.h>
Chris@69 25 #include <opusfile.h>
Chris@69 26 #if defined(_WIN32)
Chris@69 27 # include "win32utf8.h"
Chris@69 28 # undef fileno
Chris@69 29 # define fileno _fileno
Chris@69 30 #endif
Chris@69 31
Chris@69 32 /*Use shorts, they're smaller.*/
Chris@69 33 #if !defined(OP_FIXED_POINT)
Chris@69 34 # define OP_FIXED_POINT (1)
Chris@69 35 #endif
Chris@69 36
Chris@69 37 #if defined(OP_FIXED_POINT)
Chris@69 38
Chris@69 39 typedef opus_int16 op_sample;
Chris@69 40
Chris@69 41 # define op_read_native op_read
Chris@69 42
Chris@69 43 /*TODO: The convergence after 80 ms of preroll is far from exact.
Chris@69 44 Our comparison is very rough.
Chris@69 45 Need to find some way to do this better.*/
Chris@69 46 # define MATCH_TOL (16384)
Chris@69 47
Chris@69 48 # define ABS(_x) ((_x)<0?-(_x):(_x))
Chris@69 49
Chris@69 50 # define MATCH(_a,_b) (ABS((_a)-(_b))<MATCH_TOL)
Chris@69 51
Chris@69 52 /*Don't have fixed-point downmixing code.*/
Chris@69 53 # undef OP_WRITE_SEEK_SAMPLES
Chris@69 54
Chris@69 55 #else
Chris@69 56
Chris@69 57 typedef float op_sample;
Chris@69 58
Chris@69 59 # define op_read_native op_read_float
Chris@69 60
Chris@69 61 /*TODO: The convergence after 80 ms of preroll is far from exact.
Chris@69 62 Our comparison is very rough.
Chris@69 63 Need to find some way to do this better.*/
Chris@69 64 # define MATCH_TOL (16384.0/32768)
Chris@69 65
Chris@69 66 # define FABS(_x) ((_x)<0?-(_x):(_x))
Chris@69 67
Chris@69 68 # define MATCH(_a,_b) (FABS((_a)-(_b))<MATCH_TOL)
Chris@69 69
Chris@69 70 # if defined(OP_WRITE_SEEK_SAMPLES)
Chris@69 71 /*Matrices for downmixing from the supported channel counts to stereo.*/
Chris@69 72 static const float DOWNMIX_MATRIX[8][8][2]={
Chris@69 73 /*mono*/
Chris@69 74 {
Chris@69 75 {1.F,1.F}
Chris@69 76 },
Chris@69 77 /*stereo*/
Chris@69 78 {
Chris@69 79 {1.F,0.F},{0.F,1.F}
Chris@69 80 },
Chris@69 81 /*3.0*/
Chris@69 82 {
Chris@69 83 {0.5858F,0.F},{0.4142F,0.4142F},{0,0.5858F}
Chris@69 84 },
Chris@69 85 /*quadrophonic*/
Chris@69 86 {
Chris@69 87 {0.4226F,0.F},{0,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
Chris@69 88 },
Chris@69 89 /*5.0*/
Chris@69 90 {
Chris@69 91 {0.651F,0.F},{0.46F,0.46F},{0,0.651F},{0.5636F,0.3254F},{0.3254F,0.5636F}
Chris@69 92 },
Chris@69 93 /*5.1*/
Chris@69 94 {
Chris@69 95 {0.529F,0.F},{0.3741F,0.3741F},{0.F,0.529F},{0.4582F,0.2645F},
Chris@69 96 {0.2645F,0.4582F},{0.3741F,0.3741F}
Chris@69 97 },
Chris@69 98 /*6.1*/
Chris@69 99 {
Chris@69 100 {0.4553F,0.F},{0.322F,0.322F},{0.F,0.4553F},{0.3943F,0.2277F},
Chris@69 101 {0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
Chris@69 102 },
Chris@69 103 /*7.1*/
Chris@69 104 {
Chris@69 105 {0.3886F,0.F},{0.2748F,0.2748F},{0.F,0.3886F},{0.3366F,0.1943F},
Chris@69 106 {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
Chris@69 107 }
Chris@69 108 };
Chris@69 109
Chris@69 110 static void write_samples(float *_samples,int _nsamples,int _nchannels){
Chris@69 111 float stereo_pcm[120*48*2];
Chris@69 112 int i;
Chris@69 113 for(i=0;i<_nsamples;i++){
Chris@69 114 float l;
Chris@69 115 float r;
Chris@69 116 int ci;
Chris@69 117 l=r=0.F;
Chris@69 118 for(ci=0;ci<_nchannels;ci++){
Chris@69 119 l+=DOWNMIX_MATRIX[_nchannels-1][ci][0]*_samples[i*_nchannels+ci];
Chris@69 120 r+=DOWNMIX_MATRIX[_nchannels-1][ci][1]*_samples[i*_nchannels+ci];
Chris@69 121 }
Chris@69 122 stereo_pcm[2*i+0]=l;
Chris@69 123 stereo_pcm[2*i+1]=r;
Chris@69 124 }
Chris@69 125 fwrite(stereo_pcm,sizeof(*stereo_pcm)*2,_nsamples,stdout);
Chris@69 126 }
Chris@69 127 # endif
Chris@69 128
Chris@69 129 #endif
Chris@69 130
Chris@69 131 static long nfailures;
Chris@69 132
Chris@69 133 static void verify_seek(OggOpusFile *_of,opus_int64 _byte_offset,
Chris@69 134 ogg_int64_t _pcm_offset,ogg_int64_t _pcm_length,op_sample *_bigassbuffer){
Chris@69 135 opus_int64 byte_offset;
Chris@69 136 ogg_int64_t pcm_offset;
Chris@69 137 ogg_int64_t duration;
Chris@69 138 op_sample buffer[120*48*8];
Chris@69 139 int nchannels;
Chris@69 140 int nsamples;
Chris@69 141 int li;
Chris@69 142 int lj;
Chris@69 143 int i;
Chris@69 144 byte_offset=op_raw_tell(_of);
Chris@69 145 if(_byte_offset!=-1&&byte_offset<_byte_offset){
Chris@69 146 fprintf(stderr,"\nRaw position out of tolerance: requested %li, "
Chris@69 147 "got %li.\n",(long)_byte_offset,(long)byte_offset);
Chris@69 148 nfailures++;
Chris@69 149 }
Chris@69 150 pcm_offset=op_pcm_tell(_of);
Chris@69 151 if(_pcm_offset!=-1&&pcm_offset>_pcm_offset){
Chris@69 152 fprintf(stderr,"\nPCM position out of tolerance: requested %li, "
Chris@69 153 "got %li.\n",(long)_pcm_offset,(long)pcm_offset);
Chris@69 154 nfailures++;
Chris@69 155 }
Chris@69 156 if(pcm_offset<0||pcm_offset>_pcm_length){
Chris@69 157 fprintf(stderr,"\nPCM position out of bounds: got %li.\n",
Chris@69 158 (long)pcm_offset);
Chris@69 159 nfailures++;
Chris@69 160 }
Chris@69 161 nsamples=op_read_native(_of,buffer,sizeof(buffer)/sizeof(*buffer),&li);
Chris@69 162 if(nsamples<0){
Chris@69 163 fprintf(stderr,"\nFailed to read PCM data after seek: %i\n",nsamples);
Chris@69 164 nfailures++;
Chris@69 165 li=op_current_link(_of);
Chris@69 166 }
Chris@69 167 for(lj=0;lj<li;lj++){
Chris@69 168 duration=op_pcm_total(_of,lj);
Chris@69 169 if(0<=pcm_offset&&pcm_offset<duration){
Chris@69 170 fprintf(stderr,"\nPCM data after seek came from the wrong link: "
Chris@69 171 "expected %i, got %i.\n",lj,li);
Chris@69 172 nfailures++;
Chris@69 173 }
Chris@69 174 pcm_offset-=duration;
Chris@69 175 if(_bigassbuffer!=NULL)_bigassbuffer+=op_channel_count(_of,lj)*duration;
Chris@69 176 }
Chris@69 177 duration=op_pcm_total(_of,li);
Chris@69 178 if(pcm_offset+nsamples>duration){
Chris@69 179 fprintf(stderr,"\nPCM data after seek exceeded link duration: "
Chris@69 180 "limit %li, got %li.\n",(long)duration,(long)(pcm_offset+nsamples));
Chris@69 181 nfailures++;
Chris@69 182 }
Chris@69 183 nchannels=op_channel_count(_of,li);
Chris@69 184 if(_bigassbuffer!=NULL){
Chris@69 185 for(i=0;i<nsamples*nchannels;i++){
Chris@69 186 if(!MATCH(buffer[i],_bigassbuffer[pcm_offset*nchannels+i])){
Chris@69 187 ogg_int64_t j;
Chris@69 188 fprintf(stderr,"\nData after seek doesn't match declared PCM "
Chris@69 189 "position: mismatch %G\n",
Chris@69 190 (double)buffer[i]-_bigassbuffer[pcm_offset*nchannels+i]);
Chris@69 191 for(j=0;j<duration-nsamples;j++){
Chris@69 192 for(i=0;i<nsamples*nchannels;i++){
Chris@69 193 if(!MATCH(buffer[i],_bigassbuffer[j*nchannels+i]))break;
Chris@69 194 }
Chris@69 195 if(i==nsamples*nchannels){
Chris@69 196 fprintf(stderr,"\nData after seek appears to match position %li.\n",
Chris@69 197 (long)i);
Chris@69 198 }
Chris@69 199 }
Chris@69 200 nfailures++;
Chris@69 201 break;
Chris@69 202 }
Chris@69 203 }
Chris@69 204 }
Chris@69 205 #if defined(OP_WRITE_SEEK_SAMPLES)
Chris@69 206 write_samples(buffer,nsamples,nchannels);
Chris@69 207 #endif
Chris@69 208 }
Chris@69 209
Chris@69 210 #define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
Chris@69 211
Chris@69 212 /*A simple wrapper that lets us count the number of underlying seek calls.*/
Chris@69 213
Chris@69 214 static op_seek_func real_seek;
Chris@69 215
Chris@69 216 static long nreal_seeks;
Chris@69 217
Chris@69 218 static int seek_stat_counter(void *_stream,opus_int64 _offset,int _whence){
Chris@69 219 if(_whence==SEEK_SET)nreal_seeks++;
Chris@69 220 /*SEEK_CUR with an offset of 0 is free, as is SEEK_END with an offset of 0
Chris@69 221 (assuming we know the file size), so don't count them.*/
Chris@69 222 else if(_offset!=0)nreal_seeks++;
Chris@69 223 return (*real_seek)(_stream,_offset,_whence);
Chris@69 224 }
Chris@69 225
Chris@69 226 #define NSEEK_TESTS (1000)
Chris@69 227
Chris@69 228 static void print_duration(FILE *_fp,ogg_int64_t _nsamples){
Chris@69 229 ogg_int64_t seconds;
Chris@69 230 ogg_int64_t minutes;
Chris@69 231 ogg_int64_t hours;
Chris@69 232 ogg_int64_t days;
Chris@69 233 ogg_int64_t weeks;
Chris@69 234 seconds=_nsamples/48000;
Chris@69 235 _nsamples-=seconds*48000;
Chris@69 236 minutes=seconds/60;
Chris@69 237 seconds-=minutes*60;
Chris@69 238 hours=minutes/60;
Chris@69 239 minutes-=hours*60;
Chris@69 240 days=hours/24;
Chris@69 241 hours-=days*24;
Chris@69 242 weeks=days/7;
Chris@69 243 days-=weeks*7;
Chris@69 244 if(weeks)fprintf(_fp,"%liw",(long)weeks);
Chris@69 245 if(weeks||days)fprintf(_fp,"%id",(int)days);
Chris@69 246 if(weeks||days||hours){
Chris@69 247 if(weeks||days)fprintf(_fp,"%02ih",(int)hours);
Chris@69 248 else fprintf(_fp,"%ih",(int)hours);
Chris@69 249 }
Chris@69 250 if(weeks||days||hours||minutes){
Chris@69 251 if(weeks||days||hours)fprintf(_fp,"%02im",(int)minutes);
Chris@69 252 else fprintf(_fp,"%im",(int)minutes);
Chris@69 253 fprintf(_fp,"%02i",(int)seconds);
Chris@69 254 }
Chris@69 255 else fprintf(_fp,"%i",(int)seconds);
Chris@69 256 fprintf(_fp,".%03is",(int)(_nsamples+24)/48);
Chris@69 257 }
Chris@69 258
Chris@69 259 int main(int _argc,const char **_argv){
Chris@69 260 OpusFileCallbacks cb;
Chris@69 261 OggOpusFile *of;
Chris@69 262 void *fp;
Chris@69 263 #if defined(_WIN32)
Chris@69 264 win32_utf8_setup(&_argc,&_argv);
Chris@69 265 #endif
Chris@69 266 if(_argc!=2){
Chris@69 267 fprintf(stderr,"Usage: %s <file.opus>\n",_argv[0]);
Chris@69 268 return EXIT_FAILURE;
Chris@69 269 }
Chris@69 270 memset(&cb,0,sizeof(cb));
Chris@69 271 if(strcmp(_argv[1],"-")==0)fp=op_fdopen(&cb,fileno(stdin),"rb");
Chris@69 272 else{
Chris@69 273 /*Try to treat the argument as a URL.*/
Chris@69 274 fp=op_url_stream_create(&cb,_argv[1],
Chris@69 275 OP_SSL_SKIP_CERTIFICATE_CHECK(1),NULL);
Chris@69 276 /*Fall back assuming it's a regular file name.*/
Chris@69 277 if(fp==NULL)fp=op_fopen(&cb,_argv[1],"rb");
Chris@69 278 }
Chris@69 279 if(cb.seek!=NULL){
Chris@69 280 real_seek=cb.seek;
Chris@69 281 cb.seek=seek_stat_counter;
Chris@69 282 }
Chris@69 283 of=op_open_callbacks(fp,&cb,NULL,0,NULL);
Chris@69 284 if(of==NULL){
Chris@69 285 fprintf(stderr,"Failed to open file '%s'.\n",_argv[1]);
Chris@69 286 return EXIT_FAILURE;
Chris@69 287 }
Chris@69 288 if(op_seekable(of)){
Chris@69 289 op_sample *bigassbuffer;
Chris@69 290 ogg_int64_t size;
Chris@69 291 ogg_int64_t pcm_offset;
Chris@69 292 ogg_int64_t pcm_length;
Chris@69 293 ogg_int64_t nsamples;
Chris@69 294 long max_seeks;
Chris@69 295 int nlinks;
Chris@69 296 int ret;
Chris@69 297 int li;
Chris@69 298 int i;
Chris@69 299 /*Because we want to do sample-level verification that the seek does what
Chris@69 300 it claimed, decode the entire file into memory.*/
Chris@69 301 nlinks=op_link_count(of);
Chris@69 302 fprintf(stderr,"Opened file containing %i links with %li seeks "
Chris@69 303 "(%0.3f per link).\n",nlinks,nreal_seeks,nreal_seeks/(double)nlinks);
Chris@69 304 /*Reset the seek counter.*/
Chris@69 305 nreal_seeks=0;
Chris@69 306 nsamples=0;
Chris@69 307 for(li=0;li<nlinks;li++){
Chris@69 308 nsamples+=op_pcm_total(of,li)*op_channel_count(of,li);
Chris@69 309 }
Chris@69 310 /*Until we find another way to do the comparisons that solves the MATCH_TOL
Chris@69 311 problem, disable this.*/
Chris@69 312 #if 0
Chris@69 313 bigassbuffer=_ogg_malloc(sizeof(*bigassbuffer)*nsamples);
Chris@69 314 if(bigassbuffer==NULL){
Chris@69 315 fprintf(stderr,
Chris@69 316 "Buffer allocation failed. Seek offset detection disabled.\n");
Chris@69 317 }
Chris@69 318 #else
Chris@69 319 bigassbuffer=NULL;
Chris@69 320 #endif
Chris@69 321 pcm_offset=op_pcm_tell(of);
Chris@69 322 if(pcm_offset!=0){
Chris@69 323 fprintf(stderr,"Initial PCM offset was not 0, got %li instead.!\n",
Chris@69 324 (long)pcm_offset);
Chris@69 325 nfailures++;
Chris@69 326 }
Chris@69 327 /*Disabling the linear scan for now.
Chris@69 328 Only test on non-borken files!*/
Chris@69 329 #if 0
Chris@69 330 {
Chris@69 331 op_sample smallerbuffer[120*48*8];
Chris@69 332 ogg_int64_t pcm_print_offset;
Chris@69 333 ogg_int64_t si;
Chris@69 334 opus_int32 bitrate;
Chris@69 335 int saw_hole;
Chris@69 336 pcm_print_offset=pcm_offset-48000;
Chris@69 337 bitrate=0;
Chris@69 338 saw_hole=0;
Chris@69 339 for(si=0;si<nsamples;){
Chris@69 340 ogg_int64_t next_pcm_offset;
Chris@69 341 opus_int32 next_bitrate;
Chris@69 342 op_sample *buf;
Chris@69 343 int buf_size;
Chris@69 344 buf=bigassbuffer==NULL?smallerbuffer:bigassbuffer+si;
Chris@69 345 buf_size=(int)OP_MIN(nsamples-si,
Chris@69 346 (int)(sizeof(smallerbuffer)/sizeof(*smallerbuffer))),
Chris@69 347 ret=op_read_native(of,buf,buf_size,&li);
Chris@69 348 if(ret==OP_HOLE){
Chris@69 349 /*Only warn once in a row.*/
Chris@69 350 if(saw_hole)continue;
Chris@69 351 saw_hole=1;
Chris@69 352 /*This is just a warning.
Chris@69 353 As long as the timestamps are still contiguous we're okay.*/
Chris@69 354 fprintf(stderr,"\nHole in PCM data at sample %li\n",
Chris@69 355 (long)pcm_offset);
Chris@69 356 continue;
Chris@69 357 }
Chris@69 358 else if(ret<=0){
Chris@69 359 fprintf(stderr,"\nFailed to read PCM data: %i\n",ret);
Chris@69 360 exit(EXIT_FAILURE);
Chris@69 361 }
Chris@69 362 saw_hole=0;
Chris@69 363 /*If we have gaps in the PCM positions, seeking is not likely to work
Chris@69 364 near them.*/
Chris@69 365 next_pcm_offset=op_pcm_tell(of);
Chris@69 366 if(pcm_offset+ret!=next_pcm_offset){
Chris@69 367 fprintf(stderr,"\nGap in PCM offset: expecting %li, got %li\n",
Chris@69 368 (long)(pcm_offset+ret),(long)next_pcm_offset);
Chris@69 369 nfailures++;
Chris@69 370 }
Chris@69 371 pcm_offset=next_pcm_offset;
Chris@69 372 si+=ret*op_channel_count(of,li);
Chris@69 373 if(pcm_offset>=pcm_print_offset+48000){
Chris@69 374 next_bitrate=op_bitrate_instant(of);
Chris@69 375 if(next_bitrate>=0)bitrate=next_bitrate;
Chris@69 376 fprintf(stderr,"\r%s... [%li left] (%0.3f kbps) ",
Chris@69 377 bigassbuffer==NULL?"Scanning":"Loading",nsamples-si,bitrate/1000.0);
Chris@69 378 pcm_print_offset=pcm_offset;
Chris@69 379 }
Chris@69 380 }
Chris@69 381 ret=op_read_native(of,smallerbuffer,8,&li);
Chris@69 382 if(ret<0){
Chris@69 383 fprintf(stderr,"Failed to read PCM data: %i\n",ret);
Chris@69 384 nfailures++;
Chris@69 385 }
Chris@69 386 if(ret>0){
Chris@69 387 fprintf(stderr,"Read too much PCM data!\n");
Chris@69 388 nfailures++;
Chris@69 389 }
Chris@69 390 }
Chris@69 391 #endif
Chris@69 392 pcm_length=op_pcm_total(of,-1);
Chris@69 393 size=op_raw_total(of,-1);
Chris@69 394 fprintf(stderr,"\rLoaded (%0.3f kbps average). \n",
Chris@69 395 op_bitrate(of,-1)/1000.0);
Chris@69 396 fprintf(stderr,"Testing raw seeking to random places in %li bytes...\n",
Chris@69 397 (long)size);
Chris@69 398 max_seeks=0;
Chris@69 399 for(i=0;i<NSEEK_TESTS;i++){
Chris@69 400 long nseeks_tmp;
Chris@69 401 opus_int64 byte_offset;
Chris@69 402 nseeks_tmp=nreal_seeks;
Chris@69 403 byte_offset=(opus_int64)(rand()/(double)RAND_MAX*size);
Chris@69 404 fprintf(stderr,"\r\t%3i [raw position %li]... ",
Chris@69 405 i,(long)byte_offset);
Chris@69 406 ret=op_raw_seek(of,byte_offset);
Chris@69 407 if(ret<0){
Chris@69 408 fprintf(stderr,"\nSeek failed: %i.\n",ret);
Chris@69 409 nfailures++;
Chris@69 410 }
Chris@69 411 if(i==28){
Chris@69 412 i=28;
Chris@69 413 }
Chris@69 414 verify_seek(of,byte_offset,-1,pcm_length,bigassbuffer);
Chris@69 415 nseeks_tmp=nreal_seeks-nseeks_tmp;
Chris@69 416 max_seeks=nseeks_tmp>max_seeks?nseeks_tmp:max_seeks;
Chris@69 417 }
Chris@69 418 fprintf(stderr,"\rTotal seek operations: %li (%.3f per raw seek, %li maximum).\n",
Chris@69 419 nreal_seeks,nreal_seeks/(double)NSEEK_TESTS,max_seeks);
Chris@69 420 nreal_seeks=0;
Chris@69 421 fprintf(stderr,"Testing exact PCM seeking to random places in %li "
Chris@69 422 "samples (",(long)pcm_length);
Chris@69 423 print_duration(stderr,pcm_length);
Chris@69 424 fprintf(stderr,")...\n");
Chris@69 425 max_seeks=0;
Chris@69 426 for(i=0;i<NSEEK_TESTS;i++){
Chris@69 427 ogg_int64_t pcm_offset2;
Chris@69 428 long nseeks_tmp;
Chris@69 429 nseeks_tmp=nreal_seeks;
Chris@69 430 pcm_offset=(ogg_int64_t)(rand()/(double)RAND_MAX*pcm_length);
Chris@69 431 fprintf(stderr,"\r\t%3i [PCM position %li]... ",
Chris@69 432 i,(long)pcm_offset);
Chris@69 433 ret=op_pcm_seek(of,pcm_offset);
Chris@69 434 if(ret<0){
Chris@69 435 fprintf(stderr,"\nSeek failed: %i.\n",ret);
Chris@69 436 nfailures++;
Chris@69 437 }
Chris@69 438 pcm_offset2=op_pcm_tell(of);
Chris@69 439 if(pcm_offset!=pcm_offset2){
Chris@69 440 fprintf(stderr,"\nDeclared PCM position did not perfectly match "
Chris@69 441 "request: requested %li, got %li.\n",
Chris@69 442 (long)pcm_offset,(long)pcm_offset2);
Chris@69 443 nfailures++;
Chris@69 444 }
Chris@69 445 verify_seek(of,-1,pcm_offset,pcm_length,bigassbuffer);
Chris@69 446 nseeks_tmp=nreal_seeks-nseeks_tmp;
Chris@69 447 max_seeks=nseeks_tmp>max_seeks?nseeks_tmp:max_seeks;
Chris@69 448 }
Chris@69 449 fprintf(stderr,"\rTotal seek operations: %li (%.3f per exact seek, %li maximum).\n",
Chris@69 450 nreal_seeks,nreal_seeks/(double)NSEEK_TESTS,max_seeks);
Chris@69 451 nreal_seeks=0;
Chris@69 452 fprintf(stderr,"OK.\n");
Chris@69 453 _ogg_free(bigassbuffer);
Chris@69 454 }
Chris@69 455 else{
Chris@69 456 fprintf(stderr,"Input was not seekable.\n");
Chris@69 457 exit(EXIT_FAILURE);
Chris@69 458 }
Chris@69 459 op_free(of);
Chris@69 460 if(nfailures>0){
Chris@69 461 fprintf(stderr,"FAILED: %li failure conditions encountered.\n",nfailures);
Chris@69 462 }
Chris@69 463 return nfailures!=0?EXIT_FAILURE:EXIT_SUCCESS;
Chris@69 464 }