Mercurial > hg > sv-dependency-builds
comparison 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 |
comparison
equal
deleted
inserted
replaced
68:85d5306e114e | 69:7aeed7906520 |
---|---|
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 } |