JavaScript 展示获取视频流

<!DOCTYPE html>
<html>
<head>
    <!-- 设置文档使用的字符编码 -->
    <meta charset="UTF-8">
    <!-- 设置 viewport 元标签,以确保页面在移动设备上正确显示和缩放 -->
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <!-- 启用苹果移动设备的全屏模式 -->
    <meta name="apple-mobile-web-capable" content="yes">
    <!-- 页面标题 -->
    <title>展示获取视频流</title>
    <!-- 页面样式 -->
    <style>
        /* 设置视频元素样式 */
        #videoId {
            width: 512px;
            height: 256px;
        }

        /* 按钮样式 */
        .button-like-link {
            display: inline-block;
            padding: 10px 20px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            text-decoration: none;
            color: #333;
            cursor: pointer;
            border-radius: 4px; /* 添加圆角 */
            transition: background-color 0.3s, color 0.3s; /* 添加过渡效果 */
            font-size: 14px;
        }

        /* 鼠标悬停时的样式 */
        .button-like-link:hover {
            background-color: #e7e7e7;
            color: #000;
        }

        /* 禁用状态时的样式 */
        .button-like-link:disabled {
            background-color: #ccc;
            color: #999;
            cursor: not-allowed;
        }

    </style>
</head>
<body>
<!-- 控制按钮区域 -->
<div id="controls">
    <!-- 开始按钮 -->
    <button id="startId" class="button-like-link">开始</button>
    <!-- 停止按钮 -->
    <button id="stopId" class="button-like-link">停止</button>
    <!-- 下载按钮 -->
    <a id="downloadId" class="button-like-link" href="javascript:;">下载</a>
</div>
<!-- 视频播放区域 -->
<video controls autoplay="false" id="videoId"></video>
</body>

<script>
    let startButton = document.getElementById('startId');
    let stopButton = document.getElementById('stopId');
    let downloadButton = document.getElementById('downloadId');
    // 获取视频元素
    let video = document.getElementById('videoId');

    // 声明一个全局变量以便在不同的函数中访问媒体流对象
    let mediaStream;
    // 捕获媒体流
    let mediaRecorder;

    /**
     * 点击开始按钮时,先打开视频设备,然后获取视频设备的媒体流数据,然后收集媒体流数据
     * @returns {Promise<void>}
     */
    startButton.onclick = async () => {
        // 媒体约束对象,指定获取的媒体类型为音频和视频,视频宽高为理想值1280x720,使用用户前置摄像头,帧率为10到15
        let constraints = {
            audio: true,
            video: true
        };

        // 1. 打开视频设备,获取用户媒体设备的流对象
        mediaStream = await navigator.mediaDevices.getUserMedia(constraints);

        // 将获取到的媒体设备的流对象,赋值给视频元素的 srcObject 属性
        video.srcObject = mediaStream;
        // 当媒体元数据已加载时执行的回调函数,开始播放视频
        video.onloadedmetadata = () => {
            video.play();
        };

        startButton.style.background = "red";
        startButton.style.color = "black";

        // 2. 捕获媒体流
        mediaRecorder = new MediaRecorder(mediaStream);
        mediaRecorder.start();
        // console.log(mediaRecorder.state);
        // console.log("recorder started");

        // 3. 收集录制的数据
        let chunks = [];
        // 当关闭mediaRecorder.stop()时会触发这个事件
        mediaRecorder.ondataavailable = (event) => {
            chunks.push(event.data);
            // console.log(event.data)
            // console.log(chunks)

            // 测试,收集到的数据是否可以播放
            //     MP4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件
            //     WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件
            //     Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件
            //
            //     格式   MIME-type
            //     MP4  video/mp4
            //     WebM video/webm
            //     Ogg  video/ogg
            let blob = new Blob(chunks, {type: "video/ogg; codecs=opus"});
            let url = URL.createObjectURL(blob);
            video.src = url;
            // 更新下载按钮的 href 属性,以便用户可以下载视频
            downloadButton.href = url;
            downloadButton.download = `${new Date().toLocaleString()}_recorded_video.mp4`;
            downloadButton.disabled = false; // 启用下载按钮
        };
    }


    /**
     * 停止捕获视频流
     */
    stopButton.onclick = () => {
        if (mediaStream) {
            // 停止所有媒体流上的轨道
            mediaStream.getTracks().forEach(track => {
                track.stop();
            });

            // 停止媒体流的捕获
            mediaRecorder.stop();
            // console.log(mediaRecorder.state);

            // 清除视频元素的 srcObject 属性
            video.srcObject = null;

            startButton.style.background = "";
            startButton.style.color = "";
        }
    }
</script>

</html>

测试验证媒体流传输

前端

关注后解锁

服务端

关注后解锁

引入ASR模型

修改服务端

关注后解锁

引入ASR模型-增量翻译

修改前端

关注后解锁


长按录音

关注后解锁


长按录音-基于模型适配服务端

关注后解锁


以上都是前置条件与基础知识的储备,接下来才是真正的基于Web实时通信实现的实时流媒体传输

WebRTC全称是Web Real-Time Communication网页即时通信

WebRTC在2011年6月1日开源,并在Google、Mozilla、Opera等各家巨头公司的支持下被纳入W3C 推荐标准,给浏览器和移动应用提供了即时通信的能力。

优势

  • 跨平台(Web、Windows、MacOS、Linux、iOS、Android)
  • 实时传输
  • 音视频引擎
  • 免费、免插件、免安装
  • 主流浏览器支持

应用场景

  • 音视频会议
  • 即时通讯工具 IM
  • 直播
  • 共享远程桌面
  • 等等

RTCPeerConnection

使用WebRTC实现实时通信最核心的API就是RTCPeerConnection,它代表一个由本地计算机到远端的WebRTC连接,该接口提供了创建、保持、监控及关闭连接的方法的实现,有点类似于socket

相关的API

  • createOffer 创建Offer方法
  • setLocalDescription 设置本地SDP描述信息
  • peer.onicecandidate 设置完本地SDP描述信息后会触发该方法,打开一个连接,开始运转媒体流
  • setRemoteDescription 设置远端的SDP描述信息,由本地发送
  • peer.ontrack 设置完远端SDP描述信息后会触发该方法,接收对方的媒体流
  • createAnswer 远端创建应答Answer方法
  • RTCIceCandidate RTC网络信息,IP、端口等
  • addIceCandidate 连接添加对方的网络信息
分类: 人工智能

毛巳煜

高级软件开发全栈架构师