2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
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.
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.
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.
22 #include "mediastreamer-config.h"
25 #include "mediastreamer2/mssndcard.h"
26 #include "mediastreamer2/msfilter.h"
28 #include <sys/soundcard.h>
34 #include <sys/ioctl.h>
37 #ifdef HAVE_ALLOCA_H /*FreeBSD does not have alloca.h*/
41 MSFilter *ms_oss_read_new(MSSndCard *card);
42 MSFilter *ms_oss_write_new(MSSndCard *card);
44 static int oss_open(const char *devname, int bits,int stereo, int rate, int *minsz)
49 int min_size=0,blocksize=512;
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);
61 /* reset is maybe not needed but takes time*/
62 /*ioctl(fd, SNDCTL_DSP_RESET, 0); */
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));
73 err=ioctl(fd,SNDCTL_DSP_SETFMT,&p);
75 ms_warning("oss_open: can't set sample format:%s.",strerror(errno));
79 p = bits; /* 16 bits */
80 err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p);
82 ms_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno));
85 p = rate; /* rate in khz*/
86 err=ioctl(fd, SNDCTL_DSP_SPEED, &p);
88 ms_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno));
91 p = stereo; /* stereo or not */
92 err=ioctl(fd, SNDCTL_DSP_STEREO, &p);
94 ms_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno));
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);
101 /* try to subdivide BLKSIZE to reach blocksize if necessary */
102 if (min_size>blocksize)
105 p=min_size/blocksize;
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;
114 ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
115 if (min_size>blocksize)
117 ms_warning("dsp block size set to %i.",min_size);
119 /* no need to access the card with less latency than needed*/
123 ms_message("/dev/dsp opened: rate=%i,bits=%i,stereo=%i blocksize=%i.",
124 rate,bits,stereo,min_size);
126 if( ioctl( fd, SNDCTL_DSP_GETISPACE, &info ) == -1 ) {
127 ms_warning("oss_open: can't get ispace:%s.",strerror(errno));
130 ms_warning("oss_open: audio buffer size: %i.", info.fragsize * sizeof( short ));
134 /* start recording !!! Alex */
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));
146 typedef struct OssData{
155 MSBufferizer * bufferizer;
157 bool_t write_started;
161 static void oss_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent)
163 OssData *d=(OssData*)card->data;
166 if (d->mixdev==NULL) return;
168 case MS_SND_CARD_MASTER:
169 osscmd=SOUND_MIXER_VOLUME;
171 case MS_SND_CARD_CAPTURE:
172 osscmd=SOUND_MIXER_IGAIN;
174 case MS_SND_CARD_PLAYBACK:
175 osscmd=SOUND_MIXER_PCM;
178 ms_warning("oss_card_set_level: unsupported command.");
181 p=(((int)percent)<<8 | (int)percent);
182 mix_fd = open(d->mixdev, O_WRONLY);
183 ioctl(mix_fd,MIXER_WRITE(osscmd), &p);
187 static int oss_get_level(MSSndCard *card, MSSndCardMixerElem e)
189 OssData *d=(OssData*)card->data;
192 if (d->mixdev==NULL) return -1;
194 case MS_SND_CARD_MASTER:
195 osscmd=SOUND_MIXER_VOLUME;
197 case MS_SND_CARD_CAPTURE:
198 osscmd=SOUND_MIXER_IGAIN;
200 case MS_SND_CARD_PLAYBACK:
201 osscmd=SOUND_MIXER_PCM;
204 ms_warning("oss_card_get_level: unsupported command.");
207 mix_fd = open(d->mixdev, O_RDONLY);
208 ioctl(mix_fd,MIXER_READ(osscmd), &p);
213 static void oss_set_source(MSSndCard *card, MSSndCardCapture source)
215 OssData *d=(OssData*)card->data;
218 if (d->mixdev==NULL) return;
221 case MS_SND_CARD_MIC:
222 p = 1 << SOUND_MIXER_MIC;
224 case MS_SND_CARD_LINE:
225 p = 1 << SOUND_MIXER_LINE;
229 mix_fd = open(d->mixdev, O_WRONLY);
230 ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p);
234 static void oss_init(MSSndCard *card){
235 OssData *d=ms_new(OssData,1);
239 d->read_started=FALSE;
240 d->write_started=FALSE;
245 d->bufferizer=ms_bufferizer_new();
246 ms_mutex_init(&d->mutex,NULL);
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);
256 ms_mutex_destroy(&d->mutex);
260 #define DSP_NAME "/dev/dsp"
261 #define MIXER_NAME "/dev/mixer"
263 static void oss_detect(MSSndCardManager *m);
264 static MSSndCard *oss_duplicate(MSSndCard *obj);
266 MSSndCardDesc oss_card_desc={
270 .set_level=oss_set_level,
271 .get_level=oss_get_level,
272 .set_capture=oss_set_source,
275 .create_reader=ms_oss_read_new,
276 .create_writer=ms_oss_write_new,
278 .duplicate=oss_duplicate
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);
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);
300 static void oss_detect(MSSndCardManager *m){
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);
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);
318 static void * oss_thread(void *p){
319 MSSndCard *card=(MSSndCard*)p;
320 OssData *d=(OssData*)card->data;
322 uint8_t *rtmpbuff=NULL;
323 uint8_t *wtmpbuff=NULL;
326 d->pcmfd=oss_open(d->pcmdev,d->bits,d->stereo,d->rate,&bsize);
328 rtmpbuff=(uint8_t*)malloc(bsize);
329 wtmpbuff=(uint8_t*)malloc(bsize);
330 if(rtmpbuff == NULL || wtmpbuff == NULL) {
336 while(d->read_started || d->write_started){
338 if (d->read_started){
339 struct timeval timeout;
342 if (rm==NULL) rm=allocb(bsize,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 ) {
350 if (FD_ISSET( d->pcmfd, &read_fds ) && ioctl( d->pcmfd, SNDCTL_DSP_GETISPACE, &info ) != -1)
352 if (info.bytes>=bsize)
354 err=read(d->pcmfd,rm->b_wptr,bsize);
356 ms_warning("Fail to read %i bytes from soundcard: %s",
357 bsize,strerror(errno));
360 ms_mutex_lock(&d->mutex);
362 ms_mutex_unlock(&d->mutex);
369 timeout.tv_usec = 5000;
370 select(0, 0, NULL, NULL, &timeout );
376 timeout.tv_usec = 5000;
377 select(0, 0, NULL, NULL, &timeout );
380 int sz = read(d->pcmfd,rtmpbuff,bsize);
381 if( sz!=bsize) ms_warning("sound device read returned %i !",sz);
383 if (d->write_started){
386 if( ms_bufferizer_get_avail(d->bufferizer)>=bsize && ioctl( d->pcmfd, SNDCTL_DSP_GETOSPACE, &info ) == 0 ) {
387 if( info.fragstotal - info.fragments > 15 ) {
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);
399 ms_warning("drop fragment when buffer gets too much data (%i - discarded:%i)", info.fragstotal - info.fragments, c);
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);
410 ms_warning("Fail to write %i bytes from soundcard: %s",
411 bsize,strerror(errno));
418 memset(wtmpbuff,0,bsize);
419 sz = write(d->pcmfd,wtmpbuff,bsize);
420 if( sz!=bsize) ms_warning("sound device write returned %i !",sz);
430 if (rm!=NULL) freemsg(rm);
431 /*reset to default parameters */
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;
446 ms_mutex_unlock(&d->mutex);
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);
457 static void _flush_buffer(MSBufferizer *obj){
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);
469 d->write_started=TRUE;
471 _flush_buffer(d->bufferizer);
472 ms_mutex_unlock(&d->mutex);
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);
483 static mblk_t *oss_get(MSSndCard *card){
484 OssData *d=(OssData*)card->data;
486 ms_mutex_lock(&d->mutex);
488 ms_mutex_unlock(&d->mutex);
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);
500 static void oss_read_preprocess(MSFilter *f){
501 MSSndCard *card=(MSSndCard*)f->data;
505 static void oss_read_postprocess(MSFilter *f){
506 MSSndCard *card=(MSSndCard*)f->data;
510 static void oss_read_process(MSFilter *f){
511 MSSndCard *card=(MSSndCard*)f->data;
513 while((m=oss_get(card))!=NULL){
514 ms_queue_put(f->outputs[0],m);
518 static void oss_write_preprocess(MSFilter *f){
519 MSSndCard *card=(MSSndCard*)f->data;
523 static void oss_write_postprocess(MSFilter *f){
524 MSSndCard *card=(MSSndCard*)f->data;
528 static void oss_write_process(MSFilter *f){
529 MSSndCard *card=(MSSndCard*)f->data;
531 while((m=ms_queue_get(f->inputs[0]))!=NULL){
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;
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);
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);
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 },
564 MSFilterDesc oss_read_desc={
567 .text=N_("Sound capture filter for OSS drivers"),
568 .category=MS_FILTER_OTHER,
571 .preprocess=oss_read_preprocess,
572 .process=oss_read_process,
573 .postprocess=oss_read_postprocess,
578 MSFilterDesc oss_write_desc={
581 .text=N_("Sound playback filter for OSS drivers"),
582 .category=MS_FILTER_OTHER,
585 .preprocess=oss_write_preprocess,
586 .process=oss_write_process,
587 .postprocess=oss_write_postprocess,
591 MSFilter *ms_oss_read_new(MSSndCard *card){
592 MSFilter *f=ms_filter_new_from_desc(&oss_read_desc);
598 MSFilter *ms_oss_write_new(MSSndCard *card){
599 MSFilter *f=ms_filter_new_from_desc(&oss_write_desc);
604 MS_FILTER_DESC_EXPORT(oss_read_desc)
605 MS_FILTER_DESC_EXPORT(oss_write_desc)