Hi Benny and list, I've been checking out pjsip-0.7.0 for sometime and desperately need whatever help that u guys can. All i want is to run the whole pjsip package minus port audio, coz my sound system is a little bit different. I took a leaf out of symbian_sound.cpp and modified pasound.c to fit my arm system. Using snd_info sample application i can see that it can open and close the audio correctly. Now i'm stuck at the callbacks - i'm unable to figure out how PaRecorderCallback() and PaPlayerCallback() performs audio read and write. For example in - static int PaRecorderCallback(const void *input, void *output, unsigned long frameCount, //const PaStreamCallbackTimeInfo* timeInfo, //PaStreamCallbackFlags statusFlags, void *userData ); both *input and *output looks unused, while userData hold pj_media stream info. How does the function returns the audio data that was read. Maybe i've done it wrong. Is there other ways to port pjmedia to a custom audio platform? Thanks for the help, Nigel Modified pasound.c /* $Id: pasound.c 1266 2007-05-11 15:14:34Z bennylp $ */ /* * Copyright (C) 2003-2007 Benny Prijono <benny at 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> #define ACC_MVL 1 #define CZDEBUG 1 #ifdef ACC_MVL #include <errno.h> typedef short Uint8; #include "/home/nigel1/platform/kernel/linux-2.6.16.16/arch/arm/mach-mv88w8xx8/audio/audio.h" //#include <signal.h> //#include <sys/wait.h> #include <fcntl.h> //#include <sys/stat.h> //#include <sys/mman.h> //#include <sys/types.h> //#include <sys/poll.h> #define AUDIO_DEVICE_PATH "/dev/audio" #define paUInt8 8 #define paInt16 16 #define paInt32 32 #define paAbort -1 #define paContinue 0 int audfd = -1; pjmedia_dir aud_dir; #else #include <portaudio.h> #endif #ifdef CZDEBUG #define cztrace(x) printf x; #else #define cztrace(x) #endif #if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND #define THIS_FILE "pasound.c" static int snd_init_count; 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 ) { cztrace(("PaRecorderCallback frameCount=%d\n", frameCount)); pjmedia_snd_stream *stream = (pjmedia_snd_stream*) userData; //signed short buf[160]; //read(stream->rec_id, buf, 160); //return read(stream->rec_id, output, frameCount); #if 0 pjmedia_snd_stream *stream = (pjmedia_snd_stream*) userData; pj_status_t status; //PJ_UNUSED_ARG(output); //PJ_UNUSED_ARG(timeInfo); if (stream->quit_flag){ cztrace(("PaRecorderCallback stream->quit_flag\n")); goto on_break; } if (input == NULL){ cztrace(("PaRecorderCallback input == NULL\n")); 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; #endif } static int PaPlayerCallback( const void *input, void *output, unsigned long frameCount, //const PaStreamCallbackTimeInfo* timeInfo, //PaStreamCallbackFlags statusFlags, void *userData ) { #if 0 pjmedia_snd_stream *stream = (pjmedia_snd_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){ cztrace(("PaPlayerCallback stream->quit_flag\n")); goto on_break; } if (output == NULL){ cztrace(("PaPlayerCallback output == NULL\n")); 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; #endif } static int PaRecorderPlayerCallback( const void *input, void *output, unsigned long frameCount, //const PaStreamCallbackTimeInfo* timeInfo, //PaStreamCallbackFlags statusFlags, void *userData ) { int rc; cztrace(("PaRecorderPlayerCallback\n")); rc = PaRecorderCallback(input, output, frameCount, userData); if (rc != paContinue) return rc; rc = PaPlayerCallback(input, output, frameCount, userData); return rc; } /* Logging callback from PA */ static void pa_log_cb(const char *log) { PJ_LOG(5,(THIS_FILE, "PA message: %s", log)); } /* We should include pa_debugprint.h for this, but the header * is not available publicly. :( */ typedef void (*PaUtilLogCallback ) (const char *log); void PaUtil_SetDebugPrintFunction(PaUtilLogCallback cb); /* * Init sound library. */ PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) { cztrace(("pjmedia_snd_init\n")); if (++snd_init_count == 1) { int err; PaUtil_SetDebugPrintFunction(&pa_log_cb); snd_mgr.factory = factory; err = PJ_SUCCESS; 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; } } /* * Get device count. */ PJ_DEF(int) pjmedia_snd_get_dev_count(void) { cztrace(("pjmedia_snd_get_dev_count\n")); return 1; } /* * Get device info. */ PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) { static pjmedia_snd_dev_info info; cztrace(("pjmedia_snd_get_dev_info index=%d\n", index)); //if(index != audfd) return NULL; pj_bzero(&info, sizeof(info)); strncpy(info.name, "ACC MVL SOUND", strlen("ACC MVL SOUND") ); //pa_info->name, sizeof(info.name)); info.input_count = 1; //pa_info->maxInputChannels; info.output_count = 1; //pa_info->maxOutputChannels; info.default_samples_per_sec = 8000; //(unsigned)pa_info->defaultSampleRate; return &info; } /* Get PortAudio default input device ID */ static int pa_get_default_input_dev(int channel_count) { cztrace(("pa_get_default_input_dev\n")); if(audfd!=-1) return audfd; else return -1; } /* Get PortAudio default output device ID */ static int pa_get_default_output_dev(int channel_count) { cztrace(("pa_get_default_output_dev\n")); if(audfd!=-1) return audfd; else 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) { cztrace(("pjmedia_snd_open_rec\n")); pj_pool_t *pool; pjmedia_snd_stream *stream; int sampleFormat; const pjmedia_snd_dev_info *paDevInfo = NULL; unsigned paFrames, paRate, paLatency; if(audfd<0){ audfd = open(AUDIO_DEVICE_PATH, O_WRONLY); if (audfd < 0) { pj_pool_release(pool); return -1; } } index = audfd; aud_dir=PJMEDIA_DIR_CAPTURE; paDevInfo = pjmedia_snd_get_dev_info(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_T(pool, pjmedia_snd_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; /* Frames in PortAudio is number of samples in a single channel */ paFrames = samples_per_frame / channel_count; paRate = 8000; paLatency = 16; //(unsigned)(paSI->inputLatency * 1000); PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample " "rate=%d, ch=%d, " "bits=%d, %d samples per frame, latency=%d ms", paDevInfo->name, "ACC_MVL", //paHostApiInfo->name, paRate, channel_count, bits_per_sample, samples_per_frame, paLatency)); *p_snd_strm = stream; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { cztrace(("pjmedia_snd_open_player\n")); pj_pool_t *pool; pjmedia_snd_stream *stream; int sampleFormat; const pjmedia_snd_dev_info *paDevInfo = NULL; unsigned paFrames, paRate, paLatency; if(audfd<0){ audfd = open(AUDIO_DEVICE_PATH, O_WRONLY); if (audfd < 0) { pj_pool_release(pool); return -1; } } index = audfd; aud_dir=PJMEDIA_DIR_PLAYBACK; paDevInfo = pjmedia_snd_get_dev_info(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_T(pool, pjmedia_snd_stream); stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, paDevInfo->name); stream->dir = stream->dir = PJMEDIA_DIR_PLAYBACK; stream->play_id = index; stream->rec_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->play_cb = play_cb; paRate = 8000; paLatency = 16; //(unsigned)(paSI->outputLatency * 1000); PJ_LOG(5,(THIS_FILE, "Opened device %d: %s(%s) for playing, sample rate=%d" ", ch=%d, " "bits=%d, %d samples per frame, latency=%d ms", index, paDevInfo->name, "ACC_MVL", //paHostApiInfo->name, paRate, channel_count, bits_per_sample, samples_per_frame, paLatency)); *p_snd_strm = stream; return PJ_SUCCESS; } /* * Open both player and recorder. */ PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { pj_pool_t *pool; pjmedia_snd_stream *stream; int sampleFormat; const pjmedia_snd_dev_info *paDevInfo = NULL; unsigned paFrames, paRate, paInputLatency, paOutputLatency; int arg, ret; unsigned char dataStr[2]; if(clock_rate!=8000) return -1; cztrace(("pjmedia_snd_open rec_id=%d play_id=%d clock_rate=%d channel_count=%d\n", rec_id,play_id,clock_rate,channel_count)); cztrace(("pjmedia_snd_open samples_per_frame=%d bits_per_sample=%d\n", samples_per_frame,bits_per_sample)); if(audfd<0){ audfd = open(AUDIO_DEVICE_PATH, O_RDWR); if (audfd < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } } //#if 0 arg = 0; ret = ioctl(audfd, AUDIO_SET_MONO, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = 0; ret = ioctl(audfd, AUDIO_SET_TXDISCARDTHRE, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = channel_count; ret = ioctl(audfd, AUDIO_AKM_SET_CHANNEL, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = clock_rate; /* sampling rate */ ret = ioctl(audfd, AUDIO_SET_SAMPLINGRATE, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = bits_per_sample; /* sample size */ ret = ioctl(audfd, AUDIO_SET_TXBITPERSAMPLE, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = bits_per_sample; /* sample size */ ret = ioctl(audfd, AUDIO_SET_RXBITPERSAMPLE, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = clock_rate; /* sampling rate */ ret = ioctl(audfd, AUDIO_AKM_SET_SPEED, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = bits_per_sample; ret = ioctl(audfd, AUDIO_AKM_SET_FMT, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = 10; ret = ioctl(audfd, AUDIO_SET_TXTRHESHOLD, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = 10; ret = ioctl(audfd, AUDIO_SET_RXTRHESHOLD, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } arg = 54; /* audio enable */ ret = ioctl(audfd, AUDIO_START_RXTX, arg); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } // vol dataStr[0] = 0x0A; dataStr[1] = 255-205; ret = ioctl(audfd, AUDIO_SET_AKM_REG, dataStr); if(ret < 0) { printf("[%s-%d]-errno:%d\n", __FUNCTION__, __LINE__, errno); goto err_out; } //#endif cztrace(("pjmedia_snd_open audfd=%d\n", audfd)); play_id = rec_id = audfd; aud_dir=PJMEDIA_DIR_CAPTURE_PLAYBACK; paDevInfo = pjmedia_snd_get_dev_info(rec_id); 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_T(pool, pjmedia_snd_stream); stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, paDevInfo->name); stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; stream->play_id = play_id; stream->rec_id = rec_id; 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; stream->play_cb = play_cb; /* Frames in PortAudio is number of samples in a single channel */ paFrames = samples_per_frame / channel_count; paRate = 8000; //(unsigned)(paSI->sampleRate); paInputLatency = 16; //(unsigned)(paSI->inputLatency * 1000); paOutputLatency = 16;//(unsigned)(paSI->outputLatency * 1000); PJ_LOG(5,(THIS_FILE, "Opened device %s(%s)/%s(%s) for recording and " "playback, sample rate=%d, ch=%d, " "bits=%d, %d samples per frame, input latency=%d ms, " "output latency=%d ms", paDevInfo->name, "ACC_MVL", //paRecHostApiInfo->name, paDevInfo->name, "ACC_MVL", //paPlayHostApiInfo->name, paRate, channel_count, bits_per_sample, samples_per_frame, paInputLatency, paOutputLatency)); *p_snd_strm = stream; return PJ_SUCCESS; err_out: pj_pool_release(pool); return PJ_RETURN_OS_ERROR(err); } /* * Get stream info. */ PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, pjmedia_snd_stream_info *pi) { //const PaStreamInfo *paPlaySI = NULL, *paRecSI = NULL; cztrace(("pjmedia_snd_stream_get_info\n")); PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); // PJ_ASSERT_RETURN(strm->play_strm || strm->rec_strm, PJ_EINVALIDOP); pj_bzero(pi, sizeof(*pi)); pi->dir =aud_dir; pi->play_id = audfd; //strm->play_id; pi->rec_id = audfd; //strm->rec_id; pi->clock_rate = 8000; pi->channel_count = 1; pi->samples_per_frame = 160; //num_of_channel x clock_rate x PTIME / 1000 pi->bits_per_sample = 2 * 8; pi->rec_latency = 16; pi->play_latency = 16; return PJ_SUCCESS; } /* * Start stream. */ PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) { int err = 0; int thread_status; cztrace(("pjmedia_snd_stream_start\n")); if (stream->play_id == stream->rec_id){ thread_status = pthread_create( &stream->play_thread, NULL, PaRecorderPlayerCallback, stream ); if(thread_status!=0){ stream->play_thread_initialized=1; stream->play_thread_exited=0; stream->play_cb=PaRecorderPlayerCallback; stream->rec_thread_initialized=1; stream->rec_thread_exited=0; stream->rec_cb=PaRecorderPlayerCallback; } }else cztrace(("pjmedia_snd_stream_start stream->play_id != stream->rec_id \n")); PJ_LOG(5,(THIS_FILE, "Starting %s stream..", stream->name.ptr)); PJ_LOG(5,(THIS_FILE, "Done, status=%d", err)); return PJ_SUCCESS; } /* * Stop stream. */ PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) { int i, err = 0; cztrace(("pjmedia_snd_stream_stop\n")); stream->quit_flag = 1; PJ_LOG(5,(THIS_FILE, "Stopping stream..")); //pthread_cancel( &stream->play_thread); /* XXX: Safe to call this if the thread has exited on its own? */ pthread_join( &stream->play_thread, NULL ); PJ_LOG(5,(THIS_FILE, "Done, status=%d", err)); return err ? PJMEDIA_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS; } /* * Destroy stream. */ PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) { /* if (stream->play_id ||stream->rec_id ) { if(stream->play_id == stream->rec_id) close(stream->play_id); else{ if(stream->play_id >=0) close(stream->play_id); if(stream->rec_id >=0) close(stream->rec_id); } } */ cztrace(("pjmedia_snd_stream_close\n")); if(audfd) { close(audfd); audfd=-1; } pj_pool_release(stream->pool); return PJ_SUCCESS; } /* * Deinitialize sound library. */ PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) { cztrace(("pjmedia_snd_deinit\n")); PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down..")); return PJ_SUCCESS; } #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ _________________________________________________________________ Discover the new Windows Vista http://search.msn.com/results.aspx?q=windows+vista&mkt=en-US&form=QBRE -------------- next part -------------- An HTML attachment was scrubbed... URL: http://lists.pjsip.org/pipermail/pjsip_lists.pjsip.org/attachments/20070914/f7bd7330/attachment-0001.html