PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb) { pjmedia_jbuf *jb; pj_status_t status; jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jbuf); status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count); if (status != PJ_SUCCESS) return status; pj_strdup_with_null(pool, &jb->jb_name, name); jb->jb_frame_size = frame_size; jb->jb_frame_ptime = ptime; jb->jb_prefetch = PJ_MIN(PJMEDIA_JB_DEFAULT_INIT_DELAY,max_count*4/5); jb->jb_min_prefetch = 0; jb->jb_max_prefetch = max_count*4/5; jb->jb_max_count = max_count; jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime; jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4); pj_math_stat_init(&jb->jb_delay); pj_math_stat_init(&jb->jb_burst); pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE); pjmedia_jbuf_reset(jb); *p_jb = jb; return PJ_SUCCESS; }
static unsigned max_common_substr_len(const pj_str_t* str1, const pj_str_t* str2) { unsigned max_len = 0; /* We compare only on first MAX_COMPARE_LEN char */ unsigned tree[MAX_COMPARE_LEN][MAX_COMPARE_LEN]; unsigned m1=0, m2=0; int i=0, j=0; if(str1->slen == 0 || str2->slen == 0) { return 0; } /* Init tree */ for(i=0;i < MAX_COMPARE_LEN;i++) { pj_bzero(tree[i], PJ_ARRAY_SIZE( tree[i] )); } m1 = PJ_MIN(str1->slen, MAX_COMPARE_LEN); m2 = PJ_MIN(str2->slen, MAX_COMPARE_LEN); for (i = 0; i < m1; i++) { for (j = 0; j < m2; j++) { if (str1->ptr[i] != str2->ptr[j]) { tree[i][j] = 0; } else { if ((i == 0) || (j == 0)) { tree[i][j] = 1; } else { tree[i][j] = 1 + tree[i - 1][j - 1]; } if (tree[i][j] > max_len) { max_len = tree[i][j]; } } } } return max_len; }
static pj_status_t alt_vid_codec_enum_codecs( pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]) { unsigned i, max_cnt; PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(alt_vid_codecs)); *count = 0; for (i=0; i<max_cnt; ++i) { pj_memcpy(&codecs[*count], &alt_vid_codecs[i].info, sizeof(pjmedia_vid_codec_info)); (*count)++; } return PJ_SUCCESS; }
/* * Get cipher list supported by SSL/TLS backend. */ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], unsigned *cipher_num) { unsigned i; PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); if (ciphers_num_ == 0) { RSocket sock; CSecureSocket *secure_sock; TPtrC16 proto(_L16("TLS1.0")); secure_sock = CSecureSocket::NewL(sock, proto); if (secure_sock) { TBuf8<128> ciphers_buf(0); secure_sock->AvailableCipherSuites(ciphers_buf); ciphers_num_ = ciphers_buf.Length() / 2; if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_)) ciphers_num_ = PJ_ARRAY_SIZE(ciphers_); for (i = 0; i < ciphers_num_; ++i) { ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + ciphers_buf[i*2+1]); ciphers_[i].name = get_cipher_name(ciphers_[i].id); } } delete secure_sock; } if (ciphers_num_ == 0) { *cipher_num = 0; return PJ_ENOTFOUND; } *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); for (i = 0; i < *cipher_num; ++i) ciphers[i] = ciphers_[i].id; return PJ_SUCCESS; }
PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper) { if(jb->jb_last_op != oper) { jb->jb_last_op = oper; if (jb->jb_status == JB_STATUS_INITIALIZING) { /* Switch status 'initializing' -> 'processing' after some OP * switch cycles and current OP is GET (burst level is calculated * based on PUT burst), so burst calculation is guaranted to be * performed right after the status switching. */ if (++jb->jb_init_cycle_cnt >= INIT_CYCLE && oper == JB_OP_GET) { jb->jb_status = JB_STATUS_PROCESSING; /* To make sure the burst calculation will be done right after * this, adjust burst level if it exceeds max burst level. */ jb->jb_level = PJ_MIN(jb->jb_level, jb->jb_max_burst); } else { jb->jb_level = 0; return; } } /* Perform jitter calculation based on PUT burst-level only, since * GET burst-level may not be accurate, e.g: when VAD is active. * Note that when burst-level is too big, i.e: exceeds jb_max_burst, * the GET op may be idle, in this case, we better skip the jitter * calculation. */ if (oper == JB_OP_GET && jb->jb_level <= jb->jb_max_burst) jbuf_calculate_jitter(jb); jb->jb_level = 0; } /* Call discard algorithm */ if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) { (*jb->jb_discard_algo)(jb); } }
/* * Enum codecs supported by this factory. */ static pj_status_t openh264_enum_codecs( pjmedia_vid_codec_factory *factory, unsigned *count, pjmedia_vid_codec_info codecs[]) { unsigned i, max_cnt; PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &openh264_factory.base, PJ_EINVAL); max_cnt = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc)); *count = 0; for (i=0; i<max_cnt; ++i) { if (codec_desc[i].enabled) { pj_memcpy(&codecs[*count], &codec_desc[i].info, sizeof(pjmedia_vid_codec_info)); (*count)++; } } return PJ_SUCCESS; }
/* * Create the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned tail_ms, unsigned latency_ms, unsigned options, pjmedia_echo_state **p_echo ) { unsigned ptime, lat_cnt; unsigned delay_buf_opt = 0; pjmedia_echo_state *ec; pj_status_t status; /* Create new pool and instantiate and init the EC */ pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL); ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); ec->pool = pool; ec->obj_name = pool->obj_name; ec->samples_per_frame = samples_per_frame; ec->frm_buf = (pj_int16_t*)pj_pool_alloc(pool, samples_per_frame<<1); pj_list_init(&ec->lat_buf); pj_list_init(&ec->lat_free); /* Select the backend algorithm */ if (0) { /* Dummy */ ; #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &speex_aec_op; #endif #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) { ec->op = &ipp_aec_op; #endif } else { ec->op = &echo_supp_op; } /* Completeness check for EC operation playback and capture, they must * be implemented both or none. */ pj_assert(!ec->op->ec_capture == !ec->op->ec_playback); PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name)); /* Instantiate EC object */ status = (*ec->op->ec_create)(pool, clock_rate, channel_count, samples_per_frame, tail_ms, options, &ec->state); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } /* If EC algo does not have playback and capture callbakcs, * create latency buffer and delay buffer to handle drift. */ if (ec->op->ec_playback && ec->op->ec_capture) { latency_ms = 0; } else { /* Create latency buffers */ ptime = samples_per_frame * 1000 / clock_rate; if (latency_ms > ptime) { /* Normalize latency with delaybuf/WSOLA latency */ latency_ms -= PJ_MIN(ptime, PJMEDIA_WSOLA_DELAY_MSEC); } if (latency_ms < ptime) { /* Give at least one frame delay to simplify programming */ latency_ms = ptime; } lat_cnt = latency_ms / ptime; while (lat_cnt--) { struct frame *frm; frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) + sizeof(struct frame)); pj_list_push_back(&ec->lat_free, frm); } /* Create delay buffer to compensate drifts */ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO) delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO; status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate, samples_per_frame, channel_count, (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime, delay_buf_opt, &ec->delay_buf); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } } PJ_LOG(4,(ec->obj_name, "%s created, clock_rate=%d, channel=%d, " "samples per frame=%d, tail length=%d ms, " "latency=%d ms", ec->op->name, clock_rate, channel_count, samples_per_frame, tail_ms, latency_ms)); /* Done */ *p_echo = ec; return PJ_SUCCESS; }
PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb, const void *frame, pj_size_t frame_size, pj_uint32_t bit_info, int frame_seq, pj_uint32_t ts, pj_bool_t *discarded) { pj_size_t min_frame_size; int new_size, cur_size; pj_status_t status; cur_size = jb_framelist_eff_size(&jb->jb_framelist); /* Attempt to store the frame */ min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size); status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, min_frame_size, bit_info, ts, PJMEDIA_JB_NORMAL_FRAME); /* Jitter buffer is full, remove some older frames */ while (status == PJ_ETOOMANY) { int distance; unsigned removed; /* Remove as few as possible just to make this frame in. Note that * the cases of seq-jump, out-of-order, and seq restart should have * been handled/normalized by previous call of jb_framelist_put_at(). * So we're confident about 'distance' value here. */ distance = (frame_seq - jb_framelist_origin(&jb->jb_framelist)) - jb->jb_max_count + 1; pj_assert(distance > 0); removed = jb_framelist_remove_head(&jb->jb_framelist, distance); status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, min_frame_size, bit_info, ts, PJMEDIA_JB_NORMAL_FRAME); jb->jb_discard += removed; } /* Get new JB size after PUT */ new_size = jb_framelist_eff_size(&jb->jb_framelist); /* Return the flag if this frame is discarded */ if (discarded) *discarded = (status != PJ_SUCCESS); if (status == PJ_SUCCESS) { if (jb->jb_prefetching) { TRACE__((jb->jb_name.ptr, "PUT prefetch_cnt=%d/%d", new_size, jb->jb_prefetch)); if (new_size >= jb->jb_prefetch) jb->jb_prefetching = PJ_FALSE; } jb->jb_level += (new_size > cur_size ? new_size-cur_size : 1); jbuf_update(jb, JB_OP_PUT); } else jb->jb_discard++; }
static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { int diff, cur_size; cur_size = jb_framelist_eff_size(&jb->jb_framelist); pj_math_stat_update(&jb->jb_burst, jb->jb_level); jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level, jb->jb_level); /* Burst level is decreasing */ if (jb->jb_level < jb->jb_eff_level) { enum { STABLE_HISTORY_LIMIT = 20 }; jb->jb_stable_hist++; /* Only update the effective level (and prefetch) if 'stable' * condition is reached (not just short time impulse) */ if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) { diff = (jb->jb_eff_level - jb->jb_max_hist_level) / 3; if (diff < 1) diff = 1; /* Update effective burst level */ jb->jb_eff_level -= diff; /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; } /* Reset history */ jb->jb_max_hist_level = 0; jb->jb_stable_hist = 0; TRACE__((jb->jb_name.ptr,"jb updated(1), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); } } /* Burst level is increasing */ else if (jb->jb_level > jb->jb_eff_level) { /* Instaneous set effective burst level to recent maximum level */ jb->jb_eff_level = PJ_MIN(jb->jb_max_hist_level, (int)(jb->jb_max_count*4/5)); /* Update prefetch based on level */ if (jb->jb_init_prefetch) { jb->jb_prefetch = jb->jb_eff_level; if (jb->jb_prefetch > jb->jb_max_prefetch) jb->jb_prefetch = jb->jb_max_prefetch; } jb->jb_stable_hist = 0; /* Do not reset max_hist_level. */ //jb->jb_max_hist_level = 0; TRACE__((jb->jb_name.ptr,"jb updated(2), lvl=%d pre=%d, size=%d", jb->jb_eff_level, jb->jb_prefetch, cur_size)); } /* Level is unchanged */ else { jb->jb_stable_hist = 0; } }
static pj_status_t pj_vpx_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { vpx_private *vpx = (vpx_private*) codec->codec_data; const vpx_codec_cx_pkt_t *pkt; /* Default return */ *has_more = PJ_FALSE; output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; if (vpx->enc_frame_len == 0) { /* * For now we assume that we have only one cx data here * Which is probably fine as we do not ask encoder to bufferize */ //PJ_LOG(4, (THIS_FILE, "Encode one frame at %p", vpx->enc_iter)); pkt = vpx_codec_get_cx_data(&vpx->encoder, &vpx->enc_iter); if (pkt == NULL ) { if (!vpx->encoder.err) { PJ_LOG(3, (THIS_FILE, "Encoder packet dropped")); return PJ_SUCCESS; } else { PJ_LOG(1, (THIS_FILE, "Failed to get cx datas : %s", vpx_codec_err_to_string(vpx->encoder.err))); return PJMEDIA_CODEC_EFAILED; } } else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { pj_memcpy(vpx->enc_buf, pkt->data.frame.buf, pkt->data.frame.sz); vpx->enc_frame_len = pkt->data.frame.sz; vpx->enc_processed = 0; vpx->enc_buf_is_keyframe = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? PJ_TRUE : PJ_FALSE; //PJ_LOG(4, (THIS_FILE, "Encoded with 0 byte : %d", ((pj_uint8_t*)(vpx->enc_buf))[0])); } else { PJ_LOG(6, (THIS_FILE, "Vpx packet kind %d not taken into account", pkt->kind)); return PJ_SUCCESS; } } // TODO we should support if iter not over too if(vpx->enc_frame_len > 0) { //PJ_LOG(4, (THIS_FILE, "We have an enc_frame : %d; max : %d", vpx->enc_frame_len, vpx->param.enc_mtu)); /* Reserve 1 octet for vp8 packetization info */ unsigned max_size = vpx->param.enc_mtu - 1; unsigned remaining_size = vpx->enc_frame_len - vpx->enc_processed; /* TODO : we could equally distributed packets sizes */ unsigned payload_len = PJ_MIN(remaining_size, max_size); pj_uint8_t* p = (pj_uint8_t*) output->buf; pj_uint8_t* s = (pj_uint8_t*) vpx->enc_buf; //PJ_LOG(4, (THIS_FILE, "Payload : %d", payload_len)); output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->bit_info = 0; if (vpx->enc_buf_is_keyframe) { output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } /* Set vp8 packetization info */ p[0] = 0; if(vpx->enc_processed == 0) p[0] |= 0x10; if(!vpx->enc_buf_is_keyframe) p[0] |= 0x20; pj_memcpy( (p + 1), (s + vpx->enc_processed), payload_len); output->size = payload_len + 1; vpx->enc_processed += payload_len; *has_more = !(vpx->enc_processed == vpx->enc_frame_len); } //PJ_LOG(4, (THIS_FILE, "Encoded size %d", output->size)); return PJ_SUCCESS; }
/* * Initialize DirectSound player device. */ static pj_status_t init_player_stream( struct dsound_stream *ds_strm, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned buffer_count) { HRESULT hr; HWND hwnd; PCMWAVEFORMAT pcmwf; DSBUFFERDESC dsbdesc; DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; unsigned bytes_per_frame; unsigned max_latency; unsigned i; PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); /* Check device ID */ if (dev_id == -1) dev_id = 0; PJ_ASSERT_RETURN(dev_id>=0 && dev_id < (int)dev_count, PJ_EINVAL); /* * Create DirectSound device. */ hr = DirectSoundCreate(dev_info[dev_id].lpGuid, &ds_strm->ds.play.lpDs, NULL); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); hwnd = GetForegroundWindow(); if (hwnd == NULL) { hwnd = GetDesktopWindow(); } hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd, DSSCL_PRIORITY); if FAILED(hr) return PJ_RETURN_OS_ERROR(hr); /* * Set up wave format structure for initialize DirectSound play * buffer. */ init_waveformatex(&pcmwf, clock_rate, channel_count); bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; /* Set up DSBUFFERDESC structure. */ pj_bzero(&dsbdesc, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; /* * Create DirectSound playback buffer. */ hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc, &ds_strm->ds.play.lpDsBuffer, NULL); if (FAILED(hr) ) return PJ_RETURN_OS_ERROR(hr); /* * Create event for play notification. */ ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); if (ds_strm->hEvent == NULL) return pj_get_os_error(); /* * Setup notification for play. */ hr = IDirectSoundBuffer_QueryInterface( ds_strm->ds.play.lpDsBuffer, &IID_IDirectSoundNotify, (LPVOID *)&ds_strm->lpDsNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); for (i=0; i<buffer_count; ++i) { dsPosNotify[i].dwOffset = i * bytes_per_frame; dsPosNotify[i].hEventNotify = ds_strm->hEvent; } hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, buffer_count, dsPosNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); hr = IDirectSoundBuffer_SetCurrentPosition(ds_strm->ds.play.lpDsBuffer, 0); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); ds_strm->dwBytePos = 0; ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame; ds_strm->timestamp.u64 = 0; /* * Play latency does not need to be on a frame boundry, it is just how far * ahead of the read pointer we set the write pointer. So we should just * use the user configured latency. However, if the latency measured in * bytes causes more buffers than we are allowed, we must cap the latency * at the time contained in 1-buffer_count. */ max_latency = (1 - buffer_count) * samples_per_frame * 1000 / clock_rate / channel_count; ds_strm->latency = PJ_MIN(max_latency, snd_output_latency); /* Done setting up play device. */ PJ_LOG(5,(THIS_FILE, " DirectSound player \"%s\" initialized (clock_rate=%d, " "channel_count=%d, samples_per_frame=%d (%dms))", dev_info[dev_id].info.name, clock_rate, channel_count, samples_per_frame, samples_per_frame * 1000 / clock_rate)); return PJ_SUCCESS; }