Mercurial > mplayer.hg
changeset 13832:2f8cfe66dbfd
Different buffering scheme, avoiding possible races (SDL is using threads!).
author | reimar |
---|---|
date | Sun, 31 Oct 2004 21:02:47 +0000 |
parents | 719c3c818335 |
children | b2eaddcce355 |
files | libao2/ao_sdl.c |
diffstat | 1 files changed, 66 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/libao2/ao_sdl.c Sun Oct 31 19:58:36 2004 +0000 +++ b/libao2/ao_sdl.c Sun Oct 31 21:02:47 2004 +0000 @@ -46,68 +46,78 @@ #define SAMPLESIZE 1024 #endif -// General purpose Ring-buffering routines - -#define BUFFSIZE 4096 -#define NUM_BUFS 8 +#define CHUNK_SIZE 4096 +#define NUM_CHUNKS 8 +// This type of ring buffer may never fill up completely, at least +// one byte must always be unused. +// For performance reasons (alignment etc.) one whole chunk always stays +// empty, not only one byte. +#define BUFFSIZE ((NUM_CHUNKS + 1) * CHUNK_SIZE) -static unsigned char *buffer[NUM_BUFS]; +static unsigned char *buffer; -static unsigned int buf_read=0; -static unsigned int buf_write=0; -static unsigned int buf_read_pos=0; -static unsigned int buf_write_pos=0; +// may only be modified by SDL's playback thread or while it is stopped +static volatile int read_pos; +// may only be modified by mplayer's thread +static volatile int write_pos; #ifdef USE_SDL_INTERNAL_MIXER static unsigned char volume=SDL_MIX_MAXVOLUME; #endif -static int full_buffers=0; -static int buffered_bytes=0; +// may only be called by mplayer's thread +// return value may change between immediately following two calls, +// and the real number of free bytes might be larger! +static int buf_free() { + int free = read_pos - write_pos - CHUNK_SIZE; + if (free < 0) free += BUFFSIZE; + return free; +} + +// may only be called by SDL's playback thread +// return value may change between immediately following two calls, +// and the real number of buffered bytes might be larger! +static int buf_used() { + int used = write_pos - read_pos; + if (used < 0) used += BUFFSIZE; + return used; +} static int write_buffer(unsigned char* data,int len){ - int len2=0; - int x; - while(len>0){ - if(full_buffers==NUM_BUFS) break; - x=BUFFSIZE-buf_write_pos; - if(x>len) x=len; - memcpy(buffer[buf_write]+buf_write_pos,data+len2,x); - if (buf_write_pos==0) - ++full_buffers; - len2+=x; len-=x; - buffered_bytes+=x; buf_write_pos+=x; - if(buf_write_pos>=BUFFSIZE){ - // block is full, find next! - buf_write=(buf_write+1)%NUM_BUFS; - buf_write_pos=0; - } + int first_len = BUFFSIZE - write_pos; + int free = buf_free(); + if (len > free) len = free; + if (first_len > len) first_len = len; + // till end of buffer + memcpy (&buffer[write_pos], data, first_len); + if (len > first_len) { // we have to wrap around + // remaining part from beginning of buffer + memcpy (buffer, &data[first_len], len - first_len); } - return len2; + write_pos = (write_pos + len) % BUFFSIZE; + return len; } static int read_buffer(unsigned char* data,int len){ - int len2=0; - int x; - while(len>0){ - if(buffered_bytes==0) break; // no more data buffered! - x=BUFFSIZE-buf_read_pos; - if(x>len) x=len; - if (x>buffered_bytes) x=buffered_bytes; + int first_len = BUFFSIZE - read_pos; + int buffered = buf_used(); + if (len > buffered) len = buffered; + if (first_len > len) first_len = len; + // till end of buffer #ifdef USE_SDL_INTERNAL_MIXER - SDL_MixAudio(data+len2,buffer[buf_read]+buf_read_pos,x,volume); + SDL_MixAudio (data, &buffer[read_pos], first_len, volume); #else - memcpy(data+len2,buffer[buf_read]+buf_read_pos,x); + memcpy (data, &buffer[read_pos], first_len); #endif - len2+=x; len-=x; - buffered_bytes-=x; buf_read_pos+=x; - if(buf_read_pos>=BUFFSIZE){ - // block is empty, find next! - buf_read=(buf_read+1)%NUM_BUFS; - --full_buffers; - buf_read_pos=0; - } + if (len > first_len) { // we have to wrap around + // remaining part from beginning of buffer +#ifdef USE_SDL_INTERNAL_MIXER + SDL_MixAudio (&data[first_len], buffer, len - first_len, volume); +#else + memcpy (&data[first_len], buffer, len - first_len); +#endif } - return len2; + read_pos = (read_pos + len) % BUFFSIZE; + return len; } // end ring buffer stuff @@ -168,9 +178,8 @@ /* SDL Audio Specifications */ SDL_AudioSpec aspec, obtained; - int i; /* Allocate ring-buffer memory */ - for(i=0;i<NUM_BUFS;i++) buffer[i]=(unsigned char *) malloc(BUFFSIZE); + buffer = (unsigned char *) malloc(BUFFSIZE); mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_SDL_INFO, rate, (channels > 1) ? "Stereo" : "Mono", audio_out_format_name(format)); @@ -272,6 +281,7 @@ mp_msg(MSGT_AO,MSGL_V,"SDL: buf size = %d\n",obtained.size); ao_data.buffersize=obtained.size; + reset(); /* unsilence audio, if callback is ready */ SDL_PauseAudio(0); @@ -281,7 +291,7 @@ // close audio device static void uninit(int immed){ mp_msg(MSGT_AO,MSGL_V,"SDL: Audio Subsystem shutting down!\n"); - while(buffered_bytes > 0) + while(buf_free() < BUFFSIZE - CHUNK_SIZE) usec_sleep(50000); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); @@ -292,15 +302,11 @@ //printf("SDL: reset called!\n"); + SDL_PauseAudio(1); /* Reset ring-buffer state */ - buf_read=0; - buf_write=0; - buf_read_pos=0; - buf_write_pos=0; - - full_buffers=0; - buffered_bytes=0; - + read_pos = 0; + write_pos = 0; + SDL_PauseAudio(0); } // stop playing, keep buffers (for pause) @@ -322,7 +328,7 @@ // return: how many bytes can be played without blocking static int get_space(){ - return NUM_BUFS*BUFFSIZE - buffered_bytes; + return buf_free(); } // plays 'len' bytes of 'data' @@ -348,7 +354,8 @@ // return: delay in seconds between first and last sample in buffer static float get_delay(){ - return (float)(buffered_bytes + ao_data.buffersize)/(float)ao_data.bps; + int buffered = BUFFSIZE - CHUNK_SIZE - buf_free(); // could be less + return (float)(buffered + ao_data.buffersize)/(float)ao_data.bps; }