GStreamer 是一个用于创建流媒体应用程序的框架。使用 GStreamer 框架,可以设计和构建可以处理任何类型的流数据流的低延迟应用程序,包括音频和视频功能。
GStreamer 核心为插件、数据流和媒体类型处理提供了一个框架。它还提供了一个 API 来使用各种插件编写应用程序。
基于插件的框架提供了各种编解码器和其他功能,这些功能可以链接和排列在管道中,它定义了数据的流向。但是,在撰写本文时,还没有针对 GStreamer 应用程序的官方 Node.js 端口/绑定。
在这篇文章中,我们将通过以下部分讨论 GStreamer 的功能以及使用 Node 进行设置:
功能和用例
GStreamer 插件架构
GStreamer 安装
使用 Node.js 设置 GStreamer
一些 GStreamer 限制
GStreamer 的主要用例之一是构建媒体播放器。作为开发人员,您可以使用大量强大的工具来创建媒体管道,而无需编写任何代码。
默认情况下,GStreamer 包含用于构建支持多种格式的媒体播放器的组件,包括 MP3、Ogg/Vorbis、MPEG-1/2、AVI、QuickTime、mod 等。
然而,GStreamer 提供的功能比其他媒体播放器要多得多。它的主要优点是可插入组件可以混合到任意管道中,以便编写视频或音频编辑应用程序。具体来说,GStreamer 提供:
多媒体应用程序的 API
插件架构
管道架构
一种媒体类型处理/协商机制
通过插件,GStreamer 可以桥接到其他多媒体框架以重用现有组件(例如编解码器)和其他平台输入/输出机制。
GStreamer 的核心本质上与媒体无关。它只知道字节和块,并且只包含基本元素。所有的媒体处理功能都由内核外部的插件提供。这些告诉核心如何处理特定类型的媒体。
GStreamer 插件可以分为以下几组:
协议处理
来源:音频和视频(涉及协议插件)
格式:解析器、格式化程序、复用器、解复用器、元数据、字幕
编解码器:编码器和解码器
过滤器:转换器、混音器、效果器
sinks:用于音频和视频(涉及协议插件)
要安装 GStreamer,文档提供了多种方法供您选择,具体取决于您选择的操作系统。
对于 macOS X,我们需要安装 OSX Snow Leopard (10.6) 或更高版本以及 XCode 3.2.6 或更高版本。但是,推荐的系统版本是带有 XCode 8 的 macOS Sierra。我们可以从 GStreamer 下载页面安装运行时和开发安装程序,我们可以在这里找到。
对于其他操作系统/环境,包括 Windows、iOS、Android 和 Linux,我们可以查看文档中的下载页面,其中包含所有支持的生态系统列表以及为开发目的构建 GStreamer SDK 的方法。
我们可以导航到/Library/Frameworks/GStreamer.framework/Commands系统路径上的 以查看可用的命令来使用 GStreamer。一些流行的命令包括gst-launch-1.0, gst-inspect-1.0, gst-play-1.0.
安装后,我们可以继续使用 Node.js 运行时来使用 GStreamer 管道并将结果输出到 Web 浏览器。
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →
让我们创建一个我们选择的文件夹并使用 npm 或 Yarn 安装 Express.js,然后按照说明设置一个带有package.json文件的基本项目:
npm install express or yarn add express
然后继续创建一个index.js文件来保存我们使用 GStreamer 的流式传输示例的 JavaScript 代码。请参阅index.js下面的文件:
const express = require('express') const http = require('http') const net = require('net'); const child = require('child_process'); const app = express(); app.use(express.static(__dirname + '/')); const httpServer = http.createServer(app); const port = 3000; //send the html page which holds the video tag app.get('/', function (req, res) { res.send('index.html'); }); //stop the connection app.post('/stop', function (req, res) { console.log('Connection closed using /stop endpoint.'); if (gstMuxer != undefined) { gstMuxer.kill(); //kill the GStreamer Pipeline } gstMuxer = undefined; res.end(); }); //send the video stream app.get('/stream', function (req, res) { res.writeHead(200, { 'Content-Type': 'video/webm', }); const tcpServer = net.createServer(function (socket) { socket.on('data', function (data) { res.write(data); }); socket.on('close', function () { console.log('Socket closed.'); res.end(); }); }); tcpServer.maxConnections = 1; tcpServer.listen(function () { console.log("Connection started."); if (gstMuxer == undefined) { console.log("inside gstMuxer == undefined"); const cmd = 'gst-launch-1.0'; const args = getGstPipelineArguments(this); const gstMuxer = child.spawn(cmd, args); gstMuxer.stderr.on('data', onSpawnError); gstMuxer.on('exit', onSpawnExit); } else { console.log("New GST pipeline rejected because gstMuxer != undefined."); } }); }); httpServer.listen(port); console.log(`Camera Streaming App listening at http://localhost:${port}`) process.on('uncaughtException', function (err) { console.log(err); }); //functions function onSpawnError(data) { console.log(data.toString()); } function onSpawnExit(code) { if (code != null) { console.log('GStreamer error, exit code ' + code); } } function getGstPipelineArguments(tcpServer) { const args = ['/Users/alexandernnakwue/Downloads/samplevideo.mp4', 'pattern=ball', '!', 'video/x-raw,width=320,height=240,framerate=100/1', '!', 'vpuenc_h264', 'bitrate=2000', '!', 'mp4mux', 'fragment-duration=10', '!', 'tcpclientsink', 'host=localhost', 'port=' + tcpServer.address().port]; return args; }
正如我们在上面的文件中看到的,我们有三个端点:
用于发送 HTML 页面的端点,其中包含视频标签
发送视频流的端点
结束连接的端点
接下来,创建 HTML 页面 ( index.html),其中包含视频标签,如下所示。
<!DOCTYPE html> <head> <title>GStreamer with NodeJS Demo</title> <meta name="viewport" content="width=device-width, initial-scale=0.9"> <style> html, body { overflow: hidden; } </style> <script> function buffer() { //Start playback as soon as possible to minimize latency at startup const dStream = document.getElementById('vidStream'); try { dStream.play(); } catch (error) { console.log(error); } } </script> </head> <body οnlοad="buffer();"> <video id="vidStream" width="640" height="480" muted> <source src="/stream" type="video/mp4" /> <source src="/stream" type="video/webm" /> <source src="/stream" type="video/ogg" /> <!-- fallback --> Your browser does not support the video element. </video> </body>
正如我在介绍中提到的,目前没有针对 Node.js 的官方端口或绑定。上面的代码改编自这个Stack Overflow 帖子。
我们可以继续使用gst-launch-1.0命令启动流式应用程序和参数,其中包括用于流式传输的视频或音频源、TCP 端口和地址、设置等。随着页面加载,我们正在使用该方法尽快播放视频流play()。
注意:这仅适用于基于 Chromium 的浏览器。我将在下面解释更多。l
目前针对 Node.js 的 GStreamer 实现是非标准化的并且仍然缺乏。例如,当前的实现并不完全兼容浏览器,并且仅适用于基于 Chromium 的实现,因为 Chrome 中加载资源所需的一些 HTTP 标头不可用。漫画帮追番App,解锁VIP限制无广告,动漫画质清晰度超高!此外,在某些系统架构上构建 GStreamer 仍然是一项艰巨的任务,因为它仍然包含大量错误。
GStreamer 尚不直接支持多种不同编程语言的端口。这意味着打算在 Node.js 应用程序中使用 GStreamer 的开发人员需要直接从 Nodenode-addon-api调用代码。但是,此方法需要大量工作,并且在使用node-gypC构建时尤其容易出错。
我们可能已经注意到,目前 Node.js 的 GStreamer 绑定有限。
还有其他可用的绑定,例如node-gstreamer-superficial,但根据文档,它不会尝试成为 GStreamer 的完整 JS 绑定,并且希望有一天会被替换为(或实现为)node-gir。
其他可用的绑定或 hack 无法按预期工作、未标准化或容易出错。这确实是一个巨大的挑战,在不久的将来,需要构建一个标准化的、行业范围的 Node.js 绑定端口。