source: mediastreamer2/src/oss.c @ 1291:184b41c9bdbc

Last change on this file since 1291:184b41c9bdbc was 1291:184b41c9bdbc, checked in by Simon Morlat <simon.morlat@…>, 2 years ago

fix bug in closing fd in oss.c

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