⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 oss.c

📁 linphone 网络电话 linphone 网络电话 linphone 网络电话
💻 C
字号:
/*mediastreamer2 library - modular sound and video processing and streamingCopyright (C) 2006  Simon MORLAT (simon.morlat@linphone.org)This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.*/#include "mediastreamer2/mssndcard.h"#include "mediastreamer2/msfilter.h"#include <sys/soundcard.h>#include <errno.h>#include <assert.h>#include <fcntl.h>#include <sys/time.h>#include <sys/ioctl.h>#include <unistd.h>#include <alloca.h>MSFilter *ms_oss_read_new(MSSndCard *card);MSFilter *ms_oss_write_new(MSSndCard *card);static int oss_open(const char *devname, int bits,int stereo, int rate, int *minsz){	int fd;	int p=0,cond=0;	int i=0;	int min_size=0,blocksize=512;	int err;	int frag;	audio_buf_info info;  	//g_message("opening sound device");	fd=open(devname,O_RDWR|O_NONBLOCK);	if (fd<0) return -EWOULDBLOCK;	/* unset nonblocking mode */	/* We wanted non blocking open but now put it back to normal ; thanks Xine !*/	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK);	/* reset is maybe not needed but takes time*/	/*ioctl(fd, SNDCTL_DSP_RESET, 0); */	/* This code is used to limit the internal buffer of the sound	   card so that no internal delay can occur in the sound card */	frag = ( ( 32767 << 16 ) | 7 );	if( ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &frag ) ) {		ms_warning("oss_open: can't set fragment size:%s.",strerror(errno));	}		p=AFMT_S16_NE;		err=ioctl(fd,SNDCTL_DSP_SETFMT,&p);	if (err<0){		ms_warning("oss_open: can't set sample format:%s.",strerror(errno));	}		p =  bits;  /* 16 bits */	err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p);	if (err<0){		ms_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno));	}	p =  rate;  /* rate in khz*/	err=ioctl(fd, SNDCTL_DSP_SPEED, &p);	if (err<0){		ms_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno));	}		p =  stereo;  /* stereo or not */	err=ioctl(fd, SNDCTL_DSP_STEREO, &p);	if (err<0){		ms_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno));	}		if (rate==16000) blocksize=4096;	/* oss emulation is not very good at 16khz */	else blocksize=blocksize*(rate/8000);	ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);	/* try to subdivide BLKSIZE to reach blocksize if necessary */	if (min_size>blocksize)	{		cond=1;		p=min_size/blocksize;		while(cond)		{			i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p);			//printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno);			if ((i==0) || (p==1)) cond=0;			else p=p/2;		}	}	ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);	if (min_size>blocksize)	{		ms_warning("dsp block size set to %i.",min_size);	}else{		/* no need to access the card with less latency than needed*/		min_size=blocksize;	}	ms_message("/dev/dsp opened: rate=%i,bits=%i,stereo=%i blocksize=%i.",			rate,bits,stereo,min_size);		if( ioctl( fd, SNDCTL_DSP_GETISPACE, &info ) == -1 ) {		ms_warning("oss_open: can't get ispace:%s.",strerror(errno));	}	else{		ms_warning("oss_open: audio buffer size: %i.", info.fragsize * sizeof( short ));	}	/* start recording !!! Alex */	{		int fl,res;				fl=PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT;		res=ioctl(fd, SNDCTL_DSP_SETTRIGGER, &fl);		if (res<0) ms_warning("OSS_TRIGGER: %s",strerror(errno));	} 	*minsz=min_size;	return fd;}typedef struct OssData{	char *pcmdev;	char *mixdev;	int pcmfd;	int rate;	int bits;	ms_thread_t thread;	ms_mutex_t mutex;	queue_t rq;	MSBufferizer * bufferizer;	bool_t read_started;	bool_t write_started;	bool_t stereo;} OssData;static void oss_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent){	OssData *d=(OssData*)card->data;	int p,mix_fd;	int osscmd;	if (d->mixdev==NULL) return;	switch(e){		case MS_SND_CARD_MASTER:			osscmd=SOUND_MIXER_VOLUME;		break;		case MS_SND_CARD_CAPTURE:			osscmd=SOUND_MIXER_IGAIN;		break;		case MS_SND_CARD_PLAYBACK:			osscmd=SOUND_MIXER_PCM;		break;		default:			ms_warning("oss_card_set_level: unsupported command.");			return;	}	p=(((int)percent)<<8 | (int)percent);	mix_fd = open(d->mixdev, O_WRONLY);	ioctl(mix_fd,MIXER_WRITE(osscmd), &p);	close(mix_fd);}static int oss_get_level(MSSndCard *card, MSSndCardMixerElem e){	OssData *d=(OssData*)card->data;	int p=0,mix_fd;	int osscmd;	if (d->mixdev==NULL) return -1;	switch(e){		case MS_SND_CARD_MASTER:			osscmd=SOUND_MIXER_VOLUME;		break;		case MS_SND_CARD_CAPTURE:			osscmd=SOUND_MIXER_IGAIN;		break;		case MS_SND_CARD_PLAYBACK:			osscmd=SOUND_MIXER_PCM;		break;		default:			ms_warning("oss_card_get_level: unsupported command.");			return -1;	}	mix_fd = open(d->mixdev, O_RDONLY);	ioctl(mix_fd,MIXER_READ(osscmd), &p);	close(mix_fd);	return p>>8;}static void oss_set_source(MSSndCard *card, MSSndCardCapture source){	OssData *d=(OssData*)card->data;	int p=0;	int mix_fd;	if (d->mixdev==NULL) return;	switch(source){		case MS_SND_CARD_MIC:			p = 1 << SOUND_MIXER_MIC;		break;		case MS_SND_CARD_LINE:			p = 1 << SOUND_MIXER_LINE;		break;	}		mix_fd = open(d->mixdev, O_WRONLY);	ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p);	close(mix_fd);}static void oss_init(MSSndCard *card){	OssData *d=ms_new(OssData,1);	d->pcmdev=NULL;	d->mixdev=NULL;	d->pcmfd=-1;	d->read_started=FALSE;	d->write_started=FALSE;	d->bits=16;	d->rate=8000;	d->stereo=FALSE;	qinit(&d->rq);	d->bufferizer=ms_bufferizer_new();	ms_mutex_init(&d->mutex,NULL);	card->data=d;}static void oss_uninit(MSSndCard *card){	OssData *d=(OssData*)card->data;	if (d->pcmdev!=NULL) ms_free(d->pcmdev);	if (d->mixdev!=NULL) ms_free(d->mixdev);	ms_bufferizer_destroy(d->bufferizer);	flushq(&d->rq,0);	ms_mutex_destroy(&d->mutex);	ms_free(d);}#define DSP_NAME "/dev/dsp"#define MIXER_NAME "/dev/mixer"static void oss_detect(MSSndCardManager *m);static MSSndCard *oss_duplicate(MSSndCard *obj);MSSndCardDesc oss_card_desc={	.driver_type="OSS",	.detect=oss_detect,	.init=oss_init,	.set_level=oss_set_level,	.get_level=oss_get_level,	.set_capture=oss_set_source,	.create_reader=ms_oss_read_new,	.create_writer=ms_oss_write_new,	.uninit=oss_uninit,	.duplicate=oss_duplicate};static MSSndCard *oss_duplicate(MSSndCard *obj){	MSSndCard *card=ms_snd_card_new(&oss_card_desc);	OssData *dcard=(OssData*)card->data;	OssData *dobj=(OssData*)obj->data;	dcard->pcmdev=ms_strdup(dobj->pcmdev);	dcard->mixdev=ms_strdup(dobj->mixdev);	card->name=ms_strdup(obj->name);	return card;}static MSSndCard *oss_card_new(const char *pcmdev, const char *mixdev){	MSSndCard *card=ms_snd_card_new(&oss_card_desc);	OssData *d=(OssData*)card->data;	d->pcmdev=ms_strdup(pcmdev);	d->mixdev=ms_strdup(mixdev);	card->name=ms_strdup(pcmdev);	return card;}static void oss_detect(MSSndCardManager *m){	int i;	char pcmdev[sizeof(DSP_NAME)+3];	char mixdev[sizeof(MIXER_NAME)+3];	if (access(DSP_NAME,F_OK)==0){		MSSndCard *card=oss_card_new(DSP_NAME,MIXER_NAME);		ms_snd_card_manager_add_card(m,card);		card=oss_card_new(DSP_NAME,MIXER_NAME);		ms_snd_card_manager_add_card(m,card);	}	for(i=0;i<10;i++){		snprintf(pcmdev,sizeof(pcmdev),"%s%i",DSP_NAME,i);		snprintf(mixdev,sizeof(mixdev),"%s%i",MIXER_NAME,i);		if (access(pcmdev,F_OK)==0){		  MSSndCard *card=oss_card_new(pcmdev,mixdev);		  ms_snd_card_manager_add_card(m,card);		}	}}static void * oss_thread(void *p){	MSSndCard *card=(MSSndCard*)p;	OssData *d=(OssData*)card->data;	int bsize=0;	uint8_t *rtmpbuff=NULL;	uint8_t *wtmpbuff=NULL;	int err;	mblk_t *rm=NULL;	d->pcmfd=oss_open(d->pcmdev,d->bits,d->stereo,d->rate,&bsize);	if (d->pcmfd>=0){		rtmpbuff=(uint8_t*)malloc(bsize);		wtmpbuff=(uint8_t*)malloc(bsize);		if(rtmpbuff == NULL || wtmpbuff == NULL) {			free(rtmpbuff);			free(wtmpbuff);			return NULL;		}	}	while(d->read_started || d->write_started){		if (d->pcmfd>=0){			if (d->read_started){				struct timeval timeout;				fd_set read_fds;				audio_buf_info info;				if (rm==NULL) rm=allocb(bsize,0);				timeout.tv_sec = 0;				timeout.tv_usec = 0;				FD_ZERO( &read_fds );				FD_SET( d->pcmfd, &read_fds );				if( select( d->pcmfd + 1, &read_fds, NULL, NULL, &timeout ) == -1 ) {				}				if (FD_ISSET( d->pcmfd, &read_fds ) &&  ioctl( d->pcmfd, SNDCTL_DSP_GETISPACE, &info ) != -1)				{					if (info.bytes>=bsize)					{						err=read(d->pcmfd,rm->b_wptr,bsize);						if (err<0){							ms_warning("Fail to read %i bytes from soundcard: %s",								   bsize,strerror(errno));						}else{							rm->b_wptr+=err;							ms_mutex_lock(&d->mutex);							putq(&d->rq,rm);							ms_mutex_unlock(&d->mutex);							rm=NULL;						}					}					else					  {					    timeout.tv_sec = 0;					    timeout.tv_usec = 5000;					    select(0, 0, NULL, NULL, &timeout );					  }				}				else				  {				    timeout.tv_sec = 0;				    timeout.tv_usec = 5000;				    select(0, 0, NULL, NULL, &timeout );				  }			}else {				int sz = read(d->pcmfd,rtmpbuff,bsize);				if( sz!=bsize) ms_warning("sound device read returned %i !",sz);			}			if (d->write_started){				audio_buf_info info;				if( ms_bufferizer_get_avail(d->bufferizer)>=bsize && ioctl( d->pcmfd, SNDCTL_DSP_GETOSPACE, &info ) == 0 ) {					if( info.fragstotal - info.fragments > 15 ) {						static int c=0;						/* drop the fragment if the buffer starts to fill up */						/* we got too much data: I prefer to empty the incoming buffer */						while (ms_bufferizer_get_avail(d->bufferizer)>bsize*4){							ms_mutex_lock(&d->mutex);							err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);							err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);							err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);							err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);							ms_mutex_unlock(&d->mutex);							c=c+err*4;							ms_warning("drop fragment when buffer gets too much data (%i - discarded:%i)", info.fragstotal - info.fragments, c);							if (err==0)							  break;						}					}else {						ms_mutex_lock(&d->mutex);						err=ms_bufferizer_read(d->bufferizer,wtmpbuff,bsize);						ms_mutex_unlock(&d->mutex);						err=write(d->pcmfd,wtmpbuff,bsize);						if (err<0){							ms_warning("Fail to write %i bytes from soundcard: %s",								   bsize,strerror(errno));						}					}				}			}else {				int sz;				memset(wtmpbuff,0,bsize);				sz = write(d->pcmfd,wtmpbuff,bsize);				if( sz!=bsize) ms_warning("sound device write returned %i !",sz);			}		}else usleep(20000);	}	if (d->pcmfd>=0) {		close(d->pcmfd);		d->pcmfd=-1;	}	free(rtmpbuff);	free(wtmpbuff);	if (rm!=NULL) freemsg(rm);	/*reset to default parameters */	d->bits=16;	d->rate=8000;	d->stereo=FALSE;	return NULL;}static void oss_start_r(MSSndCard *card){	OssData *d=(OssData*)card->data;	if (d->read_started==FALSE && d->write_started==FALSE){		d->read_started=TRUE;		ms_thread_create(&d->thread,NULL,oss_thread,card);	}else d->read_started=TRUE;}static void oss_stop_r(MSSndCard *card){	OssData *d=(OssData*)card->data;	d->read_started=FALSE;	if (d->write_started==FALSE){		ms_thread_join(d->thread,NULL);	}}static void oss_start_w(MSSndCard *card){	OssData *d=(OssData*)card->data;	if (d->read_started==FALSE && d->write_started==FALSE){		d->write_started=TRUE;		ms_thread_create(&d->thread,NULL,oss_thread,card);	}else{		d->write_started=TRUE;	}}static void oss_stop_w(MSSndCard *card){	OssData *d=(OssData*)card->data;	d->write_started=FALSE;	if (d->read_started==FALSE){		ms_thread_join(d->thread,NULL);	}}static mblk_t *oss_get(MSSndCard *card){	OssData *d=(OssData*)card->data;	mblk_t *m;	ms_mutex_lock(&d->mutex);	m=getq(&d->rq);	ms_mutex_unlock(&d->mutex);	return m;}static void oss_put(MSSndCard *card, mblk_t *m){	OssData *d=(OssData*)card->data;	ms_mutex_lock(&d->mutex);	ms_bufferizer_put(d->bufferizer,m);	ms_mutex_unlock(&d->mutex);}static void oss_read_preprocess(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	oss_start_r(card);}static void oss_read_postprocess(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	oss_stop_r(card);}static void oss_read_process(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	mblk_t *m;	while((m=oss_get(card))!=NULL){		ms_queue_put(f->outputs[0],m);	}}static void oss_write_preprocess(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	oss_start_w(card);}static void oss_write_postprocess(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	oss_stop_w(card);}static void oss_write_process(MSFilter *f){	MSSndCard *card=(MSSndCard*)f->data;	mblk_t *m;	while((m=ms_queue_get(f->inputs[0]))!=NULL){		oss_put(card,m);	}}static int set_rate(MSFilter *f, void *arg){	MSSndCard *card=(MSSndCard*)f->data;	OssData *d=(OssData*)card->data;	d->rate=*((int*)arg);	return 0;}static int set_nchannels(MSFilter *f, void *arg){	MSSndCard *card=(MSSndCard*)f->data;	OssData *d=(OssData*)card->data;	d->stereo=(*((int*)arg)==2);	return 0;}static MSFilterMethod oss_methods[]={	{	MS_FILTER_SET_SAMPLE_RATE	, set_rate	},	{	MS_FILTER_SET_NCHANNELS		, set_nchannels	},	{	0				, NULL		}};MSFilterDesc oss_read_desc={	.id=MS_OSS_READ_ID,	.name="MSOssRead",	.text="Sound capture filter for OSS drivers",	.category=MS_FILTER_OTHER,	.ninputs=0,	.noutputs=1,	.preprocess=oss_read_preprocess,	.process=oss_read_process,	.postprocess=oss_read_postprocess,	.methods=oss_methods};MSFilterDesc oss_write_desc={	.id=MS_OSS_WRITE_ID,	.name="MSOssWrite",	.text="Sound playback filter for OSS drivers",	.category=MS_FILTER_OTHER,	.ninputs=1,	.noutputs=0,	.preprocess=oss_write_preprocess,	.process=oss_write_process,	.postprocess=oss_write_postprocess,	.methods=oss_methods};MSFilter *ms_oss_read_new(MSSndCard *card){	MSFilter *f=ms_filter_new_from_desc(&oss_read_desc);	f->data=card;	return f;}MSFilter *ms_oss_write_new(MSSndCard *card){	MSFilter *f=ms_filter_new_from_desc(&oss_write_desc);	f->data=card;	return f;}MS_FILTER_DESC_EXPORT(oss_read_desc)MS_FILTER_DESC_EXPORT(oss_write_desc)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -