|
@@ -0,0 +1,108 @@
|
|
|
|
+/*
|
|
|
|
+ * libdatachannel media sender example
|
|
|
|
+ * Copyright (c) 2020 Staz Modrzynski
|
|
|
|
+ * Copyright (c) 2020 Paul-Louis Ageneau
|
|
|
|
+ *
|
|
|
|
+ * This program is free software; you can redistribute it and/or
|
|
|
|
+ * modify it under the terms of the GNU General Public License
|
|
|
|
+ * as published by the Free Software Foundation; either version 2
|
|
|
|
+ * of the License, or (at your option) any later version.
|
|
|
|
+ *
|
|
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
+ * GNU General Public License for more details.
|
|
|
|
+ *
|
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
|
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "rtc/rtc.hpp"
|
|
|
|
+
|
|
|
|
+#include <cstddef>
|
|
|
|
+#include <iostream>
|
|
|
|
+#include <memory>
|
|
|
|
+#include <stdexcept>
|
|
|
|
+#include <utility>
|
|
|
|
+
|
|
|
|
+#include <nlohmann/json.hpp>
|
|
|
|
+
|
|
|
|
+#ifdef _WIN32
|
|
|
|
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
|
|
|
+#include <winsock2.h>
|
|
|
|
+#else
|
|
|
|
+#include <arpa/inet.h>
|
|
|
|
+#include <netinet/in.h>
|
|
|
|
+#include <sys/socket.h>
|
|
|
|
+typedef int SOCKET;
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+using nlohmann::json;
|
|
|
|
+
|
|
|
|
+const int BUFFER_SIZE = 2048;
|
|
|
|
+
|
|
|
|
+int main() {
|
|
|
|
+ try {
|
|
|
|
+ rtc::InitLogger(rtc::LogLevel::Debug);
|
|
|
|
+ auto pc = std::make_shared<rtc::PeerConnection>();
|
|
|
|
+
|
|
|
|
+ pc->onStateChange(
|
|
|
|
+ [](rtc::PeerConnection::State state) { std::cout << "State: " << state << std::endl; });
|
|
|
|
+
|
|
|
|
+ pc->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
|
|
|
|
+ std::cout << "Gathering State: " << state << std::endl;
|
|
|
|
+ if (state == rtc::PeerConnection::GatheringState::Complete) {
|
|
|
|
+ auto description = pc->localDescription();
|
|
|
|
+ json message = {{"type", description->typeString()},
|
|
|
|
+ {"sdp", std::string(description.value())}};
|
|
|
|
+ std::cout << message << std::endl;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
+ sockaddr_in addr = {};
|
|
|
|
+ addr.sin_family = AF_INET;
|
|
|
|
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
|
|
+ addr.sin_port = htons(6000);
|
|
|
|
+
|
|
|
|
+ if (bind(sock, reinterpret_cast<const sockaddr *>(&addr), sizeof(addr)) < 0)
|
|
|
|
+ throw std::runtime_error("Failed to bind UDP socket on 127.0.0.1:6000");
|
|
|
|
+
|
|
|
|
+ int rcvBufSize = 212992;
|
|
|
|
+ setsockopt(sock, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char *>(&rcvBufSize),
|
|
|
|
+ sizeof(rcvBufSize));
|
|
|
|
+
|
|
|
|
+ const rtc::SSRC ssrc = 42;
|
|
|
|
+ rtc::Description::Video media("video", rtc::Description::Direction::SendOnly);
|
|
|
|
+ media.addH264Codec(96); // Must match the payload type of the external h264 RTP stream
|
|
|
|
+ media.addSSRC(ssrc, "video-send");
|
|
|
|
+ auto track = pc->addTrack(media);
|
|
|
|
+
|
|
|
|
+ pc->setLocalDescription();
|
|
|
|
+
|
|
|
|
+ std::cout << "RTP video stream expected on localhost:6000" << std::endl;
|
|
|
|
+ std::cout << "Please copy/paste the answer provided by the browser: " << std::endl;
|
|
|
|
+ std::string sdp;
|
|
|
|
+ std::getline(std::cin, sdp);
|
|
|
|
+
|
|
|
|
+ json j = json::parse(sdp);
|
|
|
|
+ rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
|
|
|
|
+ pc->setRemoteDescription(answer);
|
|
|
|
+
|
|
|
|
+ // Receive from UDP
|
|
|
|
+ char buffer[BUFFER_SIZE];
|
|
|
|
+ int len;
|
|
|
|
+ while ((len = recv(sock, buffer, BUFFER_SIZE, 0)) >= 0) {
|
|
|
|
+ if (len < sizeof(rtc::RtpHeader) || !track->isOpen())
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ auto rtp = reinterpret_cast<rtc::RtpHeader *>(buffer);
|
|
|
|
+ rtp->setSsrc(ssrc);
|
|
|
|
+
|
|
|
|
+ track->send(reinterpret_cast<const std::byte *>(buffer), len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } catch (const std::exception &e) {
|
|
|
|
+ std::cerr << "Error: " << e.what() << std::endl;
|
|
|
|
+ }
|
|
|
|
+}
|