Procházet zdrojové kódy

Merge pull request #638 from paullouisageneau/fix-media-sfu

Enhance media SFU example
Paul-Louis Ageneau před 3 roky
rodič
revize
c53bc9a16e

+ 1 - 1
examples/media-receiver/main.html

@@ -15,7 +15,7 @@
         const offer = JSON.parse(document.querySelector('textarea').value);
         const pc = new RTCPeerConnection({
             // Recommended for libdatachannel
-            bundlePolicy: "max-bundle",
+            bundlePolicy: 'max-bundle',
         });
 
         pc.onicegatheringstatechange = (state) => {

+ 1 - 1
examples/media-sender/main.html

@@ -17,7 +17,7 @@
         const offer = JSON.parse(document.querySelector('textarea').value);
         const pc = new RTCPeerConnection({
             // Recommended for libdatachannel
-            bundlePolicy: "max-bundle",
+            bundlePolicy: 'max-bundle',
         });
 
         pc.onicegatheringstatechange = (state) => {

+ 25 - 25
examples/media-sfu/main.cpp

@@ -57,13 +57,11 @@ int main() {
 		    3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
 
 		auto track = pc->addTrack(media);
-		pc->setLocalDescription();
 
 		auto session = std::make_shared<rtc::RtcpReceivingSession>();
 		track->setMediaHandler(session);
 
-		const rtc::SSRC targetSSRC = 4;
-
+		const rtc::SSRC targetSSRC = 42;
 		track->onMessage(
 		    [&receivers, targetSSRC](rtc::binary message) {
 			    // This is an RTP packet
@@ -77,28 +75,28 @@ int main() {
 		    },
 		    nullptr);
 
+		pc->setLocalDescription();
+
 		// Set the sender's answer
-		{
-			std::cout << "Please copy/paste the answer provided by the SENDER: " << std::endl;
-			std::string sdp;
-			std::getline(std::cin, sdp);
-			std::cout << "Got answer" << sdp << std::endl;
-			json j = json::parse(sdp);
-			rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
-			pc->setRemoteDescription(answer);
-		}
+		std::cout << "Please copy/paste the answer provided by the SENDER: " << std::endl;
+		std::string sdp;
+		std::getline(std::cin, sdp);
+		std::cout << "Got answer" << sdp << std::endl;
+		json j = json::parse(sdp);
+		rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
+		pc->setRemoteDescription(answer);
 
 		// For each receiver
 		while (true) {
-			auto pc = std::make_shared<Receiver>();
-			pc->conn = std::make_shared<rtc::PeerConnection>();
-			pc->conn->onStateChange([](rtc::PeerConnection::State state) {
+			auto r = std::make_shared<Receiver>();
+			r->conn = std::make_shared<rtc::PeerConnection>();
+			r->conn->onStateChange([session](rtc::PeerConnection::State state) {
 				std::cout << "State: " << state << std::endl;
 			});
-			pc->conn->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
+			r->conn->onGatheringStateChange([r](rtc::PeerConnection::GatheringState state) {
 				std::cout << "Gathering State: " << state << std::endl;
 				if (state == rtc::PeerConnection::GatheringState::Complete) {
-					auto description = pc->conn->localDescription();
+					auto description = r->conn->localDescription();
 					json message = {{"type", description->typeString()},
 					                {"sdp", std::string(description.value())}};
 					std::cout << "Please copy/paste this offer to the RECEIVER: " << message
@@ -107,15 +105,17 @@ int main() {
 			});
 			rtc::Description::Video media("video", rtc::Description::Direction::SendOnly);
 			media.addH264Codec(96);
-			media.setBitrate(
-			    3000); // Request 3Mbps (Browsers do not encode more than 2.5MBps from a webcam)
-
+			media.setBitrate(3000);
 			media.addSSRC(targetSSRC, "video-send");
 
-			pc->track = pc->conn->addTrack(media);
-			pc->conn->setLocalDescription();
+			r->track = r->conn->addTrack(media);
+
+			r->track->onOpen([session]() {
+				session->requestKeyframe(); // So the receiver can start playing immediately
+			});
+			r->track->onMessage([](rtc::binary var) {}, nullptr);
 
-			pc->track->onMessage([](rtc::binary var) {}, nullptr);
+			r->conn->setLocalDescription();
 
 			std::cout << "Please copy/paste the answer provided by the RECEIVER: " << std::endl;
 			std::string sdp;
@@ -123,9 +123,9 @@ int main() {
 			std::cout << "Got answer" << sdp << std::endl;
 			json j = json::parse(sdp);
 			rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
-			pc->conn->setRemoteDescription(answer);
+			r->conn->setRemoteDescription(answer);
 
-			receivers.push_back(pc);
+			receivers.push_back(r);
 		}
 
 	} catch (const std::exception &e) {

+ 50 - 39
examples/media-sfu/main.html

@@ -2,84 +2,95 @@
 <html lang="en">
 <head>
     <meta charset="UTF-8">
-    <title>libdatachannel media example</title>
+    <title>libdatachannel media SFU example</title>
 </head>
 <body>
 
 <div style="display:inline-block; width:40%;">
     <h1>SENDER</h1>
     <p id="send-help">Please enter the offer provided to you by the application: </p>
-    <textarea style="width:100%;" id=send-text rows="50"></textarea>
-    <button id=send-btn>Submit</button>
+    <textarea style="width:100%;" id="send-text" cols="40" rows="25"></textarea>
+    <button id="send-button">Submit</button>
 </div>
 <div style="display:inline-block; width:40%;">
     <h1>RECEIVER</h1>
     <p id="recv-help">Please enter the offer provided to you by the application: </p>
-    <textarea id=recv-text style="width:100%;" rows="50"></textarea>
-    <button id=recv-btn>Submit</button>
+    <textarea id="recv-text" style="width:100%;" cols="40" rows="25"></textarea>
+    <button id="recv-button">Submit</button>
 </div>
-<div id="videos">
 
+<div id="videos">
 </div>
+
 <script>
-    document.querySelector('#send-btn').addEventListener('click',  async () => {
-        let offer = JSON.parse(document.querySelector('#send-text').value);
-        rtc = new RTCPeerConnection({
+    document.getElementById('send-button').addEventListener('click',  async () => {
+        const pc = new RTCPeerConnection({
             // Recommended for libdatachannel
-            bundlePolicy: "max-bundle",
+            bundlePolicy: 'max-bundle',
         });
 
-        rtc.onicegatheringstatechange = (state) => {
-            if (rtc.iceGatheringState === 'complete') {
+        pc.onicegatheringstatechange = (state) => {
+            if (pc.iceGatheringState === 'complete') {
                 // We only want to provide an answer once all of our candidates have been added to the SDP.
-                let answer = rtc.localDescription;
-                document.querySelector('#send-text').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
-                document.querySelector('#send-help').value = 'Please paste the answer in the application.';
+                const answer = pc.localDescription;
+                document.getElementById('send-text').value = JSON.stringify({
+                    type: answer.type,
+                    sdp: answer.sdp
+                });
+                document.getElementById('send-help').value = 'Please paste the answer in the application.';
                 alert('Please paste the answer in the application.');
             }
         }
-        await rtc.setRemoteDescription(offer);
 
-        let media = await navigator.mediaDevices.getUserMedia({
+        const offer = JSON.parse(document.getElementById('send-text').value);
+        await pc.setRemoteDescription(offer);
+
+        const media = await navigator.mediaDevices.getUserMedia({
             video: {
                 width: 1280,
                 height: 720
             }
         });
-        media.getTracks().forEach(track => rtc.addTrack(track, media));
-        let answer = await rtc.createAnswer();
-        await rtc.setLocalDescription(answer);
+        media.getTracks().forEach(track => pc.addTrack(track, media));
+
+        const answer = await pc.createAnswer();
+        await pc.setLocalDescription(answer);
     });
 
-    document.querySelector('#recv-btn').addEventListener('click',  async () => {
-        let offer = JSON.parse(document.querySelector('#recv-text').value);
-        rtc = new RTCPeerConnection({
+    document.getElementById('recv-button').addEventListener('click',  async () => {
+        const pc = new RTCPeerConnection({
             // Recommended for libdatachannel
-            bundlePolicy: "max-bundle",
+            bundlePolicy: 'max-bundle',
         });
 
-        rtc.onicegatheringstatechange = (state) => {
-            if (rtc.iceGatheringState === 'complete') {
+        pc.onicegatheringstatechange = (state) => {
+            if (pc.iceGatheringState === 'complete') {
                 // We only want to provide an answer once all of our candidates have been added to the SDP.
-                let answer = rtc.localDescription;
-                document.querySelector('#recv-text').value = JSON.stringify({"type": answer.type, sdp: answer.sdp});
-                document.querySelector('#recv-help').value = 'Please paste the answer in the application.';
+                const answer = pc.localDescription;
+                document.getElementById('recv-text').value = JSON.stringify({
+                    type: answer.type,
+                    sdp: answer.sdp
+                });
+                document.getElementById('recv-help').value = 'Please paste the answer in the application.';
                 alert('Please paste the answer in the application.');
             }
         }
+
         let trackCount = 0;
-        rtc.ontrack = (ev) => {
-            let thisID = trackCount++;
+        pc.ontrack = (evt) => {
+            const id = trackCount++;
+            document.getElementById('videos').innerHTML += `<video width="100%" height="100%" id="video-${id}"></video>`;
 
-            document.querySelector("#videos").innerHTML += "<video width=100% height=100% id='video-" + thisID + "'></video>";
-            let tracks = [];
-            rtc.getReceivers().forEach(recv => tracks.push(recv.track));
-            document.querySelector("#video-" + thisID).srcObject = new MediaStream(tracks);
-            document.querySelector("#video-" + thisID).play();
+            const video = document.getElementById(`video-${id}`);
+			video.srcObject = evt.streams[0];
+            video.play();
         };
-        await rtc.setRemoteDescription(offer);
-        let answer = await rtc.createAnswer();
-        await rtc.setLocalDescription(answer);
+
+        const offer = JSON.parse(document.getElementById('recv-text').value);
+        await pc.setRemoteDescription(offer);
+
+        const answer = await pc.createAnswer();
+        await pc.setLocalDescription(answer);
     });
 </script>