/* API: create stream */ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_strm) { struct bb10_factory *af = (struct bb10_factory*)f; pj_status_t status; pj_pool_t* pool; struct bb10_stream* stream; pool = pj_pool_create (af->pf, "bb10%p", 1024, 1024, NULL); if (!pool) return PJ_ENOMEM; /* Allocate and initialize comon stream data */ stream = PJ_POOL_ZALLOC_T (pool, struct bb10_stream); stream->base.op = &bb10_stream_op; stream->pool = pool; stream->af = af; stream->user_data = user_data; stream->pb_cb = play_cb; stream->ca_cb = rec_cb; stream->quit = 0; pj_memcpy(&stream->param, param, sizeof(*param)); /* Init playback */ if (param->dir & PJMEDIA_DIR_PLAYBACK) { status = bb10_open_playback (stream, param); if (status != PJ_SUCCESS) { pj_pool_release (pool); return status; } } /* Init capture */ if (param->dir & PJMEDIA_DIR_CAPTURE) { status = bb10_open_capture (stream, param); if (status != PJ_SUCCESS) { if (param->dir & PJMEDIA_DIR_PLAYBACK) { close_play_pcm(stream); } pj_pool_release (pool); return status; } } *p_strm = &stream->base; return PJ_SUCCESS; }
static pj_status_t bb10_stream_destroy (pjmedia_aud_stream *s) { struct bb10_stream *stream = (struct bb10_stream*)s; TRACE_((THIS_FILE,"bb10_stream_destroy()")); bb10_stream_stop (s); close_play_pcm(stream); close_capture_pcm(stream); pj_pool_release (stream->pool); return PJ_SUCCESS; }
/* API: create stream */ static pj_status_t bb10_factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_strm) { struct bb10_factory *af = (struct bb10_factory*)f; pj_status_t status; pj_pool_t* pool; struct bb10_stream* stream; pool = pj_pool_create (af->pf, "bb10%p", 1024, 1024, NULL); if (!pool) return PJ_ENOMEM; /* Allocate and initialize comon stream data */ stream = PJ_POOL_ZALLOC_T (pool, struct bb10_stream); stream->base.op = &bb10_stream_op; stream->pool = pool; stream->af = af; stream->user_data = user_data; stream->pb_cb = play_cb; stream->ca_cb = rec_cb; stream->quit = 0; pj_memcpy(&stream->param, param, sizeof(*param)); /* Init playback */ if (param->dir & PJMEDIA_DIR_PLAYBACK) { status = bb10_open_playback (stream, param); if (status != PJ_SUCCESS) { pj_pool_release (pool); return status; } } /* Init capture */ if (param->dir & PJMEDIA_DIR_CAPTURE) { status = bb10_open_capture (stream, param); if (status != PJ_SUCCESS) { if (param->dir & PJMEDIA_DIR_PLAYBACK) { close_play_pcm(stream); } pj_pool_release (pool); return status; } } /* Set the audio routing ONLY if app explicitly asks one */ if ((param->dir & PJMEDIA_DIR_PLAYBACK) && (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE)) { status = bb10_stream_set_cap(&stream->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, ¶m->output_route); if (status != PJ_SUCCESS) { TRACE_((THIS_FILE, "Error setting output route")); bb10_stream_destroy(&stream->base); return status; } } else { /* Legacy behavior: if none specified, set to speaker */ status = bb10_initialize_playback_ctrl(stream, false); } *p_strm = &stream->base; return PJ_SUCCESS; }
/** * Play audio received from PJMEDIA */ static int pb_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->pb_buf_size; unsigned long nframes = stream->pb_frames; void *user_data = stream->user_data; char *buf = stream->pb_buf; pj_timestamp tstamp; int result = 0; int policy; struct sched_param param; TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size)); if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { param.sched_priority = 18; pthread_setschedparam (pthread_self(), policy, ¶m); } pj_bzero (buf, size); tstamp.u64 = 0; /* Do the final initialization now the thread has started. */ if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; /* pointer to buffer filled by PJMEDIA */ frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; /* Read the audio from pjmedia */ result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); if (result != size || result < 0) { /* either the write to output device has failed or not the * full amount of bytes have been written. This usually happens * when audio routing is being changed by another thread * Use a status variable for reading the error */ snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { /* Call has failed nothing we can do except log and * continue */ PJ_LOG(4,(THIS_FILE, "underrun: playback channel status error")); } else { /* The status of the error has been read * RIM say these are expected so we can "re-prepare" the stream */ PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", status.status)); if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN || status.status == SND_PCM_STATUS_ERROR ) { if (snd_pcm_plugin_prepare (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel prepare error")); } } } } tstamp.u64 += nframes; } flush_play(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; }
/** * Play audio received from PJMEDIA */ static int pb_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; /* Handle from bb10_open_playback */ /* Will be 640 */ int size = stream->pb_buf_size; /* 160 frames for 20ms */ unsigned long nframes = stream->pb_frames; void *user_data = stream->user_data; char *buf = stream->pb_buf; pj_timestamp tstamp; int result = 0; pj_bzero (buf, size); tstamp.u64 = 0; TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size)); /* Do the final initialization now the thread has started. */ if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; /* pointer to buffer filled by PJMEDIA */ frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); if (result != size || result < 0) { snd_pcm_channel_status_t status; if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel status error")); continue; } if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) { if (snd_pcm_plugin_prepare (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel prepare error")); continue; } } TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result)); } tstamp.u64 += nframes; } flush_play(stream); close_play_pcm(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; }