]> sjero.net Git - linphone/blob - linphone/mediastreamer2/src/oss.c
remote ortp and add it as a submodule instead.
[linphone] / linphone / mediastreamer2 / src / oss.c
1 /*
2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include "mediastreamer-config.h"
23 #endif
24
25 #include "mediastreamer2/mssndcard.h"
26 #include "mediastreamer2/msfilter.h"
27
28 #include <sys/soundcard.h>
29
30 #include <errno.h>
31 #include <assert.h>
32 #include <fcntl.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <unistd.h>
36
37 #ifdef HAVE_ALLOCA_H /*FreeBSD does not have alloca.h*/
38 #include <alloca.h>
39 #endif
40
41 MSFilter *ms_oss_read_new(MSSndCard *card);
42 MSFilter *ms_oss_write_new(MSSndCard *card);
43
44 static int oss_open(const char *devname, int bits,int stereo, int rate, int *minsz)
45 {
46         int fd;
47         int p=0,cond=0;
48         int i=0;
49         int min_size=0,blocksize=512;
50         int err;
51         int frag;
52         audio_buf_info info;
53   
54         //g_message("opening sound device");
55         fd=open(devname,O_RDWR|O_NONBLOCK);
56         if (fd<0) return -EWOULDBLOCK;
57         /* unset nonblocking mode */
58         /* We wanted non blocking open but now put it back to normal ; thanks Xine !*/
59         fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK);
60
61         /* reset is maybe not needed but takes time*/
62         /*ioctl(fd, SNDCTL_DSP_RESET, 0); */
63
64         /* This code is used to limit the internal buffer of the sound
65            card so that no internal delay can occur in the sound card */
66         frag = ( ( 32767 << 16 ) | 7 );
67         if( ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &frag ) ) {
68                 ms_warning("oss_open: can't set fragment size:%s.",strerror(errno));
69         }
70         
71         p=AFMT_S16_NE;
72         
73         err=ioctl(fd,SNDCTL_DSP_SETFMT,&p);
74         if (err<0){
75                 ms_warning("oss_open: can't set sample format:%s.",strerror(errno));
76         }
77
78         
79         p =  bits;  /* 16 bits */
80         err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p);
81         if (err<0){
82                 ms_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno));
83         }
84
85         p =  rate;  /* rate in khz*/
86         err=ioctl(fd, SNDCTL_DSP_SPEED, &p);
87         if (err<0){
88                 ms_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno));
89         }
90         
91         p =  stereo;  /* stereo or not */
92         err=ioctl(fd, SNDCTL_DSP_STEREO, &p);
93         if (err<0){
94                 ms_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno));
95         }
96         
97         if (rate==16000) blocksize=4096;        /* oss emulation is not very good at 16khz */
98         else blocksize=blocksize*(rate/8000);
99         ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
100
101         /* try to subdivide BLKSIZE to reach blocksize if necessary */
102         if (min_size>blocksize)
103         {
104                 cond=1;
105                 p=min_size/blocksize;
106                 while(cond)
107                 {
108                         i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p);
109                         //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno);
110                         if ((i==0) || (p==1)) cond=0;
111                         else p=p/2;
112                 }
113         }
114         ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
115         if (min_size>blocksize)
116         {
117                 ms_warning("dsp block size set to %i.",min_size);
118         }else{
119                 /* no need to access the card with less latency than needed*/
120                 min_size=blocksize;
121         }
122
123         ms_message("/dev/dsp opened: rate=%i,bits=%i,stereo=%i blocksize=%i.",
124                         rate,bits,stereo,min_size);
125         
126         if( ioctl( fd, SNDCTL_DSP_GETISPACE, &info ) == -1 ) {
127                 ms_warning("oss_open: can't get ispace:%s.",strerror(errno));
128         }
129         else{
130                 ms_warning("oss_open: audio buffer size: %i.", info.fragsize * sizeof( short ));
131         }
132
133
134         /* start recording !!! Alex */
135         {
136                 int fl,res;
137                 
138                 fl=PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT;
139                 res=ioctl(fd, SNDCTL_DSP_SETTRIGGER, &fl);
140                 if (res<0) ms_warning("OSS_TRIGGER: %s",strerror(errno));
141         } 
142         *minsz=min_size;
143         return fd;
144 }
145
146 typedef struct OssData{
147         char *pcmdev;
148         char *mixdev;
149         int pcmfd;
150         int rate;
151         int bits;
152         ms_thread_t thread;
153         ms_mutex_t mutex;
154         queue_t rq;
155         MSBufferizer * bufferizer;
156         bool_t read_started;
157         bool_t write_started;
158         bool_t stereo;
159 } OssData;
160
161 static void oss_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
162 {
163         OssData *d=(OssData*)card->data;
164         int p,mix_fd;
165         int osscmd;
166         if (d->mixdev==NULL) return;
167         switch(e){
168                 case MS_SND_CARD_MASTER:
169                         osscmd=SOUND_MIXER_VOLUME;
170                 break;
171                 case MS_SND_CARD_CAPTURE:
172                         osscmd=SOUND_MIXER_IGAIN;
173                 break;
174                 case MS_SND_CARD_PLAYBACK:
175                         osscmd=SOUND_MIXER_PCM;
176                 break;
177                 default:
178                         ms_warning("oss_card_set_level: unsupported command.");
179                         return;
180         }
181         p=(((int)percent)<<8 | (int)percent);
182         mix_fd = open(d->mixdev, O_WRONLY);
183         ioctl(mix_fd,MIXER_WRITE(osscmd), &p);
184         close(mix_fd);
185 }
186
187 static int oss_get_level(MSSndCard *card, MSSndCardMixerElem e)
188 {
189         OssData *d=(OssData*)card->data;
190         int p=0,mix_fd;
191         int osscmd;
192         if (d->mixdev==NULL) return -1;
193         switch(e){
194                 case MS_SND_CARD_MASTER:
195                         osscmd=SOUND_MIXER_VOLUME;
196                 break;
197                 case MS_SND_CARD_CAPTURE:
198                         osscmd=SOUND_MIXER_IGAIN;
199                 break;
200                 case MS_SND_CARD_PLAYBACK:
201                         osscmd=SOUND_MIXER_PCM;
202                 break;
203                 default:
204                         ms_warning("oss_card_get_level: unsupported command.");
205                         return -1;
206         }
207         mix_fd = open(d->mixdev, O_RDONLY);
208         ioctl(mix_fd,MIXER_READ(osscmd), &p);
209         close(mix_fd);
210         return p>>8;
211 }
212
213 static void oss_set_source(MSSndCard *card, MSSndCardCapture source)
214 {
215         OssData *d=(OssData*)card->data;
216         int p=0;
217         int mix_fd;
218         if (d->mixdev==NULL) return;
219
220         switch(source){
221                 case MS_SND_CARD_MIC:
222                         p = 1 << SOUND_MIXER_MIC;
223                 break;
224                 case MS_SND_CARD_LINE:
225                         p = 1 << SOUND_MIXER_LINE;
226                 break;
227         }
228         
229         mix_fd = open(d->mixdev, O_WRONLY);
230         ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p);
231         close(mix_fd);
232 }
233
234 static void oss_init(MSSndCard *card){
235         OssData *d=ms_new(OssData,1);
236         d->pcmdev=NULL;
237         d->mixdev=NULL;
238         d->pcmfd=-1;
239         d->read_started=FALSE;
240         d->write_started=FALSE;
241         d->bits=16;
242         d->rate=8000;
243         d->stereo=FALSE;
244         qinit(&d->rq);
245         d->bufferizer=ms_bufferizer_new();
246         ms_mutex_init(&d->mutex,NULL);
247         card->data=d;
248 }
249
250 static void oss_uninit(MSSndCard *card){
251         OssData *d=(OssData*)card->data;
252         if (d->pcmdev!=NULL) ms_free(d->pcmdev);
253         if (d->mixdev!=NULL) ms_free(d->mixdev);
254         ms_bufferizer_destroy(d->bufferizer);
255         flushq(&d->rq,0);
256         ms_mutex_destroy(&d->mutex);
257         ms_free(d);
258 }
259
260 #define DSP_NAME "/dev/dsp"
261 #define MIXER_NAME "/dev/mixer"
262
263 static void oss_detect(MSSndCardManager *m);
264 static MSSndCard *oss_duplicate(MSSndCard *obj);
265
266 MSSndCardDesc oss_card_desc={
267         .driver_type="OSS",
268         .detect=oss_detect,
269         .init=oss_init,
270         .set_level=oss_set_level,
271         .get_level=oss_get_level,
272         .set_capture=oss_set_source,
273         .set_control=NULL,
274         .get_control=NULL,
275         .create_reader=ms_oss_read_new,
276         .create_writer=ms_oss_write_new,
277         .uninit=oss_uninit,
278         .duplicate=oss_duplicate
279 };
280
281 static MSSndCard *oss_duplicate(MSSndCard *obj){
282         MSSndCard *card=ms_snd_card_new(&oss_card_desc);
283         OssData *dcard=(OssData*)card->data;
284         OssData *dobj=(OssData*)obj->data;
285         dcard->pcmdev=ms_strdup(dobj->pcmdev);
286         dcard->mixdev=ms_strdup(dobj->mixdev);
287         card->name=ms_strdup(obj->name);
288         return card;
289 }
290
291 static MSSndCard *oss_card_new(const char *pcmdev, const char *mixdev){
292         MSSndCard *card=ms_snd_card_new(&oss_card_desc);
293         OssData *d=(OssData*)card->data;
294         d->pcmdev=ms_strdup(pcmdev);
295         d->mixdev=ms_strdup(mixdev);
296         card->name=ms_strdup(pcmdev);
297         return card;
298 }
299
300 static void oss_detect(MSSndCardManager *m){
301         int i;
302         char pcmdev[sizeof(DSP_NAME)+3];
303         char mixdev[sizeof(MIXER_NAME)+3];
304         if (access(DSP_NAME,F_OK)==0){
305                 MSSndCard *card=oss_card_new(DSP_NAME,MIXER_NAME);
306                 ms_snd_card_manager_add_card(m,card);
307         }
308         for(i=0;i<10;i++){
309                 snprintf(pcmdev,sizeof(pcmdev),"%s%i",DSP_NAME,i);
310                 snprintf(mixdev,sizeof(mixdev),"%s%i",MIXER_NAME,i);
311                 if (access(pcmdev,F_OK)==0){
312                   MSSndCard *card=oss_card_new(pcmdev,mixdev);
313                   ms_snd_card_manager_add_card(m,card);
314                 }
315         }
316 }
317
318 static void * oss_thread(void *p){
319         MSSndCard *card=(MSSndCard*)p;
320         OssData *d=(OssData*)card->data;
321         int bsize=0;
322         uint8_t *rtmpbuff=NULL;
323         uint8_t *wtmpbuff=NULL;
324         int err;
325         mblk_t *rm=NULL;
326         d->pcmfd=oss_open(d->pcmdev,d->bits,d->stereo,d->rate,&bsize);
327         if (d->pcmfd>=0){
328                 rtmpbuff=(uint8_t*)malloc(bsize);
329                 wtmpbuff=(uint8_t*)malloc(bsize);
330                 if(rtmpbuff == NULL || wtmpbuff == NULL) {
331                         free(rtmpbuff);
332                         free(wtmpbuff);
333                         return NULL;
334                 }
335         }
336         while(d->read_started || d->write_started){
337                 if (d->pcmfd>=0){
338                         if (d->read_started){
339                                 struct timeval timeout;
340                                 fd_set read_fds;
341                                 audio_buf_info info;
342                                 if (rm==NULL) rm=allocb(bsize,0);
343
344                                 timeout.tv_sec = 0;
345                                 timeout.tv_usec = 0;
346                                 FD_ZERO( &read_fds );
347                                 FD_SET( d->pcmfd, &read_fds );
348                                 if( select( d->pcmfd + 1, &read_fds, NULL, NULL, &timeout ) == -1 ) {
349                                 }
350                                 if (FD_ISSET( d->pcmfd, &read_fds ) &&  ioctl( d->pcmfd, SNDCTL_DSP_GETISPACE, &info ) != -1)
351                                 {
352                                         if (info.bytes>=bsize)
353                                         {
354                                                 err=read(d->pcmfd,rm->b_wptr,bsize);
355                                                 if (err<0){
356                                                         ms_warning("Fail to read %i bytes from soundcard: %s",
357                                                                    bsize,strerror(errno));
358                                                 }else{
359                                                         rm->b_wptr+=err;
360                                                         ms_mutex_lock(&d->mutex);
361                                                         putq(&d->rq,rm);
362                                                         ms_mutex_unlock(&d->mutex);
363                                                         rm=NULL;
364                                                 }
365                                         }
366                                         else
367                                           {
368                                             timeout.tv_sec = 0;
369                                             timeout.tv_usec = 5000;
370                                             select(0, 0, NULL, NULL, &timeout );
371                                           }
372                                 }
373                                 else
374                                   {
375                                     timeout.tv_sec = 0;
376                                     timeout.tv_usec = 5000;
377                                     select(0, 0, NULL, NULL, &timeout );
378                                   }
379                         }else {
380                                 int sz = read(d->pcmfd,rtmpbuff,bsize);
381                                 if( sz!=bsize) ms_warning("sound device read returned %i !",sz);
382                         }
383                         if (d->write_started){
384
385                                 audio_buf_info info;
386                                 if( ms_bufferizer_get_avail(d->bufferizer)>=bsize && ioctl( d->pcmfd, SNDCTL_DSP_GETOSPACE, &info ) == 0 ) {
387                                         if( info.fragstotal - info.fragments > 15 ) {
388                                                 static int c=0;
389                                                 /* drop the fragment if the buffer starts to fill up */
390                                                 /* we got too much data: I prefer to empty the incoming buffer */
391                                                 while (ms_bufferizer_get_avail(d->bufferizer)>bsize*4){
392                                                         ms_mutex_lock(&d->mutex);
393                                                         err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
394                                                         err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
395                                                         err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
396                                                         err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
397                                                         ms_mutex_unlock(&d->mutex);
398                                                         c=c+err*4;
399                                                         ms_warning("drop fragment when buffer gets too much data (%i - discarded:%i)", info.fragstotal - info.fragments, c);
400                                                         if (err==0)
401                                                           break;
402                                                 }
403
404                                         }else {
405                                                 ms_mutex_lock(&d->mutex);
406                                                 err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);
407                                                 ms_mutex_unlock(&d->mutex);
408                                                 err=write(d->pcmfd,wtmpbuff,bsize);
409                                                 if (err<0){
410                                                         ms_warning("Fail to write %i bytes from soundcard: %s",
411                                                                    bsize,strerror(errno));
412                                                 }
413                                         }
414                                 }
415
416                         }else {
417                                 int sz;
418                                 memset(wtmpbuff,0,bsize);
419                                 sz = write(d->pcmfd,wtmpbuff,bsize);
420                                 if( sz!=bsize) ms_warning("sound device write returned %i !",sz);
421                         }
422                 }else usleep(20000);
423         }
424         if (d->pcmfd>=0) {
425                 close(d->pcmfd);
426                 d->pcmfd=-1;
427         }
428         free(rtmpbuff);
429         free(wtmpbuff);
430         if (rm!=NULL) freemsg(rm);
431         /*reset to default parameters */
432         //d->bits=16;
433         //d->rate=8000;
434         //d->stereo=FALSE;
435         return NULL;
436 }
437
438 static void oss_start_r(MSSndCard *card){
439         OssData *d=(OssData*)card->data;
440         ms_mutex_lock(&d->mutex);
441         if (d->read_started==FALSE && d->write_started==FALSE){
442                 d->read_started=TRUE;
443                 ms_thread_create(&d->thread,NULL,oss_thread,card);
444         }else d->read_started=TRUE;
445         flushq(&d->rq,0);
446         ms_mutex_unlock(&d->mutex);
447 }
448
449 static void oss_stop_r(MSSndCard *card){
450         OssData *d=(OssData*)card->data;
451         d->read_started=FALSE;
452         if (d->write_started==FALSE){
453                 ms_thread_join(d->thread,NULL);
454         }
455 }
456
457 static void _flush_buffer(MSBufferizer *obj){
458         flushq(&obj->q,0);
459         obj->size=0;
460 }
461
462 static void oss_start_w(MSSndCard *card){
463         OssData *d=(OssData*)card->data;
464         ms_mutex_lock(&d->mutex);
465         if (d->read_started==FALSE && d->write_started==FALSE){
466                 d->write_started=TRUE;
467                 ms_thread_create(&d->thread,NULL,oss_thread,card);
468         }else{
469                 d->write_started=TRUE;
470         }
471         _flush_buffer(d->bufferizer);
472         ms_mutex_unlock(&d->mutex);
473 }
474
475 static void oss_stop_w(MSSndCard *card){
476         OssData *d=(OssData*)card->data;
477         d->write_started=FALSE;
478         if (d->read_started==FALSE){
479                 ms_thread_join(d->thread,NULL);
480         }
481 }
482
483 static mblk_t *oss_get(MSSndCard *card){
484         OssData *d=(OssData*)card->data;
485         mblk_t *m;
486         ms_mutex_lock(&d->mutex);
487         m=getq(&d->rq);
488         ms_mutex_unlock(&d->mutex);
489         return m;
490 }
491
492 static void oss_put(MSSndCard *card, mblk_t *m){
493         OssData *d=(OssData*)card->data;
494         ms_mutex_lock(&d->mutex);
495         ms_bufferizer_put(d->bufferizer,m);
496         ms_mutex_unlock(&d->mutex);
497 }
498
499
500 static void oss_read_preprocess(MSFilter *f){
501         MSSndCard *card=(MSSndCard*)f->data;
502         oss_start_r(card);
503 }
504
505 static void oss_read_postprocess(MSFilter *f){
506         MSSndCard *card=(MSSndCard*)f->data;
507         oss_stop_r(card);
508 }
509
510 static void oss_read_process(MSFilter *f){
511         MSSndCard *card=(MSSndCard*)f->data;
512         mblk_t *m;
513         while((m=oss_get(card))!=NULL){
514                 ms_queue_put(f->outputs[0],m);
515         }
516 }
517
518 static void oss_write_preprocess(MSFilter *f){
519         MSSndCard *card=(MSSndCard*)f->data;
520         oss_start_w(card);
521 }
522
523 static void oss_write_postprocess(MSFilter *f){
524         MSSndCard *card=(MSSndCard*)f->data;
525         oss_stop_w(card);
526 }
527
528 static void oss_write_process(MSFilter *f){
529         MSSndCard *card=(MSSndCard*)f->data;
530         mblk_t *m;
531         while((m=ms_queue_get(f->inputs[0]))!=NULL){
532                 oss_put(card,m);
533         }
534 }
535
536 static int get_rate(MSFilter *f, void *arg){
537         MSSndCard *card=(MSSndCard*)f->data;
538         OssData *d=(OssData*)card->data;
539         *((int*)arg)=d->rate;
540         return 0;
541 }
542
543 static int set_rate(MSFilter *f, void *arg){
544         MSSndCard *card=(MSSndCard*)f->data;
545         OssData *d=(OssData*)card->data;
546         d->rate=*((int*)arg);
547         return 0;
548 }
549
550 static int set_nchannels(MSFilter *f, void *arg){
551         MSSndCard *card=(MSSndCard*)f->data;
552         OssData *d=(OssData*)card->data;
553         d->stereo=(*((int*)arg)==2);
554         return 0;
555 }
556
557 static MSFilterMethod oss_methods[]={
558         {       MS_FILTER_GET_SAMPLE_RATE       , get_rate      },
559         {       MS_FILTER_SET_SAMPLE_RATE       , set_rate      },
560         {       MS_FILTER_SET_NCHANNELS         , set_nchannels },
561         {       0                               , NULL          }
562 };
563
564 MSFilterDesc oss_read_desc={
565         .id=MS_OSS_READ_ID,
566         .name="MSOssRead",
567         .text=N_("Sound capture filter for OSS drivers"),
568         .category=MS_FILTER_OTHER,
569         .ninputs=0,
570         .noutputs=1,
571         .preprocess=oss_read_preprocess,
572         .process=oss_read_process,
573         .postprocess=oss_read_postprocess,
574         .methods=oss_methods
575 };
576
577
578 MSFilterDesc oss_write_desc={
579         .id=MS_OSS_WRITE_ID,
580         .name="MSOssWrite",
581         .text=N_("Sound playback filter for OSS drivers"),
582         .category=MS_FILTER_OTHER,
583         .ninputs=1,
584         .noutputs=0,
585         .preprocess=oss_write_preprocess,
586         .process=oss_write_process,
587         .postprocess=oss_write_postprocess,
588         .methods=oss_methods
589 };
590
591 MSFilter *ms_oss_read_new(MSSndCard *card){
592         MSFilter *f=ms_filter_new_from_desc(&oss_read_desc);
593         f->data=card;
594         return f;
595 }
596
597
598 MSFilter *ms_oss_write_new(MSSndCard *card){
599         MSFilter *f=ms_filter_new_from_desc(&oss_write_desc);
600         f->data=card;
601         return f;
602 }
603
604 MS_FILTER_DESC_EXPORT(oss_read_desc)
605 MS_FILTER_DESC_EXPORT(oss_write_desc)