如何修改正在进行的连接上的流以暂停/恢复流传输

问题描述 投票:0回答:2

我最近开始为我的学校学习和构建一个应用程序,用于在我学校的服务器 (VPS) 上使用 WebRTC 和 PeerJs 进行远程在线课程。到目前为止,我能够建立 1 对 1 对等连接,但很难暂停和恢复流传输。

我正在寻求一些有关如何在活动连接时为自己和远程用户暂停和恢复视频和音频流的帮助。当我执行

localStream.getVideoTracks()[0].enabled = false
时,它只会为我禁用视频(不适用于远程用户)。

正如一些人建议的replaceTrack API,但我并不是无法找到有关它的教程,因为我是新手。

我的代码(感谢Link)如下所示:

var url = new URL(window.location.href)
var disableStreamInBeginning = url.searchParams.get("disableStreamInBeginning")
var passwordProtectedRoom = url.searchParams.get("passwordProtectedRoom")
var muteAllInBeginning = url.searchParams.get("muteAllInBeginning")
var isVideoCall = url.searchParams.get("isVideoCall")
var singleOrConference = url.searchParams.get("singleOrConference")

const conferenceView = document.getElementById('conference')
const loader = document.getElementById('loader')
const localVideoView = document.getElementById('local-video')
const remoteVideoView = document.getElementById('remote-video')
const remoteVideoDiv = document.getElementById('remote-video-div')
if(typeof disableStreamInBeginning !== 'undefined' && disableStreamInBeginning == 'true'){
    var disbaleSelfStream = true
} else {
    var disbaleSelfStream = false
}
if(typeof passwordProtectedRoom !== 'undefined' && passwordProtectedRoom == 'true'){
    var passwordProtected = true
} else {
    var passwordProtected = false
}
if(typeof muteAllInBeginning !== 'undefined' && muteAllInBeginning == 'true'){
    var muteAll = true
} else {
    var muteAll = false
}
if(typeof isVideoCall !== 'undefined' && isVideoCall == 'true'){
    var videoCall = true
} else {
    var videoCall = false
}
if(typeof singleOrConference !== 'undefined' && singleOrConference == 'conference'){
    var isConference = true
    conferenceView.style.display = 'block'
} else {
    var isConference = false
    localVideoView.style.opacity = 0
    remoteVideoView.style.opacity = 0
    remoteVideoDiv.style.opacity = 0
}

var selectedCamera = 'user'
let localStream;

const socket = io('/');
localVideoView.muted = true;
const peers = {};
const peer = new Peer(undefined, {
    host: '/',
    port: '443',
    path: '/myapp',
    secure: true
})

// Handelling incoming call connection
peer.on("call", async (call) => {
    let stream = null;
    try {
        stream = await navigator.mediaDevices.getUserMedia(
            {
                video: {
                    facingMode: selectedCamera
                },
                audio: true
            });
        call.answer(stream);
        call.on("stream", (remoteVideoStream) => {
            addVideoStream(remoteVideoView, remoteVideoStream);
        });
    } catch (err) {
        console.log('peer.on("call": ' + err);
    };
});

// On new user connected
socket.on("user-connected", async (userId) => {
    connectDataToNewUser(userId);
    try {
        stream = await navigator.mediaDevices.getUserMedia(
            {
                audio: true,
                video: true,
            })
    }
    catch (err) {
        console.log('socket.on("user-connected": ' + err);
    };
    connectMediaToNewUser(userId, stream);
});

// Show  own Video on own device screen
(async () => {
    try {
        localStream= await navigator.mediaDevices.getUserMedia(
            {
                video: {
                    facingMode: selectedCamera
                },
                audio: true
            });
            addVideoStream(localVideoView, localStream);
    } catch (err) {
        console.log('(async () =>: ' + err);
    }
})();

peer.on("open", (id) => {
    socket.emit("join-room", ROOM_ID, id);
});

peer.on("error", (err) => {
    console.log('peer.on("error": ' + err);
})

socket.on("user-disconnected", (userId) => {
    if (peers[userId]) {
        peers[userId].close();
    }
});

// Set up event listener for an "another user" data connection established event
peer.on("connection", (conn) => {
    conn.on("data", (data) => {
        console.log('Received data ' + data);
    });
    // Set up event listener for connection conn established event
    conn.on("open", () => {
        conn.send('Hello!');
    });
});

// Initiate a Data call (Messages) to user
const connectDataToNewUser = (userId) => {
    let conn = peer.connect(userId);
    conn.on("data", (data) => {
        console.log('Received data: ' + data);
    });
    conn.on("open", () => {
        conn.send('hi!');
    });
};

// Initiate a Media call (Audio/Video) to user
const connectMediaToNewUser = (userId, stream) => {
    const call = peer.call(userId, stream);
    call.on("stream", (userVideoStream) => {
        addVideoStream(remoteVideoView, userVideoStream);
    });
    call.on("close", () => {
        remoteVideoView.remove();
    });
    call.on("error", (error) => {
        console.log('connectMediaToNewUser' + error);
    });
    peers[userId] = call;
};

const addVideoStream = (video, stream) => {
    video.srcObject = stream;
    video.addEventListener("loadedmetadata", () => {
        if(disbaleSelfStream){
            systemStream.getVideoTracks()[0].enabled = false
            systemStream.getAudioTracks()[0].enabled = false
        } else {
            loader.style.opacity = 0
            video.style.opacity = 1
            video.play()
            remoteVideoDiv.style.opacity = 0
        }
    });
};

服务器端代码:

const express = require('express')
const app = express()
const httpPort = process.env.PORT || 80
const httpsPort = 443
const { ExpressPeerServer } = require('peer')
const path = require('path')
const http = require('http')
const https = require('https')
const fs = require('fs')

// Certificate & credentials
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'key.pem'))
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'cert.pem'))
const credentials = {
    key: privateKey,
    cert: certificate
}

const httpsServer = https.createServer(credentials, app).listen(httpsPort, () => { console.log('Peer Server listening to port ' + httpsPort) })

const peerServer = ExpressPeerServer(httpsServer, {
        debug: true,
        path: '/myapp'
})

app.use(peerServer)

const io = require('socket.io')(httpsServer, {
   forceNew: true,
   transports: ["polling"],
})
const { v4: uuidV4 } = require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
  res.redirect(`/${uuidV4()}`)
})

app.get('/:room', (req, res) => {
  res.render('room', { roomId: req.params.room })
})

io.on('connection', (socket) => {
    socket.on('join-room', (roomId, userId) => {
        socket.join(roomId)
        socket.broadcast.to(roomId).emit('user-connected', userId)

        socket.on('disconnect', () => {
            socket.broadcast.to(roomId).emit('user-disconnected', userId)
        })
        socket.on('text-message', message => {
            socket.broadcast.to(roomId).emit('text-message-received', message)
        })
        socket.on('system-stream-updated', remoteUserId => {
            socket.broadcast.to(roomId).emit('new-remote-stream', remoteUserId)
        })
    })
})

还有 room.ejs(如果需要)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script>
    const ROOM_ID = "<%= roomId %>"
  </script>
  <script src="peer.min.js" defer></script>
  <script src="/socket.io/socket.io.js" defer></script>
  <script src="client.js" defer></script>
  <title>Interface</title>
  <style type="text/css">
        html, body {
            padding: 0;
            margin: 0;
        }
        .container, .local-video {
            position: absolute;
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
        .remote-video-div {
            position: absolute;
            max-width: 30%;
            width: 30%;
            margin: 16px;
        }
        .remote-video {
            max-width: 100%;
            width: 100%;
            margin-bottom: -5px;
        }
        .video-inset {
            outline: unset;
            visibility: hidden;
            position: relative;
            margin:0; 
            padding:0; 
        }
        
        .background-black {
            background-color: #000000 !important;
        }
        
        .display-none {
            display: none;
        }
        
        .loader {
            margin: 250px auto;
            border: 7px solid #9e9c9c;
            border-radius: 50%;
            border-top: 7px solid #ffffff;
            width: 40px;
            height: 40px;
            -webkit-animation: spin 2s linear infinite; /* Safari */
            animation: spin 2s linear infinite;
        /* Safari */
        @-webkit-keyframes spin {
            0% { -webkit-transform: rotate(0deg); }
            100% { -webkit-transform: rotate(360deg); }
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
  </style>
  <link rel="icon" type="image/ico" href="favicon.ico"/>
</head>
<body>
    <div class="local-video-div background-black">
        <video class="local-video" autoplay></video>
    </div>
    <div class="container background-black display-none" id="loader">
        <div class="loader"></div>
    </div>
    <div class="remote-video-div background-black">
        <video class="remote-video" autoplay onclick="remoteVideoClick()"></video>
    </div>
    <div class="container background-black display-none" id="conference">
    </div>
</body>
</html>

提前致谢。

socket.io webrtc peerjs
2个回答
1
投票

我遇到了同样的问题,正在寻找解决方案。这很有趣,因为你的问题本身解决了我的问题!我这样做了...

 <button
        onClick={() =>
          (stream.getVideoTracks()[0].enabled =
            !stream.getVideoTracks()[0].enabled)
        }
  >

顺便说一下,你可能已经注意到(从“onClick”)我正在反应上这样做。但是,就您而言,我认为您应该首先从设备获取视频流并将其设置为变量。然后使用该流变量调用用户,当您需要时,您可以通过执行stream.getVideoTracks()[0].enabled = !stream.getVideoTracks()[0].enabled 来停止和启动流式传输。但是,请确保您拥有该流的一个真实来源,并始终从那里引用它。我这样做是为了做出反应,它对我有用。希望它也适合你!


0
投票

麦克风+系统声音包括暂停/恢复

import React, { useState } from "react";

let recorder;
var audioRecord;
let chunks = [];
let isPaused = false;

const App = () => {
  const [attachment, setAttachment] = useState("");

  const onScreen = () => {
    navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
      if (!MediaRecorder.isTypeSupported("audio/webm"))
        return alert("Browser not supported");
      audioRecord = new MediaRecorder(stream, {
        mimeType: "audio/webm",
      });
      navigator.mediaDevices
        .getDisplayMedia({
          video: true,
          preferCurrentTab: true,
        })
        .then((screenStream) => {
          let combinedStream = new MediaStream([
            ...screenStream.getTracks(),
            ...stream.getTracks(),
          ]);
          recorder = new MediaRecorder(combinedStream);
          recorder.start();
          recorder.ondataavailable = (event) => {
            chunks.push(event.data);
          };
        })
        .catch((err) => console.log(err));
    });
  };

  function downloadVideo() {
    console.log("download");
    if (recorder && recorder.state !== "inactive") {
      recorder.stop();
      recorder.stream.getTracks().forEach((track) => track.stop());
      audioRecord.stop();
      audioRecord.stream.getTracks().forEach((track) => track.stop());
      recorder.stop();
      recorder.stream.getTracks().forEach((track) => track.stop());
      recorder.onstop = (videoBlob) => {
        let file = new File(chunks, `video.mp4`, {
          type: "video/mp4",
        });
        setAttachment(file);
        chunks = [];
        let url = URL.createObjectURL(file);
        console.log(file, "file");
      };
      recorder.ondataavailable = (event) => {
        chunks.push(event.data);
      };
    }
  }

  function pauseVideo() {
    if (recorder && recorder.state === "recording") {
      recorder.pause();
      isPaused = true;
    }
  }

  function resumeVideo() {
    if (recorder && recorder.state === "paused") {
      recorder.resume();
      isPaused = false;
    }
  }

  return (
    <>
      <div>
        <button onClick={onScreen}>Start</button>
        <button onClick={downloadVideo}>Download</button>
        <button onClick={pauseVideo}>Pause</button>
        <button onClick={resumeVideo}>Resume</button>
      </div>
    </>
  );
};

export default App;
© www.soinside.com 2019 - 2024. All rights reserved.