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