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

Posted

技术标签:

【中文标题】如何修改正在进行的连接上的流以暂停/恢复流传输【英文标题】:How to modify stream on ongoing connection to pause/resume stream transmission 【发布时间】:2021-09-28 23:42:16 【问题描述】:

我最近开始为我的学校学习和构建一个应用程序,用于在我学校的服务器 (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>

提前致谢。

【问题讨论】:

@Julio Spinelli,期待您的帮助。 所以你也需要用 peerjs 进行单向调用吗?因为您当前的代码将使用两种方式流式传输。 【参考方案1】:

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

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

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

【讨论】:

以上是关于如何修改正在进行的连接上的流以暂停/恢复流传输的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS - 正在下载的流视频

Java IO学习笔记

bqplot可视化中的流数据

深入理解Java中的流---结合Hadoop进行详解

管道传输到可写流时暂停可读流

IO流输入输出流,字符字节流