📄 alsa.c
字号:
error: snd_pcm_close( p_sys->p_snd_pcm );#ifdef ALSA_DEBUG snd_output_close( p_sys->p_snd_stderr );#endif free( p_sys ); return VLC_EGENERIC;}/***************************************************************************** * Play: nothing to do *****************************************************************************/static void Play( aout_instance_t *p_aout ){ if( !p_aout->output.p_sys->b_playing ) { p_aout->output.p_sys->b_playing = 1; /* get the playing date of the first aout buffer */ p_aout->output.p_sys->start_date = aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); /* wake up the audio output thread */ vlc_mutex_lock( &p_aout->output.p_sys->lock ); vlc_cond_signal( &p_aout->output.p_sys->wait ); vlc_mutex_unlock( &p_aout->output.p_sys->lock ); }}/***************************************************************************** * Close: close the ALSA device *****************************************************************************/static void Close( vlc_object_t *p_this ){ aout_instance_t *p_aout = (aout_instance_t *)p_this; struct aout_sys_t * p_sys = p_aout->output.p_sys; int i_snd_rc; /* Make sure that the thread will stop once it is waken up */ vlc_object_kill( p_aout ); /* make sure the audio output thread is waken up */ vlc_mutex_lock( &p_aout->output.p_sys->lock ); vlc_cond_signal( &p_aout->output.p_sys->wait ); vlc_mutex_unlock( &p_aout->output.p_sys->lock ); /* */ vlc_thread_join( p_aout ); p_aout->b_die = false; i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm ); if( i_snd_rc > 0 ) { msg_Err( p_aout, "failed closing ALSA device (%s)", snd_strerror( i_snd_rc ) ); }#ifdef ALSA_DEBUG snd_output_close( p_sys->p_snd_stderr );#endif free( p_sys );}/***************************************************************************** * ALSAThread: asynchronous thread used to DMA the data to the device *****************************************************************************/static void* ALSAThread( vlc_object_t* p_this ){ aout_instance_t * p_aout = (aout_instance_t*)p_this; struct aout_sys_t * p_sys = p_aout->output.p_sys; p_sys->p_status = (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof()); /* Wait for the exact time to start playing (avoids resampling) */ vlc_mutex_lock( &p_sys->lock ); while( !p_sys->start_date && vlc_object_alive (p_aout) ) vlc_cond_wait( &p_sys->wait, &p_sys->lock ); vlc_mutex_unlock( &p_sys->lock ); if( !vlc_object_alive (p_aout) ) goto cleanup; mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 ); while ( vlc_object_alive (p_aout) ) { ALSAFill( p_aout ); }cleanup: snd_pcm_drop( p_sys->p_snd_pcm ); free( p_aout->output.p_sys->p_status ); return NULL;}/***************************************************************************** * ALSAFill: function used to fill the ALSA buffer as much as possible *****************************************************************************/static void ALSAFill( aout_instance_t * p_aout ){ struct aout_sys_t * p_sys = p_aout->output.p_sys; aout_buffer_t * p_buffer; snd_pcm_status_t * p_status = p_sys->p_status; int i_snd_rc; mtime_t next_date; /* Fill in the buffer until space or audio output buffer shortage */ /* Get the status */ i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status" ); goto error; } /* Handle buffer underruns and get the status again */ if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN ) { /* Prepare the device */ i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); if( i_snd_rc ) { msg_Err( p_aout, "cannot recover from buffer underrun" ); goto error; } msg_Dbg( p_aout, "recovered from buffer underrun" ); /* Get the new status */ i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status after recovery" ); goto error; } /* Underrun, try to recover as quickly as possible */ next_date = mdate(); } else { /* Here the device should be in RUNNING state, p_status is valid. */ snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status ); if( delay == 0 ) /* workaround buggy alsa drivers */ if( snd_pcm_delay( p_sys->p_snd_pcm, &delay ) < 0 ) delay = 0; /* FIXME: use a positive minimal delay */ int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay ); next_date = mdate() + ( (mtime_t)i_bytes * 1000000 / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate * p_aout->output.output.i_frame_length );#ifdef ALSA_DEBUG snd_pcm_state_t state = snd_pcm_status_get_state( p_status ); if( state != SND_PCM_STATE_RUNNING ) msg_Err( p_aout, "pcm status (%d) != RUNNING", state ); msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes ); msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame ); msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate ); msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length ); msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) );#endif } p_buffer = aout_OutputNextBuffer( p_aout, next_date, (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i')) ); /* Audio output buffer shortage -> stop the fill process and wait */ if( p_buffer == NULL ) goto error; for (;;) { i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer, p_buffer->i_nb_samples ); if( i_snd_rc != -ESTRPIPE ) break; /* a suspend event occurred * (stream is suspended and waiting for an application recovery) */ msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." ); while( vlc_object_alive (p_aout) && vlc_object_alive (p_aout->p_libvlc) && ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN ) { msleep( 1000000 ); } if( i_snd_rc < 0 ) /* Device does not supprot resuming, restart it */ i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); } if( i_snd_rc < 0 ) msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) ); aout_BufferFree( p_buffer ); return;error: if( i_snd_rc < 0 ) msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) ); msleep( p_sys->i_period_time >> 1 );}static void GetDevicesForCard( module_config_t *p_item, int i_card );static void GetDevices( module_config_t *p_item );/***************************************************************************** * config variable callback *****************************************************************************/static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name, vlc_value_t newval, vlc_value_t oldval, void *p_unused ){ module_config_t *p_item; int i; (void)newval; (void)oldval; (void)p_unused; p_item = config_FindConfig( p_this, psz_name ); if( !p_item ) return VLC_SUCCESS; /* Clear-up the current list */ if( p_item->i_list ) { /* Keep the first entrie */ for( i = 1; i < p_item->i_list; i++ ) { free( (char *)p_item->ppsz_list[i] ); free( (char *)p_item->ppsz_list_text[i] ); } /* TODO: Remove when no more needed */ p_item->ppsz_list[i] = NULL; p_item->ppsz_list_text[i] = NULL; } p_item->i_list = 1; GetDevices( p_item ); /* Signal change to the interface */ p_item->b_dirty = true; return VLC_SUCCESS;}static void GetDevicesForCard( module_config_t *p_item, int i_card ){ int i_pcm_device = -1; int i_err = 0; snd_pcm_info_t *p_pcm_info; snd_ctl_t *p_ctl; char psz_dev[64]; char *psz_card_name; sprintf( psz_dev, "hw:%i", i_card ); if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 ) return; if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0) psz_card_name = _("Unknown soundcard"); snd_pcm_info_alloca( &p_pcm_info ); for (;;) { char *psz_device, *psz_descr; if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 ) i_pcm_device = -1; if( i_pcm_device < 0 ) break; snd_pcm_info_set_device( p_pcm_info, i_pcm_device ); snd_pcm_info_set_subdevice( p_pcm_info, 0 ); snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK ); if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 ) { if( i_err != -ENOENT ) { /*printf( "get_devices_for_card(): " "snd_ctl_pcm_info() " "failed (%d:%d): %s.\n", i_card, i_pcm_device, snd_strerror( -i_err ) );*/ } continue; } if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 ) break; if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name, snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 ) { free( psz_device ); break; } p_item->ppsz_list = (char **)realloc( p_item->ppsz_list, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list_text = (char **)realloc( p_item->ppsz_list_text, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list[ p_item->i_list ] = psz_device; p_item->ppsz_list_text[ p_item->i_list ] = psz_descr; p_item->i_list++; p_item->ppsz_list[ p_item->i_list ] = NULL; p_item->ppsz_list_text[ p_item->i_list ] = NULL; } snd_ctl_close( p_ctl );}static void GetDevices( module_config_t *p_item ){ int i_card = -1; int i_err = 0; if( ( i_err = snd_card_next( &i_card ) ) != 0 ) { /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ return; } while( i_card > -1 ) { GetDevicesForCard( p_item, i_card ); if( ( i_err = snd_card_next( &i_card ) ) != 0 ) { /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/ break; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -