📄 audiolib.c
字号:
/* Is audio task still ok ? */ if(shmemptr->audio_status < 0) { audio_errno = AUDIO_ERR_ATASK; return -1; } if(audio_capt) { audio_errno = AUDIO_ERR_MODE; return -1; } update_output_status(); /* If the number of bytes we got isn't big enough to fill the next buffer, copy buf into audio_left_buf */ if (audio_bytes_left+size < audio_buffer_size) { memcpy(audio_left_buf+audio_bytes_left,buf,size); audio_bytes_left += size; return size; } nb = 0; /* if audio_left_buf contains something, output that first */ if (audio_bytes_left) { memcpy(audio_left_buf+audio_bytes_left,buf,audio_buffer_size-audio_bytes_left); if(shmemptr->used_flag[NBUF(n_audio)]) { audio_errno = AUDIO_ERR_BOVFL; return -1; } if(swap && audio_size==16) swpcpy((void*)shmemptr->audio_data[NBUF(n_audio)],audio_left_buf,audio_buffer_size); else memcpy((void*)shmemptr->audio_data[NBUF(n_audio)],audio_left_buf,audio_buffer_size); shmemptr->used_flag[NBUF(n_audio)] = 1; nb = audio_buffer_size-audio_bytes_left; audio_bytes_left = 0; n_audio++; } /* copy directly to the shmem buffers */ while(size-nb >= audio_buffer_size) { if(shmemptr->used_flag[NBUF(n_audio)]) { audio_errno = AUDIO_ERR_BOVFL; return -1; } if(swap && audio_size==16) swpcpy((void*)shmemptr->audio_data[NBUF(n_audio)],(void*)(buf+nb),audio_buffer_size); else memcpy((void*)shmemptr->audio_data[NBUF(n_audio)],(void*)(buf+nb),audio_buffer_size); shmemptr->used_flag[NBUF(n_audio)] = 1; nb += audio_buffer_size; n_audio++; } /* copy the remainder into audio_left_buf */ if(nb<size) { audio_bytes_left = size-nb; memcpy(audio_left_buf,buf+nb,audio_bytes_left); } return size;}/* * The audio task */#ifdef HAVE_SYS_SOUNDCARD_Hstatic void system_error(const char *str, int fd, int use_strerror){ if(use_strerror) sprintf((char*)shmemptr->error_string, "Error %s - %s",str,strerror(errno)); else sprintf((char*)shmemptr->error_string, "Error %s",str); shmemptr->audio_status = -1; if( fd >= 0 ) close(fd);#ifdef FORK_NOT_THREAD exit(1);#else pthread_exit(NULL);#endif}#endif /* HAVE_SYS_SOUNDCARD_H */#ifdef HAVE_SYS_SOUNDCARD_Hvoid do_audio(void){ int fd = -1; int tmp, ret, caps, afmt, frag; int nbdone, nbque, ndiff, nbpend, nbset, maxdiff; uint8_t *buf = NULL; fd_set selectset; struct count_info count; struct audio_buf_info info; struct timeval tv; const char *audio_dev_name;#ifndef FORK_NOT_THREAD struct sched_param schedparam; sigset_t blocked_signals; /* Set the capture thread in a reasonable state - cancellation enabled and asynchronous, SIGINT's ignored... */ /* PTHREAD_CANCEL_ASYNCHRONOUS is evil. *//* if( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL) ) { system_error( "Bad pthread_setcancelstate", fd, 0 ); } if( pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL) ) { system_error( "Bad pthread_setcanceltype", fd, 0 ); }*/ sigaddset( &blocked_signals, SIGINT ); if( pthread_sigmask( SIG_BLOCK, &blocked_signals, NULL )) { system_error( "Bad pthread_sigmask", fd, 0 ); }#endif /* * Fragment size and max possible number of frags */ switch (audio_buffer_size) { case 8192: frag = 0x7fff000d; break; case 4096: frag = 0x7fff000c; break; case 2048: frag = 0x7fff000b; break; case 1024: frag = 0x7fff000a; break; default: system_error("Audio internal error - audio_buffer_size",fd,0); } /* if somebody plays with BUFFSIZE without knowing what he does ... */ if (audio_buffer_size>BUFFSIZE) system_error("Audio internal error audio_buffer_size > BUFFSIZE",fd,0);/* * Open Audio device, set number of frags wanted */ audio_dev_name = getenv("LAV_AUDIO_DEV"); if(!audio_dev_name) audio_dev_name = "/dev/dsp"; if(audio_capt) fd=open(audio_dev_name, O_RDONLY, 0); else fd=open(audio_dev_name, O_RDWR, 0); if (fd<0) system_error(audio_dev_name,fd,1); ret = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); if(ret<0) system_error("in ioctl SNDCTL_DSP_SETFRAGMENT", fd, 1);/* * Setup sampling parameters. */ afmt = (audio_size==16) ? AFMT_S16_LE : AFMT_U8; tmp = afmt; ret = ioctl(fd, SNDCTL_DSP_SETFMT, &tmp); if(ret<0 || tmp!=afmt) system_error("setting sound format",fd,0); tmp = stereo; /* 0=mono, 1=stereo */ ret = ioctl(fd, SNDCTL_DSP_STEREO, &tmp); if(ret<0 || tmp!=stereo) system_error("setting mono/stereo",fd,0); tmp = audio_rate; ret = ioctl(fd, SNDCTL_DSP_SPEED, &tmp); if(ret<0) { system_error("setting sound rate",fd,0); } else if(tmp != audio_rate) { mjpeg_warn("Sound card told us it's using rate %dHz instead of %dHz", tmp, audio_rate); }/* Calculate number of bytes corresponding to TIME_STAMP_TOL */ maxdiff = audio_byte_rate / (1000000/TIME_STAMP_TOL);/* * Check that the device has capability to do mmap and trigger */ ret = ioctl(fd, SNDCTL_DSP_GETCAPS, &caps); if(ret<0) system_error("getting audio device capabilities",fd,1); if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) system_error("Soundcard cant do mmap or trigger",fd,0);/* * Get the size of the input/output buffer and do the mmap */ if (audio_capt) ret = ioctl(fd, SNDCTL_DSP_GETISPACE, &info); else ret = ioctl(fd, SNDCTL_DSP_GETOSPACE, &info); if(ret<0) system_error("in ioctl SNDCTL_DSP_GET[IO]SPACE",fd,1); if (info.fragsize != audio_buffer_size) system_error("Soundcard fragment size unexpected",fd,0);/* * Original comment: * Normally we should get at least 8 fragments (if we use 8KB buffers) * or even more if we use 4KB od 2 KB buffers * We consider 4 fragments as the absolut minimum here! * * A.Stevens Jul 2000: I'm a bit puzzled by the above. A 4096 byte * buffer takes 1/20th second to fill at 44100 stereo. So provide we * empty one frag in less than this we should be o.k. hardly onerous. * Presumably the problem was that this code wasn't running real-time * and so could get starved on a load system. * Anyway, insisting on 8 frags of 8192 bytes puts us sure out of luck * drivers for quite a few modern PCI soundcards ... so lets try for 2 * and see what real-time scheduling can do! */ if (info.fragstotal < 2) { system_error("Could not get enough audio buffer fragments",fd,0); } tmp = info.fragstotal*info.fragsize; if( !audio_capt || mmap_capt ) { if (audio_capt) buf=mmap(NULL, tmp, PROT_READ , MAP_SHARED, fd, 0); else buf=mmap(NULL, tmp, PROT_WRITE, MAP_SHARED, fd, 0); if (buf==MAP_FAILED) system_error("mapping audio buffer",fd, 1); /* * Put device into hold */ tmp = 0; ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp); if(ret<0) system_error("in ioctl SNDCTL_DSP_SETTRIGGER",fd,1); }/* * Signal the parent that initialization is done */ shmemptr->audio_status = 1;/* * nbdone is the number of buffers processed by the audio driver * so far (ie. the number of buffers read or written) * nbque is the number of buffers which have been queued so far * for playing (not used in audio capture) * nbset Number of buffers set (with real data or 0s) * * If we do playback: Wait until the first buffer arrives */ nbdone = 0; nbque = 0; nbset = 0; if(!audio_capt) { while(!shmemptr->audio_start) { usleep(10000); if(shmemptr->exit_flag) {#ifndef FORK_NOT_THREAD exit(0);#else pthread_exit(NULL);#endif } } /* Copy as many buffers as are allready here */ for(nbque=0;nbque<info.fragstotal;nbque++) { if(!shmemptr->used_flag[NBUF(nbque)]) break; memcpy(buf+nbque*info.fragsize, (void*) shmemptr->audio_data[NBUF(nbque)], info.fragsize); /* Mark the buffer as free */ shmemptr->used_flag[NBUF(nbque)] = 0; } for(nbset=nbque;nbset<info.fragstotal;nbset++) memset(buf+nbset*info.fragsize,0,info.fragsize); }#ifndef FORK_NOT_THREAD /* Now we're ready to go move to Real-time scheduling... */ schedparam.sched_priority = 1; if(setpriority(PRIO_PROCESS, 0, -20)) { /* Give myself maximum priority */ mjpeg_warn("Unable to set negative priority for audio thread."); } if( (ret = pthread_setschedparam( pthread_self(), SCHED_FIFO, &schedparam ) ) ) { mjpeg_warn("Pthread Real-time scheduling for audio thread could not be enabled."); }#endif/* * Fire up audio device for mmap capture playback (not necessary for read) */ if( !audio_capt || mmap_capt ) { if(audio_capt) tmp = PCM_ENABLE_INPUT; else tmp = PCM_ENABLE_OUTPUT; ret = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &tmp); if(ret<0) system_error("in ioctl SNDCTL_DSP_SETTRIGGER",fd,1); } /* The recording/playback loop */ while(1) { /* Wait until new audio data can be read/written */ if( !audio_capt || mmap_capt ) { FD_ZERO(&selectset); FD_SET(fd, &selectset); retry: if(audio_capt) ret = select(fd+1, &selectset, NULL, NULL, NULL); else ret = select(fd+1, NULL, &selectset, NULL, NULL); if(ret<0) { if (errno == EINTR) goto retry; else system_error("waiting on audio with select",fd,1); } } else { if( read(fd, (void *)shmemptr->audio_data[NBUF(nbdone)], info.fragsize ) != info.fragsize ) { system_error( "Sound driver returned partial fragment!\n", fd,1 ); } } /* Get time - this time is after at least one buffer has been recorded/played (because select did return), and before the the audio status obtained by the following ioctl */ gettimeofday(&tv,NULL); /* Get the status of the sound buffer */ usleep(1000); if(audio_capt) ret = ioctl(fd, SNDCTL_DSP_GETIPTR, &count); else ret = ioctl(fd, SNDCTL_DSP_GETOPTR, &count); if (ret<0) system_error("in ioctl SNDCTL_DSP_GET[IO]PTR",fd,1); /* Get the difference of minimum number of bytes after the select call and bytes actually present - this gives us a measure of accuracy of the time in tv. TODO Note: count.bytes can overflow in extreme situations (more than 3 hrs recording with 44.1 KHz, 16bit stereo), ndiff should be calculated correctly. */ ndiff = count.bytes - audio_buffer_size*(nbdone+1); /* Uncomment this and run testrec if you're getting audio capture problems... */ /* mjpeg_info( "CB=%08d ND=%06d BL=%03d NB=%d", count.bytes, ndiff, count.blocks, NBUF(nbdone) ); */ if(ndiff>maxdiff) { tv.tv_sec = tv.tv_usec = 0; } else { /* Adjust timestamp to take into account delay between sync and now indicated by ndiff */ tv.tv_usec -= ndiff * 1000000 / audio_byte_rate; if( tv.tv_usec < 0 ) { tv.tv_usec += 1000000; tv.tv_sec -= 1; } } if(audio_capt) { /* if exit_flag is set, exit immediatly */ if(shmemptr->exit_flag) { shmemptr->audio_status = -1; close(fd);#ifdef FORK_NOT_THREAD exit(0);#else pthread_exit( NULL );#endif } /* copy the ready buffers to our audio ring buffer */ if( mmap_capt ) nbpend = count.blocks; else nbpend = 1; while(nbpend) { /* Check if buffer nbdone in the ring buffer is free */ if(shmemptr->used_flag[NBUF(nbdone)]) system_error("Audio ring buffer overflow",fd,0); if( mmap_capt ) memcpy((void*) shmemptr->audio_data[NBUF(nbdone)], buf+(nbdone%info.fragstotal)*info.fragsize, info.fragsize); /* Get the status of the sound buffer after copy, this permits us to see if an overrun occured */ ret = ioctl(fd, SNDCTL_DSP_GETIPTR, &count); if(ret<0) system_error("in ioctl SNDCTL_DSP_GETIPTR",fd,1); if( mmap_capt ) nbpend += count.blocks; /* if nbpend >= total frags, a overrun most probably occured */ shmemptr->status[NBUF(nbdone)] = (nbpend >= info.fragstotal) ? -1 : 1; shmemptr->tmstmp[NBUF(nbdone)] = tv; shmemptr->used_flag[NBUF(nbdone)] = 1; nbdone++; nbpend--; /* timestamps of all following buffers are unreliable */ tv.tv_sec = tv.tv_usec = 0; } } else { /* Update the number and status of frags(=buffers) already output */ nbpend = count.blocks; while(nbpend) { /* check for overflow of the status flags in the ringbuffer */ if(shmemptr->status[NBUF(nbdone)]) system_error("Audio ring buffer overflow",fd,0); /* We have a buffer underrun during write if nbdone>=nbque */ shmemptr->tmstmp[NBUF(nbdone)] = tv; shmemptr->status[NBUF(nbdone)] = (nbdone<nbque) ? 1 : -1; nbdone++; nbpend--; /* timestamps of all following buffers are unreliable */ tv.tv_sec = tv.tv_usec = 0; } /* If exit_flag is set and all buffers are played, exit */ if(shmemptr->exit_flag && nbdone >= nbque) { shmemptr->audio_status = -1; close(fd);#ifdef FORK_NOT_THREAD exit(0);#else pthread_exit( NULL );#endif } /* Fill into the soundcard memory as many buffers as fit and are available */ while(nbque-nbdone < info.fragstotal) { if(!shmemptr->used_flag[NBUF(nbque)]) break; if(nbque>nbdone) memcpy(buf+(nbque%info.fragstotal)*info.fragsize, (void*) shmemptr->audio_data[NBUF(nbque)], info.fragsize); /* Mark the buffer as free */ shmemptr->used_flag[NBUF(nbque)] = 0; nbque++; } if(nbset<nbque) nbset = nbque; while(nbset-nbdone < info.fragstotal) { memset(buf+(nbset%info.fragstotal)*info.fragsize,0,info.fragsize); nbset++; } } }}#elsevoid do_audio(){ fprintf(stderr, "ILLEGAL CALL TO do_audio in audiolib.c: NOT IMPLEMENTED IN IRIX !\n");}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -