当前位置: 首页 > article >正文

web前端录制canvas视频和video的声音,并合并成一个文件进行下载

 一、captureStream

captureStream‌是一个Web API方法,用于捕获指定元素的媒体流。该方法通常用于从<video>、<audio>或<canvas>元素中捕获实时视频流或音频流,以便进行进一步的处理,如直播、录制或分析‌。

captureStream()方法能够实时捕获视频流,适用于直播、实时监控等场景。

它返回一个MediaStream对象,该对象包含了捕获的视频流数据。相比其他视频捕获方法,captureStream()提供了较低的延迟和更好的灵活性‌。

1、captureStream() 返回的 MediaStream 对象为空

  • 视频元素尚未加载完成,导致无法捕获视频流。确保视频元素已经加载完成,可以通过监听 loadedmetadatacanplay 事件来确认。
  • 视频元素的源路径不正确或无法访问。检查视频元素的源路径是否正确,并确保视频文件可以正常访问。
const videoElement = document.createElement('video');
videoElement.src = 'video.mp4';videoElement.addEventListener('loadedmetadata', () => {const mediaStream = videoElement.captureStream();if (mediaStream) {// 处理捕获的视频流} else {console.error('Failed to capture stream');}
});

2、处理 captureStream() 返回的 MediaStream 对象

  • 使用 MediaRecorder 将 MediaStream 对象录制为视频文件。
  • 使用 RTCPeerConnectionMediaStream 对象推送到 WebRTC 服务器,实现实时通信。
const mediaRecorder = new MediaRecorder(mediaStream);mediaRecorder.ondataavailable = (event) => {if (event.data.size > 0) {// 处理录制的视频数据}
};
mediaRecorder.start();

3、AudioContext对象

如果浏览器不支持在video、audio对象上使用captureStream获取流数据,可以考虑使用AudioContext对象进行声音数据获取。

const audioContext = new (window.AudioContext || window.webkitAudioContext)()
const source = audioContext.createMediaElementSource(videoElement)
const destination = audioContext.createMediaStreamDestination()
source.connect(destination)
const audioStream = destination.stream

二、MediaStream()

构造函数**MediaStream()** 返回新建的 MediaStream 实例,该实例作为媒体流的内容的集合载体,其可能包含多个媒体数据轨,每个数据轨则由一个 MediaStreamTrack 对象表示。如果给出相应参数,在指定的数据轨则被添加到新的流中。否则,该流中不包含任何数据轨。

newStream = new MediaStream();
newStream = new MediaStream(stream);
newStream = new MediaStream(tracks[]);

参数:

stream:这是另一个 MediaStream 对象,其数据轨会被自动添加到新建的流中。且这些数据轨不会从原流中移除,即变成了两条流共享的数据。

tracks:这是 MediaStreamTrack 对象的 Array 类型的成员,代表了每一个添加到流中的数据轨。

返回值:

新建的 MediaStream 对象,会包含创建时已给的数据轨内容,若没有给定任何数据轨则内容为空。

合并录制的视频流和音频流

let canvas = document.getElementById('canvas')
let stream = canvas.captureStream(30)
let video = document.getElementById('video')
let audioStream = video.captureStream(30)// 创建一个新的MediaStream,将视频跟音频流合并进去
let combinedStream = new MediaStream([ ...stream.getTracks(), ...audioStream.getAudioTracks()
]);

 

 三、录制Demo

// 判断是否是微信环境
export function isWeChatEnv() {const userAgent = navigator.userAgent.toLowerCase();return /micromessenger/.test(userAgent);
}
    let recorder = null; // 录制对象// 录制视频recordVideo(type) {console.error('录制视频')// 开始执行渲染canvas和播放video的代码逻辑、、、、、、this.recording = truelet canvas = document.getElementById('canvas')let stream = canvas.captureStream(30)// 获取音频上下文let audioStreamArr = []// let audioContextArr = []// let sourceArr = []for(let key in this.videoEles) {if(this.videoEles[key]?.captureStream) {const audioStream = this.videoEles[key].captureStream(30)audioStreamArr.push(audioStream)} else {const audioContext = new (window.AudioContext || window.webkitAudioContext)()const source = audioContext.createMediaElementSource(this.videoEles[key])const destination = audioContext.createMediaStreamDestination()source.connect(destination)const audioStream = destination.streamaudioStreamArr.push(audioStream)// const source = new MediaElementAudioSourceNode(audioContext, {//   mediaElement: this.videoEles[key]// })// const gainNode = new GainNode(audioContext)// source.connect(gainNode)// const destination = audioContext.createMediaStreamDestination()// gainNode.connect(destination)// const audioStream = destination.stream// audioContextArr.push(audioContext)// sourceArr.push(source)}}let audioChunks = []audioStreamArr.map(it => {audioChunks.push(...it.getAudioTracks())})// 创建一个新的MediaStream,将视频跟音频流合并进去let combinedStream = new MediaStream([ ...stream.getTracks(), ...audioChunks]);const userAgent = navigator.userAgent || navigator.vendorconsole.error('userAgent::', userAgent)let options = {}/*if ('MediaSource' in window && MediaSource.isTypeSupported('video/webm; codecs="vp9"')) {console.log('Your browser supports VP9 via MediaSource Extensions');} else {console.log('Your browser does NOT support VP9 via MediaSource Extensions');}*/// Androidif (userAgent.match(/Android/i)) {options = {audioBitsPerSecond: 128000,videoBitsPerSecond: 2500000,// mimeType: 'video/webm; codecs="vp8,opus"',mimeType: isWeChatEnv() ? 'video/webm; codecs="vp8,opus"' : 'video/mp4; codecs="avc1.64001e"',}} else if (/iPad|iPhone|iPod/.test(userAgent)) {options = {audioBitsPerSecond: 128000,videoBitsPerSecond: 2500000,mimeType: 'video/mp4; codecs="avc1"',}} else {options = {audioBitsPerSecond: 128000,videoBitsPerSecond: 2500000,// mimeType: 'video/webm; codecs="vp8"',mimeType: 'video/mp4; codecs="avc1"',}}// 初始化录制对象recorder = new MediaRecorder(combinedStream, options)let recordData = []// 收集录制数据recorder.ondataavailable = function(event) {if(event.data && event.data.size) {recordData.push(event.data)}}// 监听录制结束事件recorder.onstop = async () => {console.log('recorder onstop')let  blobType = 'video/mp4'let url = URL.createObjectURL(new Blob(recordData, {type: blobType}))console.log(url)// const a = document.createElement('a');// a.href = url;// a.download = 'recording.webm';// a.click();// URL.revokeObjectURL(url);const time = new Date().toJSON()if (window.navigator && window.navigator?.msSaveOrOpenBlob) {window.navigator.msSaveBlob(url, `${'recording'}_${time}.mp4`);console.log('IOS下载', url)} else {console.log('安卓下载', url)const link = document.createElement("a");link.style.display = "none";link.href = url;// link.target = '_blank';link.setAttribute("download", `${'recording'}_${time}.mp4`);document.body.appendChild(link);link.click();document.body.removeChild(link);}}recorder.start(10000)}// 结束录制recorder.stop()

相关文章:

web前端录制canvas视频和video的声音,并合并成一个文件进行下载

一、captureStream ‌captureStream‌是一个Web API方法&#xff0c;用于捕获指定元素的媒体流。该方法通常用于从<video>、<audio>或<canvas>元素中捕获实时视频流或音频流&#xff0c;以便进行进一步的处理&#xff0c;如直播、录制或分析‌。 captureStr…...

Golang 并发机制-7:sync.Once实战应用指南

Go的并发模型是其突出的特性之一&#xff0c;但强大的功能也带来了巨大的责任。sync.Once是由Go的sync包提供的同步原语。它的目的是确保一段代码只执行一次&#xff0c;而不管有多少协程试图执行它。这听起来可能很简单&#xff0c;但它改变了并发环境中管理一次性操作的规则。…...

【AI实践】Cursor上手-跑通Hello World和时间管理功能

背景 学习目的&#xff1a;熟悉Cursor使用环境&#xff0c;跑通基本开发链路。 本人背景&#xff1a;安卓开发不熟悉&#xff0c;了解科技软硬件常识 实践 基础操作 1&#xff0c;下载安装安卓Android Studio 创建一个empty project 工程&#xff0c;名称为helloworld 2&am…...

深度学习 视频推荐

以下为你呈现一个基于深度学习实现视频推荐的简化代码示例。这里我们使用的是协同过滤思想结合神经网络的方式,借助 TensorFlow 和 Keras 库来构建模型。在这个示例中,假设已有用户对视频的评分数据,目标是预测用户对未评分视频的评分,进而为用户推荐可能感兴趣的视频。 1…...

缓存组件<keep-alive>

缓存组件<keep-alive> 1.组件作用 组件, 默认会缓存内部的所有组件实例&#xff0c;当组件需要缓存时首先考虑使用此组件。 2.使用场景 场景1&#xff1a;tab切换时&#xff0c;对应的组件保持原状态&#xff0c;使用keep-alive组件 使用&#xff1a;KeepAlive | Vu…...

SpringBoot单机模式的极限是什么?为什么会引入分布式?

Spring Boot 单机模式的极限 Spring Boot 单机模式的极限主要体现在以下几个方面&#xff1a; 硬件资源限制&#xff1a; CPU&#xff1a;单机性能受限于 CPU 核心数和主频&#xff0c;无法无限扩展。内存&#xff1a;内存容量有限&#xff0c;无法应对大规模数据处理或高并发…...

【多模态大模型】系列4:目标检测(ViLD、GLIP)

目录 1 ViLD2 GLIP 1 ViLD OPEN-VOCABULARY OBJECT DETECTION VIA VISION AND LANGUAGE KNOWLEDGE DISTILLATION 从标题就能看出来&#xff0c;作者是把CLIP模型当成一个Teacher&#xff0c;去蒸馏他自己的网络&#xff0c;从而能Zero Shot去做目标检测。 现在的目标检测数据…...

计算机网络结课设计:通过思科Cisco进行中小型校园网搭建

上学期计算机网络课程的结课设计是使用思科模拟器搭建一个中小型校园网&#xff0c;当时花了几天时间查阅相关博客总算是做出来了&#xff0c;在验收后一直没管&#xff0c;在寒假想起来了简单分享一下&#xff0c;希望可以给有需求的小伙伴一些帮助 目录 一、设计要求 二、…...

从零到一:基于Rook构建云原生Ceph存储的全面指南(下)

接上篇&#xff1a;《从零到一&#xff1a;基于Rook构建云原生Ceph存储的全面指南&#xff08;上&#xff09;》 链接: link 六.Rook部署云原生CephFS文件系统 6.1 部署cephfs storageclass cephfs文件系统与RBD服务类似&#xff0c;要想在kubernetes pod里使用cephfs&#…...

mysql的语句备份详解

使用mysqldump工具备份&#xff08;适用于逻辑备份&#xff09; mysqldump是 MySQL 自带的一个非常实用的逻辑备份工具&#xff0c;它可以将数据库中的数据和结构以 SQL 语句的形式导出到文件中。 1. 备份整个数据库 mysqldump -u [用户名] -p [数据库名] > [备份文件名].…...

AutoMQ 如何实现没有写性能劣化的极致冷读效率

前言 追赶读&#xff08;Catch-up Read&#xff0c;冷读&#xff09;是消息和流系统常见和重要的场景。 削峰填谷&#xff1a;对于消息来说&#xff0c;消息通常用作业务间的解耦和削峰填谷。削峰填谷要求消息队列能将上游发送的数据堆积住&#xff0c;让下游在容量范围内消费…...

【Rabbitmq篇】高级特性----TTL,死信队列,延迟队列

目录 一.TTL ???1.设置消息的TTL 2.设置队列的TTL 3.俩者区别? 二.死信队列 定义&#xff1a; 消息成为死信的原因&#xff1a; 1.消息被拒绝&#xff08;basic.reject 或 basic.nack&#xff09; 2.消息过期&#xff08;TTL&#xff09; 3.队列达到最大长度? …...

【Java】多线程和高并发编程(三):锁(中)深入ReentrantLock

文章目录 3、深入ReentrantLock3.1 ReentrantLock和synchronized的区别3.2 AQS概述3.3 加锁流程源码剖析3.3.1 加锁流程概述3.3.2 三种加锁源码分析3.3.2.1 lock方法3.3.2.2 tryLock方法3.3.2.3 lockInterruptibly方法 3.4 释放锁流程源码剖析3.4.1 释放锁流程概述3.4.2 释放锁…...

Unity 高度可扩展的技能与多 Buff 框架详解

一、框架设计 1.1 核心思想 组件化设计: 将技能和 Buff 抽象为可复用的组件&#xff0c;通过组合不同的组件实现复杂的效果。 数据驱动: 使用 ScriptableObject 或 JSON 等数据格式定义技能和 Buff 的属性&#xff0c;方便配置和修改。 事件驱动: 利用 Unity 的事件系统或自…...

电路笔记(元器件):AD 5263数字电位计(暂记)

AD5263 是四通道、15 V、256位数字电位计&#xff0c;可通过SPI/I2C配置具体电平值。 配置模式&#xff1a; W引脚作为电位器的抽头&#xff0c;可在A-B之间调整任意位置的电阻值。也可将W与A(或B)引脚短接&#xff0c;A-W间的电阻总是0欧姆&#xff0c;通过数字接口调整电位器…...

《大规模动画优化(一):GPU 顶点动画的生成》

GPU 顶点动画&#xff08;Vertex Animation Texture, VAT&#xff09; GPU 顶点动画&#xff08;Vertex Animation Texture, VAT&#xff09;烘焙的核心思想是&#xff1a; 在 CPU 端预先计算动画顶点数据&#xff0c;并存储到纹理&#xff08;Texture2D&#xff09;中&#xf…...

webpack【初体验】使用 webpack 打包一个程序

打包前 共 3 个文件 dist\index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Webpack 示例&…...

VMware安装CentOS 7(全网超详细图文保姆版教程)

文章目录 一、下载及安装 VMware1.1 VMware下载1.2 CentOS下载 二、搭建虚拟机环境2.1 创建新虚拟机2.2 选择自定义2.3 选择虚拟机硬件兼容性2.4 选择稍后安装操作系统2.5 选择Linux系统 版本选择 centos 7 64位2.6 设备你虚拟机的名字和保存位置&#xff08;保存位置建议在编辑…...

mysql BUG 导致 show processlist 有大量的show slave stauts 处于init状态

一、详细报错信息&#xff1a; 1、执行show slave status\G 卡住 && stop slave也卡住 2、show processlist 发现 Waiting for commit lock NULL 锁 3、错误日志报错主备同步用户认证失败 二、报错原因&#xff08;分析过程&#xff09;&#xff1a; 1、排查备库日志…...

机器学习在癌症分子亚型分类中的应用

学习笔记&#xff1a;机器学习在癌症分子亚型分类中的应用——Cancer Cell 研究解析 1. 文章基本信息 标题&#xff1a;Classification of non-TCGA cancer samples to TCGA molecular subtypes using machine learning发表期刊&#xff1a;Cancer Cell发表时间&#xff1a;20…...

从MySQL优化到脑力健康:技术人与效率的双重提升

文章目录 零&#xff1a;前言一&#xff1a;MySQL性能优化的核心知识点1. 索引优化的最佳实践实战案例&#xff1a; 2. 高并发事务的处理机制实战案例&#xff1a; 3. 查询性能调优实战案例&#xff1a; 4. 缓存与连接池的优化实战案例&#xff1a; 二&#xff1a;技术工作者的…...

Qt:项目文件解析

目录 QWidget基础项目文件解析 .pro文件解析 widget.h文件解析 widget.cpp文件解析 widget.ui文件解析 main.cpp文件解析 认识对象模型 窗口坐标系 QWidget基础项目文件解析 .pro文件解析 工程新建好之后&#xff0c;在工程目录列表中有⼀个后缀为 ".pro" …...

react使用if判断

1、第一种 function Dade(req:any){console.log(req)if(req.data.id 1){return <span>66666</span>}return <span style{{color:"red"}}>8888</span>}2、使用 {win.map((req,index) > ( <> <Dade data{req}/>{req.id 1 ?…...

conda 修复 libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found 简便方法

ImportError: /data/home/hum/anaconda3/envs/ipc/bin/../lib/libstdc.so.6: version GLIBCXX_3.4.30 not found (required by /home/hum/anaconda3/envs/ipc/lib/python3.11/site-packages/paddle/base/libpaddle.so) 1. 检查版本 strings /data/home/hum/anaconda3/envs/ipc/…...

在服务器部署JVM后,如何评估JVM的工作能力,比如吞吐量

在服务器部署JVM后&#xff0c;评估其工作能力&#xff08;如吞吐量&#xff09;可以通过以下步骤进行&#xff1a; 1. 选择合适的基准测试工具 JMH (Java Microbenchmark Harness)&#xff1a;适合微基准测试&#xff0c;测量特定代码片段的性能。Apache JMeter&#xff1a;…...

python学opencv|读取图像(六十)先后使用cv2.erode()函数和cv2.dilate()函数实现图像处理

【1】引言 前序学习进程中&#xff0c;先后了解了使用cv2.erode()函数和cv2.dilate()函数实现图像腐蚀和膨胀处理的效果&#xff0c;相关文章链接为&#xff1a; python学opencv|读取图像&#xff08;五十八&#xff09;使用cv2.erode()函数实现图像腐蚀处理-CSDN博客 pytho…...

Itext源代码阅读(2) -- PdfReader

本文基于Itext 5&#xff0c;Itext7相较itext5虽然有较大变化&#xff0c;但是原理是一样的。 参考资料&#xff1a; 使用iText处理pdf文件的入门级教程_itextpdf 教程-CSDN博客 比较详实的介绍了长用的itext 的pdf处理。 深入iText7&#xff1a;第5章源代码实践指南-CSDN博…...

JavaScript-Object 对象的相关方法

1. Object.getPrototypeOf() Object.getPrototypeOf方法返回参数对象的原型。这是获取原型对象的标准方法。 var F function () {}; var f new F(); Object.getPrototypeOf(f) F.prototype // true 上面代码中&#xff0c;实例对象 f的原型是 F.prototype。 下面是几种特殊对…...

Flink 内存模型各部分大小计算公式

Flink 的运行平台 如果 Flink 是运行在 yarn 或者 standalone 模式的话&#xff0c;其实都是运行在 JVM 的基础上的&#xff0c;所以首先 Flink 组件运行所需要给 JVM 本身要耗费的内存大小。无论是 JobManager 或者 TaskManager &#xff0c;他们 JVM 内存的大小都是一样的&a…...

每日一题——缺失的第一个正整数

缺失的第一个正整数 题目描述进阶&#xff1a;数据范围&#xff1a; 示例示例 1示例 2示例 3 题解思路代码实现代码解释复杂度分析总结 题目描述 给定一个无重复元素的整数数组 nums&#xff0c;请你找出其中没有出现的最小的正整数。 进阶&#xff1a; 时间复杂度&#xff…...