comparison src/opusfile-0.9/examples/opusfile_example.c @ 154:4664ac0c1032

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