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