+int VideoEncoder::write_srt_packet_thunk(void *opaque, uint8_t *buf, int buf_size)
+{
+ VideoEncoder *video_encoder = (VideoEncoder *)opaque;
+ return video_encoder->write_srt_packet(buf, buf_size);
+}
+
+static string print_addrinfo(const addrinfo *ai)
+{
+ char hoststr[NI_MAXHOST], portstr[NI_MAXSERV];
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hoststr, sizeof(hoststr), portstr, sizeof(portstr), NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ return "<unknown address>"; // Should basically never happen, since we're not doing DNS lookups.
+ }
+
+ if (ai->ai_family == AF_INET6) {
+ return string("[") + hoststr + "]:" + portstr;
+ } else {
+ return string(hoststr) + ":" + portstr;
+ }
+}
+
+int VideoEncoder::open_srt_socket()
+{
+ int sock = srt_create_socket();
+ if (sock == -1) {
+ fprintf(stderr, "srt_create_socket(): %s\n", srt_getlasterror_str());
+ return -1;
+ }
+
+ SRT_TRANSTYPE live = SRTT_LIVE;
+ if (srt_setsockopt(sock, 0, SRTO_TRANSTYPE, &live, sizeof(live)) < 0) {
+ fprintf(stderr, "srt_setsockopt(SRTO_TRANSTYPE): %s\n", srt_getlasterror_str());
+ srt_close(sock);
+ return -1;
+ }
+
+ if (srt_setsockopt(sock, 0, SRTO_LATENCY, &global_flags.srt_output_latency, sizeof(global_flags.srt_output_latency)) < 0) {
+ fprintf(stderr, "srt_setsockopt(SRTO_LATENCY): %s\n", srt_getlasterror_str());
+ srt_close(sock);
+ return -1;
+ }
+
+ if (!global_flags.srt_streamid.empty()) {
+ if (srt_setsockopt(sock, 0, SRTO_STREAMID, global_flags.srt_streamid.data(), global_flags.srt_streamid.size()) < 0) {
+ fprintf(stderr, "srt_setsockopt(SRTO_STREAMID): %s\n", srt_getlasterror_str());
+ srt_close(sock);
+ return -1;
+ }
+ }
+
+ if (!global_flags.srt_passphrase.empty()) {
+ if (srt_setsockopt(sock, 0, SRTO_PASSPHRASE, global_flags.srt_passphrase.data(), global_flags.srt_passphrase.size()) < 0) {
+ fprintf(stderr, "srt_setsockopt(SRTO_PASSPHRASE): %s\n", srt_getlasterror_str());
+ srt_close(sock);
+ return -1;
+ }
+ }
+
+ return sock;
+}
+
+int VideoEncoder::connect_to_srt()
+{
+ // We need to specify SOCK_DGRAM as a hint, or we'll get all addresses
+ // three times (for each of TCP, UDP, raw).
+ addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ addrinfo *ai;
+ int ret = getaddrinfo(global_flags.srt_destination_host.c_str(), global_flags.srt_destination_port.c_str(), &hints, &ai);
+ if (ret != 0) {
+ fprintf(stderr, "getaddrinfo(%s:%s): %s\n", global_flags.srt_destination_host.c_str(), global_flags.srt_destination_port.c_str(), gai_strerror(ret));
+ return -1;
+ }
+
+ for (const addrinfo *cur = ai; cur != nullptr; cur = cur->ai_next) {
+ // Seemingly, srt_create_socket() isn't universal; once we try to connect,
+ // it gets locked to either IPv4 or IPv6. So we need to create a new one
+ // for every address we try.
+ int sock = open_srt_socket();
+ if (sock == -1) {
+ // Die immediately.
+ return sock;
+ }
+ if (srt_connect(sock, cur->ai_addr, cur->ai_addrlen) < 0) {
+ fprintf(stderr, "srt_connect(%s): %s\n", print_addrinfo(cur).c_str(), srt_getlasterror_str());
+ srt_close(sock);
+ continue;
+ }
+ fprintf(stderr, "Connected to destination SRT endpoint at %s.\n", print_addrinfo(cur).c_str());
+ freeaddrinfo(ai);
+ return sock;
+ }
+
+ // Out of candidates, so give up.
+ freeaddrinfo(ai);
+ return -1;
+}
+
+int VideoEncoder::write_srt_packet(uint8_t *buf, int buf_size)
+{
+ while (buf_size > 0) {
+ if (srt_sock == -1) {
+ srt_sock = connect_to_srt();
+ if (srt_sock == -1) {
+ usleep(100000);
+ continue;
+ }
+ }
+ int to_send = min(buf_size, SRT_LIVE_DEF_PLSIZE);
+ int ret = srt_send(srt_sock, (char *)buf, to_send);
+ if (ret < 0) {
+ fprintf(stderr, "srt_send(): %s\n", srt_getlasterror_str());
+ srt_close(srt_sock);
+ srt_sock = connect_to_srt();
+ continue;
+ }
+ buf += ret;
+ buf_size -= ret;
+ }
+ return buf_size;
+}
+