一个JAVASCRIPT的MPEG1视频和MP2音频解码器
JSMpeg是用JavaScript编写的视频播放器。它由MPEG-TS分配器,MPEG1视频和MP2音频解码器,WebGL和Canvas2D渲染器以及WebAudio声音输出组成。JSMpeg可以通过Ajax加载静态文件,并通过WebSocktes实现低延迟流(〜50ms)。
JSMpeg可以在iPhone 5S上以30fps解码720p视频,适用于任何现代浏览器(Chrome,Firefox,Safari和Edge),并以20kb的格式进行压缩。
使用它可以这样简单:
< script src = “ jsmpeg.min.js ” > </ script > < div class = “ jsmpeg ” data-url = “ video.ts ” > </ div >
下载
-
jsmpeg.min.js 70kb
缩小,20kb gzipped。 -
github.com/phoboslab/jsmpeg
源代码和文档
低延迟的视频和音频流,无处不在 – 是的,即使在IPHONE上
JSMpeg可以连接到发送二进制MPEG-TS数据的WebSocket服务器。这些数据来自哪里,取决于你。
延迟时间仅取决于您的来源,网络条件以及您的浏览器,GPU和显示器可以吐出帧的速度。对于本地网络上的屏幕截图,它可以低至50ms。
最简单的方法之一就是使用 ffmpeg
一个小型的WebSocket服务器。有关 详细信息,请参阅github上的 文档。
使用JSMPEG和进一步阅读的项目
-
解码它就像是1999年
JSMpeg的发展各种有趣的一点 -
JSMpeg – Fronteers 2015,Dominic Szablewski
谈谈MPEG1编解码器的内部工作原理 -
即时网络摄像头
将视频从iPhone / iPad流式传输到Wi-Fi上的任何浏览器 -
jsmpeg-vnc
通过浏览器控制你的Windows PC
JSMpeg – JavaScript中的MPEG1视频和MP2音频解码器
JSMpeg是用JavaScript编写的视频播放器。它由MPEG-TS分路器,MPEG1视频和MP2音频解码器,WebGL和Canvas2D渲染器以及WebAudio声音输出组成。JSMpeg可以通过Ajax加载静态视频,并通过WebSockets实现低延迟流(〜50ms)。
JSMpeg可以在iPhone 5S上以30fps的速度解码720p视频,适用于任何现代浏览器(Chrome,Firefox,Safari,Edge),并且只需要20KB的压缩文件。
使用它可以这样简单:
< script src = “ jsmpeg.min.js ” > </ script > < div class = “ jsmpeg ” data-url = “ video.ts ” > </ div >
一些更多的信息和演示:jsmpeg.com
用法
JSMpeg视频播放器可以使用jsmpeg
容器的CSS类在HTML中创建:
< div class = “ jsmpeg ” data-url = “ <url> ” > </ div >
或者直接JSMpeg.Player()
在JavaScript中调用构造函数:
var player = new JSMpeg.Player(url [,options]);
请注意,使用HTML元素(内部JSMpeg.VideoElement
)提供了一些功能JSMpeg.Player
。即一个SVG暂停/播放按钮,并能够在iOS设备上“解锁”音频。
该url
参数接受一个URL到MPEG的.ts文件或网页套接字服务器(WS:// …)。
该options
参数支持以下属性:
-
canvas
– 用于视频渲染的HTML Canvas元素。如果没有提供,渲染器将创建自己的画布元素。 -
loop
– 是否循环播放视频(仅限静态文件)。默认true
。 -
autoplay
– 是否立即开始播放(仅限静态文件)。默认false
。 -
audio
– 是否解码音频。默认true
。 -
video
– 是否解码视频。默认true
。 -
poster
– 在视频播放前用作海报的图像的URL。 -
pauseWhenHidden
– 选项卡处于非活动状态时是否暂停播放。默认true
。请注意,浏览器通常在非活动选项卡上调节JS。 -
disableGl
– 是否禁用WebGL并始终使用Canvas2D渲染器。默认false
。 -
preserveDrawingBuffer
– WebGL上下文是否创建preserveDrawingBuffer
– 通过“屏幕截图”需要canvas.toDataURL()
。默认false
。 -
progressive
– 是否以块(仅限静态文件)加载数据。启用时,可以在整个源完全加载之前开始播放。默认true
。 -
throttled
– 使用时progressive
,是否推迟加载块,当他们不需要播放呢。默认true
。 -
chunkSize
– 使用时progressive
,以字节为单位的块大小一次加载。默认1024*1024
(1mb)。 -
decodeFirstFrame
– 是否解码并显示视频的第一帧。用于设置画布大小,并使用该框架作为“海报”图像。这在使用autoplay
或流媒体源时不起作用。默认true
。 -
maxAudioLag
– 流式传输时,以秒为单位的最大入队音频长度。 -
videoBufferSize
– 流传输时,视频解码缓冲区的大小(以字节为单位)。默认512 * 1024(512kb)。您可能需要增加非常高的比特率。 -
audioBufferSize
– 流式传输时,音频解码缓冲区的大小(以字节为单位)。默认128 * 1024(128kb)。您可能需要增加非常高的比特率。
除了from之外的所有选项canvas
也可以通过data-
属性与HTML Element一起使用。例如,要在JavaScript中指定循环和自动播放:
var player = new JSMpeg.Player(' video.ts ' {loop : true,autoplay : true });
或HTML
< div class = “ jsmpeg ” data-url = “ video.ts ” data-loop = “ true ” data-autoplay = “ true ” > </ div >
请注意,camelCased
用作数据属性时,选项必须使用连字符。例如decodeFirstFrame: true
成为data-decode-first-frame="true"
HTML元素。
JSMpeg.Player API
一个JSMpeg.Player
实例支持以下方法和属性:
-
.play()
– 开始播放 -
.pause()
– 暂停播放 -
.stop()
– 停止播放并寻求开始 -
.destroy()
– 停止播放,断开源代码并清理WebGL和WebAudio状态。玩家以后不能使用。 -
.volume
– 获取或设置音量(0-1) -
.currentTime
– 以秒为单位获取或设置当前播放位置
为JSMpeg编码视频/音频
JSMpeg只支持MPEG1视频编解码器和MP2音频编解码器的MPEG-TS容器播放。视频解码器不能正确处理B帧(尽管现在的编码器似乎没有默认使用这些),视频的宽度必须是2的倍数。
你可以使用ffmpeg编码一个合适的视频,如下所示:
ffmpeg -i in.mp4 -f mpegts -codec:v mpeg1video -codec:a mp2 -b 0 out.ts
您还可以控制视频大小(-s
),帧率(-r
),视频比特率(-b:v
),音频比特率(-b:a
),音频通道数量(-ac
),采样率(-ar
)等等。有关详细信息,请参阅ffmpeg文档。
综合例子:
ffmpeg -i in.mp4 -f mpegts \ -codec:v mpeg1video -s 960x540 -b:v 1500k -r 30 -bf 0 \ -codec:a mp2 -ar 44100 -ac 1 -b:a 128k \ out.ts
性能考虑
尽管JSMpeg甚至可以在iPhone 5S上以30fps处理720p视频,但请记住,MPEG1并不像现代编解码器那样高效。MPEG1需要相当多的高清视频带宽。720p在2 Mbits / s(250kb / s)时开始显示正常。而且,比特率越高,JavaScript解码的工作就越多。
这不应该是一个静态文件的问题,或者如果你只在你的本地WiFi流。如果您不需要支持移动设备,那么10mbit / s的1080p就可以正常工作(如果您的编码器能够继续使用)。对于其他一切,我建议您使用最大2Mbit / s的540p(960×540)。
通过WebSockets流媒体
JSMpeg可以连接到发送二进制MPEG-TS数据的WebSocket服务器。在流式传输时,JSMpeg会尽可能降低延迟 – 它会立即解码所有内容,忽略视频和音频时间戳。为了使所有内容保持同步(以及延迟较低),音频数据应该非常频繁地在视频帧之间交错(-muxdelay
在ffmpeg中)。
一个单独的,缓冲的流模式,其中JSMpeg预加载数秒的数据,并提供准确的时间和音频/视频同步的一切是可以想象的,但目前尚未实现。
视频和音频的内部缓冲区相当小(分别为512kb和128kb),JSMpeg将丢弃旧的(甚至未播放的)数据,为新到达的数据腾出空间,而不会有太多模糊。这可能在网络拥塞时引入解码伪像,但确保延迟保持在最低限度。如有必要,您可以增加videoBufferSize
和audioBufferSize
通过选项。
JSMpeg附带一个用Node.js编写的小型WebSocket“relay”。该服务器通过HTTP接受MPEG-TS源,并通过WebSocket将其提供给所有连接的浏览器。传入的HTTP流可以使用ffmpeg,gstreamer或其他方式生成。
源和WebSocket中继之间的拆分是必要的,因为ffmpeg不会说WebSocket协议。但是,这种拆分还允许您在公共服务器上安装WebSocket中继,并在Internet上共享您的流(通常,路由器中的NAT会阻止公共Internet连接到本地网络)。
总之,它的工作原理是这样的:
- 运行websocket-relay.js
- 运行ffmpeg,将输出发送到中继的HTTP端口
- 将浏览器中的JSMpeg连接到中继的Websocket端口
流的示例设置:Raspberry Pi Live网络摄像头
对于这个例子,ffmpeg和WebSocket中继在同一个系统上运行。这使您可以在本地网络中查看流,但不能在公共网络上查看。
这个例子假定你的网络摄像机与Video4Linux2兼容,并且出现/dev/video0
在文件系统中。大多数USB摄像头支持UVC标准,应该工作得很好。通过加载内核模块,板载树莓相机可以作为V4L2设备使用:sudo modprobe bcm2835-v4l2
。
-
安装ffmpeg(请参阅如何在Debian / Raspbian上安装ffmpeg)。使用ffmpeg,我们可以捕捉网络摄像头的视频和音频,并将其编码成MPEG1 / MP2。
-
安装Node.js和npm(请参阅在基于Debian和Ubuntu的Linux发行版上安装Node.js以获得更新的版本)。Websocket中继是用Node.js编写的
-
安装http-server。我们将使用它来提供静态文件(view-stream.html,jsmpeg.min.js),以便我们可以在浏览器中用视频查看网站。任何其他网络服务器也可以工作(nginx,apache等):
sudo npm -g install http-server
-
安装git并克隆这个仓库(或者直接将它下载为ZIP并解压)
sudo apt-get install git git clone https://github.com/phoboslab/jsmpeg.git
-
转到jsmpeg /目录
cd jsmpeg/
-
安装Node.js Websocket库:
npm install ws
-
启动Websocket中继。为输入的HTTP视频流和我们可以在浏览器中连接的Websocket端口提供密码和端口:
node websocket-relay.js supersecret 8081 8082
-
在一个新的终端窗口(仍然在
jsmpeg/
目录中,启动,http-server
所以我们可以服务view-stream.html到浏览器:http-server
-
在浏览器中打开流式传输网站。该
http-server
会告诉你的IP(通常192.168.[...]
)和端口(通常8080
)在哪里运行:http://192.168.[...]:8080/view-stream.html
-
在第三个终端窗口中,启动ffmpeg来捕捉摄像头视频并将其发送到WebSocket中继。在目标网址中提供密码和端口(从第7步开始):
ffmpeg \ -f v4l2 \ -framerate 25 -video_size 640x480 -i /dev/video0 \ -f mpegts \ -codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \ http://localhost:8081/supersecret
您现在应该在您的浏览器中看到一个活的摄像头图像。
如果ffmpeg无法打开输入视频,则可能是因为您的摄像头不支持给定的分辨率,格式或帧速率。要获得兼容模式的列表,请运行:
ffmpeg -f v4l2 -list_formats all -i /dev/video0
要添加摄像头音频,只需使用两个独立的输入调用ffmpeg即可。
ffmpeg \ -f v4l2 \ -framerate 25 -video_size 640x480 -i /dev/video0 \ -f alsa \ -ar 44100 -c 2 -i hw:0 \ -f mpegts \ -codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \ -codec:a mp2 -b:a 128k \ -muxdelay 0.001 \ http://localhost:8081/supersecret
注意这个muxdelay
论点。这应该会减少延迟,但在流式传输视频和音频时并不总是有效 – 请参阅下面的注释。
关于fmpeg muxing和延迟的一些评论
将音频流添加到MPEG-TS有时会引起相当大的延迟。我特别发现这是使用ALSA和V4L2的Linux上的一个问题(在macOS上使用AVFoundation工作得很好)。但是,有一个简单的解决方法:只运行ffmpeg的两个实例。一个是音频,一个是视频。发送两个输出到相同的Websocket中继。由于MPEG-TS格式的简单性,两个流的适当的“混合”在继电器中自动发生。
ffmpeg \ -f v4l2 \ -framerate 25 -video_size 640x480 -i /dev/video0 \ -f mpegts \ -codec:v mpeg1video -s 640x480 -b:v 1000k -bf 0 \ -muxdelay 0.001 \ http://localhost:8081/supersecret # In a second terminal ffmpeg \ -f alsa \ -ar 44100 -c 2 -i hw:0 \ -f mpegts \ -codec:a mp2 -b:a 128k \ -muxdelay 0.001 \ http://localhost:8081/supersecret
在我的测试中,USB摄像头引入了大约180ms的延迟,似乎我们无能为力。树莓派然而有一个摄像头模块,提供较低的延迟视频采集。
要使用ffmpeg捕获Windows或MacOS上的网络摄像头输入,请参阅ffmpeg Capture / Webcam Wiki。
JSMpeg架构和内部
这个库建立在一个相当模块化的方式,同时保持在最低限度的开销。应该可以实现新的分配器,解码器,输出(渲染器,音频设备)或信号源,而无需更改任何其他部分。但是,您仍然需要继承JSMpeg.Player
以使用任何新的模块。
查看一下jsmpeg.js源代码,了解这些模块如何互连以及应该提供哪些API。我还写了一篇关于JSMpeg内部部分的博文:解码就像是1999年。
使用库的一部分而不创建一个完整的播放器也应该相当简单。例如,您可以创建一个独立的JSMpeg.Decoder.MPEG1Video
类实例,.connect()
一个渲染器,.write()
一些数据和.decode()
一个框架,而不需要触及JSMpeg的其他部分。
以前的版本
目前生活在这个回购的JSMpeg版本是完全重写原始的jsmpeg库,只能够解码原始mpeg1video。如果您正在查找旧版本,请参阅v0.2标签。