国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一小時教你用SpringBoot+WebSocket+WebRTC實(shí)現(xiàn)視頻通話

這篇具有很好參考價(jià)值的文章主要介紹了一小時教你用SpringBoot+WebSocket+WebRTC實(shí)現(xiàn)視頻通話。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1. 運(yùn)行結(jié)果

SpringBoot+WebSocket+WebRTC實(shí)現(xiàn)視頻通話

上述運(yùn)行結(jié)果中是有聲音(比較小而已)及動的畫面的(畫面不是靜止的)。

網(wǎng)上關(guān)于webrtc的文檔(文章)和視頻也挺多的,但是用SpringBoot結(jié)合WebRTC的卻屈指可數(shù),前一段時間小編我學(xué)習(xí)了一下WebRTC的相關(guān)知識,于是用SpringBoot+WebRTC實(shí)現(xiàn)了一個多人的線上自習(xí)室(有畫面,但是沒有聲音的那種,開啟聲音也挺簡單,在js代碼里設(shè)置一下即可[運(yùn)行結(jié)果在最后的總結(jié)里])。最近CSDN有活動,正好把前一段時間學(xué)習(xí)的知識運(yùn)用起來(下述代碼只是實(shí)現(xiàn)了,但是其中的邏輯是存在一定問題的,所以如果讀者用下述代碼,切記需要改動改動哈!)。既然是WebRTC,為什么又和WebSocket扯上關(guān)系了呢?因?yàn)槔肳ebSocket技術(shù)來發(fā)送消息具有實(shí)時性,你看我在這端發(fā)送一個消息出去,只要另一端處于連接狀態(tài),那么就可以接收到這個消息。而如果使用的是http、https等的話,這一端你發(fā)送一個消息,另外一段需要刷新一下頁面才能看到消息(當(dāng)然可以搞個定時器)。結(jié)合WebSocket技術(shù),能很快速地實(shí)現(xiàn)一個視頻通話功能。

2. 實(shí)現(xiàn)

導(dǎo)入相關(guān)jar包的依賴,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

上述jar包可能有一些不需要的喔!

2.1 后端實(shí)現(xiàn)

websocket 配置類
GetHttpSessionConfig.class

package com.example.demo.websocket2;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {

        HttpSession httpSession = (HttpSession) request.getHttpSession();

        // 獲取httpsession對象

        sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
    }
}

ServerEndpointExporter Bean的定義 Config.class

package com.example.demo.websocket2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class Config {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();
    }
}

*websocket服務(wù)器類 WebSocketServer *

package com.example.demo.websocket2;

import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint(value = "/video",configurator = GetHttpSessionConfig.class)
public class WebSocketServer {

    //存儲客戶端的連接對象,每個客戶端連接都會產(chǎn)生一個連接對象
    private static ConcurrentHashMap<String,WebSocketServer> map = new ConcurrentHashMap();
    //每個連接都會有自己的會話
    private Session session;
    private String account;

    @OnOpen
    public void open(Session session,EndpointConfig config){

        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String account = String.valueOf(httpSession.getAttribute("account"));

        map.put(account,this);

        this.session = session;
        this.account = account;
    }

    @OnClose
    public void close(){
        map.remove(account);
    }

    @OnError
    public void error(Throwable error){
        error.printStackTrace();
    }

    @OnMessage
    public void getMessage(String message) throws IOException {

        Set<Map.Entry<String, WebSocketServer>> entries = map.entrySet();
        for (Map.Entry<String, WebSocketServer> entry : entries) {
            if(!entry.getKey().equals(account)){//將消息轉(zhuǎn)發(fā)到其他非自身客戶端
                entry.getValue().send(message);
            }
        }
    }

    public void send(String message) throws IOException {
        if(session.isOpen()){
            session.getBasicRemote().sendText(message);
        }
    }

    public int  getConnetNum(){
        return map.size();
    }
}

2.2 前端頁面實(shí)現(xiàn)

登錄界面的代碼就不在這兒粘貼了,下面主要展示視頻通話界面的代碼(包括css樣式和js代碼都在的)

<html>

<head>
    <title>main</title>
    <link rel = "stylesheet" href = "https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap-theme.min.css"/>

</head>

<style>

    body {
        background: #eee;
        padding: 5% 0;
    }

    video {
        background: black;
        border: 1px solid gray;
    }

    .call-page {
        position: relative;
        display: block;
        margin: 0 auto;
        width: 500px;
        height: 500px;
    }

    #localVideo {
        width: 150px;
        height: 150px;
        position: absolute;
        top: 15px;
        right: 15px;
    }

    #remoteVideo {
        width: 500px;
        height: 500px;
    }

</style>

<body>

<div id = "callPage" class = "call-page">
    <video id = "localVideo" autoplay></video>
    <video id = "remoteVideo" autoplay></video>

    <div class = "row text-center">
        <div class = "col-md-12">
            <input id = "callToUsernameInput" type = "text"
                   placeholder = "username to call" />
            <button id = "callBtn" class = "btn-success btn">Call</button>
            <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button>
        </div>
    </div>

</div>

<script type="text/javascript">
    //our username
    var connectedUser;

    //connecting to our signaling server
    var conn = new WebSocket("ws://localhost:9999/video");

    conn.onopen = function () {
        console.log("Connected to the signaling server");
    };

    //when we got a message from a signaling server
    conn.onmessage = function (msg) {
        console.log("Got message", msg.data);

        var data = JSON.parse(msg.data);

        switch(data.type) {
            case "login":
                handleLogin(data.success);
                break;
            //when somebody wants to call us
            case "offer":
                handleOffer(data.offer, data.name);
                break;
            case "answer":
                handleAnswer(data.answer);
                break;
            //when a remote peer sends an ice candidate to us
            case "candidate":
                handleCandidate(data.candidate);
                break;
            case "leave":
                handleLeave();
                break;
            default:
                break;
        }
    };

    conn.onerror = function (err) {
        console.log("Got error", err);
    };

    //alias for sending JSON encoded messages
    function send(message) {
        //attach the other peer username to our messages
        if (connectedUser) {
            message.name = connectedUser;
        }

        conn.send(JSON.stringify(message));
    }

    //******
    //UI selectors block
    //******


    var callPage = document.querySelector("#callPage");
    var callToUsernameInput = document.querySelector("#callToUsernameInput");
    var callBtn = document.querySelector("#callBtn");

    var hangUpBtn = document.querySelector("#hangUpBtn");

    var localVideo = document.querySelector("#localVideo");
    var remoteVideo = document.querySelector("#remoteVideo");

    var yourConn;
    var stream;

    // callPage.style.display = "none";

    var PeerConnection = (window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection || undefined);
    var RTCSessionDescription = (window.webkitRTCSessionDescription || window.mozRTCSessionDescription || window.RTCSessionDescription || undefined);

    navigator.getUserMedia = (navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia);
    //**********************
    //Starting a peer connection
    //**********************

    //getting local video stream
    navigator.getUserMedia({ video: true, audio: true }, function (myStream) {
        stream = myStream;

        //displaying local video stream on the page
        localVideo.srcObject = stream;

        //using Google public stun server
        var configuration = {
            "iceServers": []
        };

        yourConn = new PeerConnection(configuration);

        // setup stream listening
        yourConn.addStream(stream);

        //when a remote user adds stream to the peer connection, we display it
        yourConn.onaddstream = function (e) {
            remoteVideo.srcObject = e.stream;
        };

        // Setup ice handling
        yourConn.onicecandidate = function (event) {
            if (event.candidate) {
                send({
                    type: "candidate",
                    candidate: event.candidate
                });
            }
        };

    }, function (error) {
        console.log(error);
    });


    //initiating a call
    callBtn.addEventListener("click", function () {
        var callToUsername = callToUsernameInput.value;

        if (callToUsername.length > 0) {

            connectedUser = callToUsername;

            // create an offer
            yourConn.createOffer(function (offer) {
                send({
                    type: "offer",
                    offer: offer
                });

                yourConn.setLocalDescription(offer);
            }, function (error) {
                alert("Error when creating an offer");
            });

        }
    });

    //when somebody sends us an offer
    function handleOffer(offer, name) {
        connectedUser = name;
        yourConn.setRemoteDescription(new RTCSessionDescription(offer));

        //create an answer to an offer
        yourConn.createAnswer(function (answer) {
            yourConn.setLocalDescription(answer);

            send({
                type: "answer",
                answer: answer
            });

        }, function (error) {
            alert("Error when creating an answer");
        });
    }

    //when we got an answer from a remote user
    function handleAnswer(answer) {
        yourConn.setRemoteDescription(new RTCSessionDescription(answer));
    }

    //when we got an ice candidate from a remote user
    function handleCandidate(candidate) {
        yourConn.addIceCandidate(new RTCIceCandidate(candidate));
    }

    //hang up
    hangUpBtn.addEventListener("click", function () {

        send({
            type: "leave"
        });

        handleLeave();
    });

    function handleLeave() {
        connectedUser = null;
        remoteVideo.src = null;

        yourConn.close();
        yourConn.onicecandidate = null;
        yourConn.onaddstream = null;
    }
</script>

</body>

</html>
3. 總結(jié)

上述前端代碼參考來自這里:webrtc視頻演示,上述代碼中如果有不懂的讀者可以去仔細(xì)看看這個鏈接里的知識,里面關(guān)于webrtc有詳細(xì)的介紹及實(shí)現(xiàn),不過,沒有講多人的,它只講了一對一的,不過,前一段時間小編在參考一些大佬的實(shí)現(xiàn)思路及自己思考下,也實(shí)現(xiàn)了一個多人的,運(yùn)行結(jié)果如下:

基于SpringBoot,WebSocket,WebRTC實(shí)現(xiàn)多人自習(xí)室功能文章來源地址http://www.zghlxwxcb.cn/news/detail-496836.html

到了這里,關(guān)于一小時教你用SpringBoot+WebSocket+WebRTC實(shí)現(xiàn)視頻通話的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 手把手教你用 Jenkins 自動部署 SpringBoot

    手把手教你用 Jenkins 自動部署 SpringBoot

    CI/CD 是一種通過在應(yīng)用開發(fā)階段引入自動化來頻繁向客戶交付應(yīng)用的方法。 CI/CD 的核心概念可以總結(jié)為三點(diǎn): 持續(xù)集成 持續(xù)交付 持續(xù)部署 CI/CD 主要針對在集成新代碼時所引發(fā)的問題(俗稱\\\"集成地獄\\\")。 為什么會有集成地獄這個“雅稱”呢?大家想想我們一個項(xiàng)目部署的

    2024年02月02日
    瀏覽(30)
  • 短視頻ks(某手)高版本最新抓包方案,教你用hook大法繞過QUIC協(xié)議

    一般大多數(shù)網(wǎng)站、APP最常用的是http、https協(xié)議,而某兩款最火的短視頻dy(某音)、ks(某手)最新版使用的是quic協(xié)議(見附錄1),導(dǎo)致fiddler和charles無法直接抓到包(某手7版本以下可以直接抓到包)。 網(wǎng)上有說用fiddler + xposed + justTrustMe能繞過某音的sslpinning,呵呵,別傻了

    2023年04月11日
    瀏覽(145)
  • websocket 局域網(wǎng) webrtc 一對一 多對多 視頻通話 的示例

    websocket 局域網(wǎng) webrtc 一對一 多對多 視頻通話 的示例

    基本介紹 WebRTC(Web Real-Time Communications)是一項(xiàng)實(shí)時通訊技術(shù),它允許網(wǎng)絡(luò)應(yīng)用或者站點(diǎn),在不借助中間媒介的情況下,建立瀏覽器之間點(diǎn)對點(diǎn)(Peer-to-Peer)的連接,實(shí)現(xiàn)視頻流和(或)音頻流或者其他任意數(shù)據(jù)的傳輸。WebRTC 包含的這些標(biāo)準(zhǔn)使用戶在無需安裝任何插件或者第

    2024年04月28日
    瀏覽(30)
  • 教你用JavaScript實(shí)現(xiàn)鍵盤控制小方塊移動

    教你用JavaScript實(shí)現(xiàn)鍵盤控制小方塊移動

    歡迎來的我的小院,我是霍大俠,恭喜你今天又要進(jìn)步一點(diǎn)點(diǎn)了! 我們來用JavaScript編程實(shí)戰(zhàn)案例,做一個鍵盤控制小方塊移動的案例。該案例主要實(shí)現(xiàn)通過按下鍵盤的上下左右按鈕來控制小方塊在頁面中的移動。通過實(shí)戰(zhàn)我們會學(xué)習(xí)到position定位,鍵盤監(jiān)聽事件以及動態(tài)給

    2024年02月09日
    瀏覽(24)
  • Vue中webSocket+webRtc實(shí)現(xiàn)多人會議,webRtc實(shí)現(xiàn)

    Vue中webSocket+webRtc實(shí)現(xiàn)多人會議,webRtc實(shí)現(xiàn)

    已經(jīng)搭建好 websocket 雙端通信(可以先模擬),用于實(shí)時交換雙方信息。交換的信息也就是所謂的信令。實(shí)現(xiàn) webRtc 進(jìn)行多人會議,屏幕共享、攝像頭共享。 我這里定義的websocket信息格式如下 發(fā)給某個人,下面會用【消息格式one】指代 發(fā)給會議中所有人,下面會用【消息格式

    2024年04月23日
    瀏覽(22)
  • 可視化 | 教你用Python實(shí)現(xiàn)熱力圖(一)

    可視化 | 教你用Python實(shí)現(xiàn)熱力圖(一)

    本文正在參與新星計(jì)劃Python學(xué)習(xí)方向,詳情請看:(93條消息) lifein的博客_CSDN博客-SQL SERVER,計(jì)算機(jī)三級——數(shù)據(jù)庫領(lǐng)域博主 目錄 一、導(dǎo)引 二、內(nèi)容 (一)地圖熱力圖:(動態(tài)地圖) 1、環(huán)境搭建: 2、地圖代碼:(原始) ? ? ? ? 在可視化中,熱力圖可以使用顏色深淺

    2024年02月05日
    瀏覽(28)
  • vue+flv.js+SpringBoot+websocket實(shí)現(xiàn)視頻監(jiān)控與回放

    vue+flv.js+SpringBoot+websocket實(shí)現(xiàn)視頻監(jiān)控與回放

    需求:vue+springboot的項(xiàng)目,需要在頁面展示出海康的硬盤錄像機(jī)連接的攝像頭的實(shí)時監(jiān)控畫面以及回放功能. 之前項(xiàng)目里是純前端實(shí)現(xiàn)視頻監(jiān)控和回放功能.但是有局限性.就是ip地址必須固定.新的需求里設(shè)備ip不固定.所以必須換一種思路. 通過設(shè)備的主動注冊,讓設(shè)備去主動連接服

    2024年02月02日
    瀏覽(25)
  • 手把手教你用Python實(shí)現(xiàn)2048小游戲

    手把手教你用Python實(shí)現(xiàn)2048小游戲

    感覺好久沒有寫小游戲玩了,今天恰巧有空.這次我來用Python做個2048小游戲吧.廢話不多說,文中有非常詳細(xì)的代碼示例,需要的朋友可以參考下 目錄 一、開發(fā)環(huán)境 二、環(huán)境搭建 三、原理介紹 四、效果圖 Python版本:3.6.4 相關(guān)模塊: pygame模塊; 以及一些Python自帶的模塊。 安裝

    2024年04月28日
    瀏覽(94)
  • 一文3000字教你用Python + Jmeter 實(shí)現(xiàn)自動化性能壓測

    一文3000字教你用Python + Jmeter 實(shí)現(xiàn)自動化性能壓測

    Step01: Python腳本開發(fā) 文件路徑: D://wl//testproject//Fone-grpc//project1//test_client.py Python 腳本作用: 通過 grpc 調(diào)用底層 c++ 的接口,做數(shù)據(jù)庫的數(shù)據(jù)插入與查詢操作,然后將返回的結(jié)果進(jìn)行拼接與輸出。 2. 代碼里面將每一次調(diào)用后返回的內(nèi)容進(jìn)行拼接后,并做了成功信息的統(tǒng)計(jì),輸

    2024年02月02日
    瀏覽(30)
  • 【源碼可分享】教你用Python制作自動答題腳本,實(shí)現(xiàn)自動答題,100%正確率!

    【源碼可分享】教你用Python制作自動答題腳本,實(shí)現(xiàn)自動答題,100%正確率!

    當(dāng)今社會,人們的生活越來越依賴于計(jì)算機(jī)技術(shù),而Python作為一種高級編程語言,已經(jīng)成為了眾多程序員的首選語言。Python具有簡單易學(xué)、代碼簡潔、可讀性強(qiáng)等特點(diǎn),因此在各個領(lǐng)域都有廣泛的應(yīng)用。其中,自動化腳本是Python的一個重要應(yīng)用領(lǐng)域之一。本文將介紹如何使用

    2024年02月16日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包