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 + -
显示快捷键?