29aa59bb6983373ac0f56ade7f897c3deef9c591
[opusfile.git] / examples / opusfile_example.c
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<0){
253         fprintf(stderr,"\nError decoding '%s': %i\n",_argv[1],ret);
254         if(is_ssl)fprintf(stderr,"Possible truncation attack?\n");
255         ret=EXIT_FAILURE;
256         break;
257       }
258       li=op_current_link(of);
259       if(li!=prev_li){
260         const OpusHead *head;
261         const OpusTags *tags;
262         int             binary_suffix_len;
263         int             ci;
264         /*We found a new link.
265           Print out some information.*/
266         fprintf(stderr,"Decoding link %i:                          \n",li);
267         head=op_head(of,li);
268         fprintf(stderr,"  Channels: %i\n",head->channel_count);
269         if(op_seekable(of)){
270           ogg_int64_t duration;
271           opus_int64  size;
272           duration=op_pcm_total(of,li);
273           fprintf(stderr,"  Duration: ");
274           print_duration(stderr,duration,3);
275           fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
276           size=op_raw_total(of,li);
277           fprintf(stderr,"  Size: ");
278           print_size(stderr,size,0,"");
279           fprintf(stderr,"\n");
280         }
281         if(head->input_sample_rate){
282           fprintf(stderr,"  Original sampling rate: %lu Hz\n",
283            (unsigned long)head->input_sample_rate);
284         }
285         tags=op_tags(of,li);
286         fprintf(stderr,"  Encoded by: %s\n",tags->vendor);
287         for(ci=0;ci<tags->comments;ci++){
288           const char *comment;
289           comment=tags->user_comments[ci];
290           if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,comment)==0){
291             OpusPictureTag pic;
292             int            err;
293             err=opus_picture_tag_parse(&pic,comment);
294             fprintf(stderr,"  %.23s",comment);
295             if(err>=0){
296               fprintf(stderr,"%u|%s|%s|%ux%ux%u",pic.type,pic.mime_type,
297                pic.description,pic.width,pic.height,pic.depth);
298               if(pic.colors!=0)fprintf(stderr,"/%u",pic.colors);
299               if(pic.format==OP_PIC_FORMAT_URL){
300                 fprintf(stderr,"|%s\n",pic.data);
301               }
302               else{
303                 fprintf(stderr,"|<%u bytes of image data>\n",pic.data_length);
304               }
305               opus_picture_tag_clear(&pic);
306             }
307             else fprintf(stderr,"<error parsing picture tag>\n");
308           }
309           else fprintf(stderr,"  %s\n",tags->user_comments[ci]);
310         }
311         if(opus_tags_get_binary_suffix(tags,&binary_suffix_len)!=NULL){
312           fprintf(stderr,"<%u bytes of unknown binary metadata>\n",
313            binary_suffix_len);
314         }
315         fprintf(stderr,"\n");
316         if(!op_seekable(of)){
317           pcm_offset=op_pcm_tell(of)-ret;
318           if(pcm_offset!=0){
319             fprintf(stderr,"Non-zero starting PCM offset in link %i: %li\n",
320              li,(long)pcm_offset);
321           }
322         }
323       }
324       if(li!=prev_li||pcm_offset>=pcm_print_offset+48000){
325         opus_int32 next_bitrate;
326         opus_int64 raw_offset;
327         next_bitrate=op_bitrate_instant(of);
328         if(next_bitrate>=0)bitrate=next_bitrate;
329         raw_offset=op_raw_tell(of);
330         fprintf(stderr,"\r ");
331         print_size(stderr,raw_offset,0,"");
332         fprintf(stderr,"  ");
333         print_duration(stderr,pcm_offset,0);
334         fprintf(stderr,"  (");
335         print_size(stderr,bitrate,1," ");
336         fprintf(stderr,"bps)                    \r");
337         pcm_print_offset=pcm_offset;
338         fflush(stderr);
339       }
340       next_pcm_offset=op_pcm_tell(of);
341       if(pcm_offset+ret!=next_pcm_offset){
342         fprintf(stderr,"\nPCM offset gap! %li+%i!=%li\n",
343          (long)pcm_offset,ret,(long)next_pcm_offset);
344       }
345       pcm_offset=next_pcm_offset;
346       if(ret<=0){
347         ret=EXIT_SUCCESS;
348         break;
349       }
350       /*Ensure the data is little-endian before writing it out.*/
351       for(si=0;si<2*ret;si++){
352         out[2*si+0]=(unsigned char)(pcm[si]&0xFF);
353         out[2*si+1]=(unsigned char)(pcm[si]>>8&0xFF);
354       }
355       if(!fwrite(out,sizeof(*out)*4*ret,1,stdout)){
356         fprintf(stderr,"\nError writing decoded audio data: %s\n",
357          strerror(errno));
358         ret=EXIT_FAILURE;
359         break;
360       }
361       nsamples+=ret;
362       prev_li=li;
363     }
364     if(ret==EXIT_SUCCESS){
365       fprintf(stderr,"\nDone: played ");
366       print_duration(stderr,nsamples,3);
367       fprintf(stderr," (%li samples @ 48 kHz).\n",(long)nsamples);
368     }
369     if(op_seekable(of)&&nsamples!=duration){
370       fprintf(stderr,"\nWARNING: "
371        "Number of output samples does not match declared file duration.\n");
372       if(!output_seekable)fprintf(stderr,"Output WAV file will be corrupt.\n");
373     }
374     if(output_seekable&&nsamples!=duration){
375       make_wav_header(wav_header,nsamples);
376       if(fseek(stdout,0,SEEK_SET)||
377        !fwrite(wav_header,sizeof(wav_header),1,stdout)){
378         fprintf(stderr,"Error rewriting WAV header: %s\n",strerror(errno));
379         ret=EXIT_FAILURE;
380       }
381     }
382   }
383   op_free(of);
384   return ret;
385 }