more doxygenification (issue #5513)
[asterisk/asterisk.git] / formats / format_ogg_vorbis.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005, Jeff Ollie
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*! \file
18  *
19  * \brief OGG/Vorbis streams.
20  * 
21  */
22  
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <stdlib.h>
26 #include <sys/time.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <string.h>
31
32 #include <vorbis/codec.h>
33 #include <vorbis/vorbisenc.h>
34
35 #ifdef _WIN32
36 #include <io.h>
37 #include <fcntl.h>
38 #endif
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include "asterisk/lock.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/module.h"
49
50 #define SAMPLES_MAX 160
51 #define BLOCK_SIZE 4096
52
53
54 struct ast_filestream {
55         void *reserved[AST_RESERVED_POINTERS];
56
57         FILE *f;
58
59         /* structures for handling the Ogg container */
60         ogg_sync_state   oy;
61         ogg_stream_state os;
62         ogg_page         og;
63         ogg_packet       op;
64         
65         /* structures for handling Vorbis audio data */
66         vorbis_info      vi;
67         vorbis_comment   vc;
68         vorbis_dsp_state vd;
69         vorbis_block     vb;
70         
71         /*! \brief Indicates whether this filestream is set up for reading or writing. */
72         int writing;
73
74         /*! \brief Indicates whether an End of Stream condition has been detected. */
75         int eos;
76
77         /*! \brief Buffer to hold audio data. */
78         short buffer[SAMPLES_MAX];
79
80         /*! \brief Asterisk frame object. */
81         struct ast_frame fr;
82         char waste[AST_FRIENDLY_OFFSET];
83         char empty;
84 };
85
86 AST_MUTEX_DEFINE_STATIC(ogg_vorbis_lock);
87 static int glistcnt = 0;
88
89 static char *name = "ogg_vorbis";
90 static char *desc = "OGG/Vorbis audio";
91 static char *exts = "ogg";
92
93 /*!
94  * \brief Create a new OGG/Vorbis filestream and set it up for reading.
95  * \param f File that points to on disk storage of the OGG/Vorbis data.
96  * \return The new filestream.
97  */
98 static struct ast_filestream *ogg_vorbis_open(FILE *f)
99 {
100         int i;
101         int bytes;
102         int result;
103         char **ptr;
104         char *buffer;
105
106         struct ast_filestream *tmp;
107
108         if((tmp = malloc(sizeof(struct ast_filestream)))) {
109                 memset(tmp, 0, sizeof(struct ast_filestream));
110
111                 tmp->writing = 0;
112                 tmp->f = f;
113
114                 ogg_sync_init(&tmp->oy);
115
116                 buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
117                 bytes = fread(buffer, 1, BLOCK_SIZE, f);
118                 ogg_sync_wrote(&tmp->oy, bytes);
119
120                 result = ogg_sync_pageout(&tmp->oy, &tmp->og);
121                 if(result != 1) {
122                         if(bytes < BLOCK_SIZE) {
123                                 ast_log(LOG_ERROR, "Run out of data...\n");
124                         } else {
125                                 ast_log(LOG_ERROR, "Input does not appear to be an Ogg bitstream.\n");
126                         }
127                         fclose(f);
128                         ogg_sync_clear(&tmp->oy);
129                         free(tmp);
130                         return NULL;
131                 }
132                 
133                 ogg_stream_init(&tmp->os, ogg_page_serialno(&tmp->og));
134                 vorbis_info_init(&tmp->vi);
135                 vorbis_comment_init(&tmp->vc);
136
137                 if(ogg_stream_pagein(&tmp->os, &tmp->og) < 0) { 
138                         ast_log(LOG_ERROR, "Error reading first page of Ogg bitstream data.\n");
139                         fclose(f);
140                         ogg_stream_clear(&tmp->os);
141                         vorbis_comment_clear(&tmp->vc);
142                         vorbis_info_clear(&tmp->vi);
143                         ogg_sync_clear(&tmp->oy);
144                         free(tmp);
145                         return NULL;
146                 }
147                 
148                 if(ogg_stream_packetout(&tmp->os, &tmp->op) != 1) { 
149                         ast_log(LOG_ERROR, "Error reading initial header packet.\n");
150                         fclose(f);
151                         ogg_stream_clear(&tmp->os);
152                         vorbis_comment_clear(&tmp->vc);
153                         vorbis_info_clear(&tmp->vi);
154                         ogg_sync_clear(&tmp->oy);
155                         free(tmp);
156                         return NULL;
157                 }
158                 
159                 if(vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op) < 0) { 
160                         ast_log(LOG_ERROR, "This Ogg bitstream does not contain Vorbis audio data.\n");
161                         fclose(f);
162                         ogg_stream_clear(&tmp->os);
163                         vorbis_comment_clear(&tmp->vc);
164                         vorbis_info_clear(&tmp->vi);
165                         ogg_sync_clear(&tmp->oy);
166                         free(tmp);
167                         return NULL;
168                 }
169                 
170                 i = 0;
171                 while(i < 2) {
172                         while(i < 2){
173                                 result = ogg_sync_pageout(&tmp->oy, &tmp->og);
174                                 if(result == 0)
175                                         break;
176                                 if(result == 1) {
177                                         ogg_stream_pagein(&tmp->os, &tmp->og);
178                                         while(i < 2) {
179                                                 result = ogg_stream_packetout(&tmp->os,&tmp->op);
180                                                 if(result == 0)
181                                                         break;
182                                                 if(result < 0) {
183                                                         ast_log(LOG_ERROR, "Corrupt secondary header.  Exiting.\n");
184                                                         fclose(f);
185                                                         ogg_stream_clear(&tmp->os);
186                                                         vorbis_comment_clear(&tmp->vc);
187                                                         vorbis_info_clear(&tmp->vi);
188                                                         ogg_sync_clear(&tmp->oy);
189                                                         free(tmp);
190                                                         return NULL;
191                                                 }
192                                                 vorbis_synthesis_headerin(&tmp->vi, &tmp->vc, &tmp->op);
193                                                 i++;
194                                         }
195                                 }
196                         }
197
198                         buffer = ogg_sync_buffer(&tmp->oy, BLOCK_SIZE);
199                         bytes = fread(buffer, 1, BLOCK_SIZE, f);
200                         if(bytes == 0 && i < 2) {
201                                 ast_log(LOG_ERROR, "End of file before finding all Vorbis headers!\n");
202                                 fclose(f);
203                                 ogg_stream_clear(&tmp->os);
204                                 vorbis_comment_clear(&tmp->vc);
205                                 vorbis_info_clear(&tmp->vi);
206                                 ogg_sync_clear(&tmp->oy);
207                                 free(tmp);
208                                 return NULL;
209                         }
210                         ogg_sync_wrote(&tmp->oy, bytes);
211                 }
212                 
213                 ptr = tmp->vc.user_comments;
214                 while(*ptr){
215                         ast_log(LOG_DEBUG, "OGG/Vorbis comment: %s\n", *ptr);
216                         ++ptr;
217                 }
218                 ast_log(LOG_DEBUG, "OGG/Vorbis bitstream is %d channel, %ldHz\n", tmp->vi.channels, tmp->vi.rate);
219                 ast_log(LOG_DEBUG, "OGG/Vorbis file encoded by: %s\n", tmp->vc.vendor);
220
221                 if(tmp->vi.channels != 1) {
222                         ast_log(LOG_ERROR, "Only monophonic OGG/Vorbis files are currently supported!\n");
223                         ogg_stream_clear(&tmp->os);
224                         vorbis_comment_clear(&tmp->vc);
225                         vorbis_info_clear(&tmp->vi);
226                         ogg_sync_clear(&tmp->oy);
227                         free(tmp);
228                         return NULL;
229                 }
230                 
231
232                 if(tmp->vi.rate != 8000) {
233                         ast_log(LOG_ERROR, "Only 8000Hz OGG/Vorbis files are currently supported!\n");
234                         fclose(f);
235                         ogg_stream_clear(&tmp->os);
236                         vorbis_block_clear(&tmp->vb);
237                         vorbis_dsp_clear(&tmp->vd);
238                         vorbis_comment_clear(&tmp->vc);
239                         vorbis_info_clear(&tmp->vi);
240                         ogg_sync_clear(&tmp->oy);
241                         free(tmp);
242                         return NULL;
243                 }
244                 
245                 vorbis_synthesis_init(&tmp->vd, &tmp->vi);
246                 vorbis_block_init(&tmp->vd, &tmp->vb);
247
248                 if(ast_mutex_lock(&ogg_vorbis_lock)) {
249                         ast_log(LOG_WARNING, "Unable to lock ogg_vorbis list\n");
250                         fclose(f);
251                         ogg_stream_clear(&tmp->os);
252                         vorbis_block_clear(&tmp->vb);
253                         vorbis_dsp_clear(&tmp->vd);
254                         vorbis_comment_clear(&tmp->vc);
255                         vorbis_info_clear(&tmp->vi);
256                         ogg_sync_clear(&tmp->oy);
257                         free(tmp);
258                         return NULL;
259                 }
260                 glistcnt++;
261                 ast_mutex_unlock(&ogg_vorbis_lock);
262                 ast_update_use_count();
263         }
264         return tmp;
265 }
266
267 /*!
268  * \brief Create a new OGG/Vorbis filestream and set it up for writing.
269  * \param f File pointer that points to on-disk storage.
270  * \param comment Comment that should be embedded in the OGG/Vorbis file.
271  * \return A new filestream.
272  */
273 static struct ast_filestream *ogg_vorbis_rewrite(FILE *f, const char *comment)
274 {
275         ogg_packet header;
276         ogg_packet header_comm;
277         ogg_packet header_code;
278
279         struct ast_filestream *tmp;
280
281         if((tmp = malloc(sizeof(struct ast_filestream)))) {
282                 memset(tmp, 0, sizeof(struct ast_filestream));
283
284                 tmp->writing = 1;
285                 tmp->f = f;
286
287                 vorbis_info_init(&tmp->vi);
288
289                 if(vorbis_encode_init_vbr(&tmp->vi, 1, 8000, 0.4)) {
290                         ast_log(LOG_ERROR, "Unable to initialize Vorbis encoder!\n");
291                         free(tmp);
292                         return NULL;
293                 }
294
295                 vorbis_comment_init(&tmp->vc);
296                 vorbis_comment_add_tag(&tmp->vc, "ENCODER", "Asterisk PBX");
297                 if(comment)
298                         vorbis_comment_add_tag(&tmp->vc, "COMMENT", (char *) comment);
299
300                 vorbis_analysis_init(&tmp->vd, &tmp->vi);
301                 vorbis_block_init(&tmp->vd, &tmp->vb);
302
303                 ogg_stream_init(&tmp->os, rand());
304
305                 vorbis_analysis_headerout(&tmp->vd, &tmp->vc, &header, &header_comm, &header_code);
306                 ogg_stream_packetin(&tmp->os, &header);                                                 
307                 ogg_stream_packetin(&tmp->os, &header_comm);
308                 ogg_stream_packetin(&tmp->os, &header_code);
309
310                 while(!tmp->eos) {
311                         if(ogg_stream_flush(&tmp->os, &tmp->og) == 0)
312                                 break;
313                         fwrite(tmp->og.header, 1, tmp->og.header_len, tmp->f);
314                         fwrite(tmp->og.body, 1, tmp->og.body_len, tmp->f);
315                         if(ogg_page_eos(&tmp->og))
316                                 tmp->eos = 1;
317                 }
318
319                 if(ast_mutex_lock(&ogg_vorbis_lock)) {
320                         ast_log(LOG_WARNING, "Unable to lock ogg_vorbis list\n");
321                         fclose(f);
322                         ogg_stream_clear(&tmp->os);
323                         vorbis_block_clear(&tmp->vb);
324                         vorbis_dsp_clear(&tmp->vd);
325                         vorbis_comment_clear(&tmp->vc);
326                         vorbis_info_clear(&tmp->vi);
327                         free(tmp);
328                         return NULL;
329                 }
330                 glistcnt++;
331                 ast_mutex_unlock(&ogg_vorbis_lock);
332                 ast_update_use_count();
333         }
334         return tmp;
335 }
336
337 /*!
338  * \brief Write out any pending encoded data.
339  * \param s A OGG/Vorbis filestream.
340  */
341 static void write_stream(struct ast_filestream *s)
342 {
343         while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
344                 vorbis_analysis(&s->vb, NULL);
345                 vorbis_bitrate_addblock(&s->vb);
346                 
347                 while (vorbis_bitrate_flushpacket(&s->vd, &s->op)) {
348                         ogg_stream_packetin(&s->os, &s->op);
349                         while (!s->eos) {
350                                 if(ogg_stream_pageout(&s->os, &s->og) == 0) {
351                                         break;
352                                 }
353                                 fwrite(s->og.header, 1, s->og.header_len, s->f);
354                                 fwrite(s->og.body, 1, s->og.body_len, s->f);
355                                 if(ogg_page_eos(&s->og)) {
356                                         s->eos = 1;
357                                 }
358                         }
359                 }
360         }
361 }
362
363 /*!
364  * \brief Write audio data from a frame to an OGG/Vorbis filestream.
365  * \param s A OGG/Vorbis filestream.
366  * \param f An frame containing audio to be written to the filestream.
367  * \return -1 ifthere was an error, 0 on success.
368  */
369 static int ogg_vorbis_write(struct ast_filestream *s, struct ast_frame *f)
370 {
371         int i;
372         float **buffer;
373         short *data;
374
375         if(!s->writing) {
376                 ast_log(LOG_ERROR, "This stream is not set up for writing!\n");
377                 return -1;
378         }
379
380         if(f->frametype != AST_FRAME_VOICE) {
381                 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
382                 return -1;
383         }
384         if(f->subclass != AST_FORMAT_SLINEAR) {
385                 ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
386                 return -1;
387         }
388         if(!f->datalen)
389                 return -1;
390
391         data = (short *) f->data;
392
393         buffer = vorbis_analysis_buffer(&s->vd, f->samples);
394
395         for (i = 0; i < f->samples; i++) {
396                 buffer[0][i] = data[i]/32768.f;
397         }
398
399         vorbis_analysis_wrote(&s->vd, f->samples);
400
401         write_stream(s);
402
403         return 0;
404 }
405
406 /*!
407  * \brief Close a OGG/Vorbis filestream.
408  * \param s A OGG/Vorbis filestream.
409  */
410 static void ogg_vorbis_close(struct ast_filestream *s)
411 {
412         if(ast_mutex_lock(&ogg_vorbis_lock)) {
413                 ast_log(LOG_WARNING, "Unable to lock ogg_vorbis list\n");
414                 return;
415         }
416         glistcnt--;
417         ast_mutex_unlock(&ogg_vorbis_lock);
418         ast_update_use_count();
419
420         if(s->writing) {
421                 /* Tell the Vorbis encoder that the stream is finished
422                  * and write out the rest of the data */
423                 vorbis_analysis_wrote(&s->vd, 0);
424                 write_stream(s);
425         }
426
427         ogg_stream_clear(&s->os);
428         vorbis_block_clear(&s->vb);
429         vorbis_dsp_clear(&s->vd);
430         vorbis_comment_clear(&s->vc);
431         vorbis_info_clear(&s->vi);
432
433         if(s->writing) {
434                 ogg_sync_clear(&s->oy);
435         }
436         
437         fclose(s->f);
438         free(s);
439 }
440
441 /*!
442  * \brief Get audio data.
443  * \param s An OGG/Vorbis filestream.
444  * \param pcm Pointer to a buffere to store audio data in.
445  */
446
447 static int read_samples(struct ast_filestream *s, float ***pcm)
448 {
449         int samples_in;
450         int result;
451         char *buffer;
452         int bytes;
453
454         while (1) {
455                 samples_in = vorbis_synthesis_pcmout(&s->vd, pcm);
456                 if(samples_in > 0) {
457                         return samples_in;
458                 }
459                 
460                 /* The Vorbis decoder needs more data... */
461                 /* See ifOGG has any packets in the current page for the Vorbis decoder. */
462                 result = ogg_stream_packetout(&s->os, &s->op);
463                 if(result > 0) {
464                         /* Yes OGG had some more packets for the Vorbis decoder. */
465                         if(vorbis_synthesis(&s->vb, &s->op) == 0) {
466                                 vorbis_synthesis_blockin(&s->vd, &s->vb);
467                         }
468                         
469                         continue;
470                 }
471
472                 if(result < 0)
473                         ast_log(LOG_WARNING, "Corrupt or missing data at this page position; continuing...\n");
474                 
475                 /* No more packets left in the current page... */
476
477                 if(s->eos) {
478                         /* No more pages left in the stream */
479                         return -1;
480                 }
481
482                 while (!s->eos) {
483                         /* See ifOGG has any pages in it's internal buffers */
484                         result = ogg_sync_pageout(&s->oy, &s->og);
485                         if(result > 0) {
486                                 /* Yes, OGG has more pages in it's internal buffers,
487                                    add the page to the stream state */
488                                 result = ogg_stream_pagein(&s->os, &s->og);
489                                 if(result == 0) {
490                                         /* Yes, got a new,valid page */
491                                         if(ogg_page_eos(&s->og)) {
492                                                 s->eos = 1;
493                                         }
494                                         break;
495                                 }
496                                 ast_log(LOG_WARNING, "Invalid page in the bitstream; continuing...\n");
497                         }
498                         
499                         if(result < 0)
500                                 ast_log(LOG_WARNING, "Corrupt or missing data in bitstream; continuing...\n");
501
502                         /* No, we need to read more data from the file descrptor */
503                         /* get a buffer from OGG to read the data into */
504                         buffer = ogg_sync_buffer(&s->oy, BLOCK_SIZE);
505                         /* read more data from the file descriptor */
506                         bytes = fread(buffer, 1, BLOCK_SIZE, s->f);
507                         /* Tell OGG how many bytes we actually read into the buffer */
508                         ogg_sync_wrote(&s->oy, bytes);
509                         if(bytes == 0) {
510                                 s->eos = 1;
511                         }
512                 }
513         }
514 }
515
516 /*!
517  * \brief Read a frame full of audio data from the filestream.
518  * \param s The filestream.
519  * \param whennext Number of sample times to schedule the next call.
520  * \return A pointer to a frame containing audio data or NULL ifthere is no more audio data.
521  */
522 static struct ast_frame *ogg_vorbis_read(struct ast_filestream *s, int *whennext)
523 {
524         int clipflag = 0;
525         int i;
526         int j;
527         float **pcm;
528         float *mono;
529         double accumulator[SAMPLES_MAX];
530         int val;
531         int samples_in;
532         int samples_out = 0;
533
534         while (1) {
535                 /* See ifwe have filled up an audio frame yet */
536                 if(samples_out == SAMPLES_MAX)
537                         break;
538
539                 /* See ifVorbis decoder has some audio data for us ... */
540                 samples_in = read_samples(s, &pcm);
541                 if(samples_in <= 0)
542                         break;
543
544                 /* Got some audio data from Vorbis... */
545                 /* Convert the float audio data to 16-bit signed linear */
546                 
547                 clipflag = 0;
548
549                 samples_in = samples_in < (SAMPLES_MAX - samples_out) ? samples_in : (SAMPLES_MAX - samples_out);
550   
551                 for(j = 0; j < samples_in; j++)
552                         accumulator[j] = 0.0;
553
554                 for(i = 0; i < s->vi.channels; i++) {
555                         mono = pcm[i];
556                         for (j = 0; j < samples_in; j++) {
557                                 accumulator[j] += mono[j];
558                         }
559                 }
560
561                 for (j = 0; j < samples_in; j++) {
562                         val =  accumulator[j] * 32767.0 / s->vi.channels;
563                         if(val > 32767) {
564                                 val = 32767;
565                                 clipflag = 1;
566                         }
567                         if(val < -32768) {
568                                 val = -32768;
569                                 clipflag = 1;
570                         }
571                         s->buffer[samples_out + j] = val;
572                 }
573                         
574                 if(clipflag)
575                         ast_log(LOG_WARNING, "Clipping in frame %ld\n", (long)(s->vd.sequence));
576                 
577                 /* Tell the Vorbis decoder how many samples we actually used. */
578                 vorbis_synthesis_read(&s->vd, samples_in);
579                 samples_out += samples_in;
580         }
581
582         if(samples_out > 0) {
583                 s->fr.frametype = AST_FRAME_VOICE;
584                 s->fr.subclass = AST_FORMAT_SLINEAR;
585                 s->fr.offset = AST_FRIENDLY_OFFSET;
586                 s->fr.datalen = samples_out * 2;
587                 s->fr.data = s->buffer;
588                 s->fr.src = name;
589                 s->fr.mallocd = 0;
590                 s->fr.samples = samples_out;
591                 *whennext = samples_out;
592                 
593                 return &s->fr;
594         } else {
595                 return NULL;
596         }
597 }
598
599 /*!
600  * \brief Trucate an OGG/Vorbis filestream.
601  * \param s The filestream to truncate.
602  * \return 0 on success, -1 on failure.
603  */
604
605 static int ogg_vorbis_trunc(struct ast_filestream *s)
606 {
607         ast_log(LOG_WARNING, "Truncation is not supported on OGG/Vorbis streams!\n");
608         return -1;
609 }
610
611 /*!
612  * \brief Seek to a specific position in an OGG/Vorbis filestream.
613  * \param s The filestream to truncate.
614  * \param sample_offset New position for the filestream, measured in 8KHz samples.
615  * \param whence Location to measure 
616  * \return 0 on success, -1 on failure.
617  */
618
619 static int ogg_vorbis_seek(struct ast_filestream *s, long sample_offset, int whence) {
620         ast_log(LOG_WARNING, "Seeking is not supported on OGG/Vorbis streams!\n");
621         return -1;
622 }
623
624 static long ogg_vorbis_tell(struct ast_filestream *s) {
625         ast_log(LOG_WARNING, "Telling is not supported on OGG/Vorbis streams!\n");
626         return -1;
627 }
628
629 static char *ogg_vorbis_getcomment(struct ast_filestream *s) {
630         ast_log(LOG_WARNING, "Getting comments is not supported on OGG/Vorbis streams!\n");
631         return NULL;
632 }
633
634 int load_module()
635 {
636         return ast_format_register(name, exts, AST_FORMAT_SLINEAR,
637                                    ogg_vorbis_open,
638                                    ogg_vorbis_rewrite,
639                                    ogg_vorbis_write,
640                                    ogg_vorbis_seek,
641                                    ogg_vorbis_trunc,
642                                    ogg_vorbis_tell,
643                                    ogg_vorbis_read,
644                                    ogg_vorbis_close,
645                                    ogg_vorbis_getcomment);
646 }
647
648 int unload_module()
649 {
650         return ast_format_unregister(name);
651 }       
652
653 int usecount()
654 {
655         return glistcnt;
656 }
657
658 char *description()
659 {
660         return desc;
661 }
662
663
664 char *key()
665 {
666         return ASTERISK_GPL_KEY;
667 }
668
669 /*
670 Local Variables:
671 mode: C
672 c-file-style: "linux"
673 indent-tabs-mode: t
674 End:
675 */