[vlc-devel] [PATCH] Remove aout_Packet API from WaveOut
Denis Charmet
typx at dinauz.org
Wed Dec 26 11:00:48 CET 2012
---
modules/audio_output/waveout.c | 465 +++++++++++-----------------------------
1 file changed, 130 insertions(+), 335 deletions(-)
diff --git a/modules/audio_output/waveout.c b/modules/audio_output/waveout.c
index 424ffd1..0fcdb38 100644
--- a/modules/audio_output/waveout.c
+++ b/modules/audio_output/waveout.c
@@ -65,13 +65,15 @@ static int PlayWaveOut ( audio_output_t *, HWAVEOUT, WAVEHDR *,
block_t *, bool );
static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
-static void* WaveOutThread( void * );
-static int WaveOutClearDoneBuffers(aout_sys_t *p_sys);
+static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
static int ReloadWaveoutDevices( vlc_object_t *, const char *,
char ***, char *** );
static uint32_t findDeviceID(char *);
+static int WaveOutTimeGet(audio_output_t * , mtime_t *);
+static void WaveOutFlush( audio_output_t *, bool);
+static void WaveOutPause( audio_output_t *, bool, mtime_t);
static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
@@ -83,28 +85,21 @@ static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
*****************************************************************************/
struct aout_sys_t
{
- aout_packet_t packet;
uint32_t i_wave_device_id; /* ID of selected output device */
HWAVEOUT h_waveout; /* handle to waveout instance */
WAVEFORMATEXTENSIBLE waveformat; /* audio format */
- WAVEHDR waveheader[FRAMES_NUM];
-
- vlc_thread_t thread;
- vlc_atomic_t abort;
- HANDLE event;
- HANDLE new_buffer_event;
-
- // rental from alsa.c to synchronize startup of audiothread
- int b_playing; /* playing status */
- mtime_t start_date;
+ size_t i_frames;
int i_repeat_counter;
int i_buffer_size;
+ int i_rate;
+ bool b_spdif;
+
uint8_t *p_silence_buffer; /* buffer we use to play silence */
float soft_gain;
@@ -112,6 +107,12 @@ struct aout_sys_t
uint8_t chans_to_reorder; /* do we need channel reordering */
uint8_t chan_table[AOUT_CHAN_MAX];
+
+
+ mtime_t i_played_length;
+
+ vlc_mutex_t lock;
+ vlc_cond_t cond;
};
#include "volume.h"
@@ -151,10 +152,10 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
{
vlc_value_t val;
- p_aout->time_get = aout_PacketTimeGet;
+ p_aout->time_get = WaveOutTimeGet;
p_aout->play = Play;
- p_aout->pause = NULL;
- p_aout->flush = aout_PacketFlush;
+ p_aout->pause = WaveOutPause;
+ p_aout->flush = WaveOutFlush;
/*
check for configured audio device!
@@ -226,8 +227,7 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
fmt->i_frame_length = A52_FRAME_NB;
p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
-
- aout_PacketInit( p_aout, &p_aout->sys->packet, A52_FRAME_NB, fmt );
+ p_aout->sys->b_spdif = true;
}
else
{
@@ -265,24 +265,13 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
aout_FormatPrepare( fmt );
p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
- aout_PacketInit( p_aout, &p_aout->sys->packet, FRAME_SIZE, fmt );
-#if 0
- /* Check for hardware volume support */
- WAVEOUTCAPS wocaps;
- if( waveOutGetDevCaps( (UINT_PTR)p_aout->sys->h_waveout,
- &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR
- && (wocaps.dwSupport & WAVECAPS_VOLUME) )
- { /* FIXME: this needs to be moved to Open() */
- p_aout->volume_set = VolumeSet;
- p_aout->mute_set = MuteSet;
- p_aout->sys->volume = 0xffff.fp0;
- p_aout->sys->mute = false;
- }
- else
-#endif
- aout_SoftVolumeStart( p_aout );
+ aout_SoftVolumeStart( p_aout );
+
+ p_aout->sys->b_spdif = false;
}
+ p_aout->sys->i_rate = fmt->i_rate;
+
waveOutReset( p_aout->sys->h_waveout );
/* Allocate silence buffer */
@@ -290,7 +279,6 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
malloc( p_aout->sys->i_buffer_size );
if( p_aout->sys->p_silence_buffer == NULL )
{
- aout_PacketDestroy( p_aout );
free( p_aout->sys );
return VLC_ENOMEM;
}
@@ -302,30 +290,8 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
p_aout->sys->i_buffer_size );
/* Now we need to setup our waveOut play notification structure */
- p_aout->sys->event = CreateEvent( NULL, FALSE, FALSE, NULL );
- p_aout->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->sys->b_playing = 0;
- p_aout->sys->start_date = 0;
-
-
- /* Then launch the notification thread */
- vlc_atomic_set( &p_aout->sys->abort, 0);
- if( vlc_clone( &p_aout->sys->thread,
- WaveOutThread, p_aout, VLC_THREAD_PRIORITY_OUTPUT ) )
- {
- msg_Err( p_aout, "cannot create WaveOutThread" );
- }
-
- /* We need to kick off the playback in order to have the callback properly
- * working */
- for( int i = 0; i < FRAMES_NUM; i++ )
- {
- p_aout->sys->waveheader[i].dwFlags = WHDR_DONE;
- p_aout->sys->waveheader[i].dwUser = 0;
- }
+ p_aout->sys->i_frames = 0;
+ p_aout->sys->i_played_length = 0;
return VLC_SUCCESS;
}
@@ -441,24 +407,26 @@ static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt )
* This doesn't actually play the buffer. This just stores the buffer so it
* can be played by the callback thread.
*****************************************************************************/
-static void Play( audio_output_t *_p_aout, block_t *block )
+static void Play( audio_output_t *p_aout, block_t *block )
{
- if( !_p_aout->sys->b_playing )
+ WAVEHDR * p_waveheader = (WAVEHDR *) malloc(sizeof(WAVEHDR));
+ if(!p_waveheader)
{
- _p_aout->sys->b_playing = 1;
-
- /* get the playing date of the first aout buffer */
- _p_aout->sys->start_date = block->i_pts;
-
- msg_Dbg( _p_aout, "Wakeup sleeping output thread.");
+ msg_Err(p_aout, "Couldn't alloc WAVEHDR");
+ return;
+ }
+ while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
+ p_aout->sys->b_spdif ) != VLC_SUCCESS )
- /* wake up the audio output thread */
- SetEvent( _p_aout->sys->event );
- } else {
- SetEvent( _p_aout->sys->new_buffer_event );
+ {
+ msg_Warn( p_aout, "Couln't write frame... sleeping");
+ msleep( block->i_length );
}
- aout_PacketPlay( _p_aout, block );
+ vlc_mutex_lock( &p_aout->sys->lock );
+ p_aout->sys->i_frames++;
+ p_aout->sys->i_played_length += block->i_length;
+ vlc_mutex_unlock( &p_aout->sys->lock );
}
/*****************************************************************************
@@ -469,24 +437,6 @@ static void Stop( audio_output_t *p_aout )
aout_sys_t *p_sys = p_aout->sys;
/* Before calling waveOutClose we must reset the device */
- vlc_atomic_set( &p_sys->abort, 1);
-
- /* wake up the audio thread, to recognize that p_aout died */
- SetEvent( p_sys->event );
- SetEvent( p_sys->new_buffer_event );
-
- vlc_join( p_sys->thread, NULL );
-
- /*
- 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)
{
@@ -506,17 +456,8 @@ static void Stop( audio_output_t *p_aout )
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...");
- }
+ WaveOutFlush( p_aout, true );
}
- } else {
- WaveOutClearDoneBuffers( p_sys );
}
/* now we can Close the device */
@@ -525,15 +466,8 @@ static void Stop( audio_output_t *p_aout )
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 );
- aout_PacketDestroy( p_aout );
+ p_aout->sys->i_played_length = 0;
}
/*****************************************************************************
@@ -706,6 +640,7 @@ static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
if( p_buffer != NULL )
{
p_waveheader->lpData = (LPSTR)p_buffer->p_buffer;
+ p_waveheader->dwBufferLength = p_buffer->i_buffer;
/*
copy the buffer to the silence buffer :) so in case we don't
get the next buffer fast enough (I will repeat this one a time
@@ -731,10 +666,10 @@ static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
}
}
p_waveheader->lpData = (LPSTR)p_aout->sys->p_silence_buffer;
+ p_waveheader->dwBufferLength = p_aout->sys->i_buffer_size;
}
p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
- p_waveheader->dwBufferLength = p_aout->sys->i_buffer_size;
p_waveheader->dwFlags = 0;
result = waveOutPrepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
@@ -762,241 +697,30 @@ static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
DWORD_PTR _p_aout,
DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
{
- (void)h_waveout; (void)dwParam1; (void)dwParam2;
+ (void)dwParam2;
audio_output_t *p_aout = (audio_output_t *)_p_aout;
- int i_queued_frames = 0;
+ WAVEHDR * p_waveheader = (WAVEHDR *) dwParam1;
if( uMsg != WOM_DONE ) return;
- if( vlc_atomic_get(&p_aout->sys->abort) ) return;
-
- /* Find out the current latency */
- for( int i = 0; i < FRAMES_NUM; i++ )
- {
- /* Check if frame buf is available */
- if( !(p_aout->sys->waveheader[i].dwFlags & WHDR_DONE) )
- {
- i_queued_frames++;
- }
- }
-
- /* Don't wake up the thread too much */
- if( i_queued_frames <= FRAMES_NUM/2 )
- SetEvent( p_aout->sys->event );
-}
-
-
-/****************************************************************************
- * WaveOutClearDoneBuffers: Clear all done marked buffers, and free buffer
- ****************************************************************************
- * return value is the number of still playing buffers in the queue
- ****************************************************************************/
-static int WaveOutClearDoneBuffers(aout_sys_t *p_sys)
-{
- WAVEHDR *p_waveheader = p_sys->waveheader;
- int i_queued_frames = 0;
-
- for( int i = 0; i < FRAMES_NUM; i++ )
- {
- if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
- p_waveheader[i].dwUser )
- {
- block_t *p_buffer =
- (block_t *)(p_waveheader[i].dwUser);
- /* Unprepare and free the buffers which has just been played */
- waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
- sizeof(WAVEHDR) );
-
- if( p_waveheader[i].dwUser != 1 )
- block_Release( p_buffer );
-
- p_waveheader[i].dwUser = 0;
- }
-
- /* Check if frame buf is available */
- if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
- {
- i_queued_frames++;
- }
- }
- return i_queued_frames;
-}
-
-/*****************************************************************************
- * WaveOutThread: this thread will capture play notification events.
- *****************************************************************************
- * We use this thread to feed new audio samples to the sound card because
- * we are not authorized to use waveOutWrite() directly in the waveout
- * callback.
- *****************************************************************************/
-static void* WaveOutThread( void *data )
-{
- audio_output_t *p_aout = data;
- aout_sys_t *p_sys = p_aout->sys;
- block_t *p_buffer = NULL;
- WAVEHDR *p_waveheader = p_sys->waveheader;
- int i, i_queued_frames;
- bool b_sleek;
- mtime_t next_date;
- int canc = vlc_savecancel ();
-
- /* We don't want any resampling when using S/PDIF */
- b_sleek = p_sys->packet.format.i_format == VLC_CODEC_SPDIFL;
-
- // wait for first call to "play()"
- while( !p_sys->start_date && !vlc_atomic_get(&p_aout->sys->abort) )
- WaitForSingleObject( p_sys->event, INFINITE );
- if( vlc_atomic_get(&p_aout->sys->abort) )
- return NULL;
-
- msg_Dbg( p_aout, "will start to play in %"PRId64" us",
- (p_sys->start_date - AOUT_MAX_PTS_ADVANCE/4)-mdate());
-
- // than wait a short time... before grabbing first frames
- mwait( p_sys->start_date - AOUT_MAX_PTS_ADVANCE/4 );
-
-#define waveout_warn(msg) msg_Warn( p_aout, "aout_PacketNext no buffer "\
- "got next_date=%d ms, "\
- "%d frames to play, %s",\
- (int)(next_date/(mtime_t)1000), \
- i_queued_frames, msg);
- next_date = mdate();
-
- while( !vlc_atomic_get(&p_aout->sys->abort) )
- {
- /* Cleanup and find out the current latency */
- i_queued_frames = WaveOutClearDoneBuffers( p_sys );
-
- if( vlc_atomic_get(&p_aout->sys->abort) ) return NULL;
-
- /* Try to fill in as many frame buffers as possible */
- for( i = 0; i < FRAMES_NUM; i++ )
- {
- /* Check if frame buf is available */
- if( p_waveheader[i].dwFlags & WHDR_DONE )
- {
- // next_date = mdate() + 1000000 * i_queued_frames /
- // p_aout->format.i_rate * p_aout->i_nb_samples;
-
- // the realtime has got our back-site:) to come in sync
- if(next_date < mdate())
- next_date = mdate();
-
-
- /* Take into account the latency */
- p_buffer = aout_PacketNext( p_aout, next_date );
- if(!p_buffer)
- {
-#if 0
- msg_Dbg( p_aout, "aout_PacketNext no buffer got "
- "next_date=%"PRId64" ms, %d frames to play",
- next_date/1000, i_queued_frames);
-#endif
- // means we are too early to request a new buffer?
- waveout_warn("waiting...")
- mwait( next_date - AOUT_MAX_PTS_ADVANCE/4 );
- next_date = mdate();
- p_buffer = aout_PacketNext( p_aout, next_date );
- }
-
- if( !p_buffer && i_queued_frames )
- {
- /* We aren't late so no need to play a blank sample */
- break;
- }
-
- if( p_buffer )
- {
- mtime_t buffer_length = p_buffer->i_length;
- next_date = next_date + buffer_length;
- }
-
- /* Do the channel reordering */
- if( p_buffer && p_sys->chans_to_reorder )
- {
- aout_ChannelReorder( p_buffer->p_buffer,
- p_buffer->i_buffer,
- p_sys->waveformat.Format.nChannels,
- p_sys->chan_table,
- p_sys->waveformat.Format.wBitsPerSample );
- }
-
- PlayWaveOut( p_aout, p_sys->h_waveout,
- &p_waveheader[i], p_buffer, b_sleek );
-
- i_queued_frames++;
- }
- }
+ WaveOutClearBuffer( h_waveout, p_waveheader );
- if( vlc_atomic_get(&p_aout->sys->abort) ) return NULL;
-
- /*
- deal with the case that the loop didn't fillup the buffer to the
- max - instead of waiting that half the buffer is played before
- fillup the waveout buffers, wait only for the next sample buffer
- to arrive at the play method...
-
- this will also avoid, that the last buffer is play until the
- end, and then trying to get more data, so it will also
- work - if the next buffer will arrive some ms before the
- last buffer is finished.
- */
- if(i_queued_frames < FRAMES_NUM)
- WaitForSingleObject( p_sys->new_buffer_event, INFINITE );
- else
- WaitForSingleObject( p_sys->event, INFINITE );
-
- }
-
-#undef waveout_warn
- vlc_restorecancel (canc);
- return NULL;
+ free(p_waveheader);
+ vlc_mutex_lock( &p_aout->sys->lock );
+ p_aout->sys->i_frames--;
+ vlc_cond_broadcast( &p_aout->sys->cond );
+ vlc_mutex_unlock( &p_aout->sys->lock );
}
-#if 0
-static int VolumeSet( audio_output_t *aout, float volume )
-{
- aout_sys_t *sys = aout->sys;
- const HWAVEOUT hwo = sys->h_waveout;
-
- unsigned vol = lroundf(volume * 0xffff.fp0);
- if (vol > 0xffff)
- {
- volume = 1.f;
- vol = 0xffff;
- }
+static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
+{
+ block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
+ /* Unprepare and free the buffers which has just been played */
+ waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
- if (!sys->soft_mute)
- {
- MMRESULT r = waveOutSetVolume(hwo, vol | (vol << 16));
- if (r != MMSYSERR_NOERROR)
- {
- msg_Err(aout, "cannot set mute: multimedia error %u", r);
- return -1;
- }
- }
- sys->volume = volume;
- aout_VolumeReport(aout, volume);
- return 0;
-}
-
-static int MuteSet( audio_output_t * p_aout, bool mute )
-{
- aout_sys_t *sys = p_aout->sys;
- const HWAVEOUT hwo = sys->h_waveout;
-
- uint16_t vol = mute ? 0 : lroundf(sys->volume * 0xffff.fp0);
- MMRESULT r = waveOutSetVolume(hwo, vol | (vol << 16));
- if (r != MMSYSERR_NOERROR)
- {
- msg_Err(p_aout, "cannot set mute: multimedia error %u", r);
- return -1;
- }
- sys->soft_mute = mute;
- aout_MuteReport(p_aout, mute);
- return 0;
+ if( p_waveheader->dwUser != 1 )
+ block_Release( p_buffer );
}
-#endif
/*
reload the configuration drop down list, of the Audio Devices
@@ -1081,6 +805,9 @@ static int Open(vlc_object_t *obj)
aout->start = Start;
aout->stop = Stop;
aout_SoftVolumeInit(aout);
+
+ vlc_mutex_init( &sys->lock );
+ vlc_cond_init( &sys->cond );
return VLC_SUCCESS;
}
@@ -1089,5 +816,73 @@ static void Close(vlc_object_t *obj)
audio_output_t *aout = (audio_output_t *)obj;
aout_sys_t *sys = aout->sys;
+ vlc_cond_destroy( &sys->cond );
+ vlc_mutex_destroy( &sys->lock );
+
free(sys);
}
+
+static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
+{
+ MMTIME mmtime;
+ mmtime.wType = TIME_SAMPLES;
+
+ if( !p_aout->sys->i_frames )
+ return -1;
+
+ if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
+ != MMSYSERR_NOERROR )
+ {
+ msg_Err( p_aout, "Couldn't get audio output position");
+ return -1;
+ }
+
+ mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
+ *delay = p_aout->sys->i_played_length - i_pos;
+ return 0;
+}
+
+static void WaveOutFlush( audio_output_t *p_aout, bool wait)
+{
+ MMRESULT res;
+ msg_Warn( p_aout, "Flushing %d", wait);
+ if( !wait )
+ {
+ res = waveOutReset( p_aout->sys->h_waveout );
+ p_aout->sys->i_played_length = 0;
+ if( res != MMSYSERR_NOERROR )
+ msg_Err( p_aout, "Couldn't flush waveout");
+ }
+ else
+ {
+ vlc_mutex_lock( &p_aout->sys->lock );
+ while( p_aout->sys->i_frames )
+ {
+ vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
+ }
+ vlc_mutex_unlock( &p_aout->sys->lock );
+ }
+}
+
+static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
+{
+ MMRESULT res;
+ if(pause)
+ {
+ res = waveOutPause( p_aout->sys->h_waveout );
+ if( res != MMSYSERR_NOERROR )
+ {
+ msg_Err( p_aout, "WaveOut couldn't pause (0x%x)", res);
+ return;
+ }
+ }
+ else
+ {
+ res = waveOutRestart( p_aout->sys->h_waveout );
+ if( res != MMSYSERR_NOERROR )
+ {
+ msg_Err( p_aout, "WaveOut couldn't resume playing (0x%x)", res);
+ return;
+ }
+ }
+}
--
1.7.10.4
More information about the vlc-devel
mailing list