Appearance
全局配置
Appearance
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webRTC</title>
</head>
<body>
<!-- 录屏 -->
<video id="localVideo" autoplay playsinline muted></video>
<!-- 截图列表 -->
<div id="imgContainer"></div>
<button id="screenshotBtn" style="position: fixed;right: 0;top: 50%;">截图</button>
<script>
const constraints = {
audio: true,
video: { width: 1280, height: 720 }
}
getLocalStream(constraints)
// 获取本地音视频流
async function getLocalStream(constraints) {
// 获取媒体流
const stream = await navigator.mediaDevices.getUserMedia(constraints)
// 将媒体流设置到 video 标签上播放
playLocalStream(stream)
}
// 播放本地视频流
function playLocalStream(stream) {
const videoEl = document.getElementById('localVideo')
videoEl.srcObject = stream
}
</script>
<script>
var imgList = []
var screenshotBtn = document.getElementById('screenshotBtn')
screenshotBtn.addEventListener('click', generatePhotos)
// 拍照
function generatePhotos () {
// 添加滤镜
var filterList = [
'blur(5px)', // 模糊
'brightness(0.5)', // 亮度
'contrast(200%)', // 对比度
'grayscale(100%)', // 灰度
'hue-rotate(90deg)', // 色相旋转
'invert(100%)', // 反色
'opacity(90%)', // 透明度
'saturate(200%)', // 饱和度
'saturate(20%)', // 饱和度
'sepia(100%)', // 褐色
'drop-shadow(4px 4px 8px blue)', // 阴影
]
var videoEl = document.getElementById('localVideo')
var canvas = document.createElement('canvas')
canvas.width = videoEl.videoWidth
canvas.height = videoEl.videoHeight
var ctx = canvas.getContext('2d')
// 原图
ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height)
imgList.push(canvas.toDataURL('image/png'))
for (let i = 0; i < filterList.length; i++) {
ctx.filter = filterList[i]
ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height)
imgList.push(canvas.toDataURL('image/png'))
}
insertImgs(imgList)
}
function insertImgs(list) {
const container = document.createDocumentFragment
var imgContainer = document.getElementById('imgContainer');
var fragment = document.createDocumentFragment();
list.forEach((img) => {
var imgEl = document.createElement('img');
imgEl.src = img;
fragment.appendChild(imgEl);
});
imgContainer.appendChild(fragment)
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webRTC</title>
</head>
<body>
<h2>录屏</h2>
<video id="screenVideo" autoplay playsinline></video>
<button id="recorderStartBtn" style="position: fixed;right: 0;top: 46%;">开始录制</button>
<button id="recorderStopBtn" style="position: fixed;right: 0;top: 48%;">停止录制</button>
<button id="recorderPauseBtn" style="position: fixed;right: 0;top: 52%;">暂停录制</button>
<button id="recorderResumeBtn" style="position: fixed;right: 0;top: 54%;">继续录制</button>
<script>
// 录屏
// 获取视频流
var localStream
shareScreen()
async function shareScreen() {
localStream = await navigator.mediaDevices.getDisplayMedia({
audio: true,
video: true,
})
// 播放录屏视频流
playStream(localStream)
}
// 在视频标签中播放视频流
function playStream(stream) {
const video = document.querySelector('#screenVideo')
video.srcObject = stream
}
</script>
<script>
// 录屏保存
// 这里我们使用 MediaRecorder 来进行录制,它是一个用于录制媒体流的 API,它可以将媒体流中的数据进行录制,然后将录制的数据保存成一个文件。
// 由于 MediaRecorder api 的对 mimeType 参数的支持是有限的。所以我们需要通过 MediaRecorder.isTypeSupported 来判断当前浏览器是否支持我们需要的 mimeType
function getSupportedMimeTypes() {
const media = 'video'
// 常用的视频格式
const types = [
'webm',
'mp4',
'ogg',
'mov',
'avi',
'wmv',
'flv',
'mkv',
'ts',
'x-matroska',
]
// 常用的视频编码
const codecs = ['vp9', 'vp9.0', 'vp8', 'vp8.0', 'avc1', 'av1', 'h265', 'h264']
// 支持的媒体类型
const supported = []
const isSupported = MediaRecorder.isTypeSupported
// 遍历判断所有的媒体类型
types.forEach((type) => {
const mimeType = `${media}/${type}`
codecs.forEach((codec) =>
[
`${mimeType};codecs=${codec}`,
`${mimeType};codecs=${codec.toUpperCase()}`,
].forEach((variation) => {
if (isSupported(variation)) supported.push(variation)
}),
)
if (isSupported(mimeType)) supported.push(mimeType)
})
return supported
}
console.log(getSupportedMimeTypes())
function getRecorder () {
const kbps = 1024
const Mbps = kbps * kbps
const options = {
audioBitsPerSecond: 128000,
videoBitsPerSecond: 2500000,
mimeType: 'video/webm; codecs="vp8,opus"',
}
// https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder
return new MediaRecorder(localStream, options)
}
var mediaRecorder = getRecorder()
// 开始录制
var recorderStartBtn = document.getElementById('recorderStartBtn')
recorderStartBtn.addEventListener('click', handleRecorderStart)
// 暂停录制
var recorderPauseBtn = document.getElementById('recorderPauseBtn')
recorderPauseBtn.addEventListener('click', handleRecorderPause)
// 继续录制
var recorderResumeBtn = document.getElementById('recorderResumeBtn')
recorderResumeBtn.addEventListener('click', handleRecorderResume)
// 停止录制
var recorderStopBtn = document.getElementById('recorderStopBtn')
recorderStopBtn.addEventListener('click', handleRecorderStop)
// 录制媒体流
function handleRecorderStart() {
// 开始录制媒体,这个方法调用时可以通过给timeslice参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块,而不是以默认的方式录制一个非常大的整块内容。
mediaRecorder.start()
// 调用它用来处理 dataavailable 事件,该事件可用于获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象.)
mediaRecorder.ondataavailable = (e) => {
// 将录制的数据合并成一个 Blob 对象
// const blob = new Blob([e.data], { type: e.data.type })
// 🌸重点是这个地方,我们不要把获取到的 e.data.type设置成 blob 的 type,而是直接改成 mp4
const blob = new Blob([e.data], { type: 'video/mp4' })
downloadBlob(blob)
}
mediaRecorder.onstart = (e) => {
console.log("开始录制", e)
}
mediaRecorder.onstop = (e) => {
console.log("停止录制", e)
}
mediaRecorder.onpause = (e) => {
console.log("暂停录制", e)
}
mediaRecorder.onresume = (e) => {
console.log("继续录制", e)
}
}
function handleRecorderPause() {
mediaRecorder.pause()
}
function handleRecorderResume() {
mediaRecorder.resume()
}
function handleRecorderStop() {
mediaRecorder.stop()
}
// 下载 Blob
function downloadBlob(blob) {
// 将 Blob 对象转换成一个 URL 地址
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
// 设置 a 标签的 href 属性为刚刚生成的 URL 地址
a.href = url
// 设置 a 标签的 download 属性为文件名
a.download = `${new Date().getTime()}.${blob.type.split('/')[1]}`
// 模拟点击 a 标签
a.click()
// 释放 URL 地址
URL.revokeObjectURL(url)
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webRTC</title>
</head>
<body>
<h1>录像</h1>
<video id="cameraVideo" autoplay playsinline muted></video>
<button id="recorderStartBtn" style="position: fixed;right: 0;top: 52%;">开始录制</button>
<button id="recorderPauseBtn" style="position: fixed;right: 0;top: 54%;">暂停录制</button>
<button id="recorderResumeBtn" style="position: fixed;right: 0;top: 56%;">继续录制</button>
<button id="recorderStopBtn" style="position: fixed;right: 0;top: 58%;">停止录制</button>
<script>
// 录像
const constraints = {
audio: true,
video: { width: 1280, height: 720 }
}
var cameraStream
getLocalStream(constraints)
// 获取本地音视频流
async function getLocalStream(constraints) {
// 获取媒体流
cameraStream = await navigator.mediaDevices.getUserMedia(constraints)
// 将媒体流设置到 video 标签上播放
playLocalStream(cameraStream)
}
// 播放本地视频流
function playLocalStream(stream) {
const videoEl = document.getElementById('cameraVideo')
videoEl.srcObject = stream
}
</script>
<script>
// 录屏保存
// 这里我们使用 MediaRecorder 来进行录制,它是一个用于录制媒体流的 API,它可以将媒体流中的数据进行录制,然后将录制的数据保存成一个文件。
// 由于 MediaRecorder api 的对 mimeType 参数的支持是有限的。所以我们需要通过 MediaRecorder.isTypeSupported 来判断当前浏览器是否支持我们需要的 mimeType
function getSupportedMimeTypes() {
const media = 'video'
// 常用的视频格式
const types = [
'webm',
'mp4',
'ogg',
'mov',
'avi',
'wmv',
'flv',
'mkv',
'ts',
'x-matroska',
]
// 常用的视频编码
const codecs = ['vp9', 'vp9.0', 'vp8', 'vp8.0', 'avc1', 'av1', 'h265', 'h264']
// 支持的媒体类型
const supported = []
const isSupported = MediaRecorder.isTypeSupported
// 遍历判断所有的媒体类型
types.forEach((type) => {
const mimeType = `${media}/${type}`
codecs.forEach((codec) =>
[
`${mimeType};codecs=${codec}`,
`${mimeType};codecs=${codec.toUpperCase()}`,
].forEach((variation) => {
if (isSupported(variation)) supported.push(variation)
}),
)
if (isSupported(mimeType)) supported.push(mimeType)
})
return supported
}
console.log(getSupportedMimeTypes())
var mediaRecorder
function getRecorder () {
const kbps = 1024
const Mbps = kbps * kbps
const options = {
audioBitsPerSecond: 128000,
videoBitsPerSecond: 2500000,
mimeType: 'video/webm; codecs="vp8,opus"',
}
// https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder
return new MediaRecorder(cameraStream, options)
}
// 开始录制
var recorderStartBtn = document.getElementById('recorderStartBtn')
recorderStartBtn.addEventListener('click', handleRecorderStart)
// 暂停录制
var recorderPauseBtn = document.getElementById('recorderPauseBtn')
recorderPauseBtn.addEventListener('click', handleRecorderPause)
// 继续录制
var recorderResumeBtn = document.getElementById('recorderResumeBtn')
recorderResumeBtn.addEventListener('click', handleRecorderResume)
// 停止录制
var recorderStopBtn = document.getElementById('recorderStopBtn')
recorderStopBtn.addEventListener('click', handleRecorderStop)
// 录制媒体流
function handleRecorderStart() {
mediaRecorder = getRecorder()
// 开始录制媒体,这个方法调用时可以通过给timeslice参数设置一个毫秒值,如果设置这个毫秒值,那么录制的媒体会按照你设置的值进行分割成一个个单独的区块,而不是以默认的方式录制一个非常大的整块内容。
mediaRecorder.start()
// 调用它用来处理 dataavailable 事件,该事件可用于获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象.)
mediaRecorder.ondataavailable = (e) => {
// 将录制的数据合并成一个 Blob 对象
// const blob = new Blob([e.data], { type: e.data.type })
// 🌸重点是这个地方,我们不要把获取到的 e.data.type设置成 blob 的 type,而是直接改成 mp4
const blob = new Blob([e.data], { type: 'video/mp4' })
downloadBlob(blob)
}
mediaRecorder.onstart = (e) => {
console.log("开始录制", e)
}
mediaRecorder.onstop = (e) => {
console.log("停止录制", e)
}
mediaRecorder.onpause = (e) => {
console.log("暂停录制", e)
}
mediaRecorder.onresume = (e) => {
console.log("继续录制", e)
}
}
function handleRecorderPause() {
mediaRecorder.pause()
}
function handleRecorderResume() {
mediaRecorder.resume()
}
function handleRecorderStop() {
mediaRecorder.stop()
}
// 下载 Blob
function downloadBlob(blob) {
// 将 Blob 对象转换成一个 URL 地址
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
// 设置 a 标签的 href 属性为刚刚生成的 URL 地址
a.href = url
// 设置 a 标签的 download 属性为文件名
a.download = `${new Date().getTime()}.${blob.type.split('/')[1]}`
// 模拟点击 a 标签
a.click()
// 释放 URL 地址
URL.revokeObjectURL(url)
}
</script>
</body>
</html>