Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ dependencies:
description: libpeer
license: MIT
url: https://github.com/sepfy/libpeer
version: 0.0.2
version: 0.0.3
140 changes: 133 additions & 7 deletions src/peer_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,31 +360,30 @@ static void peer_connection_state_new(PeerConnection* pc, DtlsSrtpRole role, int
sdp_append_h264(&pc->local_sdp);
sdp_append(&pc->local_sdp, "a=fingerprint:sha-256 %s", pc->dtls_srtp.local_fingerprint);
sdp_append(&pc->local_sdp, peer_connection_dtls_role_setup_value(role));
strcat(pc->local_sdp.content, description);
sdp_append(&pc->local_sdp, "%s", description);
}

switch (pc->config.audio_codec) {
case CODEC_PCMA:

sdp_append_pcma(&pc->local_sdp);
sdp_append(&pc->local_sdp, "a=fingerprint:sha-256 %s", pc->dtls_srtp.local_fingerprint);
sdp_append(&pc->local_sdp, peer_connection_dtls_role_setup_value(role));
strcat(pc->local_sdp.content, description);
sdp_append(&pc->local_sdp, "%s", description);
break;

case CODEC_PCMU:

sdp_append_pcmu(&pc->local_sdp);
sdp_append(&pc->local_sdp, "a=fingerprint:sha-256 %s", pc->dtls_srtp.local_fingerprint);
sdp_append(&pc->local_sdp, peer_connection_dtls_role_setup_value(role));
strcat(pc->local_sdp.content, description);
sdp_append(&pc->local_sdp, "%s", description);
break;

case CODEC_OPUS:
sdp_append_opus(&pc->local_sdp);
sdp_append(&pc->local_sdp, "a=fingerprint:sha-256 %s", pc->dtls_srtp.local_fingerprint);
sdp_append(&pc->local_sdp, peer_connection_dtls_role_setup_value(role));
strcat(pc->local_sdp.content, description);
sdp_append(&pc->local_sdp, "%s", description);
break;

default:
break;
Expand All @@ -394,7 +393,7 @@ static void peer_connection_state_new(PeerConnection* pc, DtlsSrtpRole role, int
sdp_append_datachannel(&pc->local_sdp);
sdp_append(&pc->local_sdp, "a=fingerprint:sha-256 %s", pc->dtls_srtp.local_fingerprint);
sdp_append(&pc->local_sdp, peer_connection_dtls_role_setup_value(role));
strcat(pc->local_sdp.content, description);
sdp_append(&pc->local_sdp, "%s", description);
}

pc->b_local_description_created = 1;
Expand All @@ -404,6 +403,133 @@ static void peer_connection_state_new(PeerConnection* pc, DtlsSrtpRole role, int
}
}

int peer_connection_recv(PeerConnection* pc) {
if (!pc) return -1;

int total_bytes = 0;

switch (pc->state) {
case PEER_CONNECTION_NEW:
if (!pc->b_local_description_created) {
peer_connection_state_new(pc, DTLS_SRTP_ROLE_SERVER, 1);
}
break;

case PEER_CONNECTION_CHECKING:
if (agent_select_candidate_pair(&pc->agent) < 0) {
STATE_CHANGED(pc, PEER_CONNECTION_FAILED);
return -1;
}
if (agent_connectivity_check(&pc->agent) == 0) {
STATE_CHANGED(pc, PEER_CONNECTION_CONNECTED);
}
break;

case PEER_CONNECTION_CONNECTED:
if (dtls_srtp_handshake(&pc->dtls_srtp, NULL) == 0) {
if (pc->config.datachannel) {
// Close any existing SCTP association first
if (pc->sctp.sock) {
sctp_destroy_association(&pc->sctp);
}
sctp_create_association(&pc->sctp, &pc->dtls_srtp);
pc->sctp.userdata = pc->config.user_data;
}
STATE_CHANGED(pc, PEER_CONNECTION_COMPLETED);
}
break;

case PEER_CONNECTION_COMPLETED: {
// Process incoming data
int ret;
while ((ret = agent_recv(&pc->agent, pc->agent_buf, sizeof(pc->agent_buf))) > 0) {
pc->agent_ret = ret;
total_bytes += ret;

if (rtcp_probe(pc->agent_buf, pc->agent_ret)) {
dtls_srtp_decrypt_rtcp_packet(&pc->dtls_srtp, pc->agent_buf, &pc->agent_ret);
peer_connection_incoming_rtcp(pc, pc->agent_buf, pc->agent_ret);
} else if (dtls_srtp_probe(pc->agent_buf)) {
// Check if the packet is a data channel packet
ret = dtls_srtp_read(&pc->dtls_srtp, pc->temp_buf, sizeof(pc->temp_buf));
if (ret > 0) {
sctp_incoming_data(&pc->sctp, (char*)pc->temp_buf, ret);
}
} else if (rtp_packet_validate(pc->agent_buf, pc->agent_ret)) {
dtls_srtp_decrypt_rtp_packet(&pc->dtls_srtp, pc->agent_buf, &pc->agent_ret);
uint32_t ssrc = rtp_get_ssrc(pc->agent_buf);
if (ssrc == pc->remote_assrc) {
rtp_decoder_decode(&pc->artp_decoder, pc->agent_buf, pc->agent_ret);
} else if (ssrc == pc->remote_vssrc) {
rtp_decoder_decode(&pc->vrtp_decoder, pc->agent_buf, pc->agent_ret);
}
}
}

// Check connection timeout
if (KEEPALIVE_CONNCHECK > 0 &&
(ports_get_epoch_time() - pc->agent.binding_request_time) > KEEPALIVE_CONNCHECK) {
STATE_CHANGED(pc, PEER_CONNECTION_CLOSED);
return -1;
}
break;
}

case PEER_CONNECTION_FAILED:
case PEER_CONNECTION_DISCONNECTED:
case PEER_CONNECTION_CLOSED:
return -1;

default:
break;
}
return total_bytes;
}

int peer_connection_send(PeerConnection* pc) {
if (!pc) return -1;

// Only send data in COMPLETED state
if (pc->state == PEER_CONNECTION_COMPLETED) {
uint8_t* data;
int bytes;
int total_bytes = 0;

#if (CONFIG_VIDEO_BUFFER_SIZE) > 0
data = buffer_peak_head(pc->video_rb, &bytes);
if (data) {
rtp_encoder_encode(&pc->vrtp_encoder, data, bytes);
buffer_pop_head(pc->video_rb);
total_bytes += bytes;
}
#endif

#if (CONFIG_AUDIO_BUFFER_SIZE) > 0
data = buffer_peak_head(pc->audio_rb, &bytes);
if (data) {
rtp_encoder_encode(&pc->artp_encoder, data, bytes);
buffer_pop_head(pc->audio_rb);
total_bytes += bytes;
}
#endif

#if (CONFIG_DATA_BUFFER_SIZE) > 0
data = buffer_peak_head(pc->data_rb, &bytes);
if (data) {
if (pc->config.datachannel == DATA_CHANNEL_STRING) {
sctp_outgoing_data(&pc->sctp, (char*)data, bytes, PPID_STRING, 0);
} else {
sctp_outgoing_data(&pc->sctp, (char*)data, bytes, PPID_BINARY, 0);
}
buffer_pop_head(pc->data_rb);
total_bytes += bytes;
}
#endif
return total_bytes;
}
return 0;
}

int peer_connection_loop(PeerConnection* pc) {
int bytes;
uint8_t* data = NULL;
Expand Down
29 changes: 29 additions & 0 deletions src/peer_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,35 @@ void peer_connection_destroy(PeerConnection* pc);

void peer_connection_close(PeerConnection* pc);

/**
* @brief recv data from peer connection
*
* This function can be called in a loop to keep receiving data from peer connection.
*
* @param[in] peer connection
* @return 0 if success, -1 if error
*/
int peer_connection_recv(PeerConnection* pc);

/**
* @brief send data to peer connection
*
* This function can be called in a loop to keep sending data to peer connection.
*
* @param[in] peer connection
* @return 0 if success, -1 if error
*/
int peer_connection_send(PeerConnection* pc);

/**
* @brief loop peer connection
*
* This function is used to loop peer connection.
* This handles both incoming and outgoing data in a single loop.
*
* @param[in] peer connection
* @note More efficient than calling peer_connection_recv() and peer_connection_send() separately.
*/
int peer_connection_loop(PeerConnection* pc);

int peer_connection_create_datachannel(PeerConnection* pc, DecpChannelType channel_type, uint16_t priority, uint32_t reliability_parameter, char* label, char* protocol);
Expand Down