annotate src/opusfile-0.9/examples/opusfile_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 <string.h>
Chris@69 24 #include <opusfile.h>
Chris@69 25 #if defined(_WIN32)
Chris@69 26 # include "win32utf8.h"
Chris@69 27 # undef fileno
Chris@69 28 # define fileno _fileno
Chris@69 29 #endif
Chris@69 30
Chris@69 31 static void print_duration(FILE *_fp,ogg_int64_t _nsamples,int _frac){
Chris@69 32 ogg_int64_t seconds;
Chris@69 33 ogg_int64_t minutes;
Chris@69 34 ogg_int64_t hours;
Chris@69 35 ogg_int64_t days;
Chris@69 36 ogg_int64_t weeks;
Chris@69 37 _nsamples+=_frac?24:24000;
Chris@69 38 seconds=_nsamples/48000;
Chris@69 39 _nsamples-=seconds*48000;
Chris@69 40 minutes=seconds/60;
Chris@69 41 seconds-=minutes*60;
Chris@69 42 hours=minutes/60;
Chris@69 43 minutes-=hours*60;
Chris@69 44 days=hours/24;
Chris@69 45 hours-=days*24;
Chris@69 46 weeks=days/7;
Chris@69 47 days-=weeks*7;
Chris@69 48 if(weeks)fprintf(_fp,"%liw",(long)weeks);
Chris@69 49 if(weeks||days)fprintf(_fp,"%id",(int)days);
Chris@69 50 if(weeks||days||hours){
Chris@69 51 if(weeks||days)fprintf(_fp,"%02ih",(int)hours);
Chris@69 52 else fprintf(_fp,"%ih",(int)hours);
Chris@69 53 }
Chris@69 54 if(weeks||days||hours||minutes){
Chris@69 55 if(weeks||days||hours)fprintf(_fp,"%02im",(int)minutes);
Chris@69 56 else fprintf(_fp,"%im",(int)minutes);
Chris@69 57 fprintf(_fp,"%02i",(int)seconds);
Chris@69 58 }
Chris@69 59 else fprintf(_fp,"%i",(int)seconds);
Chris@69 60 if(_frac)fprintf(_fp,".%03i",(int)(_nsamples/48));
Chris@69 61 fprintf(_fp,"s");
Chris@69 62 }
Chris@69 63
Chris@69 64 static void print_size(FILE *_fp,opus_int64 _nbytes,int _metric,
Chris@69 65 const char *_spacer){
Chris@69 66 static const char SUFFIXES[7]={' ','k','M','G','T','P','E'};
Chris@69 67 opus_int64 val;
Chris@69 68 opus_int64 den;
Chris@69 69 opus_int64 round;
Chris@69 70 int base;
Chris@69 71 int shift;
Chris@69 72 base=_metric?1000:1024;
Chris@69 73 round=0;
Chris@69 74 den=1;
Chris@69 75 for(shift=0;shift<6;shift++){
Chris@69 76 if(_nbytes<den*base-round)break;
Chris@69 77 den*=base;
Chris@69 78 round=den>>1;
Chris@69 79 }
Chris@69 80 val=(_nbytes+round)/den;
Chris@69 81 if(den>1&&val<10){
Chris@69 82 if(den>=1000000000)val=(_nbytes+(round/100))/(den/100);
Chris@69 83 else val=(_nbytes*100+round)/den;
Chris@69 84 fprintf(_fp,"%li.%02i%s%c",(long)(val/100),(int)(val%100),
Chris@69 85 _spacer,SUFFIXES[shift]);
Chris@69 86 }
Chris@69 87 else if(den>1&&val<100){
Chris@69 88 if(den>=1000000000)val=(_nbytes+(round/10))/(den/10);
Chris@69 89 else val=(_nbytes*10+round)/den;
Chris@69 90 fprintf(_fp,"%li.%i%s%c",(long)(val/10),(int)(val%10),
Chris@69 91 _spacer,SUFFIXES[shift]);
Chris@69 92 }
Chris@69 93 else fprintf(_fp,"%li%s%c",(long)val,_spacer,SUFFIXES[shift]);
Chris@69 94 }
Chris@69 95
Chris@69 96 static void put_le32(unsigned char *_dst,opus_uint32 _x){
Chris@69 97 _dst[0]=(unsigned char)(_x&0xFF);
Chris@69 98 _dst[1]=(unsigned char)(_x>>8&0xFF);
Chris@69 99 _dst[2]=(unsigned char)(_x>>16&0xFF);
Chris@69 100 _dst[3]=(unsigned char)(_x>>24&0xFF);
Chris@69 101 }
Chris@69 102
Chris@69 103 /*Make a header for a 48 kHz, stereo, signed, 16-bit little-endian PCM WAV.*/
Chris@69 104 static void make_wav_header(unsigned char _dst[44],ogg_int64_t _duration){
Chris@69 105 /*The chunk sizes are set to 0x7FFFFFFF by default.
Chris@69 106 Many, though not all, programs will interpret this to mean the duration is
Chris@69 107 "undefined", and continue to read from the file so long as there is actual
Chris@69 108 data.*/
Chris@69 109 static const unsigned char WAV_HEADER_TEMPLATE[44]={
Chris@69 110 'R','I','F','F',0xFF,0xFF,0xFF,0x7F,
Chris@69 111 'W','A','V','E','f','m','t',' ',
Chris@69 112 0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
Chris@69 113 0x80,0xBB,0x00,0x00,0x00,0xEE,0x02,0x00,
Chris@69 114 0x04,0x00,0x10,0x00,'d','a','t','a',
Chris@69 115 0xFF,0xFF,0xFF,0x7F
Chris@69 116 };
Chris@69 117 memcpy(_dst,WAV_HEADER_TEMPLATE,sizeof(WAV_HEADER_TEMPLATE));
Chris@69 118 if(_duration>0){
Chris@69 119 if(_duration>0x1FFFFFF6){
Chris@69 120 fprintf(stderr,"WARNING: WAV output would be larger than 2 GB.\n");
Chris@69 121 fprintf(stderr,
Chris@69 122 "Writing non-standard WAV header with invalid chunk sizes.\n");
Chris@69 123 }
Chris@69 124 else{
Chris@69 125 opus_uint32 audio_size;
Chris@69 126 audio_size=(opus_uint32)(_duration*4);
Chris@69 127 put_le32(_dst+4,audio_size+36);
Chris@69 128 put_le32(_dst+40,audio_size);
Chris@69 129 }
Chris@69 130 }
Chris@69 131 }
Chris@69 132
Chris@69 133 int main(int _argc,const char **_argv){
Chris@69 134 OggOpusFile *of;
Chris@69 135 ogg_int64_t duration;
Chris@69 136 unsigned char wav_header[44];
Chris@69 137 int ret;
Chris@69 138 int is_ssl;
Chris@69 139 int output_seekable;
Chris@69 140 #if defined(_WIN32)
Chris@69 141 win32_utf8_setup(&_argc,&_argv);
Chris@69 142 #endif
Chris@69 143 if(_argc!=2){
Chris@69 144 fprintf(stderr,"Usage: %s <file.opus>\n",_argv[0]);
Chris@69 145 return EXIT_FAILURE;
Chris@69 146 }
Chris@69 147 is_ssl=0;
Chris@69 148 if(strcmp(_argv[1],"-")==0){
Chris@69 149 OpusFileCallbacks cb={NULL,NULL,NULL,NULL};
Chris@69 150 of=op_open_callbacks(op_fdopen(&cb,fileno(stdin),"rb"),&cb,NULL,0,&ret);
Chris@69 151 }
Chris@69 152 else{
Chris@69 153 OpusServerInfo info;
Chris@69 154 /*Try to treat the argument as a URL.*/
Chris@69 155 of=op_open_url(_argv[1],&ret,OP_GET_SERVER_INFO(&info),NULL);
Chris@69 156 #if 0
Chris@69 157 if(of==NULL){
Chris@69 158 OpusFileCallbacks cb={NULL,NULL,NULL,NULL};
Chris@69 159 void *fp;
Chris@69 160 /*For debugging: force a file to not be seekable.*/
Chris@69 161 fp=op_fopen(&cb,_argv[1],"rb");
Chris@69 162 cb.seek=NULL;
Chris@69 163 cb.tell=NULL;
Chris@69 164 of=op_open_callbacks(fp,&cb,NULL,0,NULL);
Chris@69 165 }
Chris@69 166 #else
Chris@69 167 if(of==NULL)of=op_open_file(_argv[1],&ret);
Chris@69 168 #endif
Chris@69 169 else{
Chris@69 170 if(info.name!=NULL){
Chris@69 171 fprintf(stderr,"Station name: %s\n",info.name);
Chris@69 172 }
Chris@69 173 if(info.description!=NULL){
Chris@69 174 fprintf(stderr,"Station description: %s\n",info.description);
Chris@69 175 }
Chris@69 176 if(info.genre!=NULL){
Chris@69 177 fprintf(stderr,"Station genre: %s\n",info.genre);
Chris@69 178 }
Chris@69 179 if(info.url!=NULL){
Chris@69 180 fprintf(stderr,"Station homepage: %s\n",info.url);
Chris@69 181 }
Chris@69 182 if(info.bitrate_kbps>=0){
Chris@69 183 fprintf(stderr,"Station bitrate: %u kbps\n",
Chris@69 184 (unsigned)info.bitrate_kbps);
Chris@69 185 }
Chris@69 186 if(info.is_public>=0){
Chris@69 187 fprintf(stderr,"%s\n",
Chris@69 188 info.is_public?"Station is public.":"Station is private.");
Chris@69 189 }
Chris@69 190 if(info.server!=NULL){
Chris@69 191 fprintf(stderr,"Server software: %s\n",info.server);
Chris@69 192 }
Chris@69 193 if(info.content_type!=NULL){
Chris@69 194 fprintf(stderr,"Content-Type: %s\n",info.content_type);
Chris@69 195 }
Chris@69 196 is_ssl=info.is_ssl;
Chris@69 197 opus_server_info_clear(&info);
Chris@69 198 }
Chris@69 199 }
Chris@69 200 if(of==NULL){
Chris@69 201 fprintf(stderr,"Failed to open file '%s': %i\n",_argv[1],ret);
Chris@69 202 return EXIT_FAILURE;
Chris@69 203 }
Chris@69 204 duration=0;
Chris@69 205 output_seekable=fseek(stdout,0,SEEK_CUR)!=-1;
Chris@69 206 if(op_seekable(of)){
Chris@69 207 opus_int64 size;
Chris@69 208 fprintf(stderr,"Total number of links: %i\n",op_link_count(of));
Chris@69 209 duration=op_pcm_total(of,-1);
Chris@69 210 fprintf(stderr,"Total duration: ");
Chris@69 211 print_duration(stderr,duration,3);
Chris@69 212 fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
Chris@69 213 size=op_raw_total(of,-1);
Chris@69 214 fprintf(stderr,"Total size: ");
Chris@69 215 print_size(stderr,size,0,"");
Chris@69 216 fprintf(stderr,"\n");
Chris@69 217 }
Chris@69 218 else if(!output_seekable){
Chris@69 219 fprintf(stderr,"WARNING: Neither input nor output are seekable.\n");
Chris@69 220 fprintf(stderr,
Chris@69 221 "Writing non-standard WAV header with invalid chunk sizes.\n");
Chris@69 222 }
Chris@69 223 make_wav_header(wav_header,duration);
Chris@69 224 if(!fwrite(wav_header,sizeof(wav_header),1,stdout)){
Chris@69 225 fprintf(stderr,"Error writing WAV header: %s\n",strerror(errno));
Chris@69 226 ret=EXIT_FAILURE;
Chris@69 227 }
Chris@69 228 else{
Chris@69 229 ogg_int64_t pcm_offset;
Chris@69 230 ogg_int64_t pcm_print_offset;
Chris@69 231 ogg_int64_t nsamples;
Chris@69 232 opus_int32 bitrate;
Chris@69 233 int prev_li;
Chris@69 234 prev_li=-1;
Chris@69 235 nsamples=0;
Chris@69 236 pcm_offset=op_pcm_tell(of);
Chris@69 237 if(pcm_offset!=0){
Chris@69 238 fprintf(stderr,"Non-zero starting PCM offset: %li\n",(long)pcm_offset);
Chris@69 239 }
Chris@69 240 pcm_print_offset=pcm_offset-48000;
Chris@69 241 bitrate=0;
Chris@69 242 for(;;){
Chris@69 243 ogg_int64_t next_pcm_offset;
Chris@69 244 opus_int16 pcm[120*48*2];
Chris@69 245 unsigned char out[120*48*2*2];
Chris@69 246 int li;
Chris@69 247 int si;
Chris@69 248 /*Although we would generally prefer to use the float interface, WAV
Chris@69 249 files with signed, 16-bit little-endian samples are far more
Chris@69 250 universally supported, so that's what we output.*/
Chris@69 251 ret=op_read_stereo(of,pcm,sizeof(pcm)/sizeof(*pcm));
Chris@69 252 if(ret==OP_HOLE){
Chris@69 253 fprintf(stderr,"\nHole detected! Corrupt file segment?\n");
Chris@69 254 continue;
Chris@69 255 }
Chris@69 256 else if(ret<0){
Chris@69 257 fprintf(stderr,"\nError decoding '%s': %i\n",_argv[1],ret);
Chris@69 258 if(is_ssl)fprintf(stderr,"Possible truncation attack?\n");
Chris@69 259 ret=EXIT_FAILURE;
Chris@69 260 break;
Chris@69 261 }
Chris@69 262 li=op_current_link(of);
Chris@69 263 if(li!=prev_li){
Chris@69 264 const OpusHead *head;
Chris@69 265 const OpusTags *tags;
Chris@69 266 int binary_suffix_len;
Chris@69 267 int ci;
Chris@69 268 /*We found a new link.
Chris@69 269 Print out some information.*/
Chris@69 270 fprintf(stderr,"Decoding link %i: \n",li);
Chris@69 271 head=op_head(of,li);
Chris@69 272 fprintf(stderr," Channels: %i\n",head->channel_count);
Chris@69 273 if(op_seekable(of)){
Chris@69 274 ogg_int64_t duration;
Chris@69 275 opus_int64 size;
Chris@69 276 duration=op_pcm_total(of,li);
Chris@69 277 fprintf(stderr," Duration: ");
Chris@69 278 print_duration(stderr,duration,3);
Chris@69 279 fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
Chris@69 280 size=op_raw_total(of,li);
Chris@69 281 fprintf(stderr," Size: ");
Chris@69 282 print_size(stderr,size,0,"");
Chris@69 283 fprintf(stderr,"\n");
Chris@69 284 }
Chris@69 285 if(head->input_sample_rate){
Chris@69 286 fprintf(stderr," Original sampling rate: %lu Hz\n",
Chris@69 287 (unsigned long)head->input_sample_rate);
Chris@69 288 }
Chris@69 289 tags=op_tags(of,li);
Chris@69 290 fprintf(stderr," Encoded by: %s\n",tags->vendor);
Chris@69 291 for(ci=0;ci<tags->comments;ci++){
Chris@69 292 const char *comment;
Chris@69 293 comment=tags->user_comments[ci];
Chris@69 294 if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,comment)==0){
Chris@69 295 OpusPictureTag pic;
Chris@69 296 int err;
Chris@69 297 err=opus_picture_tag_parse(&pic,comment);
Chris@69 298 fprintf(stderr," %.23s",comment);
Chris@69 299 if(err>=0){
Chris@69 300 fprintf(stderr,"%u|%s|%s|%ux%ux%u",pic.type,pic.mime_type,
Chris@69 301 pic.description,pic.width,pic.height,pic.depth);
Chris@69 302 if(pic.colors!=0)fprintf(stderr,"/%u",pic.colors);
Chris@69 303 if(pic.format==OP_PIC_FORMAT_URL){
Chris@69 304 fprintf(stderr,"|%s\n",pic.data);
Chris@69 305 }
Chris@69 306 else{
Chris@69 307 fprintf(stderr,"|<%u bytes of image data>\n",pic.data_length);
Chris@69 308 }
Chris@69 309 opus_picture_tag_clear(&pic);
Chris@69 310 }
Chris@69 311 else fprintf(stderr,"<error parsing picture tag>\n");
Chris@69 312 }
Chris@69 313 else fprintf(stderr," %s\n",tags->user_comments[ci]);
Chris@69 314 }
Chris@69 315 if(opus_tags_get_binary_suffix(tags,&binary_suffix_len)!=NULL){
Chris@69 316 fprintf(stderr,"<%u bytes of unknown binary metadata>\n",
Chris@69 317 binary_suffix_len);
Chris@69 318 }
Chris@69 319 fprintf(stderr,"\n");
Chris@69 320 if(!op_seekable(of)){
Chris@69 321 pcm_offset=op_pcm_tell(of)-ret;
Chris@69 322 if(pcm_offset!=0){
Chris@69 323 fprintf(stderr,"Non-zero starting PCM offset in link %i: %li\n",
Chris@69 324 li,(long)pcm_offset);
Chris@69 325 }
Chris@69 326 }
Chris@69 327 }
Chris@69 328 if(li!=prev_li||pcm_offset>=pcm_print_offset+48000){
Chris@69 329 opus_int32 next_bitrate;
Chris@69 330 opus_int64 raw_offset;
Chris@69 331 next_bitrate=op_bitrate_instant(of);
Chris@69 332 if(next_bitrate>=0)bitrate=next_bitrate;
Chris@69 333 raw_offset=op_raw_tell(of);
Chris@69 334 fprintf(stderr,"\r ");
Chris@69 335 print_size(stderr,raw_offset,0,"");
Chris@69 336 fprintf(stderr," ");
Chris@69 337 print_duration(stderr,pcm_offset,0);
Chris@69 338 fprintf(stderr," (");
Chris@69 339 print_size(stderr,bitrate,1," ");
Chris@69 340 fprintf(stderr,"bps) \r");
Chris@69 341 pcm_print_offset=pcm_offset;
Chris@69 342 fflush(stderr);
Chris@69 343 }
Chris@69 344 next_pcm_offset=op_pcm_tell(of);
Chris@69 345 if(pcm_offset+ret!=next_pcm_offset){
Chris@69 346 fprintf(stderr,"\nPCM offset gap! %li+%i!=%li\n",
Chris@69 347 (long)pcm_offset,ret,(long)next_pcm_offset);
Chris@69 348 }
Chris@69 349 pcm_offset=next_pcm_offset;
Chris@69 350 if(ret<=0){
Chris@69 351 ret=EXIT_SUCCESS;
Chris@69 352 break;
Chris@69 353 }
Chris@69 354 /*Ensure the data is little-endian before writing it out.*/
Chris@69 355 for(si=0;si<2*ret;si++){
Chris@69 356 out[2*si+0]=(unsigned char)(pcm[si]&0xFF);
Chris@69 357 out[2*si+1]=(unsigned char)(pcm[si]>>8&0xFF);
Chris@69 358 }
Chris@69 359 if(!fwrite(out,sizeof(*out)*4*ret,1,stdout)){
Chris@69 360 fprintf(stderr,"\nError writing decoded audio data: %s\n",
Chris@69 361 strerror(errno));
Chris@69 362 ret=EXIT_FAILURE;
Chris@69 363 break;
Chris@69 364 }
Chris@69 365 nsamples+=ret;
Chris@69 366 prev_li=li;
Chris@69 367 }
Chris@69 368 if(ret==EXIT_SUCCESS){
Chris@69 369 fprintf(stderr,"\nDone: played ");
Chris@69 370 print_duration(stderr,nsamples,3);
Chris@69 371 fprintf(stderr," (%li samples @ 48 kHz).\n",(long)nsamples);
Chris@69 372 }
Chris@69 373 if(op_seekable(of)&&nsamples!=duration){
Chris@69 374 fprintf(stderr,"\nWARNING: "
Chris@69 375 "Number of output samples does not match declared file duration.\n");
Chris@69 376 if(!output_seekable)fprintf(stderr,"Output WAV file will be corrupt.\n");
Chris@69 377 }
Chris@69 378 if(output_seekable&&nsamples!=duration){
Chris@69 379 make_wav_header(wav_header,nsamples);
Chris@69 380 if(fseek(stdout,0,SEEK_SET)||
Chris@69 381 !fwrite(wav_header,sizeof(wav_header),1,stdout)){
Chris@69 382 fprintf(stderr,"Error rewriting WAV header: %s\n",strerror(errno));
Chris@69 383 ret=EXIT_FAILURE;
Chris@69 384 }
Chris@69 385 }
Chris@69 386 }
Chris@69 387 op_free(of);
Chris@69 388 return ret;
Chris@69 389 }