⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pa_unix_oss.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
 *
 * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
 */
static void OnExit( void *data )
{
    PaOssStream *stream = (PaOssStream *) data;
    assert( data );

    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );

    PaOssStream_Stop( stream, stream->callbackAbort );
    
    PA_DEBUG(( "OnExit: Stoppage\n" ));

    /* Eventually notify user all buffers have played */
    if( stream->streamRepresentation.streamFinishedCallback )
        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );

    stream->callbackAbort = 0;      /* Clear state */
    stream->isActive = 0;
}

static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
{
    PaError result = paNoError;

    if( stream->capture )
    {
        PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
                stream->capture->hostChannelCount );
        PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
    }
    if( stream->playback )
    {
        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
                stream->playback->hostChannelCount );
        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
    }

    return result;
}

/** Thread procedure for callback processing.
 *
 * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
 * callback should be used for buffer priming. When the stream is cancelled a separate function will
 * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
 * before StopStream() or AbortStream() are called).
 */
static void *PaOSS_AudioThreadProc( void *userData )
{
    PaError result = paNoError;
    PaOssStream *stream = (PaOssStream*)userData;
    unsigned long framesAvail=0, framesProcessed;
    int callbackResult = paContinue;
    int triggered = stream->triggered;  /* See if SNDCTL_DSP_TRIGGER has been issued already */
    int initiateProcessing = triggered;    /* Already triggered? */
    PaStreamCallbackFlags cbFlags = 0;  /* We might want to keep state across iterations */
    
    /*
#if ( SOUND_VERSION > 0x030904 )
        audio_errinfo errinfo;
#endif
*/
    
    assert( stream );

    pthread_cleanup_push( &OnExit, stream );	/* Execute OnExit when exiting */

    /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
     * playback in sync, when the stream is restarted after being stopped we simply start by reading/
     * writing.
     */
    PA_ENSURE( PaOssStream_Prepare( stream ) );

    /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
    if( initiateProcessing )
    {
        /* Make sure devices are in blocking mode */
        if( stream->capture )
            ModifyBlocking( stream->capture->fd, 1 );
        if( stream->playback )
            ModifyBlocking( stream->playback->fd, 1 );
    }

    while( 1 )
    {
        PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */

        pthread_testcancel();

        if( stream->callbackStop && callbackResult == paContinue )
        {
            PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
            callbackResult = paComplete;
        }

        /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
         * the stream has been recently started, we will have to go right ahead and read/write in blocking
         * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
         * to non-blocking mode.
         */
        if( !initiateProcessing )
        {
            PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );  /* Wait on available frames */
            assert( framesAvail % stream->framesPerHostBuffer == 0 );
        }
        else
        {
            framesAvail = stream->framesPerHostBuffer;
        }

        while( framesAvail > 0 )
        {
            unsigned long frames = framesAvail;

            pthread_testcancel();

            PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );

            /* Read data */
            if ( stream->capture )
            {
                PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
                assert( frames == framesAvail );
            }

#if ( SOUND_VERSION >= 0x030904 )
            /*
               Check with OSS to see if there have been any under/overruns
               since last time we checked.
               */
            /*
            if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
            {
                if( errinfo.play_underruns )
                    cbFlags |= paOutputUnderflow ;
                if( errinfo.record_underruns )
                    cbFlags |= paInputUnderflow ;
            }
            else
                PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
                */
#endif

            PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
                    cbFlags );
            cbFlags = 0;
            PA_ENSURE( SetUpBuffers( stream, framesAvail ) );

            framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
                    &callbackResult );
            assert( framesProcessed == framesAvail );
            PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );

            if ( stream->playback )
            {
                frames = framesAvail;

                PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
                assert( frames == framesAvail );

                /* TODO: handle bytesWritten != bytesRequested (slippage?) */
            }

            framesAvail -= framesProcessed;
            stream->framesProcessed += framesProcessed;

            if( callbackResult != paContinue )
                break;
        }

        if( initiateProcessing || !triggered )
        {
            /* Non-blocking */
            if( stream->capture )
                PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
            if( stream->playback && !stream->sharedDevice )
                PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );

            initiateProcessing = 0;
            sem_post( &stream->semaphore );
        }

        if( callbackResult != paContinue )
        {
            stream->callbackAbort = callbackResult == paAbort;
            if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
                break;
        }
    }

    pthread_cleanup_pop( 1 );

error:
    pthread_exit( NULL );
}

/** Close the stream.
 *
 */
static PaError CloseStream( PaStream* s )
{
    PaError result = paNoError;
    PaOssStream *stream = (PaOssStream*)s;

    assert( stream );

    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
    PaOssStream_Terminate( stream );

    return result;
}

/** Start the stream.
 *
 * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
 * callback will be repeatedly called in a separate thread. If a separate thread is started this function
 * will block untill it has started processing audio, otherwise audio processing is started directly.
 */
static PaError StartStream( PaStream *s )
{
    PaError result = paNoError;
    PaOssStream *stream = (PaOssStream*)s;

    stream->isActive = 1;
    stream->isStopped = 0;
    stream->lastPosPtr = 0;
    stream->lastStreamBytes = 0;
    stream->framesProcessed = 0;

    /* only use the thread for callback streams */
    if( stream->bufferProcessor.streamCallback )
    {
        PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
        sem_wait( &stream->semaphore );
    }
    else
        PA_ENSURE( PaOssStream_Prepare( stream ) );

error:
    return result;
}

static PaError RealStop( PaOssStream *stream, int abort )
{
    PaError result = paNoError;

    if( stream->callbackMode )
    {
        if( abort )
            stream->callbackAbort = 1;
        else
            stream->callbackStop = 1;

        PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );

        stream->callbackStop = stream->callbackAbort = 0;
    }
    else
        PA_ENSURE( PaOssStream_Stop( stream, abort ) );

    stream->isStopped = 1;

error:
    return result;
}

/** Stop the stream.
 *
 * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
 * buffers.
 */
static PaError StopStream( PaStream *s )
{
    return RealStop( (PaOssStream *)s, 0 );
}

/** Abort the stream.
 *
 * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
 * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
 * the OSS device.
 */
static PaError AbortStream( PaStream *s )
{
    return RealStop( (PaOssStream *)s, 1 );
}

/** Is the stream in the Stopped state.
 *
 */
static PaError IsStreamStopped( PaStream *s )
{
    PaOssStream *stream = (PaOssStream*)s;

    return (stream->isStopped);
}

/** Is the stream in the Active state.
 *
 */
static PaError IsStreamActive( PaStream *s )
{
    PaOssStream *stream = (PaOssStream*)s;

    return (stream->isActive);
}

static PaTime GetStreamTime( PaStream *s )
{
    PaOssStream *stream = (PaOssStream*)s;
    count_info info;
    int delta;

    if( stream->playback ) {
        if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
            delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
        }
    }
    else {
        if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
            delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
            return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
        }
    }

    /* the ioctl failed, but we can still give a coarse estimate */

    return stream->framesProcessed / stream->sampleRate;
}


static double GetStreamCpuLoad( PaStream* s )
{
    PaOssStream *stream = (PaOssStream*)s;

    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}


/*
    As separate stream interfaces are used for blocking and callback
    streams, the following functions can be guaranteed to only be called
    for blocking streams.
*/


static PaError ReadStream( PaStream* s,
                           void *buffer,
                           unsigned long frames )
{
    PaOssStream *stream = (PaOssStream*)s;
    int bytesRequested, bytesRead;
    unsigned long framesRequested;
    void *userBuffer;

    /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
     * so we copy the user provided pointers */
    if( stream->bufferProcessor.userInputIsInterleaved )
        userBuffer = buffer;
    else /* Copy channels into local array */
  

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -