diff --git a/idf_component.yml b/idf_component.yml index a011f9d7..4bf861bc 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -4,4 +4,4 @@ dependencies: description: libpeer license: MIT url: https://github.com/sepfy/libpeer -version: 0.0.2 +version: 0.0.3 diff --git a/src/peer_connection.c b/src/peer_connection.c index c7709cfb..3f312af5 100644 --- a/src/peer_connection.c +++ b/src/peer_connection.c @@ -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; @@ -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; @@ -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; diff --git a/src/peer_connection.h b/src/peer_connection.h index dca32718..5cb9df68 100644 --- a/src/peer_connection.h +++ b/src/peer_connection.h @@ -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);