Commit 7ae23ab6 authored by Gildas Bazin's avatar Gildas Bazin

* modules/audio_output/directx.c: backport of directx audio changes (13374). Needs heavy testing.

parent bade4231
...@@ -41,17 +41,12 @@ ...@@ -41,17 +41,12 @@
#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */ #define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
#define FRAMES_NUM 8 /* Needs to be > 3 */ #define FRAMES_NUM 8 /* Needs to be > 3 */
/* frame buffer status */
#define FRAME_QUEUED 0
#define FRAME_EMPTY 1
/***************************************************************************** /*****************************************************************************
* DirectSound GUIDs. * DirectSound GUIDs.
* Defining them here allows us to get rid of the dxguid library during * Defining them here allows us to get rid of the dxguid library during
* the linking stage. * the linking stage.
*****************************************************************************/ *****************************************************************************/
#include <initguid.h> #include <initguid.h>
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
/***************************************************************************** /*****************************************************************************
* Useful macros * Useful macros
...@@ -134,12 +129,12 @@ typedef struct notification_thread_t ...@@ -134,12 +129,12 @@ typedef struct notification_thread_t
{ {
VLC_COMMON_MEMBERS VLC_COMMON_MEMBERS
aout_instance_t * p_aout; aout_instance_t *p_aout;
int i_frame_status[FRAMES_NUM]; /* status of each frame buffer */ int i_frame_size; /* size in bytes of one frame */
DSBPOSITIONNOTIFY p_events[FRAMES_NUM]; /* play notification events */ int i_write_slot; /* current write position in our circular buffer */
int i_frame_size; /* Size in bytes of one frame */
mtime_t start_date; mtime_t start_date;
HANDLE event;
} notification_thread_t; } notification_thread_t;
...@@ -161,7 +156,6 @@ struct aout_sys_t ...@@ -161,7 +156,6 @@ struct aout_sys_t
* takes care of mixing all the * takes care of mixing all the
* secondary buffers into the primary) */ * secondary buffers into the primary) */
LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
notification_thread_t *p_notif; /* DirectSoundThread id */ notification_thread_t *p_notif; /* DirectSoundThread id */
int b_playing; /* playing status */ int b_playing; /* playing status */
...@@ -242,7 +236,6 @@ static int OpenAudio( vlc_object_t *p_this ) ...@@ -242,7 +236,6 @@ static int OpenAudio( vlc_object_t *p_this )
{ {
aout_instance_t * p_aout = (aout_instance_t *)p_this; aout_instance_t * p_aout = (aout_instance_t *)p_this;
vlc_value_t val; vlc_value_t val;
int i;
msg_Dbg( p_aout, "OpenAudio" ); msg_Dbg( p_aout, "OpenAudio" );
...@@ -257,7 +250,6 @@ static int OpenAudio( vlc_object_t *p_this ) ...@@ -257,7 +250,6 @@ static int OpenAudio( vlc_object_t *p_this )
/* Initialize some variables */ /* Initialize some variables */
p_aout->output.p_sys->p_dsobject = NULL; p_aout->output.p_sys->p_dsobject = NULL;
p_aout->output.p_sys->p_dsbuffer = NULL; p_aout->output.p_sys->p_dsbuffer = NULL;
p_aout->output.p_sys->p_dsnotify = NULL;
p_aout->output.p_sys->p_notif = NULL; p_aout->output.p_sys->p_notif = NULL;
p_aout->output.p_sys->b_playing = 0; p_aout->output.p_sys->b_playing = 0;
...@@ -291,16 +283,6 @@ static int OpenAudio( vlc_object_t *p_this ) ...@@ -291,16 +283,6 @@ static int OpenAudio( vlc_object_t *p_this )
goto error; goto error;
} }
/* Now we need to setup our DirectSound 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;
/* Then create the notification events */
for( i = 0; i < FRAMES_NUM; i++ )
p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
CreateEvent( NULL, FALSE, FALSE, NULL );
/* Open the device */ /* Open the device */
if( val.i_int == AOUT_VAR_SPDIF ) if( val.i_int == AOUT_VAR_SPDIF )
{ {
...@@ -372,12 +354,18 @@ static int OpenAudio( vlc_object_t *p_this ) ...@@ -372,12 +354,18 @@ static int OpenAudio( vlc_object_t *p_this )
/* Calculate the frame size in bytes */ /* Calculate the frame size in bytes */
p_aout->output.i_nb_samples = FRAME_SIZE; p_aout->output.i_nb_samples = FRAME_SIZE;
aout_FormatPrepare( &p_aout->output.output ); aout_FormatPrepare( &p_aout->output.output );
p_aout->output.p_sys->i_frame_size =
FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
aout_VolumeSoftInit( p_aout ); aout_VolumeSoftInit( p_aout );
} }
/* Now we need to setup our DirectSound 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->p_notif->event = CreateEvent( 0, FALSE, FALSE, 0 );
p_aout->output.p_sys->p_notif->i_frame_size =
p_aout->output.p_sys->i_frame_size;
/* then launch the notification thread */ /* then launch the notification thread */
msg_Dbg( p_aout, "creating DirectSoundThread" ); msg_Dbg( p_aout, "creating DirectSoundThread" );
if( vlc_thread_create( p_aout->output.p_sys->p_notif, if( vlc_thread_create( p_aout->output.p_sys->p_notif,
...@@ -386,6 +374,9 @@ static int OpenAudio( vlc_object_t *p_this ) ...@@ -386,6 +374,9 @@ static int OpenAudio( vlc_object_t *p_this )
VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) ) VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
{ {
msg_Err( p_aout, "cannot create DirectSoundThread" ); msg_Err( p_aout, "cannot create DirectSoundThread" );
CloseHandle( p_aout->output.p_sys->p_notif->event );
vlc_object_destroy( p_aout->output.p_sys->p_notif );
p_aout->output.p_sys->p_notif = 0;
goto error; goto error;
} }
...@@ -564,6 +555,7 @@ static void Play( aout_instance_t *p_aout ) ...@@ -564,6 +555,7 @@ static void Play( aout_instance_t *p_aout )
if( !p_aout->output.p_sys->b_playing ) if( !p_aout->output.p_sys->b_playing )
{ {
aout_buffer_t *p_buffer; aout_buffer_t *p_buffer;
int i;
p_aout->output.p_sys->b_playing = 1; p_aout->output.p_sys->b_playing = 1;
...@@ -572,11 +564,15 @@ static void Play( aout_instance_t *p_aout ) ...@@ -572,11 +564,15 @@ static void Play( aout_instance_t *p_aout )
aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
/* fill in the first samples */ /* fill in the first samples */
p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); for( i = 0; i < FRAMES_NUM; i++ )
FillBuffer( p_aout, 0, p_buffer ); {
p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
if( !p_buffer ) break;
FillBuffer( p_aout, i, p_buffer );
}
/* wake up the audio output thread */ /* wake up the audio output thread */
SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify ); SetEvent( p_aout->output.p_sys->p_notif->event );
} }
} }
...@@ -598,9 +594,8 @@ static void CloseAudio( vlc_object_t *p_this ) ...@@ -598,9 +594,8 @@ static void CloseAudio( vlc_object_t *p_this )
{ {
p_sys->p_notif->b_die = 1; p_sys->p_notif->b_die = 1;
if( !p_sys->b_playing ) /* wake up the audio thread if needed */
/* wake up the audio thread */ if( !p_sys->b_playing ) SetEvent( p_sys->p_notif->event );
SetEvent( p_sys->p_notif->p_events[0].hEventNotify );
vlc_thread_join( p_sys->p_notif ); vlc_thread_join( p_sys->p_notif );
} }
...@@ -792,7 +787,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, ...@@ -792,7 +787,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */ dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
| DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
| DSBCAPS_GLOBALFOCUS; /* Allows background playing */ | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
/* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */ /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
...@@ -844,40 +838,8 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, ...@@ -844,40 +838,8 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/* Backup the size of a frame */ p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
/* Now the secondary buffer is created, we need to setup its position
* notification */
for( i = 0; i < FRAMES_NUM; i++ )
{
p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
p_aout->output.p_sys->p_notif->i_frame_size;
p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
}
/* Get the IDirectSoundNotify interface */
if FAILED( IDirectSoundBuffer_QueryInterface(
p_aout->output.p_sys->p_dsbuffer,
&IID_IDirectSoundNotify,
(LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
{
msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
goto error;
}
if FAILED( IDirectSoundNotify_SetNotificationPositions(
p_aout->output.p_sys->p_dsnotify,
FRAMES_NUM,
p_aout->output.p_sys->p_notif->p_events ) )
{
msg_Err( p_aout, "cannot set position notification" );
goto error;
}
p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask; p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
p_aout->output.p_sys->b_chan_reorder = p_aout->output.p_sys->b_chan_reorder =
aout_CheckChannelReorder( pi_channels_in, pi_channels_out, aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
waveformat.dwChannelMask, i_nb_channels, waveformat.dwChannelMask, i_nb_channels,
...@@ -889,10 +851,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, ...@@ -889,10 +851,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
} }
return VLC_SUCCESS; return VLC_SUCCESS;
error:
DestroyDSBuffer( p_aout );
return VLC_EGENERIC;
} }
/***************************************************************************** /*****************************************************************************
...@@ -944,12 +902,6 @@ static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format, ...@@ -944,12 +902,6 @@ static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
*****************************************************************************/ *****************************************************************************/
static void DestroyDSBuffer( aout_instance_t *p_aout ) static void DestroyDSBuffer( aout_instance_t *p_aout )
{ {
if( p_aout->output.p_sys->p_dsnotify )
{
IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
p_aout->output.p_sys->p_dsnotify = NULL;
}
if( p_aout->output.p_sys->p_dsbuffer ) if( p_aout->output.p_sys->p_dsbuffer )
{ {
IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer ); IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
...@@ -1024,6 +976,7 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame, ...@@ -1024,6 +976,7 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1, IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
p_wrap_around, l_bytes2 ); p_wrap_around, l_bytes2 );
p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
return VLC_SUCCESS; return VLC_SUCCESS;
} }
...@@ -1035,15 +988,11 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame, ...@@ -1035,15 +988,11 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
*****************************************************************************/ *****************************************************************************/
static void DirectSoundThread( notification_thread_t *p_notif ) static void DirectSoundThread( notification_thread_t *p_notif )
{ {
HANDLE notification_events[FRAMES_NUM];
HRESULT dsresult;
aout_instance_t *p_aout = p_notif->p_aout; aout_instance_t *p_aout = p_notif->p_aout;
int i, i_which_frame, i_last_frame;
mtime_t mtime;
vlc_bool_t b_sleek; vlc_bool_t b_sleek;
mtime_t last_time;
for( i = 0; i < FRAMES_NUM; i++ ) HRESULT dsresult;
notification_events[i] = p_notif->p_events[i].hEventNotify; long l_queued = 0;
/* We don't want any resampling when using S/PDIF output */ /* We don't want any resampling when using S/PDIF output */
b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'); b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
...@@ -1054,7 +1003,7 @@ static void DirectSoundThread( notification_thread_t *p_notif ) ...@@ -1054,7 +1003,7 @@ static void DirectSoundThread( notification_thread_t *p_notif )
msg_Dbg( p_notif, "DirectSoundThread ready" ); msg_Dbg( p_notif, "DirectSoundThread ready" );
/* Wait here until Play() is called */ /* Wait here until Play() is called */
WaitForSingleObject( notification_events[0], INFINITE ); WaitForSingleObject( p_notif->event, INFINITE );
if( !p_notif->b_die ) if( !p_notif->b_die )
{ {
...@@ -1079,79 +1028,64 @@ static void DirectSoundThread( notification_thread_t *p_notif ) ...@@ -1079,79 +1028,64 @@ static void DirectSoundThread( notification_thread_t *p_notif )
msg_Err( p_aout, "cannot start playing buffer" ); msg_Err( p_aout, "cannot start playing buffer" );
} }
} }
last_time = mdate();
while( !p_notif->b_die ) while( !p_notif->b_die )
{ {
aout_buffer_t *p_buffer; long l_read, l_free_slots;
long l_position, l_latency = 0; mtime_t mtime = mdate();
int i;
/* wait for the position notification */
i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
notification_events, 0,
INFINITE ) - WAIT_OBJECT_0;
if( p_notif->b_die ) /*
break; * Fill in as much audio data as we can in our circular buffer
*/
mtime = mdate(); /* Find out current play position */
if FAILED( IDirectSoundBuffer_GetCurrentPosition(
/* We take into account the current latency */ p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) )
if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition(
p_aout->output.p_sys->p_dsbuffer,
&l_position, NULL ) )
{ {
/* Latency is in samples */ msg_Err( p_aout, "GetCurrentPosition() failed!" );
l_position /= p_aout->output.output.i_bytes_per_frame; l_read = 0;
l_latency = l_position - i_which_frame * FRAME_SIZE;
/* That sucks but latency can apparently be negative up to -FRAME_SIZE
* ie. the notification is done in advance. */
if( l_latency > FRAME_SIZE * (FRAMES_NUM-1) )
l_latency -= (FRAME_SIZE * FRAMES_NUM);
else if( l_latency < -FRAME_SIZE )
l_latency += (FRAME_SIZE * FRAMES_NUM);
} }
/* Mark last frame as empty */ /* Detect underruns */
i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM; if( l_queued && mtime - last_time >
p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY; I64C(1000000) * l_queued / p_aout->output.output.i_rate )
/* Try to fill in as many frame buffers as possible */
for( i = i_which_frame + 1; i < i_which_frame + FRAMES_NUM; i++ )
{ {
/* Check if frame buf is already filled */ msg_Dbg( p_aout, "detected underrun!" );
if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED ) }
continue; last_time = mtime;
if( ((i - i_which_frame) * FRAME_SIZE - l_latency) < 0 ) /* Try to fill in as many frame buffers as possible */
{ l_read /= p_aout->output.output.i_bytes_per_frame;
msg_Warn( p_aout, "dectected underrun!"); l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read;
} if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM);
l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE;
p_buffer = aout_OutputNextBuffer( p_aout, for( i = 0; i < l_free_slots; i++ )
mtime + I64C(1000000) * ((i - i_which_frame) * FRAME_SIZE - {
l_latency) / p_aout->output.output.i_rate, b_sleek ); aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout,
mtime + I64C(1000000) * (i * FRAME_SIZE + l_queued) /
p_aout->output.output.i_rate, b_sleek );
/* If there is no audio data available and we have some buffered /* If there is no audio data available and we have some buffered
* already, then just wait for the next time */ * already, then just wait for the next time */
if( !p_buffer && (i != i_which_frame + 1) ) break; if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break;
if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
!= VLC_SUCCESS )
break;
/* Mark the frame buffer as QUEUED */ if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM,
p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED; p_buffer ) != VLC_SUCCESS ) break;
} }
/* Sleep a reasonable amount of time */
l_queued += (i * FRAME_SIZE);
msleep( I64C(1000000) * l_queued / p_aout->output.output.i_rate / 2 );
} }
/* make sure the buffer isn't playing */ /* make sure the buffer isn't playing */
IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer ); IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
/* free the events */ /* free the event */
for( i = 0; i < FRAMES_NUM; i++ ) CloseHandle( p_notif->event );
CloseHandle( notification_events[i] );
msg_Dbg( p_notif, "DirectSoundThread exiting" ); msg_Dbg( p_notif, "DirectSoundThread exiting" );
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment