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

📄 pasound.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: pasound.c 974 2007-02-19 01:13:53Z bennylp $ */
/* 
 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjmedia/sound.h>
#include <pjmedia/errno.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/string.h>
#include <portaudio.h>

#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND

#define THIS_FILE	"pasound.c"

static struct snd_mgr
{
    pj_pool_factory *factory;
} snd_mgr;

/* 
 * Sound stream descriptor.
 * This struct may be used for both unidirectional or bidirectional sound
 * streams.
 */
struct pjmedia_snd_stream
{
    pj_pool_t		*pool;
    pj_str_t		 name;
    pjmedia_dir		 dir;
    int			 play_id;
    int			 rec_id;
    int			 bytes_per_sample;
    pj_uint32_t		 samples_per_sec;
    unsigned		 samples_per_frame;
    int			 channel_count;

    PaStream		*rec_strm;
    PaStream		*play_strm;

    void		*user_data;
    pjmedia_snd_rec_cb   rec_cb;
    pjmedia_snd_play_cb  play_cb;

    pj_uint32_t		 timestamp;
    pj_uint32_t		 underflow;
    pj_uint32_t		 overflow;

    pj_bool_t		 quit_flag;

    pj_bool_t		 rec_thread_exited;
    pj_bool_t		 rec_thread_initialized;
    pj_thread_desc	 rec_thread_desc;
    pj_thread_t		*rec_thread;

    pj_bool_t		 play_thread_exited;
    pj_bool_t		 play_thread_initialized;
    pj_thread_desc	 play_thread_desc;
    pj_thread_t		*play_thread;
};


static int PaRecorderCallback(const void *input, 
			      void *output,
			      unsigned long frameCount,
			      const PaStreamCallbackTimeInfo* timeInfo,
			      PaStreamCallbackFlags statusFlags,
			      void *userData )
{
    pjmedia_snd_stream *stream = userData;
    pj_status_t status;

    PJ_UNUSED_ARG(output);
    PJ_UNUSED_ARG(timeInfo);

    if (stream->quit_flag)
	goto on_break;

    if (input == NULL)
	return paContinue;

    if (stream->rec_thread_initialized == 0) {
	status = pj_thread_register("pa_rec", stream->rec_thread_desc, 
				    &stream->rec_thread);
	stream->rec_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
    }

    if (statusFlags & paInputUnderflow)
	++stream->underflow;
    if (statusFlags & paInputOverflow)
	++stream->overflow;

    stream->timestamp += frameCount;

    status = (*stream->rec_cb)(stream->user_data, stream->timestamp, 
			       (void*)input, 
			       frameCount * stream->bytes_per_sample *
				 stream->channel_count);
    
    if (status==0) 
	return paContinue;

on_break:
    stream->rec_thread_exited = 1;
    return paAbort;
}

static int PaPlayerCallback( const void *input, 
			     void *output,
			     unsigned long frameCount,
			     const PaStreamCallbackTimeInfo* timeInfo,
			     PaStreamCallbackFlags statusFlags,
			     void *userData )
{
    pjmedia_snd_stream *stream = userData;
    pj_status_t status;
    unsigned size = frameCount * stream->bytes_per_sample *
		    stream->channel_count;

    PJ_UNUSED_ARG(input);
    PJ_UNUSED_ARG(timeInfo);

    if (stream->quit_flag)
	goto on_break;

    if (output == NULL)
	return paContinue;

    if (stream->play_thread_initialized == 0) {
	status = pj_thread_register("portaudio", stream->play_thread_desc,
				    &stream->play_thread);
	stream->play_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Player thread started"));
    }

    if (statusFlags & paOutputUnderflow)
	++stream->underflow;
    if (statusFlags & paOutputOverflow)
	++stream->overflow;

    stream->timestamp += frameCount;

    status = (*stream->play_cb)(stream->user_data, stream->timestamp, 
			        output, size);
    
    if (status==0) 
	return paContinue;

on_break:
    stream->play_thread_exited = 1;
    return paAbort;
}


static int PaRecorderPlayerCallback( const void *input, 
				     void *output,
				     unsigned long frameCount,
				     const PaStreamCallbackTimeInfo* timeInfo,
				     PaStreamCallbackFlags statusFlags,
				     void *userData )
{
    int rc;

    rc = PaRecorderCallback(input, output, frameCount, timeInfo,
			    statusFlags, userData);
    if (rc != paContinue)
	return rc;

    rc = PaPlayerCallback(input, output, frameCount, timeInfo,
			  statusFlags, userData);
    return rc;
}


/*
 * Init sound library.
 */
PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
{
    int err;

    snd_mgr.factory = factory;
    err = Pa_Initialize();

    PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
    PJ_LOG(4,(THIS_FILE, "PortAudio host api count=%d",
			 Pa_GetHostApiCount()));
    PJ_LOG(4,(THIS_FILE, "Sound device count=%d",
			 pjmedia_snd_get_dev_count()));

    return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
}


/*
 * Get device count.
 */
PJ_DEF(int) pjmedia_snd_get_dev_count(void)
{
    return Pa_GetDeviceCount();
}


/*
 * Get device info.
 */
PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
{
    static pjmedia_snd_dev_info info;
    const PaDeviceInfo *pa_info;

    pa_info = Pa_GetDeviceInfo(index);
    if (!pa_info)
	return NULL;

    pj_bzero(&info, sizeof(info));
    strncpy(info.name, pa_info->name, sizeof(info.name));
    info.name[sizeof(info.name)-1] = '\0';
    info.input_count = pa_info->maxInputChannels;
    info.output_count = pa_info->maxOutputChannels;
    info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;

    return &info;
}


/* Get PortAudio default input device ID */
static int pa_get_default_input_dev(int channel_count)
{
    int i, count;

    /* Enumerate the host api's for the default devices, and return
     * the device with suitable channels.
     */
    count = Pa_GetHostApiCount();
    for (i=0; i < count; ++i) {
	const PaHostApiInfo *pHAInfo;

	pHAInfo = Pa_GetHostApiInfo(i);
	if (!pHAInfo)
	    continue;

	if (pHAInfo->defaultInputDevice >= 0) {
	    const PaDeviceInfo *paDevInfo;

	    paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultInputDevice);

	    if (paDevInfo->maxInputChannels >= channel_count)
		return pHAInfo->defaultInputDevice;
	}
    }

    /* If still no device is found, enumerate all devices */
    count = Pa_GetDeviceCount();
    for (i=0; i<count; ++i) {
	const PaDeviceInfo *paDevInfo;

	paDevInfo = Pa_GetDeviceInfo(i);
	if (paDevInfo->maxInputChannels >= channel_count)
	    return i;
    }
    
    return -1;
}

/* Get PortAudio default output device ID */
static int pa_get_default_output_dev(int channel_count)
{
    int i, count;

    /* Enumerate the host api's for the default devices, and return
     * the device with suitable channels.
     */
    count = Pa_GetHostApiCount();
    for (i=0; i < count; ++i) {
	const PaHostApiInfo *pHAInfo;

	pHAInfo = Pa_GetHostApiInfo(i);
	if (!pHAInfo)
	    continue;

	if (pHAInfo->defaultOutputDevice >= 0) {
	    const PaDeviceInfo *paDevInfo;

	    paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultOutputDevice);

	    if (paDevInfo->maxOutputChannels >= channel_count)
		return pHAInfo->defaultOutputDevice;
	}
    }

    /* If still no device is found, enumerate all devices */
    count = Pa_GetDeviceCount();
    for (i=0; i<count; ++i) {
	const PaDeviceInfo *paDevInfo;

	paDevInfo = Pa_GetDeviceInfo(i);
	if (paDevInfo->maxOutputChannels >= channel_count)
	    return i;
    }
    
    return -1;
}


/*
 * Open stream.
 */
PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index,
					  unsigned clock_rate,
					  unsigned channel_count,
					  unsigned samples_per_frame,
					  unsigned bits_per_sample,
					  pjmedia_snd_rec_cb rec_cb,
					  void *user_data,
					  pjmedia_snd_stream **p_snd_strm)
{
    pj_pool_t *pool;
    pjmedia_snd_stream *stream;
    PaStreamParameters inputParam;
    int sampleFormat;
    const PaDeviceInfo *paDevInfo = NULL;
    const PaHostApiInfo *paHostApiInfo = NULL;
    unsigned paFrames, paRate, paLatency;
    const PaStreamInfo *paSI;
    PaError err;

    if (index < 0) {
	index = pa_get_default_input_dev(channel_count);
	if (index < 0) {
	    /* No such device. */
	    return PJMEDIA_ENOSNDREC;
	}
    }

    paDevInfo = Pa_GetDeviceInfo(index);
    if (!paDevInfo) {
	/* Assumed it is "No such device" error. */
	return PJMEDIA_ESNDINDEVID;
    }

    if (bits_per_sample == 8)
	sampleFormat = paUInt8;
    else if (bits_per_sample == 16)
	sampleFormat = paInt16;
    else if (bits_per_sample == 32)
	sampleFormat = paInt32;
    else
	return PJMEDIA_ESNDINSAMPLEFMT;
    
    pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
    if (!pool)
	return PJ_ENOMEM;

    stream = pj_pool_zalloc(pool, sizeof(*stream));
    stream->pool = pool;
    pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
    stream->dir = PJMEDIA_DIR_CAPTURE;
    stream->rec_id = index;
    stream->play_id = -1;
    stream->user_data = user_data;
    stream->samples_per_sec = clock_rate;
    stream->samples_per_frame = samples_per_frame;
    stream->bytes_per_sample = bits_per_sample / 8;
    stream->channel_count = channel_count;
    stream->rec_cb = rec_cb;

    pj_bzero(&inputParam, sizeof(inputParam));
    inputParam.device = index;
    inputParam.channelCount = channel_count;
    inputParam.hostApiSpecificStreamInfo = NULL;
    inputParam.sampleFormat = sampleFormat;
    inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;

    paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);

    /* Frames in PortAudio is number of samples in a single channel */
    paFrames = samples_per_frame / channel_count;

    err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
			 clock_rate, paFrames, 
			 paClipOff, &PaRecorderCallback, stream );
    if (err != paNoError) {
	pj_pool_release(pool);
	return PJMEDIA_ERRNO_FROM_PORTAUDIO(err);
    }

    paSI = Pa_GetStreamInfo(stream->rec_strm);
    paRate = (unsigned)paSI->sampleRate;
    paLatency = (unsigned)(paSI->inputLatency * 1000);

    PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample "
			 "rate=%d, ch=%d, "

⌨️ 快捷键说明

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