tnet_ice_candidate_t* tnet_ice_candidate_create(tnet_ice_cand_type_t type_e, tnet_socket_t* socket, tsk_bool_t is_ice_jingle, tsk_bool_t is_rtp, tsk_bool_t is_video, const char* ufrag, const char* pwd, const char *foundation) { tnet_ice_candidate_t* candidate; if(!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ TSK_DEBUG_ERROR("Failed to create candidate"); return tsk_null; } candidate->type_e = type_e; candidate->socket = tsk_object_ref(socket); candidate->local_pref = 0xFFFF; candidate->is_ice_jingle = is_ice_jingle; candidate->is_rtp = is_rtp; candidate->is_video = is_video; candidate->comp_id = is_rtp ? TNET_ICE_CANDIDATE_COMPID_RTP : TNET_ICE_CANDIDATE_COMPID_RTCP; if(foundation){ memcpy(candidate->foundation, foundation, TSK_MIN(tsk_strlen(foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); } else{ tnet_ice_utils_compute_foundation((char*)candidate->foundation, TSK_MIN(sizeof(candidate->foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); } candidate->priority = tnet_ice_utils_get_priority(candidate->type_e, candidate->local_pref, candidate->is_rtp); if(candidate->socket){ memcpy(candidate->connection_addr, candidate->socket->ip, sizeof(candidate->socket->ip)); candidate->port = candidate->socket->port; candidate->transport_e = socket->type; } tnet_ice_candidate_set_credential(candidate, ufrag, pwd); return candidate; }
int tdav_codec_set_priority(tdav_codec_id_t codec_id, int priority) { static int count = sizeof(__codecs)/sizeof(tdav_codec_decl_t); int i; if(priority < 0){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } for(i = 0; i<count; ++i){ if(__codecs[i].id == codec_id){ tdav_codec_decl_t codec_decl_1, codec_decl_2; priority = TSK_MIN(priority, count-1); codec_decl_1 = __codecs[priority]; codec_decl_2 = __codecs[i]; __codecs[i] = codec_decl_1; __codecs[priority] = codec_decl_2; // change priority if already registered and supported if(_tdav_codec_is_supported(codec_decl_2.id, *codec_decl_2.plugin) && tmedia_codec_plugin_is_registered(*codec_decl_2.plugin)){ return tmedia_codec_plugin_register_2(*codec_decl_2.plugin, priority); } return 0; } } TSK_DEBUG_ERROR("cannot find codec with id=%d", codec_id); return -2; }
int tdav_codec_set_priority(tdav_codec_id_t codec_id, int priority) { tsk_size_t i; if(priority < 0) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } for(i = 0; i < __codec_plugins_all_count && __codec_plugins_all[i]; ++i) { if(__codec_plugins_all[i]->codec_id == codec_id) { const struct tmedia_codec_plugin_def_s *codec_decl_1, *codec_decl_2; priority = TSK_MIN(priority, (int)__codec_plugins_all_count-1); codec_decl_1 = __codec_plugins_all[priority]; codec_decl_2 = __codec_plugins_all[i]; __codec_plugins_all[i] = codec_decl_1; __codec_plugins_all[priority] = codec_decl_2; // change priority if already registered and supported if(_tdav_codec_is_supported((tdav_codec_id_t)codec_decl_2->codec_id, codec_decl_2) && tmedia_codec_plugin_is_registered(codec_decl_2)) { return tmedia_codec_plugin_register_2(codec_decl_2, priority); } return 0; } } TSK_DEBUG_INFO("Cannot find codec with id=%d for priority setting", codec_id); return 0; }
/** Initialize audio consumer */ int tdav_consumer_audio_init(tdav_consumer_audio_t* self) { int ret; TSK_DEBUG_INFO("tdav_consumer_audio_init()"); if(!self){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } /* base */ if((ret = tmedia_consumer_init(TMEDIA_CONSUMER(self)))){ return ret; } /* self (should be update by prepare() by using the codec's info)*/ TMEDIA_CONSUMER(self)->audio.bits_per_sample = TDAV_BITS_PER_SAMPLE_DEFAULT; TMEDIA_CONSUMER(self)->audio.ptime = TDAV_PTIME_DEFAULT; TMEDIA_CONSUMER(self)->audio.in.channels = TDAV_CHANNELS_DEFAULT; TMEDIA_CONSUMER(self)->audio.in.rate = TDAV_RATE_DEFAULT; TMEDIA_CONSUMER(self)->audio.gain = TSK_MIN(tmedia_defaults_get_audio_consumer_gain(), TDAV_AUDIO_GAIN_MAX); tsk_safeobj_init(self); return 0; }
static tsk_bool_t tdav_codec_opus_sdp_att_match(const tmedia_codec_t* codec, const char* att_name, const char* att_value) { tdav_codec_opus_t* opus = (tdav_codec_opus_t*)codec; if(!opus) { TSK_DEBUG_ERROR("Invalid parameter"); return tsk_false; } TSK_DEBUG_INFO("[OPUS] Trying to match [%s:%s]", att_name, att_value); if(tsk_striequals(att_name, "fmtp")) { int val_int; tsk_params_L_t* params; /* e.g. FIXME */ if((params = tsk_params_fromstring(att_value, ";", tsk_true))) { tsk_bool_t ret = tsk_false; /* === maxplaybackrate ===*/ if((val_int = tsk_params_get_param_value_as_int(params, "maxplaybackrate")) != -1) { if(!_tdav_codec_opus_rate_is_valid(val_int)) { TSK_DEBUG_ERROR("[OPUS] %d not valid as maxplaybackrate value", val_int); goto done; } TMEDIA_CODEC(opus)->out.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->out.rate, val_int); TMEDIA_CODEC_AUDIO(opus)->out.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->out.rate); } /* === sprop-maxcapturerate ===*/ if((val_int = tsk_params_get_param_value_as_int(params, "sprop-maxcapturerate")) != -1) { if(!_tdav_codec_opus_rate_is_valid(val_int)) { TSK_DEBUG_ERROR("[OPUS] %d not valid as sprop-maxcapturerate value", val_int); goto done; } TMEDIA_CODEC(opus)->in.rate = TSK_MIN((int32_t)TMEDIA_CODEC(opus)->in.rate, val_int); TMEDIA_CODEC_AUDIO(opus)->in.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(codec->id, codec->in.rate); } ret = tsk_true; done: TSK_OBJECT_SAFE_FREE(params); return ret; } } return tsk_true; }
int tnet_ice_candidate_set_rflx_addr(tnet_ice_candidate_t* self, const char* addr, tnet_port_t port) { if(!self || !addr || !port){ TSK_DEBUG_ERROR("Invalid argument"); return -1; } memset(self->connection_addr, 0, sizeof(self->connection_addr)); memcpy(self->connection_addr, addr, TSK_MIN(tsk_strlen(addr), sizeof(self->connection_addr))); self->port = port; return 0; }
int trtp_srtp_match_line(const char* crypto_line, int32_t* tag, int32_t* crypto_type, char* key, tsk_size_t key_size) { char* v = strtok((char*)crypto_line, " :|;"); int32_t k = 0; while(v){ switch(k){ case 0: { if(tag){ *tag = atoi(v); } break; } case 1: { if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_80)){ if(crypto_type){ *crypto_type = HMAC_SHA1_80; } } else if(tsk_striequals(v, TRTP_SRTP_AES_CM_128_HMAC_SHA1_32)){ if(crypto_type){ *crypto_type = HMAC_SHA1_32; } } else { return -0xFF; } break; } case 2: { if(!tsk_striequals(v, "inline")){ return -0xFF; } break; } case 3: { if(key && key_size){ memset(key, 0, key_size); memcpy(key, v, TSK_MIN(key_size, tsk_strlen(v))); } return 0; } } ++k; v = strtok(tsk_null, " :|;"); } return -0xF0; }
int tnet_transport_get_public_ip_n_port(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_ip_t *ip, tnet_port_t *port) { tsk_bool_t stun_ok = tsk_false; struct tnet_nat_ctx_s* natt_ctx; const tnet_transport_t *transport = handle; if(!transport){ TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if (TNET_SOCKET_TYPE_IS_DGRAM(transport->type) && (natt_ctx = tsk_object_ref(transport->natt_ctx))) { tnet_stun_binding_id_t bind_id = kStunBindingInvalidId; // if the socket is already monitored by the transport we should pause because both the transport and // NAT binder will try to read from it // Pause the soket tnet_transport_pause_socket(transport, fd, tsk_true); // Performs STUN binding bind_id = tnet_nat_stun_bind(transport->natt_ctx, fd); // Resume the socket tnet_transport_pause_socket(transport, fd, tsk_false); if (bind_id != kStunBindingInvalidId) { char* public_ip = tsk_null; if(tnet_nat_stun_get_reflexive_address(transport->natt_ctx, bind_id, &public_ip, port) == 0){ if(ip && public_ip){ tsk_size_t ip_len = tsk_strlen(public_ip); memcpy(ip, public_ip, ip_len> sizeof(*ip)?sizeof(*ip):ip_len); } stun_ok = tsk_true; } TSK_FREE(public_ip); tnet_nat_stun_unbind(transport->natt_ctx, bind_id); } tsk_object_unref(natt_ctx); } if(!stun_ok){ if(fd == TNET_INVALID_FD && transport->local_ip){ memcpy(*ip, transport->local_ip, TSK_MIN(sizeof(tnet_ip_t), tsk_strlen(transport->local_ip))); *port = transport->bind_local_port; return 0; } else{ return tnet_transport_get_ip_n_port(handle, fd, ip, port); } } return 0; }
static void __handle_output_buffer(void *userdata, AudioQueueRef queue, AudioQueueBufferRef buffer) { OSStatus ret; void *data; tsk_size_t out_size = 0; tdav_consumer_coreaudio_t* consumer = (tdav_consumer_coreaudio_t*)userdata; if (!consumer->started) { return; } if((data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(consumer), &out_size))){ // If we can get audio to play, then copy in the buffer memcpy(buffer->mAudioData, data, TSK_MIN(consumer->buffer_size, out_size)); TSK_FREE(data); } else{ // Put silence if there is no audio to play memset(buffer->mAudioData, 0, consumer->buffer_size); } // Re-enqueue the buffer ret = AudioQueueEnqueueBuffer(consumer->queue, buffer, 0, NULL); }
// @param str e.g. "1 1 udp 1 192.168.196.1 57806 typ host name video_rtcp network_name {0C0137CC-DB78-46B6-9B6C-7E097FFA79FE} username StFEVThMK2DHThkv password qkhKUDr4WqKRwZTo generation 0" tnet_ice_candidate_t* tnet_ice_candidate_parse(const char* str) { char *v, *copy; int32_t k; tnet_ice_candidate_t* candidate; if(tsk_strnullORempty(str)){ TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } if(!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ TSK_DEBUG_ERROR("Failed to create candidate"); return tsk_null; } k = 0; copy = tsk_strdup(str); v = strtok(copy, " "); while(v){ switch(k){ case 0: { memcpy(candidate->foundation, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->foundation))); break; } case 1: { candidate->comp_id = atoi(v); break; } case 2: { candidate->transport_str = tsk_strdup(v); break; } case 3: { candidate->priority = atoi(v); break; } case 4: { memcpy(candidate->connection_addr, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->connection_addr))); break; } case 5: { tnet_family_t family; candidate->port = atoi(v); family = tnet_get_family(candidate->connection_addr, candidate->port); candidate->transport_e = _tnet_ice_candidate_get_transport_type((family == AF_INET6), candidate->transport_str); break; } case 6: { v = strtok(tsk_null, " "); tsk_strupdate(&candidate->cand_type_str, v); candidate->type_e = _tnet_ice_candtype_get_transport_type(v); break; } default: { const char* name = v; const char* value = (v = strtok(tsk_null, " ")); tsk_param_t* param = tsk_param_create(name, value); if(param){ tsk_list_push_back_data(candidate->extension_att_list, (void**)¶m); } break; } } ++k; v = strtok(tsk_null, " "); } if(k < 6){ TSK_DEBUG_ERROR("Failed to parse: %s", str); TSK_OBJECT_SAFE_FREE(candidate); } TSK_FREE(copy); return candidate; }
static int tdav_codec_h264_set(tmedia_codec_t* self, const tmedia_param_t* param) { tdav_codec_h264_t* h264 = (tdav_codec_h264_t*)self; if (param->value_type == tmedia_pvt_int32) { if(tsk_striequals(param->key, "action")){ tmedia_codec_action_t action = (tmedia_codec_action_t)TSK_TO_INT32((uint8_t*)param->value); switch(action){ case tmedia_codec_action_encode_idr: { h264->encoder.force_idr = tsk_true; break; } case tmedia_codec_action_bw_down: { h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality + 1), 31); #if HAVE_FFMPEG if (h264->encoder.context) { h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality; } #endif break; } case tmedia_codec_action_bw_up: { h264->encoder.quality = TSK_CLAMP(1, (h264->encoder.quality - 1), 31); #if HAVE_FFMPEG if (h264->encoder.context) { h264->encoder.context->global_quality = FF_QP2LAMBDA * h264->encoder.quality; } #endif break; } } return 0; } else if(tsk_striequals(param->key, "bw_kbps")){ int32_t max_bw_userdefine = self->bandwidth_max_upload; int32_t max_bw_new = *((int32_t*)param->value); if (max_bw_userdefine > 0) { // do not use more than what the user defined in it's configuration h264->encoder.max_bw_kpbs = TSK_MIN(max_bw_new, max_bw_userdefine); } else { h264->encoder.max_bw_kpbs = max_bw_new; } return 0; } else if(tsk_striequals(param->key, "bypass-encoding")){ h264->encoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false; TSK_DEBUG_INFO("[H.264] bypass-encoding = %d", h264->encoder.passthrough); return 0; } else if(tsk_striequals(param->key, "bypass-decoding")){ h264->decoder.passthrough = *((int32_t*)param->value) ? tsk_true : tsk_false; TSK_DEBUG_INFO("[H.264] bypass-decoding = %d", h264->decoder.passthrough); return 0; } else if(tsk_striequals(param->key, "rotation")){ int32_t rotation = *((int32_t*)param->value); if(h264->encoder.rotation != rotation){ h264->encoder.rotation = rotation; if (self->opened) { int ret; if ((ret = tdav_codec_h264_close_encoder(h264, kResetRotationFalse))) { return ret; } if ((ret = tdav_codec_h264_open_encoder(h264))) { return ret; } #if 0 // Not working if((ret = avcodec_close(h264->encoder.context))){ TSK_DEBUG_ERROR("Failed to close [%s] codec", TMEDIA_CODEC(h264)->plugin->desc); return ret; } h264->encoder.context->width = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.height : TMEDIA_CODEC_VIDEO(h264)->out.width; h264->encoder.context->height = (rotation == 90 || rotation == 270) ? TMEDIA_CODEC_VIDEO(h264)->out.width : TMEDIA_CODEC_VIDEO(h264)->out.height; if((ret = avcodec_open(h264->encoder.context, h264->encoder.codec)) < 0){ TSK_DEBUG_ERROR("Failed to open [%s] codec", TMEDIA_CODEC(h264)->plugin->desc); return ret; } h264->encoder.force_idr = tsk_true; #endif } } return 0; } } return -1; }
void tdav_codec_h264_rtp_callback(struct tdav_codec_h264_common_s *self, const void *data, tsk_size_t size, tsk_bool_t marker) { uint8_t* pdata = (uint8_t*)data; //TSK_DEBUG_INFO("%x %x %x %x -- %u", pdata[0], pdata[1], pdata[2], pdata[3], size); if(size>4 && pdata[0] == H264_START_CODE_PREFIX[0] && pdata[1] == H264_START_CODE_PREFIX[1]){ if(pdata[2] == H264_START_CODE_PREFIX[3]){ pdata += 3, size -= 3; } else if(pdata[2] == H264_START_CODE_PREFIX[2] && pdata[3] == H264_START_CODE_PREFIX[3]){ pdata += 4, size -= 4; } } //TSK_DEBUG_INFO("==> SCP %2x %2x %2x %2x", pdata[0], pdata[1], pdata[2], pdata[3]); if(size < H264_RTP_PAYLOAD_SIZE){ /* Can be packet in a Single Nal Unit */ // Send data over the network if(TMEDIA_CODEC_VIDEO(self)->out.callback){ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = pdata; TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = size; TMEDIA_CODEC_VIDEO(self)->out.result.duration = (3003* (30/TMEDIA_CODEC_VIDEO(self)->out.fps)); TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = marker; TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result); } } else if(size > H264_NAL_UNIT_TYPE_HEADER_SIZE){ /* Should be Fragmented as FUA */ uint8_t fua_hdr[H264_FUA_HEADER_SIZE]; /* "FU indicator" and "FU header" - 2bytes */ fua_hdr[0] = pdata[0] & 0x60/* F=0 */, fua_hdr[0] |= fu_a; fua_hdr[1] = 0x80/* S=1,E=0,R=0 */, fua_hdr[1] |= pdata[0] & 0x1f; /* type */ // discard header pdata += H264_NAL_UNIT_TYPE_HEADER_SIZE; size -= H264_NAL_UNIT_TYPE_HEADER_SIZE; while(size){ tsk_size_t packet_size = TSK_MIN(H264_RTP_PAYLOAD_SIZE, size); if(self->rtp.size < (packet_size + H264_FUA_HEADER_SIZE)){ if(!(self->rtp.ptr = tsk_realloc(self->rtp.ptr, (packet_size + H264_FUA_HEADER_SIZE)))){ TSK_DEBUG_ERROR("Failed to allocate new buffer"); return; } self->rtp.size = (packet_size + H264_FUA_HEADER_SIZE); } // set E bit if((size - packet_size) == 0){ // Last packet fua_hdr[1] |= 0x40; } // copy FUA header memcpy(self->rtp.ptr, fua_hdr, H264_FUA_HEADER_SIZE); // reset "S" bit fua_hdr[1] &= 0x7F; // copy data memcpy((self->rtp.ptr + H264_FUA_HEADER_SIZE), pdata, packet_size); pdata += packet_size; size -= packet_size; // send data if(TMEDIA_CODEC_VIDEO(self)->out.callback){ TMEDIA_CODEC_VIDEO(self)->out.result.buffer.ptr = self->rtp.ptr; TMEDIA_CODEC_VIDEO(self)->out.result.buffer.size = (packet_size + H264_FUA_HEADER_SIZE); TMEDIA_CODEC_VIDEO(self)->out.result.duration = (3003* (30/TMEDIA_CODEC_VIDEO(self)->out.fps)); TMEDIA_CODEC_VIDEO(self)->out.result.last_chunck = (size == 0); TMEDIA_CODEC_VIDEO(self)->out.callback(&TMEDIA_CODEC_VIDEO(self)->out.result); } } } }
static void *__playback_thread(void *param) { tdav_consumer_dsound_t* dsound = (tdav_consumer_dsound_t*)param; HRESULT hr; LPVOID lpvAudio1, lpvAudio2; DWORD dwBytesAudio1, dwBytesAudio2; void* data; int index; TSK_DEBUG_INFO("__playback_thread -- START"); SetPriorityClass(GetCurrentThread(), REALTIME_PRIORITY_CLASS); for(;;){ DWORD dwEvent = WaitForMultipleObjects(sizeof(dsound->notifEvents)/sizeof(HANDLE), dsound->notifEvents, FALSE, INFINITE); if(!dsound->started){ break; } else { tsk_size_t out_size = 0; data = tdav_consumer_audio_get(TDAV_CONSUMER_AUDIO(dsound), &out_size); index = (dwEvent == (TDAV_DSOUNS_CONSUMER_NOTIF_POS_COUNT-1)) ? 0 : (dwEvent + 1); // lock if((hr = IDirectSoundBuffer_Lock(dsound->secondaryBuffer, (index * dsound->bytes_per_notif), dsound->bytes_per_notif, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0)) != DS_OK){ tdav_win32_print_error("IDirectSoundBuffer_Lock", hr); goto next; } if(data){ // copy data to dsound buffers memcpy(lpvAudio1, data, TSK_MIN(dwBytesAudio1, out_size)); if(lpvAudio2){ memcpy(lpvAudio2, ((LPBYTE*)data) + dwBytesAudio1, dwBytesAudio2); } } else{ // Put silence memset(lpvAudio1, 0, dwBytesAudio1); if(lpvAudio2){ memset(lpvAudio2, 0, dwBytesAudio2); } } // unlock if((hr = IDirectSoundBuffer_Unlock(dsound->secondaryBuffer, lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2)) != DS_OK){ tdav_win32_print_error("IDirectSoundBuffer_UnLock", hr); goto next; } next: TSK_FREE(data); } } TSK_DEBUG_INFO("__playback_thread -- STOP"); return tsk_null; }