📄 waveout.c
字号:
memset( p_aout->output.p_sys->p_silence_buffer, 0, p_aout->output.p_sys->i_buffer_size ); /* Now we need to setup our waveOut play notification structure */ p_aout->output.p_sys->p_notif = vlc_object_create( p_aout, sizeof(notification_thread_t) ); p_aout->output.p_sys->p_notif->p_aout = p_aout; p_aout->output.p_sys->event = CreateEvent( NULL, FALSE, FALSE, NULL ); p_aout->output.p_sys->new_buffer_event = CreateEvent( NULL, FALSE, FALSE, NULL ); /* define startpoint of playback on first call to play() like alsa does (instead of playing a blank sample) */ p_aout->output.p_sys->b_playing = 0; p_aout->output.p_sys->start_date = 0; /* Then launch the notification thread */ if( vlc_thread_create( p_aout->output.p_sys->p_notif, "waveOut Notification Thread", WaveOutThread, VLC_THREAD_PRIORITY_OUTPUT, false ) ) { msg_Err( p_aout, "cannot create WaveOutThread" ); } /* We need to kick off the playback in order to have the callback properly * working */ for( i = 0; i < FRAMES_NUM; i++ ) { p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE; p_aout->output.p_sys->waveheader[i].dwUser = 0; } return 0;}/***************************************************************************** * Probe: probe the audio device for available formats and channels *****************************************************************************/static void Probe( aout_instance_t * p_aout ){ vlc_value_t val, text; int i_format; unsigned int i_physical_channels; var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Device"); var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL ); /* Test for 5.1 support */ i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; if( p_aout->output.output.i_physical_channels == i_physical_channels ) { if( OpenWaveOutPCM( p_aout, p_aout->output.p_sys->i_wave_device_id, &i_format, i_physical_channels, 6, p_aout->output.output.i_rate, true ) == VLC_SUCCESS ) { val.i_int = AOUT_VAR_5_1; text.psz_string = (char *)N_("5.1"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 5.1 channels" ); } } /* Test for 2 Front 2 Rear support */ i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; if( ( p_aout->output.output.i_physical_channels & i_physical_channels ) == i_physical_channels ) { if( OpenWaveOutPCM( p_aout, p_aout->output.p_sys->i_wave_device_id, &i_format, i_physical_channels, 4, p_aout->output.output.i_rate, true ) == VLC_SUCCESS ) { val.i_int = AOUT_VAR_2F2R; text.psz_string = (char *)N_("2 Front 2 Rear"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 4 channels" ); } } /* Test for stereo support */ i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; if( OpenWaveOutPCM( p_aout, p_aout->output.p_sys->i_wave_device_id, &i_format, i_physical_channels, 2, p_aout->output.output.i_rate, true ) == VLC_SUCCESS ) { val.i_int = AOUT_VAR_STEREO; text.psz_string = (char *)N_("Stereo"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 2 channels" ); } /* Test for mono support */ i_physical_channels = AOUT_CHAN_CENTER; if( OpenWaveOutPCM( p_aout, p_aout->output.p_sys->i_wave_device_id, &i_format, i_physical_channels, 1, p_aout->output.output.i_rate, true ) == VLC_SUCCESS ) { val.i_int = AOUT_VAR_MONO; text.psz_string = (char *)N_("Mono"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 1 channel" ); } /* Test for SPDIF support */ if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) { if( OpenWaveOut( p_aout, p_aout->output.p_sys->i_wave_device_id, VLC_FOURCC('s','p','d','i'), p_aout->output.output.i_physical_channels, aout_FormatNbChannels( &p_aout->output.output ), p_aout->output.output.i_rate, true ) == VLC_SUCCESS ) { msg_Dbg( p_aout, "device supports A/52 over S/PDIF" ); val.i_int = AOUT_VAR_SPDIF; text.psz_string = (char *)N_("A/52 over S/PDIF"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); if( config_GetInt( p_aout, "spdif" ) ) var_Set( p_aout, "audio-device", val ); } } var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL ); if( val.i_int <= 0 ) { /* Probe() has failed. */ var_Destroy( p_aout, "audio-device" ); return; } var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); val.b_bool = true; var_Set( p_aout, "intf-change", val );}/***************************************************************************** * Play: play a sound buffer ***************************************************************************** * This doesn't actually play the buffer. This just stores the buffer so it * can be played by the callback thread. *****************************************************************************/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 ); msg_Dbg( _p_aout, "Wakeup sleeping output thread."); /* wake up the audio output thread */ SetEvent( _p_aout->output.p_sys->event ); } else { SetEvent( _p_aout->output.p_sys->new_buffer_event ); }}/***************************************************************************** * Close: close the audio device *****************************************************************************/static void Close( vlc_object_t *p_this ){ aout_instance_t *p_aout = (aout_instance_t *)p_this; aout_sys_t *p_sys = p_aout->output.p_sys; /* Before calling waveOutClose we must reset the device */ vlc_object_kill( p_aout ); /* wake up the audio thread, to recognize that p_aout died */ SetEvent( p_sys->event ); SetEvent( p_sys->new_buffer_event ); vlc_thread_join( p_sys->p_notif ); vlc_object_release( p_sys->p_notif ); /* kill the real output then - when the feed thread is surely terminated! old code could be too early in case that "feeding" was running on termination at this point now its sure, that there will be no new data send to the driver, and we can cancel the last running playbuffers */ MMRESULT result = waveOutReset( p_sys->h_waveout ); if(result != MMSYSERR_NOERROR) { msg_Err( p_aout, "waveOutReset failed 0x%x", result ); /* now we must wait, that all buffers are played because cancel doesn't work in this case... */ if(result == MMSYSERR_NOTSUPPORTED) { /* clear currently played (done) buffers, if returnvalue > 0 (means some buffer still playing) wait for the driver event callback that one buffer is finished with playing, and check again the timeout of 5000ms is just, an emergency exit of this loop, to avoid deadlock in case of other (currently not known bugs, problems, errors cases?) */ while( (WaveOutClearDoneBuffers( p_sys ) > 0) && (WaitForSingleObject( p_sys->event, 5000) == WAIT_OBJECT_0) ) { msg_Dbg( p_aout, "Wait for waveout device..."); } } } else { WaveOutClearDoneBuffers( p_sys ); } /* now we can Close the device */ if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR ) { msg_Err( p_aout, "waveOutClose failed" ); } /* because so long, the waveout device is playing, the callback could occur and need the events */ CloseHandle( p_sys->event ); CloseHandle( p_sys->new_buffer_event); free( p_sys->p_silence_buffer ); free( p_sys );}/***************************************************************************** * OpenWaveOut: open the waveout sound device ****************************************************************************/static int OpenWaveOut( aout_instance_t *p_aout, uint32_t i_device_id, int i_format, int i_channels, int i_nb_channels, int i_rate, bool b_probe ){ MMRESULT result; unsigned int i; /* Set sound format */#define waveformat p_aout->output.p_sys->waveformat waveformat.dwChannelMask = 0; for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ ) { if( i_channels & pi_channels_src[i] ) waveformat.dwChannelMask |= pi_channels_in[i]; } switch( i_format ) { case VLC_FOURCC('s','p','d','i'): i_nb_channels = 2; /* To prevent channel re-ordering */ waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; waveformat.Format.wBitsPerSample = 16; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF; break; case VLC_FOURCC('f','l','3','2'): waveformat.Format.wBitsPerSample = sizeof(float) * 8; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; case VLC_FOURCC('s','1','6','l'): waveformat.Format.wBitsPerSample = 16; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_PCM; waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM; break; } waveformat.Format.nChannels = i_nb_channels; waveformat.Format.nSamplesPerSec = i_rate; waveformat.Format.nBlockAlign = waveformat.Format.wBitsPerSample / 8 * i_nb_channels; waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign; /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */ if( i_nb_channels <= 2 ) { waveformat.Format.cbSize = 0; } else { waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; waveformat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); } if(!b_probe) { msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id); msg_Dbg( p_aout,"waveformat.Format.cbSize = %d", waveformat.Format.cbSize); msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u", waveformat.Format.wFormatTag); msg_Dbg( p_aout,"waveformat.Format.nChannels = %u", waveformat.Format.nChannels); msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d", (int)waveformat.Format.nSamplesPerSec); msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u", (int)waveformat.Format.nAvgBytesPerSec); msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d", waveformat.Format.nBlockAlign); msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d", waveformat.Format.wBitsPerSample); msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d", waveformat.Samples.wValidBitsPerSample); msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d", waveformat.Samples.wSamplesPerBlock); msg_Dbg( p_aout,"waveformat.dwChannelMask = %lu", waveformat.dwChannelMask); } /* Open the device */ result = waveOutOpen( &p_aout->output.p_sys->h_waveout, i_device_id, (WAVEFORMATEX *)&waveformat, (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout, CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) ); if( result == WAVERR_BADFORMAT ) { msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" ); return VLC_EGENERIC; } if( result == MMSYSERR_ALLOCATED ) { msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" ); return VLC_EGENERIC; } if( result != MMSYSERR_NOERROR ) { msg_Warn( p_aout, "waveOutOpen failed" ); return VLC_EGENERIC; } p_aout->output.p_sys->b_chan_reorder = aout_CheckChannelReorder( pi_channels_in, pi_channels_out, waveformat.dwChannelMask, i_nb_channels, p_aout->output.p_sys->pi_chan_table ); if( p_aout->output.p_sys->b_chan_reorder ) { msg_Dbg( p_aout, "channel reordering needed" ); } return VLC_SUCCESS;#undef waveformat}/***************************************************************************** * OpenWaveOutPCM: open a PCM waveout sound device ****************************************************************************/static int OpenWaveOutPCM( aout_instance_t *p_aout, uint32_t i_device_id, int *i_format, int i_channels, int i_nb_channels, int i_rate, bool b_probe ){ bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32"); if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_FOURCC('f','l','3','2'), i_channels, i_nb_channels, i_rate, b_probe ) != VLC_SUCCESS ) { if ( OpenWaveOut( p_aout, i_device_id, VLC_FOURCC('s','1','6','l'), i_channels, i_nb_channels, i_rate, b_probe ) != VLC_SUCCESS ) { return VLC_EGENERIC; } else { *i_format = VLC_FOURCC('s','1','6','l'); return VLC_SUCCESS; } } else { *i_format = VLC_FOURCC('f','l','3','2'); return VLC_SUCCESS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -