2024-02-19
HTML5 是不是就真的没办法播放 FLV 等格式视频了呢?不是。解决方案是 MSE,Media Source Extensions,就是说,HTML5 不仅可以直接播放上面支持的 mp4、m3u8、webm、ogg 格式,还可以支持由 JS 处理过后的视频流,这样我们就可以用 JS 把一些不支持的视频流格式,转化为支持的格式(如 H.264 的 mp4)。B 站开源的 flv.js 就是这个技术的一个典型实现。B 站的 PC HTML5 播放器,就是用 MSE 技术,将 FLV 源用 JS 实时转码成 HTML5 支持的视频流编码格式(其实就一个文件头的差异(这里文件头改成容器。感谢评论区谦谦的指教,是容器的差异,容器不只是文件头)),提供给 HTML5 播放器播放。
原理:前端本不支持rtsp播放,这里是通过Node.js驱动ffmpeg进行后台转码FLV,并通过前端MSE技术渲染到video标签当中
核心指令:ffmpeg把rtsp转rmtp
(ffmpeg默认推流方式采用UDP方式,若需要使用TCP协议,则需要修改-rtsp_transport tcp,-c copy表示不经转码,直接进行流复制,在以前的ffmpeg里,“-c copy”是以“-vcodec copy -acodec copy”这种形式表示的。现在的ffmpeg不存在这个问题了,而且这两种都可以用)
ffmpeg -rtsp_transport tcp -i your_rtsp_url -c copy -f flv rtcmp://127.0.0.1:1935/live/
转rmtp需要nginx的nginx-rtmp-module模块,nginx.conf配置如下
rtmp {
server {
listen 1935;#监听端口,若被占用,可以更改
chunk_size 4096;#上传flv文件块儿的大小
application live { #创建一个叫live的应用
live on;#开启live的应用
allow publish 127.0.0.1;
allow play all;
}
}
}
首先确保你已安装了ffmpeg以及websocket-stream、fluent-ffmpeg模块
后端:nodejs
const WebSocket =require( 'ws')
const webSocketStream =require( 'websocket-stream/stream')
const ffmpeg =require( 'fluent-ffmpeg')
// 建立WebSocket服务
const wss = new WebSocket.Server({ port: 9999, perMessageDeflate: false })
// 监听连接
wss.on('connection', handleConnection)
// 连接时触发事件
function handleConnection (ws, req) {
console.log('一个客户端连接进来啦')
// 获取前端请求的流地址(前端websocket连接时后面带上流地址)
const url = req.url.slice(1)
// 传入连接的ws客户端 实例化一个流
const stream = webSocketStream(ws, { binary: true })
// 通过ffmpeg命令 对实时流进行格式转换 输出flv格式
const ffmpegCommand = ffmpeg(url)
.addInputOption('-rtsp_transport', 'tcp')
.on('start', function () { console.log('Stream started.') })
.on('codecData', function () { console.log('Stream codecData.') })
.on('error', function (err) {
console.log('An error occured: ', err.message)
stream.end()
})
.on('end', function () {
console.log('Stream end!')
stream.end()
})
.outputFormat('flv').videoCodec('copy').noAudio()
stream.on('close', function () {
ffmpegCommand.kill('SIGKILL')
})
try {
// 执行命令 传输到实例流中返回给客户端
ffmpegCommand.pipe(stream)
} catch (error) {
console.log(error)
}
}
前端:vue3
<template>
<div class="streamer">
<video :id="props.videoId" autoplay muted controls width="100%" height="100%"></video>
</div>
</template>
<script setup>
import flvjs from '@/static/js/flv.min'
import {defineProps,onMounted,onBeforeUnmount,ref} from 'vue'
let flvPlayer=ref(null)
const props = defineProps({
url:{
type:String,
required:true
},
videoId: {
type: String,
default: 'player'
},
})
onMounted(()=>{
console.log(props.videoId)
const videoElement = document.getElementById(props.videoId)
flvPlayer.value = flvjs.createPlayer({
isLive: true,
type: 'flv',
url: 'ws://localhost:9999/'+props.url,
enableWorker: true,//启用分离线程
enableStashBuffer: false,//关闭IO隐藏缓冲区
stashInitialSize: 128, // 减少首桢显示等待时长
autoCleanupSourceBuffer: true, //自动清除缓存
})
flvPlayer.value.attachMediaElement(videoElement)
flvPlayer.value.load()//加载
setTimeout(()=>{
flvPlayer.value.play();
} ,1000);
})
onBeforeUnmount(()=>{
if(flvPlayer.value){
flvPlayer.value.destroy()
}
})
</script>