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