PJ_DEF(pj_status_t) pj_timer_fire(int entry_code_id){ pj_thread_desc a_thread_desc; pj_thread_t *a_thread; unsigned i, j; int entry_id, heap_id; pj_timer_entry *entry; entry_id = entry_code_id % MAX_ENTRY_PER_HEAP; heap_id = entry_code_id / MAX_ENTRY_PER_HEAP; if(heap_id < 0 || heap_id >= MAX_HEAPS){ PJ_LOG(1, (THIS_FILE, "Invalid timer code %d", entry_code_id)); return PJ_EINVAL; } // First step is to register the thread if not already done if (!pj_thread_is_registered()) { char thread_name[160]; int len = pj_ansi_snprintf(thread_name, sizeof(thread_name), "timer_thread_%d", entry_code_id); thread_name[len] = '\0'; pj_thread_register(thread_name, a_thread_desc, &a_thread); PJ_LOG(5, (THIS_FILE, "Registered thread %s", thread_name)); } // Find corresponding ht pj_timer_heap_t *ht = sHeaps[heap_id]; if (ht != NULL) { PJ_LOG(5, (THIS_FILE, "FIRE timer %d of heap %d", entry_id, heap_id)); pj_timer_heap_callback* cb = NULL; lock_timer_heap(ht); // Get callback if entry valid entry = ht->entries[entry_id]; if (entry != NULL && entry->_timer_id >= 0) { cb = entry->cb; } //unlock_timer_heap(ht); //lock_timer_heap(ht); // Release slot ht->entries[entry_id] = NULL; entry->_timer_id = -1; unlock_timer_heap(ht); // Callback may modify current entry (re-enqueue), so do not fire cb before release the slot if (cb) { cb(ht, entry); } PJ_LOG(5, (THIS_FILE, "FIRE done and released")); } else { PJ_LOG(2, (THIS_FILE, "FIRE Ignore : No heap found at %d for this entry %d", heap_id, entry_code_id)); } return PJ_SUCCESS; }
void PjsipCallFront::dump_info(pj_thread_t *thread) { Q_ASSERT(thread != NULL); qLogx()<<"pj_thread_is_registered:"<<pj_thread_is_registered(); qLogx()<<"pj_thread_get_prio:"<<pj_thread_get_prio(thread); qLogx()<<"pj_thread_get_prio_min:"<<pj_thread_get_prio_min(thread); qLogx()<<"pj_thread_get_prio_max:"<<pj_thread_get_prio_max(thread); qLogx()<<"pj_thread_get_name:"<<pj_thread_get_name(thread); qLogx()<<"pj_getpid:"<<pj_getpid(); }
static void check_thread(pj_ioqueue_t *ioq) { if (pj_thread_is_registered()) return; pj_thread_t *t; char tmp[16]; pj_ansi_snprintf(tmp, sizeof(tmp), "UwpThread%02d", ioq->thread_cnt); pj_thread_register(tmp, ioq->thread_desc[ioq->thread_cnt++], &t); pj_assert(ioq->thread_cnt < PJ_ARRAY_SIZE(ioq->thread_desc)); ioq->thread_cnt %= PJ_ARRAY_SIZE(ioq->thread_desc); }
/* callback for device change operations */ static void process_aud_dev_change_event(pjmedia_aud_dev_change_event event) { pj_status_t status; if (!pj_thread_is_registered()) { status = pj_thread_register("aud_dev_observer", aud_subsys.dev_observer.thread_desc, &aud_subsys.dev_observer.thread); if (status != PJ_SUCCESS) { return; } PJ_LOG(5, (THIS_FILE, "Audio device change thread registered")); } status = pj_mutex_lock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); return; } if (!aud_subsys.dev_observer.cb) { /* there is no registered callback to call */ goto end; } switch(event) { case DEFAULT_INPUT_CHANGED: PJ_LOG(5, (THIS_FILE, "Default input device changed")); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED); break; case DEFAULT_OUTPUT_CHANGED: PJ_LOG(5, (THIS_FILE, "Default output device changed")); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED); break; case DEVICE_LIST_CHANGED: PJ_LOG(5, (THIS_FILE, "Device list changed")); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_WILL_REFRESH); pjmedia_aud_dev_refresh(); (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_DID_REFRESH); break; default: PJ_LOG(5, (THIS_FILE, "Unknown event: %d", event)); break; } end: status = pj_mutex_unlock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); } }
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 (!pj_thread_is_registered()) { // DB20071209 register if thread changed // probably a memory leak here pj_thread_register("portaudio", stream->play_thread_desc, &stream->play_thread); } 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; }
/* This callback is called every time a buffer finishes playing. */ void bqPlayerCallback(W_SLBufferQueueItf bq, void *context) { struct opensl_aud_stream *stream = (struct opensl_aud_stream*) context; SLresult result; int status; pj_assert(context != NULL); pj_assert(bq == stream->playerBufQ); if (stream->play_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("opensl_play", stream->play_thread_desc, &stream->play_thread); stream->play_thread_initialized = 1; PJ_LOG(5, (THIS_FILE, "Player thread started")); } if (!stream->quit_flag) { pjmedia_frame frame; char * buf; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = buf = stream->playerBuffer[stream->playerBufIdx++]; frame.size = stream->playerBufferSize; frame.timestamp.u64 = stream->play_timestamp.u64; frame.bit_info = 0; status = (*stream->play_cb)(stream->user_data, &frame); if (status != PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(buf, stream->playerBufferSize); stream->play_timestamp.u64 += stream->param.samples_per_frame / stream->param.channel_count; result = (*bq)->Enqueue(bq, buf, stream->playerBufferSize); if (result != SL_RESULT_SUCCESS) { PJ_LOG(3, (THIS_FILE, "Unable to enqueue next player buffer !!! %d", result)); } stream->playerBufIdx %= NUM_BUFFERS; } }
static void JNICALL OnGetFrame(JNIEnv *env, jobject obj, jbyteArray data, jint length, jlong user_data) { and_stream *strm = *(and_stream**)&user_data; pjmedia_frame f; pj_uint8_t *Y, *U, *V; pj_status_t status; void *frame_buf, *data_buf; strm->frame_ts.u64 += strm->ts_inc; if (!strm->vid_cb.capture_cb) return; if (strm->thread_initialized == 0 || !pj_thread_is_registered()) { pj_status_t status; pj_bzero(strm->thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("and_cam", strm->thread_desc, &strm->thread); if (status != PJ_SUCCESS) return; strm->thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Android camera thread registered")); } f.type = PJMEDIA_FRAME_TYPE_VIDEO; f.size = length; f.timestamp.u64 = strm->frame_ts.u64; f.buf = data_buf = (*env)->GetByteArrayElements(env, data, 0); Y = (pj_uint8_t*)f.buf; U = Y + strm->vafp.plane_bytes[0]; V = U + strm->vafp.plane_bytes[1]; /* Convert NV21 -> I420, i.e: separate V/U interleaved data plane * into U & V planes. */ if (strm->convert_to_i420 == 1) { pj_uint8_t *src = U; pj_uint8_t *dst_u = U; pj_uint8_t *end_u = U + strm->vafp.plane_bytes[1]; pj_uint8_t *dst_v = strm->convert_buf; while (dst_u < end_u) { *dst_v++ = *src++; *dst_u++ = *src++; } pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[2]); } /* Convert YV12 -> I420, i.e: swap U & V planes. We also need to * strip out padding, if any. */ else if (strm->convert_to_i420 == 2) { int y_stride = ALIGN16(strm->vafp.size.w); int uv_stride = ALIGN16(strm->vafp.size.w/2); /* Strip out Y padding */ if (y_stride > strm->vafp.size.w) { int i; pj_uint8_t *src = Y + y_stride; pj_uint8_t *dst = Y + strm->vafp.size.w; for (i = 1; i < strm->vafp.size.h; ++i) { memmove(dst, src, strm->vafp.size.w); src += y_stride; dst += strm->vafp.size.w; } } /* Swap U & V planes */ if (uv_stride == strm->vafp.size.w/2) { /* No padding, note Y plane should be no padding too! */ pj_assert(y_stride == strm->vafp.size.w); pj_memcpy(strm->convert_buf, U, strm->vafp.plane_bytes[1]); pj_memmove(U, V, strm->vafp.plane_bytes[1]); pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[1]); } else if (uv_stride > strm->vafp.size.w/2) { /* Strip & copy V plane into conversion buffer */ pj_uint8_t *src = Y + y_stride*strm->vafp.size.h; pj_uint8_t *dst = strm->convert_buf; unsigned dst_stride = strm->vafp.size.w/2; int i; for (i = 0; i < strm->vafp.size.h/2; ++i) { memmove(dst, src, dst_stride); src += uv_stride; dst += dst_stride; } /* Strip U plane */ dst = U; for (i = 0; i < strm->vafp.size.h/2; ++i) { memmove(dst, src, dst_stride); src += uv_stride; dst += dst_stride; } /* Get V plane data from conversion buffer */ pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[2]); } } status = pjmedia_vid_dev_conv_resize_and_rotate(&strm->conv, f.buf, &frame_buf); if (status == PJ_SUCCESS) { f.buf = frame_buf; } (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &f); (*env)->ReleaseByteArrayElements(env, data, data_buf, JNI_ABORT); }
static int PaPlayerCallback( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { struct pa_aud_stream *stream = (struct pa_aud_stream*) userData; pj_status_t status = 0; unsigned nsamples_req = frameCount * stream->channel_count; PJ_UNUSED_ARG(input); PJ_UNUSED_ARG(timeInfo); if (stream->quit_flag) goto on_break; if (output == NULL) return paContinue; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (stream->play_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); 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; /* Check if any buffered samples */ if (stream->play_buf_count) { /* samples buffered >= requested by sound device */ if (stream->play_buf_count >= nsamples_req) { pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, nsamples_req); stream->play_buf_count -= nsamples_req; pjmedia_move_samples(stream->play_buf, stream->play_buf + nsamples_req, stream->play_buf_count); nsamples_req = 0; return paContinue; } /* samples buffered < requested by sound device */ pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, stream->play_buf_count); nsamples_req -= stream->play_buf_count; output = (pj_int16_t*)output + stream->play_buf_count; stream->play_buf_count = 0; } /* Fill output buffer as requested */ while (nsamples_req && status == 0) { if (nsamples_req >= stream->samples_per_frame) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = output; frame.size = stream->samples_per_frame * stream->bytes_per_sample; frame.timestamp.u64 = stream->play_timestamp.u64; frame.bit_info = 0; status = (*stream->play_cb)(stream->user_data, &frame); if (status != PJ_SUCCESS) goto on_break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); nsamples_req -= stream->samples_per_frame; output = (pj_int16_t*)output + stream->samples_per_frame; } else { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = stream->play_buf; frame.size = stream->samples_per_frame * stream->bytes_per_sample; frame.timestamp.u64 = stream->play_timestamp.u64; frame.bit_info = 0; status = (*stream->play_cb)(stream->user_data, &frame); if (status != PJ_SUCCESS) goto on_break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, nsamples_req); stream->play_buf_count = stream->samples_per_frame - nsamples_req; pjmedia_move_samples(stream->play_buf, stream->play_buf+nsamples_req, stream->play_buf_count); nsamples_req = 0; } stream->play_timestamp.u64 += stream->samples_per_frame / stream->channel_count; } if (status==0) return paContinue; on_break: stream->play_thread_exited = 1; return paAbort; }
static int PaRecorderCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { struct pa_aud_stream *stream = (struct pa_aud_stream*) userData; pj_status_t status = 0; unsigned nsamples; PJ_UNUSED_ARG(output); PJ_UNUSED_ARG(timeInfo); if (stream->quit_flag) goto on_break; if (input == NULL) return paContinue; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); 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; /* Calculate number of samples we've got */ nsamples = frameCount * stream->channel_count + stream->rec_buf_count; if (nsamples >= stream->samples_per_frame) { /* If buffer is not empty, combine the buffer with the just incoming * samples, then call put_frame. */ if (stream->rec_buf_count) { unsigned chunk_count = 0; pjmedia_frame frame; chunk_count = stream->samples_per_frame - stream->rec_buf_count; pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count, (pj_int16_t*)input, chunk_count); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) stream->rec_buf; frame.size = stream->samples_per_frame * stream->bytes_per_sample; frame.timestamp.u64 = stream->rec_timestamp.u64; frame.bit_info = 0; status = (*stream->rec_cb)(stream->user_data, &frame); input = (pj_int16_t*) input + chunk_count; nsamples -= stream->samples_per_frame; stream->rec_buf_count = 0; stream->rec_timestamp.u64 += stream->samples_per_frame / stream->channel_count; } /* Give all frames we have */ while (nsamples >= stream->samples_per_frame && status == 0) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) input; frame.size = stream->samples_per_frame * stream->bytes_per_sample; frame.timestamp.u64 = stream->rec_timestamp.u64; frame.bit_info = 0; status = (*stream->rec_cb)(stream->user_data, &frame); input = (pj_int16_t*) input + stream->samples_per_frame; nsamples -= stream->samples_per_frame; stream->rec_timestamp.u64 += stream->samples_per_frame / stream->channel_count; } /* Store the remaining samples into the buffer */ if (nsamples && status == 0) { stream->rec_buf_count = nsamples; pjmedia_copy_samples(stream->rec_buf, (pj_int16_t*)input, nsamples); } } else { /* Not enough samples, let's just store them in the buffer */ pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count, (pj_int16_t*)input, frameCount * stream->channel_count); stream->rec_buf_count += frameCount * stream->channel_count; } if (status==0) return paContinue; on_break: stream->rec_thread_exited = 1; return paAbort; }
int CChannel::recvfrom(sockaddr* addr, CPacket& packet) const { int res = -1; recv_buff *rb = NULL; pj_thread_desc desc; pj_thread_t *thread = 0; if (m_iSocket == -1) { pjsua_call *call = (pjsua_call *)m_call; if(call == NULL) return -1; if(call->tnl_stream==NULL) return -1; // DEAN, prevent assert fail while garbage collector remove UDT socket on multiple instance. if (!pj_thread_is_registered(call->inst_id)) { int status = pj_thread_register(call->inst_id, "CChannel::recvfrom", desc, &thread ); if (status != PJ_SUCCESS) return -1; } pj_mutex_lock(call->tnl_stream_lock3); natnl_stream *stream = (natnl_stream *)call->tnl_stream; //get data from rBuff if (stream == NULL) { pj_mutex_unlock(call->tnl_stream_lock3); return -1; } // charles CHARLES // DEAN commeted, for using pj_sem_try_wait2 //pj_mutex_unlock(call->tnl_stream_lock3); //pj_sem_wait(stream->rbuff_sem); pj_sem_trywait2(stream->rbuff_sem); //pj_mutex_lock(call->tnl_stream_lock3); pj_mutex_lock(stream->rbuff_mutex); if (!pj_list_empty(&stream->rbuff)) { rb = stream->rbuff.next; stream->rbuff_cnt--; //PJ_LOG(4, ("channel.cpp", "rbuff_cnt=%d", stream->rbuff_cnt)); pj_list_erase(rb); /*if (rb->len > 0 && ((pj_uint32_t *)rb->buff)[0] == NO_CTL_SESS_MGR_HEADER_MAGIC) { // check the magic char *data = (char *)&rb->buff[sizeof(NO_CTL_SESS_MGR_HEADER_MAGIC)]; int len = rb->len - sizeof(NO_CTL_SESS_MGR_HEADER_MAGIC); natnl_handle_recv_msg(call->index, call->tnl_stream->med_tp, data, len); } else */if (!check_packet_integrity(rb)) { int ds = UMIN(packet.m_PacketVector[1].iov_len, rb->len - sizeof(natnl_hdr) - CPacket::m_iPktHdrSize); memcpy(packet.m_PacketVector[0].iov_base, &rb->buff[sizeof(natnl_hdr)], packet.m_PacketVector[0].iov_len); memcpy(packet.m_PacketVector[1].iov_base, &rb->buff[packet.m_PacketVector[0].iov_len+sizeof(natnl_hdr)], ds); res = rb->len - sizeof(natnl_hdr); } } pj_mutex_unlock(stream->rbuff_mutex); if (rb != NULL) { #if 1 //move rb to gcbuff pj_mutex_lock(stream->gcbuff_mutex); pj_list_push_back(&stream->gcbuff, rb); pj_mutex_unlock(stream->gcbuff_mutex); #else free(rb); rb = NULL; #endif } pj_mutex_unlock(call->tnl_stream_lock3); } if (res <= 0) { packet.setLength(-1); return -1; } packet.setLength(res - CPacket::m_iPktHdrSize); #ifdef DEBUGP printf("\nRecv Header:\n"); dumpHex((char *)packet.m_PacketVector[0].iov_base, packet.m_PacketVector[0].iov_len); char *bb = (char *)packet.m_PacketVector[0].iov_base; if(bb[0]&0x80) { printf("Data:\n"); dumpHex((char *)packet.m_PacketVector[1].iov_base, packet.m_PacketVector[1].iov_len); printf("================\n"); } #endif // convert back into local host order //for (int i = 0; i < 4; ++ i) // packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]); uint32_t* p = packet.m_nHeader; for (int i = 0; i < 4; ++ i) { *p = ntohl(*p); ++ p; } if (packet.getFlag()) { for (int j = 0, n = packet.getLength() / 4; j < n; ++ j) *((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j)); } return packet.getLength(); }
int CChannel::sendto(const sockaddr* addr, CPacket& packet) const { // convert control information into network order if (packet.getFlag()) { for (int i = 0, n = packet.getLength() / 4; i < n; ++ i) *((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i)); } uint32_t* p = packet.m_nHeader; for (int j = 0; j < 4; ++ j) { *p = htonl(*p); ++ p; } #ifdef DEBUGP //dump ctrl packet printf("\nSend Header:\n"); dumpHex((char *)packet.m_PacketVector[0].iov_base, packet.m_PacketVector[0].iov_len); char *bb = (char *)packet.m_PacketVector[0].iov_base; if(bb[0]&0x80) { printf("Data:\n"); dumpHex((char *)packet.m_PacketVector[1].iov_base, packet.m_PacketVector[1].iov_len); printf("================\n"); } #endif int res = -1; unsigned size; unsigned len; natnl_hdr hdr = {0xff, 0x00, 0x0000}; int is_tnl_data = 0; pj_thread_desc desc; pj_thread_t *thread = 0; if(m_iSocket == -1) { pjsua_call *call = (pjsua_call *)m_call; if(call == NULL) return -1; // DEAN, prevent assert fail while garbage collector remove UDT socket on multiple instance. if (!pj_thread_is_registered(call->inst_id)) { int status = pj_thread_register(call->inst_id, "CChannel::sendto", desc, &thread ); if (status != PJ_SUCCESS) return -1; } pj_mutex_lock(call->tnl_stream_lock2); natnl_stream *stream = (natnl_stream *)call->tnl_stream; if(stream == NULL) { pj_mutex_unlock(call->tnl_stream_lock2); return -1; } size = CPacket::m_iPktHdrSize + packet.getLength() + sizeof(natnl_hdr); len = (CPacket::m_iPktHdrSize + packet.getLength()); hdr.length = htons(len); memcpy((char *)&m_pktBuffer[sizeof(natnl_hdr)], packet.m_PacketVector[0].iov_base, packet.m_PacketVector[0].iov_len); memcpy((char *)&m_pktBuffer[packet.m_PacketVector[0].iov_len+sizeof(natnl_hdr)], packet.m_PacketVector[1].iov_base, packet.m_PacketVector[1].iov_len); memcpy((char *)&m_pktBuffer[0], &hdr, sizeof(natnl_hdr)); resend: // DEAN, check if this is tunnel data. If true, update last_data time. is_tnl_data = pjmedia_natnl_udt_packet_is_tnl_data(&m_pktBuffer[0], size); pj_assert(size < sizeof(m_pktBuffer)); ((pj_uint8_t*)m_pktBuffer)[size] = 0; // tunnel data flag off if (is_tnl_data) { pj_get_timestamp(&stream->last_data); // DEAN save current time ((pj_uint8_t*)m_pktBuffer)[size] = 1; // tunnel data flag on } res = pjmedia_transport_send_rtp(stream->med_tp, m_pktBuffer, size); // +Roger modified - stream pointer to med_tp #if 0 // No need to resend it, because UDT will handle this. if(res == 70011) { //EAGAIN m_pTimer->sleepto(50000); //sleep for 50 us goto resend; } #endif pj_mutex_unlock(call->tnl_stream_lock2); } res = (0 == res) ? size : -1; // convert back into local host order //for (int k = 0; k < 4; ++ k) // packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]); p = packet.m_nHeader; for (int k = 0; k < 4; ++ k) { *p = ntohl(*p); ++ p; } if (packet.getFlag()) { for (int l = 0, n = packet.getLength() / 4; l < n; ++ l) *((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l)); } return res; }
/* callbacks to get data */ int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data) { pj_status_t status = PJ_SUCCESS; pjmedia_frame frame; struct bd_stream *strm = (struct bd_stream*)user_data; unsigned nsamples_req = samples * strm->channel_count; if(!strm->go) goto on_break; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->play_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("bd_PlaybackCallback", strm->play_thread_desc, &strm->play_thread); if (status != PJ_SUCCESS) goto on_break; strm->play_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Player thread started")); } /* PLAY */ if(strm->fmt_id == PJMEDIA_FORMAT_L16) { /* Check if any buffered samples */ if (strm->play_buf_count) { /* samples buffered >= requested by sound device */ if (strm->play_buf_count >= nsamples_req) { pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, nsamples_req); strm->play_buf_count -= nsamples_req; pjmedia_move_samples(strm->play_buf, strm->play_buf + nsamples_req, strm->play_buf_count); return nsamples_req; } /* samples buffered < requested by sound device */ pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, strm->play_buf_count); nsamples_req -= strm->play_buf_count; buffer = (pj_int16_t*)buffer + strm->play_buf_count; strm->play_buf_count = 0; } /* Fill output buffer as requested */ while (nsamples_req && status == 0) { if (nsamples_req >= strm->samples_per_frame) { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = buffer; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampPlayback.u64; frame.bit_info = 0; status = (*strm->play_cb)(strm->user_data, &frame); if (status != PJ_SUCCESS) return 0; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); nsamples_req -= strm->samples_per_frame; buffer = (pj_int16_t*)buffer + strm->samples_per_frame; } else { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = strm->play_buf; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampPlayback.u64; frame.bit_info = 0; status = (*strm->play_cb)(strm->user_data, &frame); if (status != PJ_SUCCESS) return 0; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, nsamples_req); strm->play_buf_count = strm->samples_per_frame - nsamples_req; pjmedia_move_samples(strm->play_buf, strm->play_buf+nsamples_req, strm->play_buf_count); nsamples_req = 0; } strm->timestampPlayback.u64 += strm->samples_per_frame / strm->channel_count; } } else { pj_assert(!"Frame type not supported"); } if(status != PJ_SUCCESS) { return 0; } strm->timestampPlayback.u64 += strm->param.samples_per_frame / strm->param.channel_count; if (status == 0) return samples; on_break: strm->play_thread_exited = 1; return 0; }
/* callbacks to set data */ void bdimad_CaptureCallback(void *buffer, int samples, void *user_data) { pj_status_t status = PJ_SUCCESS; pjmedia_frame frame; unsigned nsamples; struct bd_stream *strm = (struct bd_stream*)user_data; if(!strm->go) goto on_break; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("bd_CaptureCallback", strm->rec_thread_desc, &strm->rec_thread); if (status != PJ_SUCCESS) goto on_break; strm->rec_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Recorder thread started")); } /* Calculate number of samples we've got */ nsamples = samples * strm->channel_count + strm->rec_buf_count; /* RECORD */ if (strm->fmt_id == PJMEDIA_FORMAT_L16) { if (nsamples >= strm->samples_per_frame) { /* If buffer is not empty, combine the buffer with the just incoming * samples, then call put_frame. */ if (strm->rec_buf_count) { unsigned chunk_count = 0; chunk_count = strm->samples_per_frame - strm->rec_buf_count; pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, (pj_int16_t*)buffer, chunk_count); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) strm->rec_buf; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampCapture.u64; frame.bit_info = 0; status = (*strm->rec_cb)(strm->user_data, &frame); buffer = (pj_int16_t*) buffer + chunk_count; nsamples -= strm->samples_per_frame; strm->rec_buf_count = 0; strm->timestampCapture.u64 += strm->samples_per_frame / strm->channel_count; } /* Give all frames we have */ while (nsamples >= strm->samples_per_frame && status == 0) { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) buffer; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampCapture.u64; frame.bit_info = 0; status = (*strm->rec_cb)(strm->user_data, &frame); buffer = (pj_int16_t*) buffer + strm->samples_per_frame; nsamples -= strm->samples_per_frame; strm->timestampCapture.u64 += strm->samples_per_frame / strm->channel_count; } /* Store the remaining samples into the buffer */ if (nsamples && status == 0) { strm->rec_buf_count = nsamples; pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer, nsamples); } } else { /* Not enough samples, let's just store them in the buffer */ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, (pj_int16_t*)buffer, samples * strm->channel_count); strm->rec_buf_count += samples * strm->channel_count; } } else { pj_assert(!"Frame type not supported"); } strm->timestampCapture.u64 += strm->param.samples_per_frame / strm->param.channel_count; if (status==0) return; on_break: strm->rec_thread_exited = 1; }