Skip to content

Commit

Permalink
pulseaudio: Rework some initialization code and have better error rep…
Browse files Browse the repository at this point in the history
…orting

Rework Pulseaudio stream starting and initialization code little bit more
readable form. Commit also reworks stream starting to work more
realiable.

Commit reworks also error reporting as it should be first class
citizen if there is better stream handling and initialization
  • Loading branch information
illuusio committed Nov 24, 2023
1 parent 8a9ebfd commit 2f48888
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 99 deletions.
101 changes: 53 additions & 48 deletions src/hostapi/pulseaudio/pa_linux_pulseaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,52 +65,47 @@
extern char *__progname;

/* PulseAudio specific functions */
int PaPulseAudio_CheckConnection( PaPulseAudio_HostApiRepresentation * ptr )
int PaPulseAudio_CheckConnection( pa_context_state_t state )
{
pa_context_state_t state;
int retCode = paNotInitialized;


/* Sanity check if ptr if NULL don't go anywhere or
* it will SIGSEGV
/* Check if contenxt if good.
* If it is then check state
*/
if( !ptr )
{
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"PaPulseAudio_CheckConnection: Host API is NULL! Can't do anything about it" );
return -1;
}

if( !ptr->context || !ptr->mainloop )
{
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"PaPulseAudio_CheckConnection: PulseAudio context or mainloop are NULL" );
return -1;
}

state = pa_context_get_state(ptr->context);

if( !PA_CONTEXT_IS_GOOD(state) )
if( PA_CONTEXT_IS_GOOD(state) )
{
switch( state )
{
/* These can be found from
* https://freedesktop.org/software/pulseaudio/doxygen/def_8h.html
*/

case PA_CONTEXT_READY:
retCode = paNoError;
break;

case PA_CONTEXT_UNCONNECTED:
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"PaPulseAudio_CheckConnection: The context hasn't been connected yet (PA_CONTEXT_UNCONNECTED)" );
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
/* These are just here if they are needed in future */
break;

case PA_CONTEXT_TERMINATED:
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Connection terminated to Pulseaudio server. (PA_CONTEXT_TERMINATED)" );
retCode = paUnanticipatedHostError;
break;

case PA_CONTEXT_FAILED:
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"PaPulseAudio_CheckConnection: The connection failed or was disconnected. (PA_CONTEXT_FAILED)" );
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Connection failed or was disconnected from Pulseaudio server. (PA_CONTEXT_FAILED)" );
retCode = paUnanticipatedHostError;
break;
}

return -1;
}
return 0;

return retCode;
}

/* Create HostAPI presensentation */
Expand Down Expand Up @@ -238,6 +233,8 @@ void PaPulseAudio_CheckContextStateCb( pa_context * c,
return;
}

ptr->context_state = pa_context_get_state(c);

pa_threaded_mainloop_signal( ptr->mainloop, 0 );
}

Expand All @@ -252,7 +249,7 @@ void PaPulseAudio_ServerInfoCb( pa_context *c,
if( !c || !i )
{
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"PaPulseAudio_ServerInfoCb: Invalid context or can't get server info" );
"PaPulseAudio_ServerInfoCb: Invalid context or can't get server info" );
pa_threaded_mainloop_signal( pulseaudioHostApi->mainloop, 0 );
return;
}
Expand Down Expand Up @@ -441,7 +438,10 @@ void PaPulseAudio_SourceListCb( pa_context * c,
void PaPulseAudio_StreamStateCb( pa_stream * s,
void *userdata )
{
PaPulseAudio_Stream *stream = (PaPulseAudio_Stream *) userdata;
const pa_buffer_attr *pulseaudioBufferAttr = NULL;
pa_stream_state_t state = PA_STREAM_UNCONNECTED;

/* If you need debug pring enable these
* char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
*/
Expand All @@ -455,11 +455,10 @@ void PaPulseAudio_StreamStateCb( pa_stream * s,
return;
}

switch( pa_stream_get_state(s) )
{
case PA_STREAM_TERMINATED:
break;
state = pa_stream_get_state(s);

switch( state )
{
case PA_STREAM_CREATING:
break;

Expand All @@ -481,9 +480,15 @@ void PaPulseAudio_StreamStateCb( pa_stream * s,
}
break;

case PA_STREAM_TERMINATED:
PA_DEBUG( ("Portaudio %s: PA_STREAM_TERMINATED '%s'\n",
__FUNCTION__,
pa_strerror( pa_context_errno( pa_stream_get_context( s ) ) )) );
break;

case PA_STREAM_FAILED:
default:
PA_DEBUG( ("Portaudio %s: FAILED '%s'\n",
PA_DEBUG( ("Portaudio %s: PA_STREAM_TERMINATED '%s'\n",
__FUNCTION__,
pa_strerror( pa_context_errno( pa_stream_get_context( s ) ) )) );

Expand Down Expand Up @@ -582,20 +587,16 @@ PaError PaPulseAudio_Initialize( PaUtilHostApiRepresentation ** hostApi,
{
pa_threaded_mainloop_wait( pulseaudioHostApi->mainloop );

switch( pa_context_get_state( pulseaudioHostApi->context ) )
result = PaPulseAudio_CheckConnection( pulseaudioHostApi->context_state );

if( result != paNotInitialized && result != paNoError )
{
case PA_CONTEXT_READY:
ret = 1;
break;
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
goto error;
break;
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
goto error;
}

if( result == paNoError )
{
ret = 1;
}
}

Expand Down Expand Up @@ -1218,6 +1219,10 @@ PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
pa_stream_set_started_callback( stream->outputStream,
PaPulseAudio_StreamStartedCb,
stream );
pa_stream_set_underflow_callback( stream->outputStream,
PaPulseAudio_StreamUnderflowCb,
stream);

}

else
Expand Down
102 changes: 53 additions & 49 deletions src/hostapi/pulseaudio/pa_linux_pulseaudio_cb.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,49 @@ PaError PaPulseAudio_CloseStreamCb( PaStream * s )
return result;
}

PaError _PaPulseAudio_WaitStreamState( pa_threaded_mainloop *mainloop, pa_stream * stream )
{
pa_stream_state_t state = PA_STREAM_UNCONNECTED;
unsigned int wait = 0;
PaError result = paNoError;

while( wait < 1000 )
{
pa_threaded_mainloop_wait( mainloop );
PaPulseAudio_Lock( mainloop );
state = pa_stream_get_state( stream );
PaPulseAudio_UnLock( mainloop );

switch(state)
{
case PA_STREAM_READY:
result = paNoError;
wait = 10000;
break;
case PA_STREAM_FAILED:
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Creating stream failed. (PA_STREAM_FAILED)" );
result = paNotInitialized;
wait = 10000;
break;
case PA_STREAM_TERMINATED:
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Stream terminated. (PA_STREAM_TERMINATED)" );
result = paNotInitialized;
wait = 10000;
break;
}

/* Creating can take some time */
if( state != PA_STREAM_CREATING )
{
wait ++;
}
}

return result;
}

PaError PaPulseAudio_StartStreamCb( PaStream * s )
{
PaError ret = paNoError;
Expand Down Expand Up @@ -717,41 +760,23 @@ PaError PaPulseAudio_StartStreamCb( PaStream * s )
{
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
/* Zero means success */
if( ! pa_stream_connect_record( stream->inputStream,
if( pa_stream_connect_record( stream->inputStream,
pulseaudioName,
&stream->inputBufferAttr,
pulseaudioStreamFlags ) )
{
pa_stream_set_started_callback( stream->inputStream,
PaPulseAudio_StreamStartedCb,
stream );
}
else
{
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Can't create record stream" );
PA_DEBUG( ("Portaudio %s: Can't read audio!\n",
__FUNCTION__) );

goto startstreamcb_error;
}
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );

for( waitLoop = 0; waitLoop < 100; waitLoop ++ )
if( _PaPulseAudio_WaitStreamState( pulseaudioHostApi->mainloop, stream->outputStream ) != paNoError )
{
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
pulseaudioState = pa_stream_get_state( stream->inputStream );
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );

if( pulseaudioState == PA_STREAM_READY )
{
break;
}
else if( pulseaudioState == PA_STREAM_FAILED ||
pulseaudioState == PA_STREAM_TERMINATED )
{
goto startstreamcb_error;
}

usleep(10000);
goto startstreamcb_error;
}
}
else
Expand Down Expand Up @@ -825,45 +850,24 @@ PaError PaPulseAudio_StartStreamCb( PaStream * s )
{
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );

if ( ! pa_stream_connect_playback( stream->outputStream,
if ( pa_stream_connect_playback( stream->outputStream,
pulseaudioName,
&stream->outputBufferAttr,
pulseaudioStreamFlags,
NULL,
NULL ) )
{
pa_stream_set_underflow_callback( stream->outputStream,
PaPulseAudio_StreamUnderflowCb,
stream);
pa_stream_set_started_callback( stream->outputStream,
PaPulseAudio_StreamStartedCb,
stream );
}
else
{
PA_PULSEAUDIO_SET_LAST_HOST_ERROR( 0,
"Can't create playback stream" );
PA_DEBUG( ("Portaudio %s: Can't write audio!\n",
__FUNCTION__) );
goto startstreamcb_error;
}
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );

for( waitLoop = 0; waitLoop < 100; waitLoop ++ )
if( _PaPulseAudio_WaitStreamState( pulseaudioHostApi->mainloop, stream->outputStream ) != paNoError )
{
PaPulseAudio_Lock( pulseaudioHostApi->mainloop );
pulseaudioState = pa_stream_get_state( stream->outputStream );
PaPulseAudio_UnLock( pulseaudioHostApi->mainloop );

if( pulseaudioState = PA_STREAM_READY )
{
break;
}
else if( pulseaudioState == PA_STREAM_FAILED ||
pulseaudioState == PA_STREAM_TERMINATED )
{
goto startstreamcb_error;
}

usleep(10000);
goto startstreamcb_error;
}

}
Expand Down
5 changes: 3 additions & 2 deletions src/hostapi/pulseaudio/pa_linux_pulseaudio_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extern "C"
/* prototypes for functions declared in this file */

#define PA_PULSEAUDIO_SET_LAST_HOST_ERROR(errorCode, errorText) \
PaUtil_SetLastHostErrorInfo(paInDevelopment, errorCode, errorText)
PaUtil_SetLastHostErrorInfo(paPulseAudio, errorCode, errorText)

#define PAPULSEAUDIO_MAX_DEVICECOUNT 1024
#define PAPULSEAUDIO_MAX_DEVICENAME 1024
Expand Down Expand Up @@ -104,6 +104,7 @@ typedef struct
pa_context *context;
int deviceCount;
pa_time_event *timeEvent;
pa_context_state_t context_state;
}
PaPulseAudio_HostApiRepresentation;

Expand Down Expand Up @@ -235,7 +236,7 @@ double GetStreamCpuLoad( PaStream * stream );
PaPulseAudio_HostApiRepresentation *PaPulseAudio_New( void );
void PaPulseAudio_Free( PaPulseAudio_HostApiRepresentation * ptr );

int PaPulseAudio_CheckConnection( PaPulseAudio_HostApiRepresentation * ptr );
int PaPulseAudio_CheckConnection( pa_context_state_t state );

void PaPulseAudio_CheckContextStateCb( pa_context * c,
void *userdata );
Expand Down

0 comments on commit 2f48888

Please sign in to comment.