PJ_DEF(Frame_Buffer *) jitter_buffer_getCompleteFrameForDecoding(Jitter_Buffer *jitter_buffer, pj_uint32_t max_wait_time_ms) { //running if(!jitter_buffer->running) return NULL; if(pj_mutex_lock(jitter_buffer->jb_mutex) != PJ_SUCCESS) { //error return NULL; } /*{ char buf[1024]; getFrameInfo(&jitter_buffer->frameList, &jitter_buffer->decode_state, buf, 1024); PJ_LOG(4, (THIS_FILE, "jb status:\n%s\n", buf)); }*/ //the first frame must be a key frame if(jitter_buffer->decode_state.inited == PJ_FALSE) jitter_buffer->waiting_for_key_frame == PJ_TRUE; //clean old frames CleanOldFrames(jitter_buffer); //get complete , continuous frame Frame_Buffer * frame = findOldestCompleteContinuousFrame(jitter_buffer); //if not got, try to wait a frame if(frame == NULL) { if(max_wait_time_ms == 0) goto ON_RET; pj_timestamp current ; pj_get_timestamp(¤t); pj_timestamp end = current; pj_add_timestamp32(&end, max_wait_time_ms); pj_int32_t wait_time_ms = max_wait_time_ms; event_reset(jitter_buffer->packet_event); while(wait_time_ms > 0) { pj_mutex_unlock(jitter_buffer->jb_mutex); if(event_wait(jitter_buffer->packet_event, wait_time_ms) == WAIT_RET_SIGNAL) { //incoming a packet pj_mutex_lock(jitter_buffer->jb_mutex); if(!jitter_buffer->running) //jitter buffer stoped goto ON_RET; CleanOldFrames(jitter_buffer); frame = findOldestCompleteContinuousFrame(jitter_buffer); if(frame != NULL) break; pj_get_timestamp(¤t); int elapsed_msec = pj_elapsed_msec(¤t, &end); wait_time_ms = elapsed_msec; } else { pj_mutex_lock(jitter_buffer->jb_mutex); //error or timeout break; } } } else event_reset(jitter_buffer->packet_event); if(frame == NULL) goto ON_RET; /*if(jitter_buffer->waiting_for_key_frame && !frame->session_info.isKeyFrame) { frame = NULL; //not a key frame goto ON_RET; }*/ //got one, update jitter if(frame->nack_count > 0) { jitter_estimator_frameNacked(&jitter_buffer->jitter_estimator); //nacked } else { //update jitter estimator UpdateJitterEstimatorForFrame(jitter_buffer, frame); } //set frame status frame_buffer_setStatus(frame, FRAME_STATUS_DECODING); //update decode state decode_state_updateFrame(frame, &jitter_buffer->decode_state); //waiting for key frame if(frame->session_info.isKeyFrame) jitter_buffer->waiting_for_key_frame = PJ_FALSE; //clean old frames CleanOldFrames(jitter_buffer); //return frame ON_RET: pj_mutex_unlock(jitter_buffer->jb_mutex); return frame; }
/* * pj_ioqueue_poll() * */ PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout) { int i, count, processed; int msec; //struct epoll_event *events = ioqueue->events; //struct queue *queue = ioqueue->queue; struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; struct queue queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL]; pj_timestamp t1, t2; PJ_CHECK_STACK(); msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; TRACE_((THIS_FILE, "start os_epoll_wait, msec=%d", msec)); pj_get_timestamp(&t1); //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec); count = os_epoll_wait( ioqueue->epfd, events, PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, msec); if (count == 0) { #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Check the closing keys only when there's no activity and when there are * pending closing keys. */ if (count == 0 && !pj_list_empty(&ioqueue->closing_list)) { pj_lock_acquire(ioqueue->lock); scan_closing_keys(ioqueue); pj_lock_release(ioqueue->lock); } #endif TRACE_((THIS_FILE, "os_epoll_wait timed out")); return count; } else if (count < 0) { TRACE_((THIS_FILE, "os_epoll_wait error")); return -pj_get_netos_error(); } pj_get_timestamp(&t2); TRACE_((THIS_FILE, "os_epoll_wait returns %d, time=%d usec", count, pj_elapsed_usec(&t1, &t2))); /* Lock ioqueue. */ pj_lock_acquire(ioqueue->lock); for (processed=0, i=0; i<count; ++i) { pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type) events[i].epoll_data; TRACE_((THIS_FILE, "event %d: events=%d", i, events[i].events)); /* * Check readability. */ if ((events[i].events & EPOLLIN) && (key_has_pending_read(h) || key_has_pending_accept(h)) && !IS_CLOSING(h) ) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[processed].key = h; queue[processed].event_type = READABLE_EVENT; ++processed; continue; } /* * Check for writeability. */ if ((events[i].events & EPOLLOUT) && key_has_pending_write(h) && !IS_CLOSING(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[processed].key = h; queue[processed].event_type = WRITEABLE_EVENT; ++processed; continue; } #if PJ_HAS_TCP /* * Check for completion of connect() operation. */ if ((events[i].events & EPOLLOUT) && (h->connecting) && !IS_CLOSING(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[processed].key = h; queue[processed].event_type = WRITEABLE_EVENT; ++processed; continue; } #endif /* PJ_HAS_TCP */ /* * Check for error condition. */ if ((events[i].events & EPOLLERR) && !IS_CLOSING(h)) { /* * We need to handle this exception event. If it's related to us * connecting, report it as such. If not, just report it as a * read event and the higher layers will handle it. */ if (h->connecting) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[processed].key = h; queue[processed].event_type = EXCEPTION_EVENT; ++processed; } else if (key_has_pending_read(h) || key_has_pending_accept(h)) { #if PJ_IOQUEUE_HAS_SAFE_UNREG increment_counter(h); #endif queue[processed].key = h; queue[processed].event_type = READABLE_EVENT; ++processed; } continue; } } for (i=0; i<processed; ++i) { if (queue[i].key->grp_lock) pj_grp_lock_add_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0); } PJ_RACE_ME(5); pj_lock_release(ioqueue->lock); PJ_RACE_ME(5); /* Now process the events. */ for (i=0; i<processed; ++i) { switch (queue[i].event_type) { case READABLE_EVENT: ioqueue_dispatch_read_event(ioqueue, queue[i].key); break; case WRITEABLE_EVENT: ioqueue_dispatch_write_event(ioqueue, queue[i].key); break; case EXCEPTION_EVENT: ioqueue_dispatch_exception_event(ioqueue, queue[i].key); break; case NO_EVENT: pj_assert(!"Invalid event!"); break; } #if PJ_IOQUEUE_HAS_SAFE_UNREG decrement_counter(queue[i].key); #endif if (queue[i].key->grp_lock) pj_grp_lock_dec_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0); } /* Special case: * When epoll returns > 0 but no descriptors are actually set! */ if (count > 0 && !processed && msec > 0) { pj_thread_sleep(msec); } pj_get_timestamp(&t1); TRACE_((THIS_FILE, "ioqueue_poll() returns %d, time=%d usec", processed, pj_elapsed_usec(&t2, &t1))); return processed; }
static int test_timer_heap(void) { int i, j; pj_timer_entry *entry; pj_pool_t *pool; pj_timer_heap_t *timer; pj_time_val delay; pj_status_t rc; int err=0; unsigned size, count; size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry); pool = pj_pool_create( mem, NULL, size, 4000, NULL); if (!pool) { PJ_LOG(3,("test", "...error: unable to create pool of %u bytes", size)); return -10; } entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry)); if (!entry) return -20; for (i=0; i<MAX_COUNT; ++i) { entry[i].cb = &timer_callback; } rc = pj_timer_heap_create(pool, MAX_COUNT, &timer); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create timer heap", rc); return -30; } count = MIN_COUNT; for (i=0; i<LOOP; ++i) { int early = 0; int done=0; int cancelled=0; int rc; pj_timestamp t1, t2, t_sched, t_cancel, t_poll; pj_time_val now, expire; pj_gettimeofday(&now); pj_srand(now.sec); t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0; // Register timers for (j=0; j<(int)count; ++j) { delay.sec = pj_rand() % DELAY; delay.msec = pj_rand() % 1000; // Schedule timer pj_get_timestamp(&t1); rc = pj_timer_heap_schedule(timer, &entry[j], &delay); if (rc != 0) return -40; pj_get_timestamp(&t2); t_sched.u32.lo += (t2.u32.lo - t1.u32.lo); // Poll timers. pj_get_timestamp(&t1); rc = pj_timer_heap_poll(timer, NULL); pj_get_timestamp(&t2); if (rc > 0) { t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); early += rc; } } // Set the time where all timers should finish pj_gettimeofday(&expire); delay.sec = DELAY; delay.msec = 0; PJ_TIME_VAL_ADD(expire, delay); // Wait unfil all timers finish, cancel some of them. do { int index = pj_rand() % count; pj_get_timestamp(&t1); rc = pj_timer_heap_cancel(timer, &entry[index]); pj_get_timestamp(&t2); if (rc > 0) { cancelled += rc; t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo); } pj_gettimeofday(&now); pj_get_timestamp(&t1); #if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 /* On Symbian, we must use OS poll (Active Scheduler poll) since * timer is implemented using Active Object. */ rc = 0; while (pj_symbianos_poll(-1, 0)) ++rc; #else rc = pj_timer_heap_poll(timer, NULL); #endif pj_get_timestamp(&t2); if (rc > 0) { done += rc; t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); } } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0); if (pj_timer_heap_count(timer)) { PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left", pj_timer_heap_count(timer))); ++err; } t_sched.u32.lo /= count; t_cancel.u32.lo /= count; t_poll.u32.lo /= count; PJ_LOG(4, (THIS_FILE, "...ok (count:%d, early:%d, cancelled:%d, " "sched:%d, cancel:%d poll:%d)", count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo, t_poll.u32.lo)); count = count * 2; if (count > MAX_COUNT) break; } pj_pool_release(pool); return err; }
int timestamp_test(void) { enum { CONSECUTIVE_LOOP = 100 }; volatile unsigned i; pj_timestamp freq, t1, t2; pj_time_val tv1, tv2; unsigned elapsed; pj_status_t rc; PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)")); /* Get and display timestamp frequency. */ if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) { app_perror("...ERROR: get timestamp freq", rc); return -1000; } PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu", freq.u32.hi, freq.u32.lo)); PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait)..")); /* * Check if consecutive readings should yield timestamp value * that is bigger than previous value. * First we get the first timestamp. */ rc = pj_get_timestamp(&t1); if (rc != PJ_SUCCESS) { app_perror("...ERROR: pj_get_timestamp", rc); return -1001; } rc = pj_gettimeofday(&tv1); if (rc != PJ_SUCCESS) { app_perror("...ERROR: pj_gettimeofday", rc); return -1002; } for (i=0; i<CONSECUTIVE_LOOP; ++i) { pj_thread_sleep(pj_rand() % 100); rc = pj_get_timestamp(&t2); if (rc != PJ_SUCCESS) { app_perror("...ERROR: pj_get_timestamp", rc); return -1003; } rc = pj_gettimeofday(&tv2); if (rc != PJ_SUCCESS) { app_perror("...ERROR: pj_gettimeofday", rc); return -1004; } /* compare t2 with t1, expecting t2 >= t1. */ if (t2.u32.hi < t1.u32.hi || (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo)) { PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp run backwards!")); return -1005; } /* compare tv2 with tv1, expecting tv2 >= tv1. */ if (PJ_TIME_VAL_LT(tv2, tv1)) { PJ_LOG(3,(THIS_FILE, "...ERROR: time run backwards!")); return -1006; } } /* * Simple test to time some loop. */ PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop")); /* Mark start time. */ if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) { app_perror("....error: cat't get timestamp", rc); return -1010; } /* Loop.. */ for (i=0; i<1000000; ++i) { /* Try to do something so that smart compilers wont * remove this silly loop. */ null_func(); } pj_thread_sleep(0); /* Mark end time. */ pj_get_timestamp(&t2); /* Get elapsed time in usec. */ elapsed = pj_elapsed_usec(&t1, &t2); PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed)); /* See if elapsed time is "reasonable". * This should be good even on 50Mhz embedded powerpc. */ if (elapsed < 1 || elapsed > 1000000) { PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, " "t1.u32.hi=%u, t1.u32.lo=%u, " "t2.u32.hi=%u, t2.u32.lo=%u)", elapsed, t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo)); return -1030; } /* Testing time/timestamp accuracy */ rc = timestamp_accuracy(); if (rc != 0) return rc; return 0; }
/* * Get NTP time. */ PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess, pjmedia_rtcp_ntp_rec *ntp) { /* Seconds between 1900-01-01 to 1970-01-01 */ #define JAN_1970 (2208988800UL) pj_timestamp ts; pj_status_t status; status = pj_get_timestamp(&ts); /* Fill up the high 32bit part */ ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64) + sess->tv_base.sec + JAN_1970; /* Calculate seconds fractions */ ts.u64 = (ts.u64 - sess->ts_base.u64) % sess->ts_freq.u64; pj_assert(ts.u64 < sess->ts_freq.u64); ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64; /* Fill up the low 32bit part */ ntp->lo = ts.u32.lo; #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0) /* On Win32, since we use QueryPerformanceCounter() as the backend * timestamp API, we need to protect against this bug: * Performance counter value may unexpectedly leap forward * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323 */ { /* * Compare elapsed time reported by timestamp with actual elapsed * time. If the difference is too excessive, then we use system * time instead. */ /* MIN_DIFF needs to be large enough so that "normal" diff caused * by system activity or context switch doesn't trigger the time * correction. */ enum { MIN_DIFF = 400 }; pj_time_val ts_time, elapsed, diff; pj_gettimeofday(&elapsed); ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970; ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF); PJ_TIME_VAL_SUB(elapsed, sess->tv_base); if (PJ_TIME_VAL_LT(ts_time, elapsed)) { diff = elapsed; PJ_TIME_VAL_SUB(diff, ts_time); } else { diff = ts_time; PJ_TIME_VAL_SUB(diff, elapsed); } if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) { TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms", PJ_TIME_VAL_MSEC(diff))); ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970; ntp->lo = (elapsed.msec * 65536 / 1000) << 16; } } #endif return status; }
static int PJ_THREAD_FUNC AndroidRecorderCallback(void* userData){ struct android_aud_stream *stream = (struct android_aud_stream*) userData; JNIEnv *jni_env = 0; ATTACH_JVM(jni_env); jmethodID read_method=0, record_method=0; int bytesRead; int size = stream->samples_per_frame * stream->bytes_per_sample; int nframes = stream->samples_per_frame / stream->channel_count; jbyte* buf; pj_status_t status = 0; jbyteArray inputBuffer; pj_timestamp tstamp, now, last_frame; int elapsed_time = 0; //Frame time in ms int frame_time = nframes * 1000 / stream->samples_per_sec; int missed_time = frame_time; int to_wait = 0; PJ_LOG(3,(THIS_FILE, "<< Enter recorder thread")); if(!stream->record){ goto on_break; } //Get methods ids read_method = jni_env->GetMethodID(stream->record_class,"read", "([BII)I"); record_method = jni_env->GetMethodID(stream->record_class,"startRecording", "()V"); if(read_method==0 || record_method==0) { goto on_break; } //Create a buffer for frames read inputBuffer = jni_env->NewByteArray(size); if (inputBuffer == 0) { PJ_LOG(2, (THIS_FILE, "Not able to allocate a buffer for input read process")); goto on_break; } //start recording //setpriority(PRIO_PROCESS, 0, -19 /*ANDROID_PRIORITY_AUDIO*/); // set priority is probably not enough cause does not change the thread group in scheduler // Temporary solution is to call the java api to set the thread priority. // A cool solution would be to port (if possible) the code from the android os regarding set_sched groups set_android_thread_priority(THREAD_PRIORITY_URGENT_AUDIO); buf = jni_env->GetByteArrayElements(inputBuffer, 0); //Init everything tstamp.u64 = 0; pj_bzero (buf, size); jni_env->CallVoidMethod(stream->record, record_method); pj_get_timestamp(&last_frame); while ( !stream->quit_flag ) { pj_bzero (buf, size); #if COMPATIBLE_ALSA pj_get_timestamp(&now); // Time between now and last frame next frame (ms) elapsed_time = pj_elapsed_msec(&last_frame, &now); pj_get_timestamp(&last_frame); //PJ_LOG (4, (THIS_FILE, "Elapsed time is %d | missed time is %d | frame time %d", elapsed_time, missed_time, frame_time)); //Update missed time // Positif if we are late // negatif if we are earlier // dividing by 2 is empiric result // on N1 if not we get buffer overflow I assume that it fill packets faster than the frequency missed_time = missed_time/2 + elapsed_time - frame_time; //PJ_LOG (4, (THIS_FILE, "And now :: Elapsed time is %d | missed time is %d", elapsed_time, missed_time)); //If we go faster than the buffer filling we have to wait no if( missed_time <= 0 ){ //if(elapsed_time < frame_time){ to_wait = - missed_time - 2; if(to_wait > 0){ // PJ_LOG (4, (THIS_FILE, "Wait for %d / %d", to_wait, frame_time)); pj_thread_sleep(to_wait); } //} } /* //PJ_LOG (4, (THIS_FILE, "Next frame %d", next_frame_in)); if (next_frame_in-2 > 0) { //PJ_LOG (4, (THIS_FILE, "Wait for buffer %d", next_frame_in)); pj_thread_sleep(next_frame_in-5); //Reset the delay we have regarding next frame retard = 0; }else{ if(next_frame_in < 0){ retard += next_frame_in; } } */ #endif bytesRead = jni_env->CallIntMethod(stream->record, read_method, inputBuffer, 0, size); if(bytesRead<=0){ PJ_LOG (3, (THIS_FILE, "Record thread : error while reading data... is there something we can do here? %d", bytesRead)); continue; } if(stream->quit_flag){ break; } if(bytesRead != size){ PJ_LOG(3, (THIS_FILE, "Overrun...")); continue; } // PJ_LOG(4,(THIS_FILE, "Valid record frame read")); //jni_env->GetByteArrayRegion(inputBuffer, 0, size, buf ); pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.size = size; frame.bit_info = 0; frame.buf = (void*) buf; frame.timestamp.u64 = tstamp.u64; // PJ_LOG(3, (THIS_FILE, "New audio record frame to treat : %d <size : %d>", frame.type, frame.size)); status = (*stream->rec_cb)(stream->user_data, &frame); // PJ_LOG(4,(THIS_FILE, "Valid record frame sent to network stack")); if (status != PJ_SUCCESS){ PJ_LOG(1, (THIS_FILE, "Error in record callback")); goto on_finish; } //Update for next step tstamp.u64 += nframes; }; on_finish: jni_env->ReleaseByteArrayElements(inputBuffer, buf, 0); jni_env->DeleteLocalRef(inputBuffer); on_break: DETACH_JVM(jni_env); PJ_LOG(3,(THIS_FILE, ">> Record thread stopped")); // pj_sem_post(stream->audio_launch_sem); stream->rec_thread_exited = 1; return 0; }
static int test(void) { pj_rbtree rb; node_key *key; pj_rbtree_node *node; pj_pool_t *pool; int err=0; int count = MIN_COUNT; int i; unsigned size; pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node); size = MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) + PJ_RBTREE_SIZE + PJ_POOL_SIZE; pool = pj_pool_create( mem, "pool", size, 0, NULL); if (!pool) { PJ_LOG(3,("test", "...error: creating pool of %u bytes", size)); return -10; } key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key)); if (!key) return -20; node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node)); if (!node) return -30; for (i=0; i<LOOP; ++i) { int j; pj_rbtree_node *prev, *it; pj_timestamp t1, t2, t_setup, t_insert, t_search, t_erase; pj_assert(rb.size == 0); t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0; for (j=0; j<count; j++) { randomize_string(key[j].str, STRSIZE); pj_get_timestamp(&t1); node[j].key = &key[j]; node[j].user_data = key[j].str; key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING); pj_get_timestamp(&t2); t_setup.u32.lo += (t2.u32.lo - t1.u32.lo); pj_get_timestamp(&t1); pj_rbtree_insert(&rb, &node[j]); pj_get_timestamp(&t2); t_insert.u32.lo += (t2.u32.lo - t1.u32.lo); } pj_assert(rb.size == (unsigned)count); // Iterate key, make sure they're sorted. prev = NULL; it = pj_rbtree_first(&rb); while (it) { if (prev) { if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) { ++err; PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", (char*)prev->user_data, (char*)it->user_data)); } } prev = it; it = pj_rbtree_next(&rb, it); } // Search. for (j=0; j<count; j++) { pj_get_timestamp(&t1); it = pj_rbtree_find(&rb, &key[j]); pj_get_timestamp(&t2); t_search.u32.lo += (t2.u32.lo - t1.u32.lo); pj_assert(it != NULL); if (it == NULL) ++err; } // Erase node. for (j=0; j<count; j++) { pj_get_timestamp(&t1); it = pj_rbtree_erase(&rb, &node[j]); pj_get_timestamp(&t2); t_erase.u32.lo += (t2.u32.lo - t1.u32.lo); } PJ_LOG(4, (THIS_FILE, "...count:%d, setup:%d, insert:%d, search:%d, erase:%d", count, t_setup.u32.lo / count, t_insert.u32.lo / count, t_search.u32.lo / count, t_erase.u32.lo / count)); count = 2 * count; if (count > MAX_COUNT) break; } pj_pool_release(pool); return err; }
int expand(pj_pool_t *pool, const char *filein, const char *fileout, int expansion_rate100, int lost_rate10, int lost_burst) { enum { LOST_RATE = 10 }; FILE *in, *out; short frame[SAMPLES_PER_FRAME]; pjmedia_wsola *wsola; pj_timestamp elapsed, zero; unsigned samples; int last_lost = 0; /* Lost burst must be > 0 */ assert(lost_rate10==0 || lost_burst > 0); in = fopen(filein, "rb"); if (!in) return 1; out = fopen(fileout, "wb"); if (!out) return 1; pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 1, 0, &wsola); samples = 0; elapsed.u64 = 0; while (fread(frame, SAMPLES_PER_FRAME*2, 1, in) == 1) { pj_timestamp t1, t2; if (lost_rate10 == 0) { /* Expansion */ pj_get_timestamp(&t1); pjmedia_wsola_save(wsola, frame, 0); pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); fwrite(frame, SAMPLES_PER_FRAME*2, 1, out); samples += SAMPLES_PER_FRAME; if ((rand() % 100) < expansion_rate100) { pj_get_timestamp(&t1); pjmedia_wsola_generate(wsola, frame); pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); samples += SAMPLES_PER_FRAME; fwrite(frame, SAMPLES_PER_FRAME*2, 1, out); } } else { /* Lost */ if ((rand() % 10) < lost_rate10) { int burst; for (burst=0; burst<lost_burst; ++burst) { pj_get_timestamp(&t1); pjmedia_wsola_generate(wsola, frame); pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); samples += SAMPLES_PER_FRAME; fwrite(frame, SAMPLES_PER_FRAME*2, 1, out); } last_lost = 1; } else { pj_get_timestamp(&t1); pjmedia_wsola_save(wsola, frame, last_lost); pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); samples += SAMPLES_PER_FRAME; fwrite(frame, SAMPLES_PER_FRAME*2, 1, out); last_lost = 0; } } } zero.u64 = 0; zero.u64 = pj_elapsed_usec(&zero, &elapsed); zero.u64 = samples * PJ_INT64(1000000) / zero.u64; assert(zero.u32.hi == 0); PJ_LOG(3,("test.c", "Processing: %f Msamples per second", zero.u32.lo/1000000.0)); PJ_LOG(3,("test.c", "CPU load for current settings: %f%%", CLOCK_RATE * 100.0 / zero.u32.lo)); pjmedia_wsola_destroy(wsola); fclose(in); fclose(out); return 0; }
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; }
pj_status_t latency_checker::start(latency_config_t &config) { pj_status_t result = PJ_EINVAL; if(m_pool == NULL) { m_dstate = 0; m_start_tone_time.u64 = 0; m_latency = 0; m_quality = 0; m_config = config; m_status_string[0] = 0; m_gain = new snd_agc("",30,1,32000,32000); m_idle_freq1_det = new tone_detector(m_config.clock_rate,IDLE_FREQ1); m_idle_freq2_det = new tone_detector(m_config.clock_rate,IDLE_FREQ2); m_active_freq1_det = new tone_detector(m_config.clock_rate,ACTIVE_FREQ1); m_active_freq2_det = new tone_detector(m_config.clock_rate,ACTIVE_FREQ2); init_generate_dual_tone(&m_idle_tone,m_config.clock_rate,IDLE_FREQ1,IDLE_FREQ2,32767); init_generate_dual_tone(&m_active_tone,m_config.clock_rate,ACTIVE_FREQ1,ACTIVE_FREQ2,32767); pj_log_set_level(0); pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC); if(pj_init()==PJ_SUCCESS) { m_caching_pool = (pj_caching_pool *)malloc(sizeof(pj_caching_pool)); pj_caching_pool_init( m_caching_pool, NULL, 0 ); m_pool_factory=&m_caching_pool->factory; m_pool = pj_pool_create(m_pool_factory, "LATENCY NATIVE", 4000, 4000, NULL); pj_log_set_level(m_config.logs.level); pj_logging_init(m_pool); pj_logging_setLogToConsole(1); pj_logging_setFilename(m_config.logs.file_name); pj_logging_setMaxLogFiles(m_config.logs.max_files); pj_logging_setMaxLogFileSize(m_config.logs.max_file_size*1024*1024); pj_logging_start(); pj_get_timestamp(&m_last_get_frame_time); m_lock = new PPJ_SemaphoreLock(m_pool,NULL,1,1); pjmedia_aud_subsys_init(m_pool_factory); #if PJMEDIA_AUDIO_DEV_HAS_ANDROID #if PJ_ANDROID_DEVICE==1 pjmedia_aud_register_factory(&pjmedia_android_factory); #endif #if PJ_ANDROID_DEVICE==2 pjmedia_aud_register_factory(&pjmedia_opensl_factory); #endif #if PJ_ANDROID_DEVICE==3 pjmedia_aud_register_factory(&pjmedia_alsa_factory); #endif #endif pjmedia_aud_param params; params.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; params.rec_id = 1; params.play_id = 6; params.clock_rate = m_config.clock_rate; params.channel_count = 1; params.samples_per_frame = m_config.min_frame_length*m_config.clock_rate/1000; params.bits_per_sample = 16; params.flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; params.input_latency_ms = m_config.min_frame_length; params.output_latency_ms = m_config.min_frame_length; result = pjmedia_aud_stream_create(¶ms,&rec_cb_s,&play_cb_s,this,&m_aud_stream); if(result==PJ_SUCCESS) { result = pjmedia_aud_stream_start(m_aud_stream); if(result==PJ_SUCCESS) { } } } } if(result!=PJ_SUCCESS) internal_clean(); return result; }
int compress(pj_pool_t *pool, const char *filein, const char *fileout, int rate10) { enum { BUF_CNT = SAMPLES_PER_FRAME * 10 }; FILE *in, *out; pjmedia_wsola *wsola; short buf[BUF_CNT]; pj_timestamp elapsed, zero; unsigned samples = 0; in = fopen(filein, "rb"); if (!in) return 1; out = fopen(fileout, "wb"); if (!out) return 1; pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 1, 0, &wsola); elapsed.u64 = 0; for (;;) { unsigned size_del, count; pj_timestamp t1, t2; int i; if (fread(buf, sizeof(buf), 1, in) != 1) break; count = BUF_CNT; size_del = 0; pj_get_timestamp(&t1); for (i=0; i<rate10; ++i) { unsigned to_del = SAMPLES_PER_FRAME; #if 0 /* Method 1: buf1 contiguous */ pjmedia_wsola_discard(wsola, buf, count, NULL, 0, &to_del); #elif 0 /* Method 2: split, majority in buf1 */ assert(count > SAMPLES_PER_FRAME); pjmedia_wsola_discard(wsola, buf, count-SAMPLES_PER_FRAME, buf+count-SAMPLES_PER_FRAME, SAMPLES_PER_FRAME, &to_del); #elif 0 /* Method 3: split, majority in buf2 */ assert(count > SAMPLES_PER_FRAME); pjmedia_wsola_discard(wsola, buf, SAMPLES_PER_FRAME, buf+SAMPLES_PER_FRAME, count-SAMPLES_PER_FRAME, &to_del); #elif 1 /* Method 4: split, each with small length */ enum { TOT_LEN = 3 * SAMPLES_PER_FRAME }; unsigned buf1_len = (rand() % TOT_LEN); short *ptr = buf + count - TOT_LEN; assert(count > TOT_LEN); if (buf1_len==0) buf1_len=SAMPLES_PER_FRAME*2; pjmedia_wsola_discard(wsola, ptr, buf1_len, ptr+buf1_len, TOT_LEN-buf1_len, &to_del); #endif count -= to_del; size_del += to_del; } pj_get_timestamp(&t2); samples += BUF_CNT; pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); assert(size_del >= SAMPLES_PER_FRAME); fwrite(buf, count, 2, out); } pjmedia_wsola_destroy(wsola); fclose(in); fclose(out); zero.u64 = 0; zero.u64 = pj_elapsed_usec(&zero, &elapsed); zero.u64 = samples * PJ_INT64(1000000) / zero.u64; assert(zero.u32.hi == 0); PJ_LOG(3,("test.c", "Processing: %f Msamples per second", zero.u32.lo/1000000.0)); PJ_LOG(3,("test.c", "CPU load for current settings: %f%%", CLOCK_RATE * 100.0 / zero.u32.lo)); return 0; }
pj_status_t get_timestamp() { return pj_get_timestamp(&ts_); }
static int sleep_duration_test(void) { enum { MIS = 20}; unsigned duration[] = { 2000, 1000, 500, 200, 100 }; unsigned i; pj_status_t rc; PJ_LOG(3,(THIS_FILE, "..running sleep duration test")); /* Test pj_thread_sleep() and pj_gettimeofday() */ for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) { pj_time_val start, stop; pj_uint32_t msec; /* Mark start of test. */ rc = pj_gettimeofday(&start); if (rc != PJ_SUCCESS) { app_perror("...error: pj_gettimeofday()", rc); return -10; } /* Sleep */ rc = pj_thread_sleep(duration[i]); if (rc != PJ_SUCCESS) { app_perror("...error: pj_thread_sleep()", rc); return -20; } /* Mark end of test. */ rc = pj_gettimeofday(&stop); /* Calculate duration (store in stop). */ PJ_TIME_VAL_SUB(stop, start); /* Convert to msec. */ msec = PJ_TIME_VAL_MSEC(stop); /* Check if it's within range. */ if (msec < duration[i] * (100-MIS)/100 || msec > duration[i] * (100+MIS)/100) { PJ_LOG(3,(THIS_FILE, "...error: slept for %d ms instead of %d ms " "(outside %d%% err window)", msec, duration[i], MIS)); return -30; } } /* Test pj_thread_sleep() and pj_get_timestamp() and friends */ for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) { pj_time_val t1, t2; pj_timestamp start, stop; pj_uint32_t msec; pj_thread_sleep(0); /* Mark start of test. */ rc = pj_get_timestamp(&start); if (rc != PJ_SUCCESS) { app_perror("...error: pj_get_timestamp()", rc); return -60; } /* ..also with gettimeofday() */ pj_gettimeofday(&t1); /* Sleep */ rc = pj_thread_sleep(duration[i]); if (rc != PJ_SUCCESS) { app_perror("...error: pj_thread_sleep()", rc); return -70; } /* Mark end of test. */ pj_get_timestamp(&stop); /* ..also with gettimeofday() */ pj_gettimeofday(&t2); /* Compare t1 and t2. */ if (PJ_TIME_VAL_LT(t2, t1)) { PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!")); return -75; } /* Get elapsed time in msec */ msec = pj_elapsed_msec(&start, &stop); /* Check if it's within range. */ if (msec < duration[i] * (100-MIS)/100 || msec > duration[i] * (100+MIS)/100) { PJ_LOG(3,(THIS_FILE, "...error: slept for %d ms instead of %d ms " "(outside %d%% err window)", msec, duration[i], MIS)); PJ_TIME_VAL_SUB(t2, t1); PJ_LOG(3,(THIS_FILE, "...info: gettimeofday() reported duration is " "%d msec", PJ_TIME_VAL_MSEC(t2))); return -76; } } /* All done. */ return 0; }
PJ_DEF(pj_status_t) pjmedia_natnl_stream_create(pj_pool_t *pool, pjsua_call *call, pjmedia_stream_info *si, natnl_stream **stream) { pj_status_t status = PJ_SUCCESS; unsigned strm_idx = 0; strm_idx = call->index; /* TODO: * - Create and start your media stream based on the parameters * in si */ PJ_ASSERT_RETURN(pool, PJ_EINVAL); PJ_LOG(4,(THIS_FILE,"natnl audio channel update..strm_idx=%d", strm_idx)); /* Check if no media is active */ if (si->dir != PJMEDIA_DIR_NONE) { /* Create session based on session info. */ #if 0 pool = pj_pool_create(strm_pool->factory, "strm%p", NATNL_STREAM_SIZE, NATNL_STREAM_INC, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); #endif pj_mutex_lock(call->tnl_stream_lock); pj_mutex_lock(call->tnl_stream_lock2); pj_mutex_lock(call->tnl_stream_lock3); // DEAN don't re-create natnl stream if (call->tnl_stream) { *stream = call->tnl_stream; pj_mutex_unlock(call->tnl_stream_lock3); pj_mutex_unlock(call->tnl_stream_lock2); pj_mutex_unlock(call->tnl_stream_lock); return PJ_SUCCESS; } call->tnl_stream = *stream = PJ_POOL_ZALLOC_T(pool, natnl_stream); PJ_ASSERT_RETURN(*stream != NULL, PJ_ENOMEM); (*stream)->call = call; (*stream)->own_pool = pool; (*stream)->med_tp = call->med_tp; pj_memcpy(&(*stream)->rem_addr, &si->rem_addr, sizeof(pj_sockaddr)); pj_list_init(&(*stream)->rbuff); pj_list_init(&(*stream)->gcbuff); pj_get_timestamp(&(*stream)->last_data_or_ka); pj_get_timestamp(&(*stream)->last_data); (*stream)->rbuff_cnt = 0; (*stream)->rx_band = (pj_band_t *)malloc(sizeof(pj_band_t)); (*stream)->tx_band = (pj_band_t *)malloc(sizeof(pj_band_t)); pj_memset((*stream)->rx_band, 0, sizeof(pj_band_t)); pj_memset((*stream)->tx_band, 0, sizeof(pj_band_t)); pj_bandwidthSetLimited((*stream)->rx_band, PJ_FALSE); pj_bandwidthSetLimited((*stream)->tx_band, PJ_FALSE); /* Create mutex to protect jitter buffer: */ status = pj_mutex_create_simple(pool, NULL, &(*stream)->rbuff_mutex); if (status != PJ_SUCCESS) { //pj_pool_t *tmp_pool = (*stream)->own_pool; (*stream)->own_pool = NULL; //pj_pool_release(tmp_pool); goto on_return; } status = pj_mutex_create_simple(pool, NULL, &(*stream)->gcbuff_mutex); if (status != PJ_SUCCESS) { (*stream)->own_pool = NULL; goto on_return; } /* Create semaphore */ status = pj_sem_create(pool, "client", 0, 65535, &(*stream)->rbuff_sem); if (status != PJ_SUCCESS) { (*stream)->own_pool = NULL; goto on_return; } // +Roger - Create Send buffer Mutex status = pj_mutex_create_simple(pool, NULL, &(*stream)->sbuff_mutex); if (status != PJ_SUCCESS) { //pj_pool_t *tmp_pool = (*stream)->own_pool; (*stream)->own_pool = NULL; //pj_pool_release(tmp_pool); goto on_return; } //------------------------------------// #if 0 /* Attach our RTP and RTCP callbacks to the media transport */ status = pjmedia_transport_attach(call_med->tp, stream, //call_med, &si->rem_addr, &si->rem_rtcp, pj_sockaddr_get_len(&si->rem_addr), &aud_rtp_cb, &aud_rtcp_cb); #endif } on_return: pj_mutex_unlock(call->tnl_stream_lock3); pj_mutex_unlock(call->tnl_stream_lock2); pj_mutex_unlock(call->tnl_stream_lock); return status; }
static int uas_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) { unsigned i; pjsip_tx_data *request; pjsip_via_hdr *via; pjsip_rx_data rdata; pj_sockaddr_in remote; pjsip_transaction **tsx; pj_timestamp t1, t2, elapsed; char branch_buf[80] = PJSIP_RFC3261_BRANCH_ID "0000000000"; pj_status_t status; /* Create the request first. */ pj_str_t str_target = pj_str("sip:[email protected]"); pj_str_t str_from = pj_str("\"Local User\" <sip:[email protected]>"); pj_str_t str_to = pj_str("\"Remote User\" <sip:[email protected]>"); pj_str_t str_contact = str_from; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } /* Create Via */ via = pjsip_via_hdr_create(request->pool); via->sent_by.host = pj_str("192.168.0.7"); via->sent_by.port = 5061; via->transport = pj_str("udp"); via->rport_param = 1; via->recvd_param = pj_str("192.168.0.7"); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via); /* Create "dummy" rdata from the tdata */ pj_bzero(&rdata, sizeof(pjsip_rx_data)); rdata.tp_info.pool = request->pool; rdata.msg_info.msg = request->msg; rdata.msg_info.from = (pjsip_from_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.to = (pjsip_to_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL); rdata.msg_info.cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL); rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.via = via; pj_sockaddr_in_init(&remote, 0, 0); status = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_LOOP_DGRAM, &remote, sizeof(pj_sockaddr_in), NULL, &rdata.tp_info.transport); if (status != PJ_SUCCESS) { app_perror(" error: unable to get loop transport", status); return status; } /* Create transaction array */ tsx = (pjsip_transaction**) pj_pool_zalloc(request->pool, working_set * sizeof(pj_pool_t*)); pj_bzero(&mod_tsx_user, sizeof(mod_tsx_user)); mod_tsx_user.id = -1; /* Benchmark */ elapsed.u64 = 0; pj_get_timestamp(&t1); for (i=0; i<working_set; ++i) { via->branch_param.ptr = branch_buf; via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN + pj_ansi_sprintf(branch_buf+PJSIP_RFC3261_BRANCH_LEN, "-%d", i); status = pjsip_tsx_create_uas(&mod_tsx_user, &rdata, &tsx[i]); if (status != PJ_SUCCESS) goto on_error; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; status = PJ_SUCCESS; on_error: for (i=0; i<working_set; ++i) { if (tsx[i]) { pjsip_tsx_terminate(tsx[i], 601); tsx[i] = NULL; } } pjsip_tx_data_dec_ref(request); flush_events(2000); return status; }
static pj_status_t check_new_decode_result(pjmedia_vid_codec *codec, SBufferInfo *bufferInfo, const pj_timestamp *ts, pj_bool_t got_keyframe) { openh264_private *ff = (openh264_private*)codec->codec_data; pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp; pjmedia_event event; /* Check for format change. * Decoder output format is set by libavcodec, in case it is different * to the configured param. */ if (bufferInfo->UsrData.sSystemBuffer.iWidth != (int)vafp->size.w || bufferInfo->UsrData.sSystemBuffer.iHeight != (int)vafp->size.h) { pj_status_t status; /* Update decoder format in param */ ff->param.dec_fmt.id = PJMEDIA_FORMAT_I420; ff->param.dec_fmt.det.vid.size.w = bufferInfo->UsrData.sSystemBuffer.iWidth; ff->param.dec_fmt.det.vid.size.h = bufferInfo->UsrData.sSystemBuffer.iHeight; /* Re-init format info and apply-param of decoder */ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id); if (!ff->dec_vfi) return PJ_ENOTSUP; pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp)); ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size; ff->dec_vafp.buffer = NULL; status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp); if (status != PJ_SUCCESS) return status; /* Realloc buffer if necessary */ if (ff->dec_vafp.framebytes > ff->dec_buf_size) { PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u", (unsigned)ff->dec_buf_size, (unsigned)ff->dec_vafp.framebytes)); ff->dec_buf_size = ff->dec_vafp.framebytes; ff->unaligned_dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size); ff->dec_buf = align_buffer_16(ff->unaligned_dec_buf); } /* Broadcast format changed event */ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec); event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt, sizeof(ff->param.dec_fmt)); pjmedia_event_publish(NULL, codec, &event, 0); } /* Check for missing/found keyframe */ if (got_keyframe) { pj_get_timestamp(&ff->last_dec_keyframe_ts); /* Broadcast keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } else if (ff->last_dec_keyframe_ts.u64 == 0) { /* Broadcast missing keyframe event */ // pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, ts, codec); // pjmedia_event_publish(NULL, codec, &event, 0); } return PJ_SUCCESS; }
static int uac_tsx_bench(unsigned working_set, pj_timestamp *p_elapsed) { unsigned i; pjsip_tx_data *request; pjsip_transaction **tsx; pj_timestamp t1, t2, elapsed; pjsip_via_hdr *via; pj_status_t status; /* Create the request first. */ pj_str_t str_target = pj_str("sip:[email protected]"); pj_str_t str_from = pj_str("\"Local User\" <sip:[email protected]>"); pj_str_t str_to = pj_str("\"Remote User\" <sip:[email protected]>"); pj_str_t str_contact = str_from; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } via = (pjsip_via_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_VIA, NULL); /* Create transaction array */ tsx = (pjsip_transaction**) pj_pool_zalloc(request->pool, working_set * sizeof(pj_pool_t*)); pj_bzero(&mod_tsx_user, sizeof(mod_tsx_user)); mod_tsx_user.id = -1; /* Benchmark */ elapsed.u64 = 0; pj_get_timestamp(&t1); for (i=0; i<working_set; ++i) { status = pjsip_tsx_create_uac(&mod_tsx_user, request, &tsx[i]); if (status != PJ_SUCCESS) goto on_error; /* Reset branch param */ via->branch_param.slen = 0; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); p_elapsed->u64 = elapsed.u64; status = PJ_SUCCESS; on_error: for (i=0; i<working_set; ++i) { if (tsx[i]) { pjsip_tsx_terminate(tsx[i], 601); tsx[i] = NULL; } } pjsip_tx_data_dec_ref(request); flush_events(2000); return status; }
int pool_perf_test() { unsigned i; pj_uint32_t pool_time=0, malloc_time=0, pool_time2=0; pj_timestamp start, end; pj_uint32_t best, worst; /* Initialize size of chunks to allocate in for the test. */ for (i=0; i<COUNT; ++i) { unsigned aligned_size; sizes[i] = MIN_SIZE + (pj_rand() % MAX_SIZE); aligned_size = sizes[i]; if (aligned_size & (PJ_POOL_ALIGNMENT-1)) aligned_size = ((aligned_size + PJ_POOL_ALIGNMENT - 1)) & ~(PJ_POOL_ALIGNMENT - 1); total_size += aligned_size; } /* Add some more for pool admin area */ total_size += 512; PJ_LOG(3, (THIS_FILE, "Benchmarking pool..")); /* Warmup */ pool_test_pool(); pool_test_malloc_free(); for (i=0; i<LOOP; ++i) { pj_get_timestamp(&start); if (pool_test_pool()) { return 1; } pj_get_timestamp(&end); pool_time += (end.u32.lo - start.u32.lo); pj_get_timestamp(&start); if (pool_test_malloc_free()) { return 2; } pj_get_timestamp(&end); malloc_time += (end.u32.lo - start.u32.lo); pj_get_timestamp(&start); if (pool_test_pool()) { return 4; } pj_get_timestamp(&end); pool_time2 += (end.u32.lo - start.u32.lo); } PJ_LOG(4,(THIS_FILE,"..LOOP count: %u",LOOP)); PJ_LOG(4,(THIS_FILE,"..number of alloc/dealloc per loop: %u",COUNT)); PJ_LOG(4,(THIS_FILE,"..pool allocation/deallocation time: %u",pool_time)); PJ_LOG(4,(THIS_FILE,"..malloc/free time: %u",malloc_time)); PJ_LOG(4,(THIS_FILE,"..pool again, second invocation: %u",pool_time2)); if (pool_time2==0) pool_time2=1; if (pool_time < pool_time2) best = pool_time, worst = pool_time2; else best = pool_time2, worst = pool_time; /* avoid division by zero */ if (best==0) best=1; if (worst==0) worst=1; PJ_LOG(3, (THIS_FILE, "..pool speedup over malloc best=%dx, worst=%dx", (int)(malloc_time/best), (int)(malloc_time/worst))); return 0; }
/* Test that we receive loopback message. */ int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, char *target_url, int *p_usec_rtt) { pj_bool_t msg_log_enabled; pj_status_t status; pj_str_t target, from, to, contact, call_id, body; pjsip_method method; pjsip_tx_data *tdata; pj_time_val timeout; PJ_UNUSED_ARG(tp_type); PJ_UNUSED_ARG(ref_tp); PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ if (my_module.id == -1) { status = pjsip_endpt_register_module( endpt, &my_module ); if (status != PJ_SUCCESS) { app_perror(" error: unable to register module", status); return -500; } } /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); /* Create a request message. */ target = pj_str(target_url); from = pj_str(FROM_HDR); to = pj_str(target_url); contact = pj_str(CONTACT_HDR); call_id = pj_str(CALL_ID_HDR); body = pj_str(BODY); pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to, &contact, &call_id, CSEQ_VALUE, &body, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return -510; } /* Reset statuses */ send_status = recv_status = NO_STATUS; /* Start time. */ pj_get_timestamp(&my_send_time); /* Send the message (statelessly). */ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ pjsip_tx_data_dec_ref(tdata); send_status = status; } /* Set the timeout (2 seconds from now) */ pj_gettimeofday(&timeout); timeout.sec += 2; /* Loop handling events until we get status */ do { pj_time_val now; pj_time_val poll_interval = { 0, 10 }; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, timeout)) { PJ_LOG(3,(THIS_FILE, " error: timeout in send/recv test")); status = -540; goto on_return; } if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) { app_perror(" error sending message", send_status); status = -550; goto on_return; } if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) { app_perror(" error receiving message", recv_status); status = -560; goto on_return; } if (send_status!=NO_STATUS && recv_status!=NO_STATUS) { /* Success! */ break; } pjsip_endpt_handle_events(endpt, &poll_interval); } while (1); if (status == PJ_SUCCESS) { unsigned usec_rt; usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time); PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt)); *p_usec_rtt = usec_rt; } /* Restore message logging. */ msg_logger_set_enabled(msg_log_enabled); status = PJ_SUCCESS; on_return: return status; }
/* * sock_producer_consumer() * * Simple producer-consumer benchmarking. Send loop number of * buf_size size packets as fast as possible. */ static int sock_producer_consumer(int sock_type, unsigned buf_size, unsigned loop, unsigned *p_bandwidth) { pj_sock_t consumer, producer; pj_pool_t *pool; char *outgoing_buffer, *incoming_buffer; pj_timestamp start, stop; unsigned i; pj_highprec_t elapsed, bandwidth; pj_size_t total_received; pj_status_t rc; /* Create pool. */ pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); if (!pool) return -10; /* Create producer-consumer pair. */ rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer); if (rc != PJ_SUCCESS) { app_perror("...error: create socket pair", rc); return -20; } /* Create buffers. */ outgoing_buffer = pj_pool_alloc(pool, buf_size); incoming_buffer = pj_pool_alloc(pool, buf_size); /* Start loop. */ pj_get_timestamp(&start); total_received = 0; for (i=0; i<loop; ++i) { pj_ssize_t sent, part_received, received; pj_time_val delay; sent = buf_size; rc = pj_sock_send(producer, outgoing_buffer, &sent, 0); if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) { app_perror("...error: send()", rc); return -61; } /* Repeat recv() until all data is part_received. * This applies only for non-UDP of course, since for UDP * we would expect all data to be part_received in one packet. */ received = 0; do { part_received = buf_size-received; rc = pj_sock_recv(consumer, incoming_buffer+received, &part_received, 0); if (rc != PJ_SUCCESS) { app_perror("...recv error", rc); return -70; } if (part_received <= 0) { PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!", part_received)); return -73; } if ((pj_size_t)part_received != buf_size-received) { if (sock_type != PJ_SOCK_STREAM) { PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", buf_size-received, part_received)); return -76; } } received += part_received; } while ((pj_size_t)received < buf_size); total_received += received; /* Stop test if it's been runnign for more than 10 secs. */ pj_get_timestamp(&stop); delay = pj_elapsed_time(&start, &stop); if (delay.sec > 10) break; } /* Stop timer. */ pj_get_timestamp(&stop); elapsed = pj_elapsed_usec(&start, &stop); /* bandwidth = total_received * 1000 / elapsed */ bandwidth = total_received; pj_highprec_mul(bandwidth, 1000); pj_highprec_div(bandwidth, elapsed); *p_bandwidth = (pj_uint32_t)bandwidth; /* Close sockets. */ pj_sock_close(consumer); pj_sock_close(producer); /* Done */ pj_pool_release(pool); return 0; }
/* * create response benchmark */ static int create_response_bench(pj_timestamp *p_elapsed) { enum { COUNT = 100 }; unsigned i, j; pjsip_via_hdr *via; pjsip_rx_data rdata; pjsip_tx_data *request; pjsip_tx_data *tdata[COUNT]; pj_timestamp t1, t2, elapsed; pj_status_t status; /* Create the request first. */ pj_str_t str_target = pj_str("sip:[email protected]"); pj_str_t str_from = pj_str("\"Local User\" <sip:[email protected]>"); pj_str_t str_to = pj_str("\"Remote User\" <sip:[email protected]>"); pj_str_t str_contact = str_from; status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, &str_target, &str_from, &str_to, &str_contact, NULL, -1, NULL, &request); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return status; } /* Create several Via headers */ via = pjsip_via_hdr_create(request->pool); via->sent_by.host = pj_str("192.168.0.7"); via->sent_by.port = 5061; via->transport = pj_str("udp"); via->rport_param = 0; via->branch_param = pj_str("012345678901234567890123456789"); via->recvd_param = pj_str("192.168.0.7"); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*) pjsip_hdr_clone(request->pool, via)); pjsip_msg_insert_first_hdr(request->msg, (pjsip_hdr*)via); /* Create "dummy" rdata from the tdata */ pj_bzero(&rdata, sizeof(pjsip_rx_data)); rdata.tp_info.pool = request->pool; rdata.msg_info.msg = request->msg; rdata.msg_info.from = (pjsip_from_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.to = (pjsip_to_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_TO, NULL); rdata.msg_info.cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_CSEQ, NULL); rdata.msg_info.cid = (pjsip_cid_hdr*) pjsip_msg_find_hdr(request->msg, PJSIP_H_FROM, NULL); rdata.msg_info.via = via; /* * Now benchmark create_response */ elapsed.u64 = 0; for (i=0; i<LOOP; i+=COUNT) { pj_bzero(tdata, sizeof(tdata)); pj_get_timestamp(&t1); for (j=0; j<COUNT; ++j) { status = pjsip_endpt_create_response(endpt, &rdata, 200, NULL, &tdata[j]); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); goto on_error; } } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&elapsed, &t2); for (j=0; j<COUNT; ++j) pjsip_tx_data_dec_ref(tdata[j]); } p_elapsed->u64 = elapsed.u64; pjsip_tx_data_dec_ref(request); return PJ_SUCCESS; on_error: for (i=0; i<COUNT; ++i) { if (tdata[i]) pjsip_tx_data_dec_ref(tdata[i]); } return -400; }
/* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { char dummy_guid[PJ_GUID_MAX_LENGTH]; pj_str_t guid; pj_status_t rc; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } #if PJ_HAS_THREADS /* Init this thread's TLS. */ if ((rc=pj_thread_init()) != 0) { return rc; } /* Critical section. */ if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0) return rc; #endif /* Init logging */ pj_log_init(); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (rc != PJ_SUCCESS) return rc; /* Init random seed. */ /* Or probably not. Let application in charge of this */ /* pj_srand( clock() ); */ /* Startup GUID. */ guid.ptr = dummy_guid; pj_generate_unique_string( &guid ); /* Startup timestamp */ #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 { pj_timestamp dummy_ts; if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { return rc; } } #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized", PJ_VERSION)); return PJ_SUCCESS; }
/* * main() */ int main(int argc, char *argv[]) { pj_caching_pool cp; pjmedia_endpt *med_endpt; pj_pool_t *pool; pjmedia_port *wav_play; pjmedia_port *wav_rec; pjmedia_port *wav_out; pj_status_t status; pjmedia_echo_state *ec; pjmedia_frame play_frame, rec_frame; unsigned opt = 0; unsigned latency_ms = 25; unsigned tail_ms = TAIL_LENGTH; pj_timestamp t0, t1; int i, repeat=1, interactive=0, c; pj_optind = 0; while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) { switch (c) { case 'd': latency_ms = atoi(pj_optarg); if (latency_ms < 25) { puts("Invalid delay"); puts(desc); } break; case 'l': tail_ms = atoi(pj_optarg); break; case 'a': { int alg = atoi(pj_optarg); switch (alg) { case 0: opt = 0; case 1: opt = PJMEDIA_ECHO_SPEEX; break; case 3: opt = PJMEDIA_ECHO_SIMPLE; break; default: puts("Invalid algorithm"); puts(desc); return 1; } } break; case 'r': repeat = atoi(pj_optarg); if (repeat < 1) { puts("Invalid repeat count"); puts(desc); return 1; } break; case 'i': interactive = 1; break; } } if (argc - pj_optind != 3) { puts("Error: missing argument(s)"); puts(desc); return 1; } /* Must init PJLIB first: */ status = pj_init(); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Must create a pool factory before we can allocate any memory. */ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); /* * Initialize media endpoint. * This will implicitly initialize PJMEDIA too. */ status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Create memory pool for our file player */ pool = pj_pool_create( &cp.factory, /* pool factory */ "wav", /* pool name. */ 4000, /* init size */ 4000, /* increment size */ NULL /* callback on error */ ); /* Open wav_play */ status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME, PJMEDIA_FILE_NO_LOOP, 0, &wav_play); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening playback WAV file", status); return 1; } /* Open recorded wav */ status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME, PJMEDIA_FILE_NO_LOOP, 0, &wav_rec); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening recorded WAV file", status); return 1; } /* play and rec WAVs must have the same clock rate */ if (wav_play->info.clock_rate != wav_rec->info.clock_rate) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* .. and channel count */ if (wav_play->info.channel_count != wav_rec->info.channel_count) { puts("Error: clock rate mismatch in the WAV files"); return 1; } /* Create output wav */ status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2], wav_play->info.clock_rate, wav_play->info.channel_count, wav_play->info.samples_per_frame, wav_play->info.bits_per_sample, 0, 0, &wav_out); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error opening output WAV file", status); return 1; } /* Create echo canceller */ status = pjmedia_echo_create2(pool, wav_play->info.clock_rate, wav_play->info.channel_count, wav_play->info.samples_per_frame, tail_ms, latency_ms, opt, &ec); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating EC", status); return 1; } /* Processing loop */ play_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); pj_get_timestamp(&t0); for (i=0; i < repeat; ++i) { for (;;) { play_frame.size = wav_play->info.samples_per_frame << 1; status = pjmedia_port_get_frame(wav_play, &play_frame); if (status != PJ_SUCCESS) break; status = pjmedia_echo_playback(ec, (short*)play_frame.buf); rec_frame.size = wav_play->info.samples_per_frame << 1; status = pjmedia_port_get_frame(wav_rec, &rec_frame); if (status != PJ_SUCCESS) break; status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0); //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf, // (short*)play_frame.buf, 0, NULL); pjmedia_port_put_frame(wav_out, &rec_frame); } pjmedia_wav_player_port_set_pos(wav_play, 0); pjmedia_wav_player_port_set_pos(wav_rec, 0); } pj_get_timestamp(&t1); i = pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 / (wav_out->info.clock_rate * wav_out->info.channel_count); PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio", i / 1000, i % 1000)); PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1))); /* Destroy file port(s) */ status = pjmedia_port_destroy( wav_play ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_port_destroy( wav_rec ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); status = pjmedia_port_destroy( wav_out ); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); /* Destroy ec */ pjmedia_echo_destroy(ec); /* Release application pool */ pj_pool_release( pool ); /* Destroy media endpoint. */ pjmedia_endpt_destroy( med_endpt ); /* Destroy pool factory */ pj_caching_pool_destroy( &cp ); /* Shutdown PJLIB */ pj_shutdown(); if (interactive) { char s[10], *dummy; puts("ENTER to quit"); dummy = fgets(s, sizeof(s), stdin); } /* Done. */ return 0; }
/* Calculate the bandwidth for the specific test configuration. * The test is simple: * - create sockpair_cnt number of producer-consumer socket pair. * - create thread_cnt number of worker threads. * - each producer will send buffer_size bytes data as fast and * as soon as it can. * - each consumer will read buffer_size bytes of data as fast * as it could. * - measure the total bytes received by all consumers during a * period of time. */ static int perform_test(pj_bool_t allow_concur, int sock_type, const char *type_name, unsigned thread_cnt, unsigned sockpair_cnt, pj_size_t buffer_size, pj_size_t *p_bandwidth) { enum { MSEC_DURATION = 5000 }; pj_pool_t *pool; test_item *items; pj_thread_t **thread; pj_ioqueue_t *ioqueue; pj_status_t rc; pj_ioqueue_callback ioqueue_callback; pj_uint32_t total_elapsed_usec, total_received; pj_highprec_t bandwidth; pj_timestamp start, stop; unsigned i; TRACE_((THIS_FILE, " starting test..")); ioqueue_callback.on_read_complete = &on_read_complete; ioqueue_callback.on_write_complete = &on_write_complete; thread_quit_flag = 0; pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); if (!pool) return -10; items = (test_item*) pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item)); thread = (pj_thread_t**) pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*)); TRACE_((THIS_FILE, " creating ioqueue..")); rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create ioqueue", rc); return -15; } rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_set_default_concurrency()", rc); return -16; } /* Initialize each producer-consumer pair. */ for (i=0; i<sockpair_cnt; ++i) { pj_ssize_t bytes; items[i].ioqueue = ioqueue; items[i].buffer_size = buffer_size; items[i].outgoing_buffer = (char*) pj_pool_alloc(pool, buffer_size); items[i].incoming_buffer = (char*) pj_pool_alloc(pool, buffer_size); items[i].bytes_recv = items[i].bytes_sent = 0; /* randomize outgoing buffer. */ pj_create_random_string(items[i].outgoing_buffer, buffer_size); /* Create socket pair. */ TRACE_((THIS_FILE, " calling socketpair..")); rc = app_socketpair(pj_AF_INET(), sock_type, 0, &items[i].server_fd, &items[i].client_fd); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create socket pair", rc); return -20; } /* Register server socket to ioqueue. */ TRACE_((THIS_FILE, " register(1)..")); rc = pj_ioqueue_register_sock(pool, ioqueue, items[i].server_fd, &items[i], &ioqueue_callback, &items[i].server_key); if (rc != PJ_SUCCESS) { app_perror("...error: registering server socket to ioqueue", rc); return -60; } /* Register client socket to ioqueue. */ TRACE_((THIS_FILE, " register(2)..")); rc = pj_ioqueue_register_sock(pool, ioqueue, items[i].client_fd, &items[i], &ioqueue_callback, &items[i].client_key); if (rc != PJ_SUCCESS) { app_perror("...error: registering server socket to ioqueue", rc); return -70; } /* Start reading. */ TRACE_((THIS_FILE, " pj_ioqueue_recv..")); bytes = items[i].buffer_size; rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op, items[i].incoming_buffer, &bytes, 0); if (rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_recv", rc); return -73; } /* Start writing. */ TRACE_((THIS_FILE, " pj_ioqueue_write..")); bytes = items[i].buffer_size; rc = pj_ioqueue_send(items[i].client_key, &items[i].send_op, items[i].outgoing_buffer, &bytes, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_write", rc); return -76; } items[i].has_pending_send = (rc==PJ_EPENDING); } /* Create the threads. */ for (i=0; i<thread_cnt; ++i) { struct thread_arg *arg; arg = (struct thread_arg*) pj_pool_zalloc(pool, sizeof(*arg)); arg->id = i; arg->ioqueue = ioqueue; arg->counter = 0; rc = pj_thread_create( pool, NULL, &worker_thread, arg, PJ_THREAD_DEFAULT_STACK_SIZE, PJ_THREAD_SUSPENDED, &thread[i] ); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create thread", rc); return -80; } } /* Mark start time. */ rc = pj_get_timestamp(&start); if (rc != PJ_SUCCESS) return -90; /* Start the thread. */ TRACE_((THIS_FILE, " resuming all threads..")); for (i=0; i<thread_cnt; ++i) { rc = pj_thread_resume(thread[i]); if (rc != 0) return -100; } /* Wait for MSEC_DURATION seconds. * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually, * but unfortunately it doesn't work when system doesn't employ * timeslicing for threads. */ TRACE_((THIS_FILE, " wait for few seconds..")); do { pj_thread_sleep(1); /* Mark end time. */ rc = pj_get_timestamp(&stop); if (thread_quit_flag) { TRACE_((THIS_FILE, " transfer limit reached..")); break; } if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) { TRACE_((THIS_FILE, " time limit reached..")); break; } } while (1); /* Terminate all threads. */ TRACE_((THIS_FILE, " terminating all threads..")); thread_quit_flag = 1; for (i=0; i<thread_cnt; ++i) { TRACE_((THIS_FILE, " join thread %d..", i)); pj_thread_join(thread[i]); } /* Close all sockets. */ TRACE_((THIS_FILE, " closing all sockets..")); for (i=0; i<sockpair_cnt; ++i) { pj_ioqueue_unregister(items[i].server_key); pj_ioqueue_unregister(items[i].client_key); } /* Destroy threads */ for (i=0; i<thread_cnt; ++i) { pj_thread_destroy(thread[i]); } /* Destroy ioqueue. */ TRACE_((THIS_FILE, " destroying ioqueue..")); pj_ioqueue_destroy(ioqueue); /* Calculate actual time in usec. */ total_elapsed_usec = pj_elapsed_usec(&start, &stop); /* Calculate total bytes received. */ total_received = 0; for (i=0; i<sockpair_cnt; ++i) { total_received = (pj_uint32_t)items[i].bytes_recv; } /* bandwidth = total_received*1000/total_elapsed_usec */ bandwidth = total_received; pj_highprec_mul(bandwidth, 1000); pj_highprec_div(bandwidth, total_elapsed_usec); *p_bandwidth = (pj_uint32_t)bandwidth; PJ_LOG(3,(THIS_FILE, " %.4s %2d %2d %8d KB/s", type_name, thread_cnt, sockpair_cnt, *p_bandwidth)); /* Done. */ pj_pool_release(pool); TRACE_((THIS_FILE, " done..")); return 0; }
static int send_recv_test(pj_ioqueue_t *ioque, pj_ioqueue_key_t *skey, pj_ioqueue_key_t *ckey, void *send_buf, void *recv_buf, pj_ssize_t bufsize, pj_timestamp *t_elapsed) { pj_status_t status; pj_ssize_t bytes; pj_time_val timeout; pj_timestamp t1, t2; int pending_op = 0; pj_ioqueue_op_key_t read_op, write_op; // Start reading on the server side. bytes = bufsize; status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...pj_ioqueue_recv error", status); return -100; } if (status == PJ_EPENDING) ++pending_op; else { /* Does not expect to return error or immediate data. */ return -115; } // Randomize send buffer. pj_create_random_string((char*)send_buf, bufsize); // Starts send on the client side. bytes = bufsize; status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0); if (status != PJ_SUCCESS && bytes != PJ_EPENDING) { return -120; } if (status == PJ_EPENDING) { ++pending_op; } // Begin time. pj_get_timestamp(&t1); // Reset indicators callback_read_size = callback_write_size = 0; callback_read_key = callback_write_key = NULL; callback_read_op = callback_write_op = NULL; // Poll the queue until we've got completion event in the server side. status = 0; while (pending_op > 0) { timeout.sec = 1; timeout.msec = 0; #ifdef PJ_SYMBIAN PJ_UNUSED_ARG(ioque); status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (callback_read_size) { if (callback_read_size != bufsize) return -160; if (callback_read_key != skey) return -161; if (callback_read_op != &read_op) return -162; } if (callback_write_size) { if (callback_write_key != ckey) return -163; if (callback_write_op != &write_op) return -164; } pending_op -= status; } if (status == 0) { PJ_LOG(3,("", "...error: timed out")); } if (status < 0) { return -170; } } // Pending op is zero. // Subsequent poll should yield zero too. timeout.sec = timeout.msec = 0; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) return -173; // End time. pj_get_timestamp(&t2); t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo); // Compare recv buffer with send buffer. if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { return -180; } // Success return 0; }
/* * pj_init(void). * Init PJLIB! */ PJ_DEF(pj_status_t) pj_init(void) { WSADATA wsa; char dummy_guid[32]; /* use maximum GUID length */ pj_str_t guid; pj_status_t rc; /* Check if PJLIB have been initialized */ if (initialized) { ++initialized; return PJ_SUCCESS; } /* Init Winsock.. */ if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Init this thread's TLS. */ if ((rc=pj_thread_init()) != PJ_SUCCESS) { return rc; } /* Init logging */ pj_log_init(); /* Init random seed. */ /* Or probably not. Let application in charge of this */ /* pj_srand( GetCurrentProcessId() ); */ /* Initialize critical section. */ if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) return rc; /* Startup GUID. */ guid.ptr = dummy_guid; pj_generate_unique_string( &guid ); /* Initialize exception ID for the pool. * Must do so after critical section is configured. */ rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); if (rc != PJ_SUCCESS) return rc; /* Startup timestamp */ #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 { pj_timestamp dummy_ts; if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) { return rc; } if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { return rc; } } #endif /* Flag PJLIB as initialized */ ++initialized; pj_assert(initialized == 1); PJ_LOG(4,(THIS_FILE, "pjlib %s for win32 initialized", PJ_VERSION)); return PJ_SUCCESS; }
/* * Benchmarking IOQueue */ static int bench_test(pj_bool_t allow_concur, int bufsize, int inactive_sock_count) { pj_sock_t ssock=-1, csock=-1; pj_sockaddr_in addr; pj_pool_t *pool = NULL; pj_sock_t *inactive_sock=NULL; pj_ioqueue_op_key_t *inactive_read_op; char *send_buf, *recv_buf; pj_ioqueue_t *ioque = NULL; pj_ioqueue_key_t *skey, *ckey, *keys[SOCK_INACTIVE_MAX+2]; pj_timestamp t1, t2, t_elapsed; int rc=0, i; /* i must be signed */ pj_str_t temp; char errbuf[PJ_ERR_MSG_SIZE]; TRACE__((THIS_FILE, " bench test %d", inactive_sock_count)); // Create pool. pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); // Allocate buffers for send and receive. send_buf = (char*)pj_pool_alloc(pool, bufsize); recv_buf = (char*)pj_pool_alloc(pool, bufsize); // Allocate sockets for sending and receiving. rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock); if (rc == PJ_SUCCESS) { rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock); } else csock = PJ_INVALID_SOCKET; if (rc != PJ_SUCCESS) { app_perror("...error: pj_sock_socket()", rc); goto on_error; } // Bind server socket. pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); addr.sin_port = pj_htons(PORT); if (pj_sock_bind(ssock, &addr, sizeof(addr))) goto on_error; pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES); // Create I/O Queue. rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_create()", rc); goto on_error; } // Set concurrency rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_set_default_concurrency()", rc); goto on_error; } // Allocate inactive sockets, and bind them to some arbitrary address. // Then register them to the I/O queue, and start a read operation. inactive_sock = (pj_sock_t*)pj_pool_alloc(pool, inactive_sock_count*sizeof(pj_sock_t)); inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool, inactive_sock_count*sizeof(pj_ioqueue_op_key_t)); pj_bzero(&addr, sizeof(addr)); addr.sin_family = pj_AF_INET(); for (i=0; i<inactive_sock_count; ++i) { pj_ssize_t bytes; rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &inactive_sock[i]); if (rc != PJ_SUCCESS || inactive_sock[i] < 0) { app_perror("...error: pj_sock_socket()", rc); goto on_error; } if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error: pj_sock_bind()", rc); goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i], NULL, &test_cb, &keys[i]); if (rc != PJ_SUCCESS) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error(1): pj_ioqueue_register_sock()", rc); PJ_LOG(3,(THIS_FILE, "....i=%d", i)); goto on_error; } bytes = bufsize; rc = pj_ioqueue_recv(keys[i], &inactive_read_op[i], recv_buf, &bytes, 0); if (rc != PJ_EPENDING) { pj_sock_close(inactive_sock[i]); inactive_sock[i] = PJ_INVALID_SOCKET; app_perror("...error: pj_ioqueue_read()", rc); goto on_error; } } // Register server and client socket. // We put this after inactivity socket, hopefully this can represent the // worst waiting time. rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); if (rc != PJ_SUCCESS) { app_perror("...error(2): pj_ioqueue_register_sock()", rc); goto on_error; } rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL, &test_cb, &ckey); if (rc != PJ_SUCCESS) { app_perror("...error(3): pj_ioqueue_register_sock()", rc); goto on_error; } // Set destination address to send the packet. pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT); // Test loop. t_elapsed.u64 = 0; for (i=0; i<LOOP; ++i) { pj_ssize_t bytes; pj_ioqueue_op_key_t read_op, write_op; // Randomize send buffer. pj_create_random_string(send_buf, bufsize); // Start reading on the server side. bytes = bufsize; rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0); if (rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_read()", rc); break; } // Starts send on the client side. bytes = bufsize; rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &addr, sizeof(addr)); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_write()", rc); break; } if (rc == PJ_SUCCESS) { if (bytes < 0) { app_perror("...error: pj_ioqueue_sendto()",(pj_status_t)-bytes); break; } } // Begin time. pj_get_timestamp(&t1); // Poll the queue until we've got completion event in the server side. callback_read_key = NULL; callback_read_size = 0; TRACE__((THIS_FILE, " waiting for key = %p", skey)); do { pj_time_val timeout = { 1, 0 }; #ifdef PJ_SYMBIAN rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout)); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif TRACE__((THIS_FILE, " poll rc=%d", rc)); } while (rc >= 0 && callback_read_key != skey); // End time. pj_get_timestamp(&t2); t_elapsed.u64 += (t2.u64 - t1.u64); if (rc < 0) { app_perror(" error: pj_ioqueue_poll", -rc); break; } // Compare recv buffer with send buffer. if (callback_read_size != bufsize || pj_memcmp(send_buf, recv_buf, bufsize)) { rc = -10; PJ_LOG(3,(THIS_FILE, " error: size/buffer mismatch")); break; } // Poll until all events are exhausted, before we start the next loop. do { pj_time_val timeout = { 0, 10 }; #ifdef PJ_SYMBIAN PJ_UNUSED_ARG(timeout); rc = pj_symbianos_poll(-1, 100); #else rc = pj_ioqueue_poll(ioque, &timeout); #endif } while (rc>0); rc = 0; } // Print results if (rc == 0) { pj_timestamp tzero; pj_uint32_t usec_delay; tzero.u32.hi = tzero.u32.lo = 0; usec_delay = pj_elapsed_usec( &tzero, &t_elapsed); PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d", bufsize, inactive_sock_count, usec_delay)); } else { PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)", rc, bufsize, inactive_sock_count+2)); } // Cleaning up. for (i=inactive_sock_count-1; i>=0; --i) { pj_ioqueue_unregister(keys[i]); } pj_ioqueue_unregister(skey); pj_ioqueue_unregister(ckey); pj_ioqueue_destroy(ioque); pj_pool_release( pool); return rc; on_error: PJ_LOG(1,(THIS_FILE, "...ERROR: %s", pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)))); if (ssock) pj_sock_close(ssock); if (csock) pj_sock_close(csock); for (i=0; i<inactive_sock_count && inactive_sock && inactive_sock[i]!=PJ_INVALID_SOCKET; ++i) { pj_sock_close(inactive_sock[i]); } if (ioque != NULL) pj_ioqueue_destroy(ioque); pj_pool_release( pool); return -1; }
/* * Test one test entry. */ static pj_status_t do_uri_test(pj_pool_t *pool, struct uri_test *entry) { pj_status_t status; int len; char *input; pjsip_uri *parsed_uri, *ref_uri; pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; pj_timestamp t1, t2; if (entry->len == 0) entry->len = pj_ansi_strlen(entry->str); #if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 input = pj_pool_alloc(pool, entry->len + 1); pj_memcpy(input, entry->str, entry->len); input[entry->len] = '\0'; #else input = entry->str; #endif /* Parse URI text. */ pj_get_timestamp(&t1); var.parse_len = var.parse_len + entry->len; parsed_uri = pjsip_parse_uri(pool, input, entry->len, 0); if (!parsed_uri) { /* Parsing failed. If the entry says that this is expected, then * return OK. */ status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : -10; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri parse error!\n" " uri='%s'\n", input)); } goto on_return; } pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.parse_time, &t2); /* Create the reference URI. */ ref_uri = entry->creator(pool); /* Print both URI. */ s1.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); s2.ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); pj_get_timestamp(&t1); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -20; goto on_return; } s1.ptr[len] = '\0'; s1.slen = len; var.print_len = var.print_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.print_time, &t2); len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); if (len < 1) { status = -30; goto on_return; } s2.ptr[len] = '\0'; s2.slen = len; /* Full comparison of parsed URI with reference URI. */ pj_get_timestamp(&t1); status = pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri); if (status != 0) { /* Not equal. See if this is the expected status. */ status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : -40; if (status != 0) { PJ_LOG(3,(THIS_FILE, " uri comparison mismatch, status=%d:\n" " uri1='%s'\n" " uri2='%s'", status, s1.ptr, s2.ptr)); } goto on_return; } else { /* Equal. See if this is the expected status. */ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -50; if (status != PJ_SUCCESS) { goto on_return; } } var.cmp_len = var.cmp_len + len; pj_get_timestamp(&t2); pj_sub_timestamp(&t2, &t1); pj_add_timestamp(&var.cmp_time, &t2); /* Compare text. */ if (entry->printed) { if (pj_strcmp2(&s1, entry->printed) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " printed='%s'\n" " expectd='%s'", s1.ptr, entry->printed)); status = -60; } } else { if (pj_strcmp(&s1, &s2) != 0) { /* Not equal. */ PJ_LOG(3,(THIS_FILE, " uri print mismatch:\n" " uri1='%s'\n" " uri2='%s'", s1.ptr, s2.ptr)); status = -70; } } on_return: return status; }
PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess, unsigned seq, unsigned rtp_ts, unsigned payload, pj_bool_t discarded) { pj_timestamp ts; pj_uint32_t arrival; pj_int32_t transit; pjmedia_rtp_status seq_st; unsigned last_seq; #if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) PJ_UNUSED_ARG(discarded); #endif if (sess->stat.rx.pkt == 0) { /* Init sequence for the first time. */ pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq); } sess->stat.rx.pkt++; sess->stat.rx.bytes += payload; /* Process the RTP packet. */ last_seq = sess->seq_ctrl.max_seq; pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st); if (seq_st.status.flag.restart) { rtcp_init_seq(sess); } if (seq_st.status.flag.dup) { sess->stat.rx.dup++; TRACE_((sess->name, "Duplicate packet detected")); } if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) { sess->stat.rx.reorder++; TRACE_((sess->name, "Out-of-order packet detected")); } if (seq_st.status.flag.bad) { sess->stat.rx.discard++; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, -1, /* lost */ (seq_st.status.flag.dup? 1:0), /* dup */ (!seq_st.status.flag.dup? 1:-1), /* discard */ -1, /* jitter */ -1, 0); /* toh */ #endif TRACE_((sess->name, "Bad packet discarded")); return; } /* Only mark "good" packets */ ++sess->received; /* Calculate loss periods. */ if (seq_st.diff > 1) { unsigned count = seq_st.diff - 1; unsigned period; period = count * sess->pkt_size * 1000 / sess->clock_rate; period *= 1000; /* Update packet lost. * The packet lost number will also be updated when we're sending * outbound RTCP RR. */ sess->stat.rx.loss += (seq_st.diff - 1); TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1)); /* Update loss period stat */ pj_math_stat_update(&sess->stat.rx.loss_period, period); } /* * Calculate jitter only when sequence is good (see RFC 3550 section A.8), * AND only when the timestamp is different than the last packet * (see RTP FAQ). */ if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) { /* Get arrival time and convert timestamp to samples */ pj_get_timestamp(&ts); ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64; arrival = ts.u32.lo; transit = arrival - rtp_ts; /* Ignore the first N packets as they normally have bad jitter * due to other threads working to establish the call */ if (sess->transit == 0 || sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS) { sess->transit = transit; sess->stat.rx.jitter.min = (unsigned)-1; } else { pj_int32_t d; pj_uint32_t jitter; d = transit - sess->transit; sess->transit = transit; if (d < 0) d = -d; sess->jitter += d - ((sess->jitter + 8) >> 4); /* Get jitter in usec */ if (d < 4294) jitter = d * 1000000 / sess->clock_rate; else { jitter = d * 1000 / sess->clock_rate; jitter *= 1000; } /* Update jitter stat */ pj_math_stat_update(&sess->stat.rx.jitter, jitter); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 0, /* lost */ 0, /* dup */ discarded, /* discard */ (sess->jitter >> 4), /* jitter */ -1, 0); /* toh */ #endif } #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) } else if (seq_st.diff > 1) {
static pj_status_t check_decode_result(pjmedia_vid_codec *codec, const pj_timestamp *ts, pj_bool_t got_keyframe) { ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data; pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp; pjmedia_event event; /* Check for format change. * Decoder output format is set by libavcodec, in case it is different * to the configured param. */ if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt || ff->dec_ctx->width != (int)vafp->size.w || ff->dec_ctx->height != (int)vafp->size.h) { pjmedia_format_id new_fmt_id; pj_status_t status; /* Get current raw format id from ffmpeg decoder context */ status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt, &new_fmt_id); if (status != PJ_SUCCESS) return status; /* Update decoder format in param */ ff->param.dec_fmt.id = new_fmt_id; ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->width; ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->height; ff->expected_dec_fmt = ff->dec_ctx->pix_fmt; /* Re-init format info and apply-param of decoder */ ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id); if (!ff->dec_vfi) return PJ_ENOTSUP; pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp)); ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size; ff->dec_vafp.buffer = NULL; status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp); if (status != PJ_SUCCESS) return status; /* Realloc buffer if necessary */ if (ff->dec_vafp.framebytes > ff->dec_buf_size) { PJ_LOG(5,(THIS_FILE, "Reallocating decoding buffer %u --> %u", (unsigned)ff->dec_buf_size, (unsigned)ff->dec_vafp.framebytes)); ff->dec_buf_size = ff->dec_vafp.framebytes; ff->dec_buf = pj_pool_alloc(ff->pool, ff->dec_buf_size); } /* Broadcast format changed event */ pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED, ts, codec); event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING; pj_memcpy(&event.data.fmt_changed.new_fmt, &ff->param.dec_fmt, sizeof(ff->param.dec_fmt)); pjmedia_event_publish(NULL, codec, &event, 0); } /* Check for missing/found keyframe */ if (got_keyframe) { pj_get_timestamp(&ff->last_dec_keyframe_ts); /* Broadcast keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_FOUND, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } else if (ff->last_dec_keyframe_ts.u64 == 0) { /* Broadcast missing keyframe event */ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, ts, codec); pjmedia_event_publish(NULL, codec, &event, 0); } return PJ_SUCCESS; }