pasound.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 818 行 · 第 1/2 页

C
818
字号
/* $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 + =
减小字号Ctrl + -
显示快捷键?