一个JAVASCRIPT的MPEG1视频和MP2音频解码器JSMpeg

一个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 >

下载

低延迟的视频和音频流,无处不在 – 是的,即使在IPHONE上

JSMpeg可以连接到发送二进制MPEG-TS数据的WebSocket服务器。这些数据来自哪里,取决于你。

延迟时间仅取决于您的来源,网络条件以及您的浏览器,GPU和显示器可以吐出帧的速度。对于本地网络上的屏幕截图,它可以低至50ms。

最简单的方法之一就是使用 ffmpeg一个小型的WebSocket服务器。有关 详细信息,请参阅github上的 文档

使用JSMPEG和进一步阅读的项目


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将丢弃旧的(甚至未播放的)数据,为新到达的数据腾出空间,而不会有太多模糊。这可能在网络拥塞时引入解码伪像,但确保延迟保持在最低限度。如有必要,您可以增加videoBufferSizeaudioBufferSize通过选项。

JSMpeg附带一个用Node.js编写的小型WebSocket“relay”。该服务器通过HTTP接受MPEG-TS源,并通过WebSocket将其提供给所有连接的浏览器。传入的HTTP流可以使用ffmpeggstreamer或其他方式生成。

源和WebSocket中继之间的拆分是必要的,因为ffmpeg不会说WebSocket协议。但是,这种拆分还允许您在公共服务器上安装WebSocket中继,并在Internet上共享您的流(通常,路由器中的NAT会阻止公共Internet连接本地网络)。

总之,它的工作原理是这样的:

  1. 运行websocket-relay.js
  2. 运行ffmpeg,将输出发送到中继的HTTP端口
  3. 将浏览器中的JSMpeg连接到中继的Websocket端口

流的示例设置:Raspberry Pi Live网络摄像头

对于这个例子,ffmpeg和WebSocket中继在同一个系统上运行。这使您可以在本地网络中查看流,但不能在公共网络上查看。

这个例子假定你的网络摄像机与Video4Linux2兼容,并且出现/dev/video0在文件系统中。大多数USB摄像头支持UVC标准,应该工作得很好。通过加载内核模块,板载树莓相机可以作为V4L2设备使用:sudo modprobe bcm2835-v4l2

  1. 安装ffmpeg(请参阅如何在Debian / Raspbian上安装ffmpeg)。使用ffmpeg,我们可以捕捉网络摄像头的视频和音频,并将其编码成MPEG1 / MP2。

  2. 安装Node.js和npm(请参阅在基于Debian和Ubuntu的Linux发行上安装Node.js以获得更新的版本)。Websocket中继是用Node.js编写的

  3. 安装http-server。我们将使用它来提供静态文件(view-stream.html,jsmpeg.min.js),以便我们可以在浏览器中用视频查看网站。任何其他网络服务器也可以工作(nginx,apache等): sudo npm -g install http-server

  4. 安装git并克隆这个仓库(或者直接将它下载为ZIP并解压)

sudo apt-get install git git clone https://github.com/phoboslab/jsmpeg.git 
  1. 转到jsmpeg /目录 cd jsmpeg/

  2. 安装Node.js Websocket库: npm install ws

  3. 启动Websocket中继。为输入的HTTP视频流和我们可以在浏览器中连接的Websocket端口提供密码和端口: node websocket-relay.js supersecret 8081 8082

  4. 在一个新的终端窗口(仍然在jsmpeg/目录中,启动,http-server所以我们可以服务view-stream.html到浏览器: http-server

  5. 在浏览器中打开流式传输网站。http-server会告诉你的IP(通常192.168.[...])和端口(通常8080)在哪里运行:http://192.168.[...]:8080/view-stream.html

  6. 在第三个终端窗口中,启动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标签