Ring Daemon
Loading...
Searching...
No Matches
socket_pair.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 * Copyright (c) 2007 The FFmpeg Project
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#include <dhtnet/ip_utils.h> // MUST BE INCLUDED FIRST
20
21#include "libav_deps.h" // THEN THIS ONE AFTER
22
23#include "socket_pair.h"
24#include "logger.h"
26
27#include <dhtnet/ice_socket.h>
28
29#include <string>
30#include <algorithm>
31
32extern "C" {
33#include "srtp.h"
34}
35
36#include <cstring>
37#include <stdexcept>
38#include <unistd.h>
39#include <sys/types.h>
40
41#ifdef _WIN32
42#define SOCK_NONBLOCK FIONBIO
43#define poll WSAPoll
44#define close(x) closesocket(x)
45#endif
46
47#ifdef __ANDROID__
48#include <asm-generic/fcntl.h>
49#define SOCK_NONBLOCK O_NONBLOCK
50#endif
51
52#ifdef __APPLE__
53#include <fcntl.h>
54#endif
55
56// Swap 2 byte, 16 bit values:
57#define Swap2Bytes(val) ((((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00))
58
59// Swap 4 byte, 32 bit values:
60#define Swap4Bytes(val) \
61 ((((val) >> 24) & 0x000000FF) | (((val) >> 8) & 0x0000FF00) | (((val) << 8) & 0x00FF0000) \
62 | (((val) << 24) & 0xFF000000))
63
64// Swap 8 byte, 64 bit values:
65#define Swap8Bytes(val) \
66 ((((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) \
67 | (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) \
68 | (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) \
69 | (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000))
70
71namespace jami {
72
73static constexpr int NET_POLL_TIMEOUT = 100; /* poll() timeout in ms */
74static constexpr int RTP_MAX_PACKET_LENGTH = 2048;
75static constexpr auto UDP_HEADER_SIZE = 8;
76static constexpr auto SRTP_OVERHEAD = 10;
77static constexpr uint32_t RTCP_RR_FRACTION_MASK = 0xFF000000;
78static constexpr unsigned MINIMUM_RTP_HEADER_SIZE = 16;
79
80enum class DataType : uint8_t { RTP = 1 << 0, RTCP = 1 << 1 };
81
83{
84public:
85 SRTPProtoContext(const char* out_suite, const char* out_key, const char* in_suite, const char* in_key)
86 {
89 if (out_suite && out_key) {
90 // XXX: see srtp_open from libavformat/srtpproto.c
92 srtp_close();
93 throw std::runtime_error("Unable to set crypto on output");
94 }
95 }
96
97 if (in_suite && in_key) {
99 srtp_close();
100 throw std::runtime_error("Unable to set crypto on input");
101 }
102 }
103 }
104
105 ~SRTPProtoContext() { srtp_close(); }
106
110
111private:
112 void srtp_close() noexcept
113 {
116 }
117};
118
119static int
121{
122 struct pollfd p = {fd, POLLOUT, 0};
123 auto ret = poll(&p, 1, NET_POLL_TIMEOUT);
124 return ret < 0 ? errno : p.revents & (POLLOUT | POLLERR | POLLHUP) ? 0 : -EAGAIN;
125}
126
127static int
129{
130 int udp_fd = -1;
131
132#ifdef __APPLE__
133 udp_fd = socket(family, SOCK_DGRAM, 0);
134 if (udp_fd >= 0 && fcntl(udp_fd, F_SETFL, O_NONBLOCK) < 0) {
135 close(udp_fd);
136 udp_fd = -1;
137 }
138#elif defined _WIN32
139 udp_fd = socket(family, SOCK_DGRAM, 0);
140 u_long block = 1;
141 if (udp_fd >= 0 && ioctlsocket(udp_fd, FIONBIO, &block) < 0) {
142 close(udp_fd);
143 udp_fd = -1;
144 }
145#else
146 udp_fd = socket(family, SOCK_DGRAM | SOCK_NONBLOCK, 0);
147#endif
148
149 if (udp_fd < 0) {
150 JAMI_ERR("socket() failed");
151 strErr();
152 return -1;
153 }
154
155 auto bind_addr = dhtnet::ip_utils::getAnyHostAddr(family);
156 if (not bind_addr.isIpv4() and not bind_addr.isIpv6()) {
157 JAMI_ERR("No IPv4/IPv6 host found for family %u", family);
158 close(udp_fd);
159 return -1;
160 }
161
162 bind_addr.setPort(port);
163 JAMI_DBG("use local address: %s", bind_addr.toString(true, true).c_str());
164 if (::bind(udp_fd, bind_addr, bind_addr.getLength()) < 0) {
165 JAMI_ERR("bind() failed");
166 strErr();
167 close(udp_fd);
168 udp_fd = -1;
169 }
170
171 return udp_fd;
172}
173
174SocketPair::SocketPair(const char* uri, int localPort)
175{
176 openSockets(uri, localPort);
177}
178
179SocketPair::SocketPair(std::unique_ptr<dhtnet::IceSocket> rtp_sock, std::unique_ptr<dhtnet::IceSocket> rtcp_sock)
180 : rtp_sock_(std::move(rtp_sock))
181 , rtcp_sock_(std::move(rtcp_sock))
182{
183 JAMI_DBG("[%p] Creating instance using ICE sockets for comp %d and %d",
184 this,
185 rtp_sock_->getCompId(),
186 rtcp_sock_->getCompId());
187
188 rtp_sock_->setOnRecv([this](uint8_t* buf, size_t len) {
189 std::lock_guard l(dataBuffMutex_);
190 rtpDataBuff_.emplace_back(buf, buf + len);
191 cv_.notify_one();
192 return len;
193 });
194 rtcp_sock_->setOnRecv([this](uint8_t* buf, size_t len) {
195 std::lock_guard l(dataBuffMutex_);
196 rtcpDataBuff_.emplace_back(buf, buf + len);
197 cv_.notify_one();
198 return len;
199 });
200}
201
203{
204 interrupt();
205 closeSockets();
206 JAMI_DBG("[%p] Instance destroyed", this);
207}
208
209bool
211{
212 std::unique_lock lock(rtcpInfo_mutex_);
213 return cvRtcpPacketReadyToRead_.wait_for(lock, interval, [this] {
214 return interrupted_ or not listRtcpRRHeader_.empty() or not listRtcpREMBHeader_.empty();
215 });
216}
217
218void
219SocketPair::saveRtcpRRPacket(uint8_t* buf, size_t len)
220{
221 if (len < sizeof(rtcpRRHeader))
222 return;
223
224 auto* header = reinterpret_cast<rtcpRRHeader*>(buf);
225 if (header->pt != 201) // 201 = RR PT
226 return;
227
228 std::lock_guard lock(rtcpInfo_mutex_);
229
230 if (listRtcpRRHeader_.size() >= MAX_LIST_SIZE) {
231 listRtcpRRHeader_.pop_front();
232 }
233
234 listRtcpRRHeader_.emplace_back(*header);
235
236 cvRtcpPacketReadyToRead_.notify_one();
237}
238
239void
240SocketPair::saveRtcpREMBPacket(uint8_t* buf, size_t len)
241{
242 if (len < sizeof(rtcpREMBHeader))
243 return;
244
245 auto* header = reinterpret_cast<rtcpREMBHeader*>(buf);
246 if (header->pt != 206) // 206 = REMB PT
247 return;
248
249 if (header->uid != 0x424D4552) // uid must be "REMB"
250 return;
251
252 std::lock_guard lock(rtcpInfo_mutex_);
253
254 if (listRtcpREMBHeader_.size() >= MAX_LIST_SIZE) {
255 listRtcpREMBHeader_.pop_front();
256 }
257
258 listRtcpREMBHeader_.push_back(*header);
259
260 cvRtcpPacketReadyToRead_.notify_one();
261}
262
263std::list<rtcpRRHeader>
265{
266 std::lock_guard lock(rtcpInfo_mutex_);
267 return std::move(listRtcpRRHeader_);
268}
269
270std::list<rtcpREMBHeader>
272{
273 std::lock_guard lock(rtcpInfo_mutex_);
274 return std::move(listRtcpREMBHeader_);
275}
276
277void
278SocketPair::createSRTP(const char* out_suite, const char* out_key, const char* in_suite, const char* in_key)
279{
280 srtpContext_.reset(new SRTPProtoContext(out_suite, out_key, in_suite, in_key));
281}
282
283void
285{
286 JAMI_WARN("[%p] Interrupting RTP sockets", this);
287 interrupted_ = true;
288 if (rtp_sock_)
289 rtp_sock_->setOnRecv(nullptr);
290 if (rtcp_sock_)
291 rtcp_sock_->setOnRecv(nullptr);
292 cv_.notify_all();
293 cvRtcpPacketReadyToRead_.notify_all();
294}
295
296void
298{
299 JAMI_DBG("[%p] Read operations in blocking mode [%s]", this, block ? "YES" : "NO");
300 readBlockingMode_ = block;
301 cv_.notify_all();
302 cvRtcpPacketReadyToRead_.notify_all();
303}
304
305void
307{
308 noWrite_ = state;
309}
310
311void
313{
314 if (rtcpHandle_ > 0 and close(rtcpHandle_))
315 strErr();
316 if (rtpHandle_ > 0 and close(rtpHandle_))
317 strErr();
318}
319
320void
322{
323 JAMI_DBG("Creating rtp socket for uri %s on port %d", uri, local_rtp_port);
324
325 char hostname[256];
326 char path[1024];
327 int dst_rtp_port;
328
329 av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &dst_rtp_port, path, sizeof(path), uri);
330
331 const int local_rtcp_port = local_rtp_port + 1;
332 const int dst_rtcp_port = dst_rtp_port + 1;
333
334 rtpDestAddr_ = dhtnet::IpAddr {hostname};
335 rtpDestAddr_.setPort(dst_rtp_port);
336 rtcpDestAddr_ = dhtnet::IpAddr {hostname};
337 rtcpDestAddr_.setPort(dst_rtcp_port);
338
339 // Open local sockets (RTP/RTCP)
340 if ((rtpHandle_ = udp_socket_create(rtpDestAddr_.getFamily(), local_rtp_port)) == -1
341 or (rtcpHandle_ = udp_socket_create(rtcpDestAddr_.getFamily(), local_rtcp_port)) == -1) {
342 closeSockets();
343 JAMI_ERR("[%p] Sockets creation failed", this);
344 throw std::runtime_error("Sockets creation failed");
345 }
346
347 JAMI_WARN("SocketPair: local{%d,%d} / %s{%d,%d}",
350 hostname,
353}
354
357{
358 unsigned ip_header_size;
359 if (rtp_sock_)
360 ip_header_size = rtp_sock_->getTransportOverhead();
361 else if (rtpDestAddr_.getFamily() == AF_INET6)
362 ip_header_size = 40;
363 else
364 ip_header_size = 20;
365 return new MediaIOHandle(
366 mtu - (srtpContext_ ? SRTP_OVERHEAD : 0) - UDP_HEADER_SIZE - ip_header_size,
367 true,
368 [](void* sp, uint8_t* buf, int len) { return static_cast<SocketPair*>(sp)->readCallback(buf, len); },
369 [](void* sp, uint8_t* buf, int len) { return static_cast<SocketPair*>(sp)->writeCallback(buf, len); },
370 0,
371 reinterpret_cast<void*>(this));
372}
373
374int
375SocketPair::waitForData()
376{
377 // System sockets
378 if (rtpHandle_ >= 0) {
379 int ret;
380 do {
381 if (interrupted_) {
382 errno = EINTR;
383 return -1;
384 }
385
386 if (not readBlockingMode_) {
387 return 0;
388 }
389
390 // work with system socket
391 struct pollfd p[2] = {{rtpHandle_, POLLIN, 0}, {rtcpHandle_, POLLIN, 0}};
392 ret = poll(p, 2, NET_POLL_TIMEOUT);
393 if (ret > 0) {
394 ret = 0;
395 if (p[0].revents & POLLIN)
396 ret |= static_cast<int>(DataType::RTP);
397 if (p[1].revents & POLLIN)
398 ret |= static_cast<int>(DataType::RTCP);
399 }
400 } while (!ret or (ret < 0 and errno == EAGAIN));
401
402 return ret;
403 }
404
405 // work with IceSocket
406 {
407 std::unique_lock lk(dataBuffMutex_);
408 cv_.wait(lk, [this] {
409 return interrupted_ or not rtpDataBuff_.empty() or not rtcpDataBuff_.empty() or not readBlockingMode_;
410 });
411 }
412
413 if (interrupted_) {
414 errno = EINTR;
415 return -1;
416 }
417
418 return static_cast<int>(DataType::RTP) | static_cast<int>(DataType::RTCP);
419}
420
421int
422SocketPair::readRtpData(void* buf, int buf_size)
423{
424 // handle system socket
425 if (rtpHandle_ >= 0) {
426 struct sockaddr_storage from;
427 socklen_t from_len = sizeof(from);
428 return static_cast<int>(recvfrom(rtpHandle_,
429 static_cast<char*>(buf),
430 buf_size,
431 0,
432 reinterpret_cast<struct sockaddr*>(&from),
433 &from_len));
434 }
435
436 // handle ICE
437 std::unique_lock lk(dataBuffMutex_);
438 if (not rtpDataBuff_.empty()) {
439 auto pkt = std::move(rtpDataBuff_.front());
440 rtpDataBuff_.pop_front();
441 lk.unlock(); // to not block our ICE callbacks
442 int pkt_size = static_cast<int>(pkt.size());
443 int len = std::min(pkt_size, buf_size);
444 std::copy_n(pkt.begin(), len, static_cast<char*>(buf));
445 return len;
446 }
447
448 return 0;
449}
450
451int
452SocketPair::readRtcpData(void* buf, int buf_size)
453{
454 // handle system socket
455 if (rtcpHandle_ >= 0) {
456 struct sockaddr_storage from;
457 socklen_t from_len = sizeof(from);
458 return static_cast<int>(recvfrom(rtcpHandle_,
459 static_cast<char*>(buf),
460 buf_size,
461 0,
462 reinterpret_cast<struct sockaddr*>(&from),
463 &from_len));
464 }
465
466 // handle ICE
467 std::unique_lock lk(dataBuffMutex_);
468 if (not rtcpDataBuff_.empty()) {
469 auto pkt = std::move(rtcpDataBuff_.front());
470 rtcpDataBuff_.pop_front();
471 lk.unlock();
472 int pkt_size = static_cast<int>(pkt.size());
473 int len = std::min(pkt_size, buf_size);
474 std::copy_n(pkt.begin(), len, static_cast<char*>(buf));
475 return len;
476 }
477
478 return 0;
479}
480
481int
482SocketPair::readCallback(uint8_t* buf, int buf_size)
483{
484 auto datatype = waitForData();
485 if (datatype < 0)
486 return datatype;
487
488 int len = 0;
489 bool fromRTCP = false;
490
491 if (datatype & static_cast<int>(DataType::RTCP)) {
492 len = readRtcpData(buf, buf_size);
493 if (len > 0) {
494 auto* header = reinterpret_cast<rtcpRRHeader*>(buf);
495 // 201 = RR PT
496 if (header->pt == 201) {
497 lastDLSR_ = Swap4Bytes(header->dlsr);
498 // JAMI_WARN("Read RR, lastDLSR : %d", lastDLSR_);
499 lastRR_time = std::chrono::steady_clock::now();
500 saveRtcpRRPacket(buf, len);
501 }
502 // 206 = REMB PT
503 else if (header->pt == 206)
504 saveRtcpREMBPacket(buf, len);
505 // 200 = SR PT
506 else if (header->pt == 200) {
507 // not used yet
508 } else {
509 JAMI_DBG("Unable to read RTCP: unknown packet type %u", header->pt);
510 }
511 fromRTCP = true;
512 }
513 }
514
515 // No RTCP… attempt RTP
516 if (!len and (datatype & static_cast<int>(DataType::RTP))) {
517 len = readRtpData(buf, buf_size);
518 fromRTCP = false;
519 }
520
521 if (len <= 0)
522 return len;
523
524 if (not fromRTCP && (buf_size < static_cast<int>(MINIMUM_RTP_HEADER_SIZE)))
525 return len;
526
527 // SRTP decrypt
528 if (not fromRTCP and srtpContext_ and srtpContext_->srtp_in.aes) {
529 int32_t gradient = 0;
530 int32_t deltaT = 0;
531 float abs = 0.0f;
532 bool res_parse = false;
533 bool res_delay = false;
534
535 res_parse = parse_RTP_ext(buf, &abs);
536 bool marker = (buf[1] & 0x80) >> 7;
537
538 if (res_parse)
539 res_delay = getOneWayDelayGradient(abs, marker, &gradient, &deltaT);
540
541 // rtpDelayCallback_ is not set for audio
542 if (rtpDelayCallback_ and res_delay)
543 rtpDelayCallback_(gradient, deltaT);
544
545 auto err = ff_srtp_decrypt(&srtpContext_->srtp_in, buf, &len);
546 if (packetLossCallback_ and (buf[2] << 8 | buf[3]) != lastSeqNumIn_ + 1)
547 packetLossCallback_();
548 lastSeqNumIn_ = buf[2] << 8 | buf[3];
549 if (err < 0)
550 JAMI_WARN("decrypt error %d", err);
551 }
552
553 if (len != 0)
554 return len;
555 else
556 return AVERROR_EOF;
557}
558
559int
561{
562 bool isRTCP = RTP_PT_IS_RTCP(buf[1]);
563
564 // System sockets?
565 if (rtpHandle_ >= 0) {
566 int fd;
567 dhtnet::IpAddr* dest_addr;
568
569 if (isRTCP) {
570 fd = rtcpHandle_;
571 dest_addr = &rtcpDestAddr_;
572 } else {
573 fd = rtpHandle_;
574 dest_addr = &rtpDestAddr_;
575 }
576
577 auto ret = ff_network_wait_fd(fd);
578 if (ret < 0)
579 return ret;
580
581 if (noWrite_)
582 return buf_size;
583 return static_cast<int>(
584 ::sendto(fd, reinterpret_cast<const char*>(buf), buf_size, 0, *dest_addr, dest_addr->getLength()));
585 }
586
587 if (noWrite_)
588 return buf_size;
589
590 // IceSocket
591 if (isRTCP)
592 return static_cast<int>(rtcp_sock_->send(buf, buf_size));
593 else
594 return static_cast<int>(rtp_sock_->send(buf, buf_size));
595}
596
597int
598SocketPair::writeCallback(uint8_t* buf, int buf_size)
599{
600 if (noWrite_)
601 return 0;
602
603 int ret;
604 bool isRTCP = RTP_PT_IS_RTCP(buf[1]);
605 unsigned int ts_LSB, ts_MSB;
607
608 // Encrypt?
609 if (not isRTCP and srtpContext_ and srtpContext_->srtp_out.aes) {
610 buf_size = ff_srtp_encrypt(&srtpContext_->srtp_out,
611 buf,
612 buf_size,
613 srtpContext_->encryptbuf,
614 sizeof(srtpContext_->encryptbuf));
615 if (buf_size < 0) {
616 JAMI_WARN("encrypt error %d", buf_size);
617 return buf_size;
618 }
619
620 buf = srtpContext_->encryptbuf;
621 }
622
623 // check if we're sending an RR, if so, detect packet loss
624 // buf_size gives length of buffer, not just header
625 if (isRTCP && static_cast<unsigned>(buf_size) >= sizeof(rtcpRRHeader)) {
626 auto* header = reinterpret_cast<rtcpRRHeader*>(buf);
627 rtcpPacketLoss_ = (header->pt == 201 && ntohl(header->fraction_lost) & RTCP_RR_FRACTION_MASK);
628 }
629
630 do {
631 if (interrupted_)
632 return -EINTR;
634 } while (ret < 0 and errno == EAGAIN);
635
636 if (buf[1] == 200) // Sender Report
637 {
638 auto* header = reinterpret_cast<rtcpSRHeader*>(buf);
639 ts_LSB = Swap4Bytes(header->timestampLSB);
640 ts_MSB = Swap4Bytes(header->timestampMSB);
641
642 currentSRTS = ts_MSB + (ts_LSB / pow(2, 32));
643
644 if (lastSRTS_ != 0 && lastDLSR_ != 0) {
645 if (histoLatency_.size() >= MAX_LIST_SIZE)
646 histoLatency_.pop_front();
647
648 currentLatency = (currentSRTS - lastSRTS_) / 2;
649 // JAMI_WARN("Current Latency : %f from sender %X", currentLatency, header->ssrc);
650 histoLatency_.push_back(currentLatency);
651 }
652
653 lastSRTS_ = currentSRTS;
654
655 // JAMI_WARN("SENDING NEW RTCP SR !! ");
656
657 } else if (buf[1] == 201) // Receiver Report
658 {
659 // auto header = reinterpret_cast<rtcpRRHeader*>(buf);
660 // JAMI_WARN("SENDING NEW RTCP RR !! ");
661 }
662
663 return ret < 0 ? -errno : ret;
664}
665
666double
668{
669 if (not histoLatency_.empty())
670 return histoLatency_.back();
671 else
672 return -1;
673}
674
675void
676SocketPair::setRtpDelayCallback(std::function<void(int, int)> cb)
677{
678 rtpDelayCallback_ = std::move(cb);
679}
680
681bool
682SocketPair::getOneWayDelayGradient(float sendTS, bool marker, int32_t* gradient, int32_t* deltaT)
683{
684 // Keep only last packet of each frame
685 if (not marker) {
686 return 0;
687 }
688
689 // 1st frame
690 if (lastSendTS_ == 0.0f) {
691 lastSendTS_ = sendTS;
692 lastReceiveTS_ = std::chrono::steady_clock::now();
693 return 0;
694 }
695
696 int32_t deltaS = static_cast<int32_t>((sendTS - lastSendTS_) * 1000); // milliseconds
697 if (deltaS < 0)
698 deltaS += 64000;
699 lastSendTS_ = sendTS;
700
701 std::chrono::steady_clock::time_point arrival_TS = std::chrono::steady_clock::now();
702 auto deltaR = static_cast<int32_t>(
703 std::chrono::duration_cast<std::chrono::milliseconds>(arrival_TS - lastReceiveTS_).count());
704 lastReceiveTS_ = arrival_TS;
705
707 *deltaT = deltaR;
708
709 return true;
710}
711
712bool
713SocketPair::parse_RTP_ext(uint8_t* buf, float* abs)
714{
715 if (not(buf[0] & 0x10))
716 return false;
717
718 uint16_t magic_word = (buf[12] << 8) + buf[13];
719 if (magic_word != 0xBEDE)
720 return false;
721
722 uint8_t sec = buf[17] >> 2;
723 uint32_t fract = ((buf[17] & 0x3) << 16 | (buf[18] << 8) | buf[19]) << 14;
724 float milli = static_cast<float>(fract / pow(2, 32));
725
726 *abs = static_cast<float>(sec) + (milli);
727 return true;
728}
729
732{
733 if (srtpContext_)
734 return srtpContext_->srtp_out.seq_largest;
735 JAMI_ERR("SRTP context not found.");
736 return 0;
737}
738
739} // namespace jami
uint8_t encryptbuf[RTP_MAX_PACKET_LENGTH]
SRTPProtoContext(const char *out_suite, const char *out_key, const char *in_suite, const char *in_key)
void openSockets(const char *uri, int localPort)
void setReadBlockingMode(bool blocking)
void setRtpDelayCallback(std::function< void(int, int)> cb)
int writeData(uint8_t *buf, int buf_size)
MediaIOHandle * createIOContext(const uint16_t mtu)
std::list< rtcpREMBHeader > getRtcpREMB()
std::list< rtcpRRHeader > getRtcpRR()
uint16_t lastSeqValOut()
double getLastLatency()
bool waitForRTCP(std::chrono::seconds interval)
SocketPair(const char *uri, int localPort)
void createSRTP(const char *out_suite, const char *out_params, const char *in_suite, const char *in_params)
void stopSendOp(bool state=true)
#define JAMI_ERR(...)
Definition logger.h:230
#define JAMI_DBG(...)
Definition logger.h:228
#define JAMI_WARN(...)
Definition logger.h:229
void jami_secure_memzero(void *ptr, size_t length)
Definition memory.cpp:45
static int ff_network_wait_fd(int fd)
rational< I > abs(const rational< I > &r)
Definition rational.h:233
static constexpr auto UDP_HEADER_SIZE
void emitSignal(Args... args)
Definition jami_signal.h:64
static int udp_socket_create(int family, int port)
static constexpr auto SRTP_OVERHEAD
static constexpr unsigned MINIMUM_RTP_HEADER_SIZE
void strErr()
Thread-safe function to print the stringified contents of errno.
Definition logger.cpp:102
static constexpr int RTP_MAX_PACKET_LENGTH
static constexpr uint32_t RTCP_RR_FRACTION_MASK
static constexpr int NET_POLL_TIMEOUT
#define Swap4Bytes(val)
int ff_srtp_encrypt(struct SRTPContext *s, const uint8_t *in, int len, uint8_t *out, int outlen)
int ff_srtp_set_crypto(struct SRTPContext *s, const char *suite, const char *params)
#define RTP_PT_IS_RTCP(x)
Definition srtp.h:54
int ff_srtp_decrypt(struct SRTPContext *s, uint8_t *buf, int *lenptr)
void ff_srtp_free(struct SRTPContext *s)