/* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. * * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. */ static void decrement_counter(pj_ioqueue_key_t *key) { pj_lock_acquire(key->ioqueue->lock); pj_mutex_lock(key->ioqueue->ref_cnt_mutex); --key->ref_count; if (key->ref_count == 0) { pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); } pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); pj_lock_release(key->ioqueue->lock); }
PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, void *buffer, pj_size_t buflen, pj_sockaddr_t *addr, int *addrlen) { pj_mutex_lock(ioque->mutex); key->op |= PJ_IOQUEUE_OP_RECV_FROM; key->rd_buf = buffer; key->rd_buflen = buflen; key->rmt_addr = addr; key->rmt_addrlen = addrlen; PJ_FD_SET(key->fd, &ioque->rfdset); pj_mutex_unlock(ioque->mutex); return PJ_IOQUEUE_PENDING; }
/* API: Register device change observer. */ PJ_DEF(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb) { pj_status_t status; status = pj_mutex_lock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); return status; } aud_subsys.dev_observer.cb = cb; status = pj_mutex_unlock(aud_subsys.dev_observer.lock); if (status != PJ_SUCCESS) { PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); } return status; }
/* Event worker thread function. */ static int event_worker_thread(void *arg) { pjmedia_event_mgr *mgr = (pjmedia_event_mgr *)arg; while (1) { /* Wait until there is an event. */ pj_sem_wait(mgr->sem); if (mgr->is_quitting) break; pj_mutex_lock(mgr->mutex); event_mgr_distribute_events(mgr, &mgr->ev_queue, &mgr->th_next_sub, PJ_TRUE); pj_mutex_unlock(mgr->mutex); } return 0; }
PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen) { if (pj_sock_send(key->fd, data, datalen, 0) != (pj_ssize_t)datalen) return -1; pj_mutex_lock(ioque->mutex); key->op |= PJ_IOQUEUE_OP_WRITE; key->wr_buf = NULL; key->wr_buflen = datalen; PJ_FD_SET(key->fd, &ioque->wfdset); pj_mutex_unlock(ioque->mutex); return PJ_IOQUEUE_PENDING; }
/* * Allocate a new Speex codec instance. */ static pj_status_t spx_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct spx_private *spx; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL); pj_mutex_lock(spx_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&spx_factory.codec_list)) { codec = spx_factory.codec_list.next; pj_list_erase(codec); } else { codec = PJ_POOL_ZALLOC_T(spx_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &spx_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(spx_factory.pool, sizeof(struct spx_private)); } pj_mutex_unlock(spx_factory.mutex); spx = (struct spx_private*) codec->codec_data; spx->enc = NULL; spx->dec = NULL; if (id->clock_rate <= 8000) spx->param_id = PARAM_NB; else if (id->clock_rate <= 16000) spx->param_id = PARAM_WB; else spx->param_id = PARAM_UWB; *p_codec = codec; return PJ_SUCCESS; }
/* * Find codecs by the unique codec identifier. This function will find * all codecs that match the codec identifier prefix. For example, if * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1", * and so on, up to the maximum count specified in the argument. */ PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id( pjmedia_vid_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_vid_codec_info *p_info[], unsigned prio[]) { unsigned i, found = 0; PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL); if (!mgr) mgr = def_vid_codec_mgr; PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); for (i=0; i<mgr->codec_cnt; ++i) { if (codec_id->slen == 0 || pj_strnicmp2(codec_id, mgr->codec_desc[i].id, codec_id->slen) == 0) { if (p_info) p_info[found] = &mgr->codec_desc[i].info; if (prio) prio[found] = mgr->codec_desc[i].prio; ++found; if (found >= *count) break; } } pj_mutex_unlock(mgr->mutex); *count = found; return found ? PJ_SUCCESS : PJ_ENOTFOUND; }
PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, pj_ioqueue_key_t *key, const void *data, pj_size_t datalen, const pj_sockaddr_t *addr, int addrlen) { if (pj_sock_sendto(key->fd, data, datalen, 0, addr, addrlen) != (pj_ssize_t)datalen) return -1; pj_mutex_lock(ioque->mutex); key->op |= PJ_IOQUEUE_OP_SEND_TO; key->wr_buf = NULL; key->wr_buflen = datalen; PJ_FD_SET(key->fd, &ioque->wfdset); pj_mutex_unlock(ioque->mutex); return PJ_IOQUEUE_PENDING; }
/* * Modify codec settings. */ static pj_status_t codec_modify( pjmedia_codec *codec, const pjmedia_codec_param *attr ) { struct opus_data *opus_data = (struct opus_data *)codec->codec_data; pj_mutex_lock (opus_data->mutex); TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); /* Set bitrate */ opus_data->cfg.bit_rate = attr->info.avg_bps; opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps)); /* Set VAD */ opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); /* Set PLC */ opus_encoder_ctl(opus_data->enc, OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; }
/* * Release read lock. * */ PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) { pj_status_t status; PJ_ASSERT_RETURN(mutex, PJ_EINVAL); status = pj_mutex_lock(mutex->read_lock); if (status != PJ_SUCCESS) { pj_assert(!"This pretty much is unexpected"); return status; } pj_assert(mutex->reader_count >= 1); --mutex->reader_count; if (mutex->reader_count == 0) pj_sem_post(mutex->write_lock); status = pj_mutex_unlock(mutex->read_lock); return status; }
static pj_status_t l16_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct l16_data *data; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory==&l16_factory.base, PJ_EINVAL); /* Lock mutex. */ pj_mutex_lock(l16_factory.mutex); /* Just release codec data pool */ data = (struct l16_data*) codec->codec_data; pj_assert(data); pj_pool_release(data->pool); /* Unlock mutex. */ pj_mutex_unlock(l16_factory.mutex); return PJ_SUCCESS; }
/* * Free codec. */ static pj_status_t spx_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct spx_private *spx; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &spx_factory.base, PJ_EINVAL); /* Close codec, if it's not closed. */ spx = (struct spx_private*) codec->codec_data; if (spx->enc != NULL || spx->dec != NULL) { spx_codec_close(codec); } /* Put in the free list. */ pj_mutex_lock(spx_factory.mutex); pj_list_push_front(&spx_factory.codec_list, codec); pj_mutex_unlock(spx_factory.mutex); return PJ_SUCCESS; }
/* If this timer callback is called, it means subscriber hasn't refreshed its * subscription on-time. Set the state to terminated. This will also send * NOTIFY with Subscription-State set to terminated. */ static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) { pjsip_event_sub *sub = entry->user_data; pj_str_t reason = { "timeout", 7 }; PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", sub, state[sub->state].ptr)); pj_mutex_lock(sub->mutex); sub->timer.id = 0; if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { /* Notify application, but prevent app from destroying the sub. */ ++sub->pending_tsx; (*sub->cb.on_sub_terminated)(sub, &reason); --sub->pending_tsx; } //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, // &reason, NULL); pj_mutex_unlock(sub->mutex); }
/* * Allocate a new SILK codec instance. */ static pj_status_t silk_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct silk_private *silk; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &silk_factory.base, PJ_EINVAL); pj_mutex_lock(silk_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&silk_factory.codec_list)) { codec = silk_factory.codec_list.next; pj_list_erase(codec); } else { codec = PJ_POOL_ZALLOC_T(silk_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &silk_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(silk_factory.pool, sizeof(struct silk_private)); } pj_mutex_unlock(silk_factory.mutex); silk = (struct silk_private*) codec->codec_data; silk->enc_ready = PJ_FALSE; silk->dec_ready = PJ_FALSE; silk->param_id = silk_get_type_for_clock_rate(id->clock_rate); /* Create pool for codec instance */ silk->pool = pjmedia_endpt_create_pool(silk_factory.endpt, "silkcodec", 512, 512); *p_codec = codec; return PJ_SUCCESS; }
/*! * \internal * \brief Find the request tdata to get the serializer it used. * \since 14.0.0 * * \param rdata The incoming message. * * \retval serializer on success. * \retval NULL on error or could not find the serializer. */ static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata) { struct ast_taskprocessor *serializer = NULL; pj_str_t tsx_key; pjsip_transaction *tsx; pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, &rdata->msg_info.cseq->method, rdata); tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (!tsx) { ast_debug(1, "Could not find %.*s transaction for %d response.\n", (int) pj_strlen(&rdata->msg_info.cseq->method.name), pj_strbuf(&rdata->msg_info.cseq->method.name), rdata->msg_info.msg->line.status.code); return NULL; } if (tsx->last_tx) { const char *serializer_name; serializer_name = tsx->last_tx->mod_data[distributor_mod.id]; if (!ast_strlen_zero(serializer_name)) { serializer = ast_taskprocessor_get(serializer_name, TPS_REF_IF_EXISTS); if (serializer) { ast_debug(3, "Found serializer %s on transaction %s\n", serializer_name, tsx->obj_name); } } } #ifdef HAVE_PJ_TRANSACTION_GRP_LOCK pj_grp_lock_release(tsx->grp_lock); #else pj_mutex_unlock(tsx->mutex); #endif return serializer; }
static pj_status_t event_mgr_distribute_events(pjmedia_event_mgr *mgr, event_queue *ev_queue, esub **next_sub, pj_bool_t rls_lock) { pj_status_t err = PJ_SUCCESS; esub * sub = mgr->esub_list.next; pjmedia_event *ev = &ev_queue->events[ev_queue->head]; while (sub != &mgr->esub_list) { *next_sub = sub->next; /* Check if the subscriber is interested in * receiving the event from the publisher. */ if (sub->epub == ev->epub || !sub->epub) { pjmedia_event_cb *cb = sub->cb; void *user_data = sub->user_data; pj_status_t status; if (rls_lock) pj_mutex_unlock(mgr->mutex); status = (*cb)(ev, user_data); if (status != PJ_SUCCESS && err == PJ_SUCCESS) err = status; if (rls_lock) pj_mutex_lock(mgr->mutex); } sub = *next_sub; } *next_sub = NULL; ev_queue->head = (ev_queue->head + 1) % MAX_EVENTS; ev_queue->is_full = PJ_FALSE; return err; }
/* * Set route-set. */ PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, const pjsip_route_hdr *route_set ) { const pjsip_route_hdr *hdr; pj_mutex_lock(sub->mutex); /* Clear existing route set. */ pj_list_init(&sub->route_set); /* Duplicate route headers. */ hdr = route_set->next; while (hdr != route_set) { pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); pj_list_insert_before(&sub->route_set, new_hdr); hdr = hdr->next; } pj_mutex_unlock(sub->mutex); return 0; }
pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) { esub *sub; PJ_ASSERT_RETURN(cb, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); sub = mgr->esub_list.next; while (sub != &mgr->esub_list) { esub *next = sub->next; if (sub->cb == cb && (sub->user_data == user_data || !user_data) && (sub->epub == epub || !epub)) { /* If the worker thread or pjmedia_event_publish() API is * in the process of distributing events, make sure that * its pointer to the next subscriber stays valid. */ if (mgr->th_next_sub == sub) mgr->th_next_sub = sub->next; if (mgr->pub_next_sub == sub) mgr->pub_next_sub = sub->next; pj_list_erase(sub); pj_list_push_back(&mgr->free_esub_list, sub); if (user_data && epub) break; } sub = next; } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_codec_g711_deinit(void) { pjmedia_codec_mgr *codec_mgr; pj_status_t status; if (g711_factory.endpt == NULL) { /* Not registered. */ return PJ_SUCCESS; } /* Lock mutex. */ pj_mutex_lock(g711_factory.mutex); /* Get the codec manager. */ codec_mgr = pjmedia_endpt_get_codec_mgr(g711_factory.endpt); if (!codec_mgr) { g711_factory.endpt = NULL; pj_mutex_unlock(g711_factory.mutex); return PJ_EINVALIDOP; } /* Unregister G711 codec factory. */ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &g711_factory.base); g711_factory.endpt = NULL; /* Destroy mutex. */ pj_mutex_destroy(g711_factory.mutex); g711_factory.mutex = NULL; /* Release pool. */ pj_pool_release(g711_factory.pool); g711_factory.pool = NULL; return status; }
/* Find subscription in the hash table. * If found, lock the subscription before returning to caller. */ static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) { pj_str_t key; pjsip_role_e role; pjsip_event_sub *sub; pjsip_method *method = &rdata->msg->line.req.method; pj_str_t *tag; if (rdata->msg->type == PJSIP_REQUEST_MSG) { if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { role = PJSIP_ROLE_UAS; tag = &rdata->to_tag; } else { pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); role = PJSIP_ROLE_UAC; tag = &rdata->to_tag; } } else { if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { role = PJSIP_ROLE_UAC; tag = &rdata->from_tag; } else { pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); role = PJSIP_ROLE_UAS; tag = &rdata->from_tag; } } create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); pj_mutex_lock(mgr.mutex); sub = pj_hash_get(mgr.ht, key.ptr, key.slen); if (sub) pj_mutex_lock(sub->mutex); pj_mutex_unlock(mgr.mutex); return sub; }
/* * Free codec. */ static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct gsm_data *gsm_data; int i; PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL); gsm_data = (struct gsm_data*) codec->codec_data; /* Close codec, if it's not closed. */ gsm_codec_close(codec); #if !PLC_DISABLED /* Clear left samples in the PLC, since codec+plc will be reused * next time. */ for (i=0; i<2; ++i) { pj_int16_t frame[160]; pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame)); pjmedia_plc_save(gsm_data->plc, frame); } #else PJ_UNUSED_ARG(i); #endif /* Re-init silence_period */ pj_set_timestamp32(&gsm_data->last_tx, 0, 0); /* Put in the free list. */ pj_mutex_lock(gsm_codec_factory.mutex); pj_list_push_front(&gsm_codec_factory.codec_list, codec); pj_mutex_unlock(gsm_codec_factory.mutex); return PJ_SUCCESS; }
PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, pj_ioqueue_t *ioque, pj_oshandle_t sock, void *user_data, const pj_ioqueue_callback *cb) { pj_ioqueue_key_t *key = NULL; pj_uint32_t value; pj_mutex_lock(ioque->mutex); if (ioque->count >= ioque->max) goto on_return; /* Set socket to nonblocking. */ value = 1; if (pj_sock_ioctl((pj_sock_t)sock, PJ_FIONBIO, &value)) { PJ_PERROR(("ioqueue", "Error setting FIONBIO")); goto on_return; } /* Create key. */ key = (pj_ioqueue_key_t*)pj_pool_calloc(pool, 1, sizeof(pj_ioqueue_key_t)); key->fd = (pj_sock_t)sock; key->user_data = user_data; /* Save callback. */ pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); /* Register */ pj_list_insert_before(&ioque->hlist, key); ++ioque->count; on_return: pj_mutex_unlock(ioque->mutex); return key; }
/* * Allocate a new OPUS codec instance. */ static pj_status_t opus_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { pjmedia_codec *codec; struct opus_private *opus; PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); pj_mutex_lock(opus_factory.mutex); /* Get free nodes, if any. */ if (!pj_list_empty(&opus_factory.codec_list)) { codec = opus_factory.codec_list.next; pj_list_erase(codec); } else { codec = PJ_POOL_ZALLOC_T(opus_factory.pool, pjmedia_codec); PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); codec->op = &opus_op; codec->factory = factory; codec->codec_data = pj_pool_alloc(opus_factory.pool, sizeof(struct opus_private)); } pj_mutex_unlock(opus_factory.mutex); opus = (struct opus_private *)codec->codec_data; opus->enc_ready = PJ_FALSE; opus->dec_ready = PJ_FALSE; /* Create pool for codec instance */ opus->pool = pjmedia_endpt_create_pool(opus_factory.endpt, "opuscodec", 512, 512); *p_codec = codec; return PJ_SUCCESS; }
/* * Initiate overlapped connect() operation (well, it's non-blocking actually, * since there's no overlapped version of connect()). */ PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key, const pj_sockaddr_t *addr, int addrlen ) { pj_status_t status; /* check parameters. All must be specified! */ PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* Check if socket has not been marked for connecting */ if (key->connecting != 0) return PJ_EPENDING; status = pj_sock_connect(key->fd, addr, addrlen); if (status == PJ_SUCCESS) { /* Connected! */ return PJ_SUCCESS; } else { if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) { /* Pending! */ pj_mutex_lock(key->mutex); key->connecting = PJ_TRUE; ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); ioqueue_add_to_set(key->ioqueue, key, EXCEPTION_EVENT); pj_mutex_unlock(key->mutex); return PJ_EPENDING; } else { /* Error! */ return status; } } }
/* * Close codec. */ static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec ) { ffmpeg_private *ff; pj_mutex_t *ff_mutex; PJ_ASSERT_RETURN(codec, PJ_EINVAL); ff = (ffmpeg_private*)codec->codec_data; ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex; pj_mutex_lock(ff_mutex); if (ff->enc_ctx) { avcodec_close(ff->enc_ctx); av_free(ff->enc_ctx); } if (ff->dec_ctx && ff->dec_ctx!=ff->enc_ctx) { avcodec_close(ff->dec_ctx); av_free(ff->dec_ctx); } ff->enc_ctx = NULL; ff->dec_ctx = NULL; pj_mutex_unlock(ff_mutex); return PJ_SUCCESS; }
/* Increment key's reference counter */ static void increment_counter(pj_ioqueue_key_t *key) { pj_mutex_lock(key->ioqueue->ref_cnt_mutex); ++key->ref_count; pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); }
/* Test with recursive mutex. */ static int recursive_mutex_test(pj_pool_t *pool) { pj_status_t rc; pj_mutex_t *mutex; PJ_LOG(3,("", "...testing recursive mutex")); /* Create mutex. */ TRACE_(("", "....create mutex")); rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex); if (rc != PJ_SUCCESS) { app_perror("...error: pj_mutex_create", rc); return -10; } /* Normal lock/unlock cycle. */ TRACE_(("", "....lock mutex")); rc = pj_mutex_lock(mutex); if (rc != PJ_SUCCESS) { app_perror("...error: pj_mutex_lock", rc); return -20; } TRACE_(("", "....unlock mutex")); rc = pj_mutex_unlock(mutex); if (rc != PJ_SUCCESS) { app_perror("...error: pj_mutex_unlock", rc); return -30; } /* Lock again. */ TRACE_(("", "....lock mutex")); rc = pj_mutex_lock(mutex); if (rc != PJ_SUCCESS) return -40; /* Try-lock should NOT fail. . */ TRACE_(("", "....trylock mutex")); rc = pj_mutex_trylock(mutex); if (rc != PJ_SUCCESS) { app_perror("...error: recursive mutex is not recursive!", rc); return -40; } /* Locking again should not fail. */ TRACE_(("", "....lock mutex")); rc = pj_mutex_lock(mutex); if (rc != PJ_SUCCESS) { app_perror("...error: recursive mutex is not recursive!", rc); return -45; } /* Unlock several times and done. */ TRACE_(("", "....unlock mutex 3x")); rc = pj_mutex_unlock(mutex); if (rc != PJ_SUCCESS) return -50; rc = pj_mutex_unlock(mutex); if (rc != PJ_SUCCESS) return -51; rc = pj_mutex_unlock(mutex); if (rc != PJ_SUCCESS) return -52; TRACE_(("", "....destroy mutex")); rc = pj_mutex_destroy(mutex); if (rc != PJ_SUCCESS) return -60; TRACE_(("", "....done")); return PJ_SUCCESS; }
/* * Decode frame. */ static pj_status_t codec_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output ) { struct opus_data *opus_data = (struct opus_data *)codec->codec_data; int decoded_samples; pjmedia_frame *inframe; int fec = 0; pj_mutex_lock (opus_data->mutex); if (opus_data->dec_frame_index == -1) { /* First packet, buffer it. */ opus_data->dec_frame[0].type = input->type; opus_data->dec_frame[0].size = input->size; opus_data->dec_frame[0].timestamp = input->timestamp; pj_memcpy(opus_data->dec_frame[0].buf, input->buf, input->size); opus_data->dec_frame_index = 0; pj_mutex_unlock (opus_data->mutex); /* Return zero decoded bytes */ output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; output->timestamp = input->timestamp; return PJ_SUCCESS; } inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) { /* Update current frame index */ opus_data->dec_frame_index++; if (opus_data->dec_frame_index > 1) opus_data->dec_frame_index = 0; /* Copy original input buffer to current indexed frame */ inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; inframe->type = input->type; inframe->size = input->size; inframe->timestamp = input->timestamp; pj_memcpy(inframe->buf, input->buf, input->size); fec = 1; } decoded_samples = opus_decode(opus_data->dec, inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? inframe->buf : NULL, inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? inframe->size : 0, (opus_int16*)output->buf, output->size / (sizeof(opus_int16) * opus_data->cfg.channel_cnt), fec); output->timestamp = inframe->timestamp; if (inframe->type == PJMEDIA_FRAME_TYPE_AUDIO) { /* Mark current indexed frame as invalid */ inframe->type = PJMEDIA_FRAME_TYPE_NONE; /* Update current frame index */ opus_data->dec_frame_index++; if (opus_data->dec_frame_index > 1) opus_data->dec_frame_index = 0; /* Copy original input buffer to current indexed frame */ inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; inframe->type = input->type; inframe->size = input->size; inframe->timestamp = input->timestamp; pj_memcpy(inframe->buf, input->buf, input->size); } if (decoded_samples < 0) { PJ_LOG(4, (THIS_FILE, "Decode failed!")); pj_mutex_unlock (opus_data->mutex); return PJMEDIA_CODEC_EFAILED; } output->size = decoded_samples * sizeof(opus_int16) * opus_data->cfg.channel_cnt; output->type = PJMEDIA_FRAME_TYPE_AUDIO; pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; }
/* * Encode frame. */ static pj_status_t codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output ) { struct opus_data *opus_data = (struct opus_data *)codec->codec_data; opus_int32 size = 0; unsigned in_pos = 0; unsigned out_pos = 0; unsigned frame_size; unsigned samples_per_frame; unsigned char tmp_buf[MAX_ENCODED_PACKET_SIZE]; unsigned tmp_bytes_left = sizeof(tmp_buf); pj_mutex_lock (opus_data->mutex); samples_per_frame = (opus_data->cfg.sample_rate * opus_data->ptime) / 1000; frame_size = samples_per_frame * opus_data->cfg.channel_cnt * sizeof(opus_int16); opus_repacketizer_init(opus_data->enc_packer); while (input->size - in_pos >= frame_size) { size = opus_encode(opus_data->enc, (const opus_int16*)(((char*)input->buf) + in_pos), samples_per_frame, tmp_buf + out_pos, (tmp_bytes_left < frame_size ? tmp_bytes_left : frame_size)); if (size < 0) { PJ_LOG(4, (THIS_FILE, "Encode failed! (%d)", size)); pj_mutex_unlock (opus_data->mutex); return PJMEDIA_CODEC_EFAILED; } else if (size > 0) { /* Only add packets containing more than the TOC */ opus_repacketizer_cat(opus_data->enc_packer, tmp_buf + out_pos, size); out_pos += size; tmp_bytes_left -= size; } in_pos += frame_size; } if (!opus_repacketizer_get_nb_frames(opus_data->enc_packer)) { /* Empty packet */ output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; output->timestamp = input->timestamp; } if (size) { size = opus_repacketizer_out(opus_data->enc_packer, output->buf, output_buf_len); if (size < 0) { PJ_LOG(4, (THIS_FILE, "Encode failed! (%d), out_size: %u", size, output_buf_len)); pj_mutex_unlock (opus_data->mutex); return PJMEDIA_CODEC_EFAILED; } } output->size = (unsigned)size; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; }
/* * Open codec. */ static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct opus_data *opus_data = (struct opus_data *)codec->codec_data; int idx, err; PJ_ASSERT_RETURN(codec && attr && opus_data, PJ_EINVAL); pj_mutex_lock (opus_data->mutex); TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); opus_data->cfg.sample_rate = attr->info.clock_rate; opus_data->cfg.channel_cnt = attr->info.channel_cnt; opus_data->ptime = attr->info.frm_ptime; /* Allocate memory used by the codec */ if (!opus_data->enc) { /* Allocate memory for max 2 channels */ opus_data->enc = pj_pool_zalloc(opus_data->pool, opus_encoder_get_size(2)); } if (!opus_data->dec) { /* Allocate memory for max 2 channels */ opus_data->dec = pj_pool_zalloc(opus_data->pool, opus_decoder_get_size(2)); } if (!opus_data->enc_packer) { opus_data->enc_packer = pj_pool_zalloc(opus_data->pool, opus_repacketizer_get_size()); } if (!opus_data->dec_packer) { opus_data->dec_packer = pj_pool_zalloc(opus_data->pool, opus_repacketizer_get_size()); } if (!opus_data->enc || !opus_data->dec || !opus_data->enc_packer || !opus_data->dec_packer) { PJ_LOG(2, (THIS_FILE, "Unable to allocate memory for the codec")); pj_mutex_unlock (opus_data->mutex); return PJ_ENOMEM; } /* Check max average bit rate */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); if (idx >= 0) { unsigned rate; rate = (unsigned)pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); if (rate < attr->info.avg_bps) attr->info.avg_bps = rate; } /* Check plc */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_INBAND_FEC, PJ_FALSE); if (idx >= 0) { unsigned plc; plc = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE; } /* Check vad */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_DTX, PJ_FALSE); if (idx >= 0) { unsigned vad; vad = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); attr->setting.vad = vad > 0? PJ_TRUE: PJ_FALSE; } /* Check cbr */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_CBR, PJ_FALSE); if (idx >= 0) { unsigned cbr; cbr = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); opus_data->cfg.cbr = cbr > 0? PJ_TRUE: PJ_FALSE; } /* Check max average bit rate */ idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); if (idx >= 0) { unsigned rate; rate = (unsigned) pj_strtoul(&attr->setting.dec_fmtp.param[idx].val); if (rate < attr->info.avg_bps) attr->info.avg_bps = rate; } TRACE_((THIS_FILE, "%s:%d: sample_rate: %u", __FUNCTION__, __LINE__, opus_data->cfg.sample_rate)); /* Initialize encoder */ err = opus_encoder_init(opus_data->enc, opus_data->cfg.sample_rate, attr->info.channel_cnt, OPUS_APPLICATION_VOIP); if (err != OPUS_OK) { PJ_LOG(2, (THIS_FILE, "Unable to create encoder")); return PJMEDIA_CODEC_EFAILED; } /* Set signal type */ opus_encoder_ctl(opus_data->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); /* Set bitrate */ opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps)); /* Set VAD */ opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); /* Set PLC */ opus_encoder_ctl(opus_data->enc, OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); /* Set bandwidth */ opus_encoder_ctl(opus_data->enc, OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant( opus_data->cfg.sample_rate))); /* Set expected packet loss */ opus_encoder_ctl(opus_data->enc, OPUS_SET_PACKET_LOSS_PERC(opus_data->cfg.packet_loss)); /* Set complexity */ opus_encoder_ctl(opus_data->enc, OPUS_SET_COMPLEXITY(opus_data->cfg.complexity)); /* Set constant bit rate */ opus_encoder_ctl(opus_data->enc, OPUS_SET_VBR(opus_data->cfg.cbr ? 0 : 1)); PJ_LOG(5, (THIS_FILE, "Initialize Opus encoder, sample rate: %d, " "avg bitrate: %d, vad: %d, plc: %d, pkt loss: %d, " "complexity: %d, constant bit rate: %d", opus_data->cfg.sample_rate, attr->info.avg_bps, attr->setting.vad?1:0, attr->setting.plc?1:0, opus_data->cfg.packet_loss, opus_data->cfg.complexity, opus_data->cfg.cbr?1:0)); /* Initialize decoder */ err = opus_decoder_init (opus_data->dec, opus_data->cfg.sample_rate, attr->info.channel_cnt); if (err != OPUS_OK) { PJ_LOG(2, (THIS_FILE, "Unable to initialize decoder")); return PJMEDIA_CODEC_EFAILED; } /* Initialize temporary decode frames used for FEC */ opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE; opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool, (opus_data->cfg.sample_rate / 1000) * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); opus_data->dec_frame[1].type = PJMEDIA_FRAME_TYPE_NONE; opus_data->dec_frame[1].buf = pj_pool_zalloc(opus_data->pool, (opus_data->cfg.sample_rate / 1000) * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); opus_data->dec_frame_index = -1; /* Initialize the repacketizers */ opus_repacketizer_init(opus_data->enc_packer); opus_repacketizer_init(opus_data->dec_packer); pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; }