nodeJs用ffmpeg直播推流到rtmp服务器上

总结

最近在写直播项目 目前比较重要的点就是推拉流 自己也去了解了一下

ffmpeg

FFmpeg 是一个开源项目,它提供了一个跨平台的命令行工具,以及一系列用于处理音频和视频数据的库。FFmpeg 能够执行多种任务,包括解封装、转封装、视频和音频的编码和解码、流处理等。它广泛应用于多媒体处理领域,被许多项目和网站推荐使用。

FFmpeg 的主要特点包括:

多平台支持:FFmpeg 可以在多种操作系统上运行,包括 Windows、Mac OS X、Linux 等。

功能丰富:它支持大量的音视频格式,能够进行编码、解码、转码、复用、解复用、滤波、转换、抓取等操作。

命令行工具:FFmpeg 提供了简洁的命令行接口,使得用户能够方便地进行批处理操作。

下载ffmpeg

官网 http://ffmpeg.org/
配置环境变量 添加到系统环境变量中
在这里插入图片描述
输入这个ffmpeg -version 出现这个 说明成功
在这里插入图片描述

node端


const http = require('http');
const express = require('express');
const socketio = require('socket.io');const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(express.static('public'))
var spawn = require('child_process').spawn;
spawn('ffmpeg', ['-h']).on('error', function (m) {console.error("FFMpeg not found in system cli; please install ffmpeg properly or make a softlink to ./!");process.exit(-1);
});io.on('connection', function (socket) {socket.emit('message', 'Hello from mediarecorder-to-rtmp server!');socket.emit('message', 'Please set rtmp destination before start streaming.');var ffmpeg_process, feedStream = false;socket.on('config_rtmpDestination', function (m) {socket._rtmpDestination = m;});socket.on('config_vcodec', function (m) {socket._vcodec = m;});socket.on('start', function (m) {if (ffmpeg_process || feedStream) {socket.emit('fatal', 'stream already started.');return;}if (!socket._rtmpDestination) {socket.emit('fatal', 'no destination given.');return;}var framerate = socket.handshake.query.framespersecond;var audioBitrate = parseInt(socket.handshake.query.audioBitrate);var audioEncoding = "64k";console.log(audioEncoding, audioBitrate);console.log('framerate on node side', framerate);var ops = ['-i', '-','-c:v', 'libx264', '-preset', 'veryfast', '-tune', 'zerolatency','-c:a', 'aac', '-ar', '44100', '-b:a', '64k','-y','-use_wallclock_as_timestamps', '1','-async', '1','-filter_complex', 'aresample=44100','-strict', 'experimental','-bufsize', '1000','-f', 'flv', socket._rtmpDestination];ffmpeg_process = spawn('ffmpeg', ops);ffmpeg_process.stderr.on('data', function (data) {socket.emit('ffmpeg_stderr', '' + data);});ffmpeg_process.on('error', function (err) {socket.emit('fatal', 'ffmpeg error!' + err);feedStream = false;console.log('意外出错',err);socket.disconnect();});ffmpeg_process.on('exit', function (err) {socket.emit('fatal', 'ffmpeg exit!' + err);socket.disconnect();console.log('exit',err);});socket.on('binarystream', function (data) {console.log(data,'48888');ffmpeg_process.stdin.write(data);});});socket.on('disconnect', function () {feedStream = false;ffmpeg_process.stdin.end();ffmpeg_process.kill('SIGINT');});
});const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {console.log(`Socket.IO server is running on port ${PORT}`);
});

解释一下代码
开启一个子线程用于运行ffmpeg

var spawn = require('child_process').spawn;
spawn('ffmpeg', ['-h']).on('error', function (m) {console.error("FFMpeg not found in system cli; please install ffmpeg properly or make a softlink to ./!");process.exit(-1);
});

用sockect进行连接 当监听到connection事件之后所做的事情

io.on('connection', function (socket) {
....
}

socket监听start消息

io.on('start', function (socket) {
....
}

设置对ffmpeg进行操作的命令 用于推流

  var ops = ['-i', '-','-c:v', 'libx264', '-preset', 'veryfast', '-tune', 'zerolatency','-c:a', 'aac', '-ar', '44100', '-b:a', '64k','-y','-use_wallclock_as_timestamps', '1','-async', '1','-filter_complex', 'aresample=44100','-strict', 'experimental','-bufsize', '1000','-f', 'flv', socket._rtmpDestination];ffmpeg_process = spawn('ffmpeg', ops);

这个data就是接收到的数据流

        ffmpeg_process.stderr.on('data', function (data) {socket.emit('ffmpeg_stderr', '' + data);});

前端部分

<!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>Document</title><link rel="stylesheet" href="./index.css">
</head>
<script src="https://cdn.bootcdn.net/ajax/libs/socket.io/4.7.2/socket.io.js"></script><body><header class="header"><h1>Start a Live Stream!</h1></header><br><br><label for="option_width" class="form">Size:</label><input class="form" type="text" id="option_width" value="1280" /> &times;<input class="form" type="text" id="option_height" value="720" /><br><br><label class="form" for="option_framerate">Frame Rate:</label><input class="form" type="text" id="option_framerate" value="15" /><br><br><label class="form" for="option_framerate">Audio bitrate:</label><select class="form" id="option_bitrate"><option value="22050">22050</option><option value="44100">44100</option><option value="11025">11025</option></select><br><br><input class="form" type="hidden" id="socket.io_address" value="/" /><label class="form" for="option_url">RTMP:</label><!-- RMTP YOUTUBE URL/KEY--><input class="form" type="text" id="option_url" style="width:33%"value="rtmp://a.rtmp.youtube.com/live2/cqg8-76xs-hztf-hfx7-9q5y" /><br><!-- RMTP YOUTUBE URL/KEY--><input class="form" type="checkbox" style="display:none" id="checkbox_Reconection" checked="true"><label class="form" style="display:none">Reconnection </label><br><div class="form">Connect the server, then start streaming.</div><br /><button class="form" id="button_server">Connect Server</button><button class="form" id="button_start">Start Streaming</button><button class="form" id="button_stop">Stop Streaming</button><br><p id="output_message"></p><textarea readonly="true" id="output_console" rows=15>Server Logs</textarea><br><div class="video"><video class="output_video" id="output_video" autoplay=true></video></div><br><script src="./index.js"></script>
</body></html>

js部分

var output_console = document.getElementById('output_console'),output_message = document.getElementById('output_message'),output_video = document.getElementById('output_video'),option_url = document.getElementById('option_url'),socketio_address = document.getElementById('socket.io_address'),option_width = document.getElementById('option_width'),option_height = document.getElementById('option_height'),option_framerate = document.getElementById('option_framerate'),option_bitrate = document.getElementById('option_bitrate'),button_start = document.getElementById('button_start'),height = parseInt(option_height.value),width = parseInt(option_width.value),framerate = parseInt(option_framerate.value),audiobitrate = parseInt(option_bitrate.value),url = option_url.value;option_height.onchange = option_height.onkeyup = function () { height = 1 * this.value; }
option_width.onchange = option_width.onkeyup = function () { width = 1 * this.value; console.log("width" + width); }
option_framerate.onchange = option_framerate.onkeyup = function () { framerate = 1 * this.value; console.log("framerate" + framerate); }
option_bitrate.onchange = option_bitrate.onkeyup = function () { audiobitrate = 1 * this.value; console.log("bitrate" + audiobitrate); }
option_url.onchange = option_url.onkeyup = function () { url = this.value; }
button_start.onclick = requestMedia;
button_server.onclick = connect_server;
var flagConnect = truevar mediaRecorder;
var socket;
var state = "stop";
console.log("state initiated = " + state);
var t;
button_start.disabled = true;
button_stop.disabled = true;
function connect_server() {navigator.getUserMedia = (navigator.mediaDevices.getUserMedia ||navigator.mediaDevices.mozGetUserMedia ||navigator.mediaDevices.msGetUserMedia ||navigator.mediaDevices.webkitGetUserMedia);if (!navigator.getUserMedia) { fail('No getUserMedia() available.'); }if (!MediaRecorder) { fail('No MediaRecorder available.'); }var socketOptions = { secure: true, reconnection: true, reconnectionDelay: 1000, timeout: 15000, pingTimeout: 15000, pingInterval: 45000, query: { framespersecond: framerate, audioBitrate: audiobitrate } };socket = io(socketOptions);socket.on('connect_timeout', (timeout) => {console.log("连接超时" + timeout);output_message.innerHTML = "Connection timed out";});socket.on('error', (error) => {console.log("state on connection error= " + error);output_message.innerHTML = "Connection error";});socket.on('connect_error', function () {console.log("连接错误" + state);});socket.on('message', function (m) {console.log("state on message= " + state + m);show_output('SERVER:' + m);});socket.on('fatal', function (m) {show_output('Fatal ERROR: unexpected:' + m);console.log("fatal socket error!!", m);console.log("state on fatal error= " + state);console.log('media recorder restarted');if (flagConnect) {output_message.innerHTML = "server is reload!";console.log("重置连接");}//should reload?});socket.on('flagConnect', function (m) {//this is the ffmpeg output for each frameshow_output('FFMPEG:' + m);console.log(m, '我是每一帧');});socket.on('disconnect', function (reason) {console.log("state disconec= " + state);show_output('ERROR: server disconnected!');console.log('失去连接' + reason);connect_server();if (flagConnect) {output_message.innerHTML = "重置";console.log("重置");}});state = "ready";console.log("state = " + state);button_start.disabled = false;button_stop.disabled = false;button_server.disabled = true;output_message.innerHTML = "Connecting local server...";
}
async function requestMedia() {var constraints = {audio: {sampleRate: audiobitrate,echoCancellation: true},video: {width: { min: 100, ideal: width, max: 1920 },height: { min: 100, ideal: height, max: 1080 },frameRate: { ideal: framerate }}}const stream = await navigator.mediaDevices.getUserMedia(constraints)video_show(stream)socket.emit('config_rtmpDestination', url);socket.emit('start', 'start');mediaRecorder = new MediaRecorder(stream);mediaRecorder.start(250)button_stop.disabled = false;button_start.disabled = true;button_server.disabled = true;var livestream = document.getElementsByClassName("Livestream");livestream.innerHtml = "test";mediaRecorder.onstop = function (e) {console.log("stopped!");console.log(e);}mediaRecorder.onpause = function (e) {console.log("media recorder paused!!");console.log(e);}mediaRecorder.onerror = function (event) {let error = event.error;console.log("error", error.name);};mediaRecorder.ondataavailable = function (e) {socket.emit("binarystream", e.data);state = "start";}
}
function video_show(stream) {console.log(stream, '111111');if ("srcObject" in output_video) {output_video.muted = true;output_video.srcObject = stream;} else {output_video.src = window.URL.createObjectURL(stream);}
}
function show_output(str) {output_console.value += "\n" + str;output_console.scrollTop = output_console.scrollHeight;
};

以上代码分为 这三个方法
在这里插入图片描述
其中 以connect-server
先使用浏览器自带的 navigator.getUserMedia获取设备 并且将配置的属性传入socket配置中 这个方法就是对socket连接进行处理 包括处理服务器端连接过程通信的过程

requestMedia() 获取媒体数据流 并且通过 socket.emit(“binarystream”, e.data);传递给后台

 const stream = await navigator.mediaDevices.getUserMedia(constraints)

实例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下周

目前弄了一个分片上传 下周弄完之后可以分享分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/13157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国际化日期(inti)

我们可以使用国际化API自动的格式化数字或者日期&#xff0c;并且格式化日期或数字的时候是按照各个国家的习惯来进行格式化的&#xff0c;非常的简单&#xff1b; const now new Date(); labelDate.textContent new Intl.DateTimeFormat(zh-CN).format(now);比如说这是按照…

DC-DC转换效率的影响因素和优化方向

一. 定义 DC-DC转换效率的定义是输入与输出功率之比&#xff1a; η P O U T P I N P O U T P O U T P L O S S η\frac{P_{OUT}}{P_{IN}}\frac{P_{OUT}}{P_{OUT}P_{LOSS}} ηPIN​POUT​​POUT​PLOSS​POUT​​ 其中POUT代表输出功率&#xff0c;PIN代表输入功率&#x…

ADS FEM 仿真设置

1、EM Simulator 选择FEM。 2、在layout界面打开的EM功能&#xff0c;这里不需要操作。 3、Partitioning 不需要操作。 4、没有叠层的话需要新建&#xff0c;过孔可以在叠层处右键添加。 5、端口需要设置GND layer。 6、设置仿真频率。 7、Output plan。 8、Options 设置 介质…

ITSS认证流程详解!

ITSS,是Information Technology Service Standards的缩写&#xff0c;中文意思是信息技术服务标准&#xff0c;是在工业和信息化部、国家标准化委的领导和支持下&#xff0c;由ITSS工作组研制的一套IT服务领域的标准库和一套提供IT服务的方法论。 国家标准化委的领导和支持下&…

你好 GPT-4o!

你好 GPT-4o&#xff01; OpenAI公司宣布推出 GPT-4o&#xff0c;这是OpenAI的新旗舰模型&#xff0c;可以实时对音频、视觉和文本进行推理。 GPT-4o&#xff08;“o”代表“o​​mni”&#xff09;是迈向更自然的人机交互的一步——它接受文本、音频、图像和视频的任意组合作…

[AI]-(第1期):OpenAI-API调用

文章目录 一、OpenAI API中使用GPT-3.5-turbo模型充值方式使用模型计费方式价格说明相关限制和条款 二、接入一个OpenAI API流程1. 获取OpenAI API 密钥2. 集成ChatGPT到小程序3. 处理用户输入4. 调用OpenAI API5. 返回回复至小程序6. 持续优化7. Postman请求示例 三、通用AI客…

43k Star!推荐一款功能强大的开源笔记软件!

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

每日学习 - APK解包

文章目录 APK的定义解析APKAPK 是什么每个文件的意义classes.dexAndroidManifest.xmlassetslibres & resources.arsc 反编译工具apktool apk解包 秒了~ APK的定义 APK&#xff08;Android Package Kit&#xff09;是用于部署和分发Android操作系统上应用程序的软件包格式。…

pytest教程-47-钩子函数-pytest_sessionfinish

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_sessionstart钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_sessionfinish钩子函数的使用方法。 pytest_sessionfinish 钩子函数在 Pytest 测试会话结束时调用&#xff0c;…

自然语言处理通用框架BERT原理解读

相关代码见文末 1.概述 问题背景: 传统Seq2Seq模型的局限性: 早期的机器翻译和文本生成任务常采用基于循环神经网络(RNN)的序列到序列(Seq2Seq)模型,这类模型在处理长序列时容易遇到梯度消失/爆炸问题,导致训练效率低,难以捕捉长期依赖。 RNN网络的问题: RNN及其变…

中国农业大学:学硕11408复试线上涨40分,今年还会持续涨吗?中国农业大学计算机考研考情分析!

中国农业大学&#xff08;China Agricultural University&#xff09;&#xff0c;简称“中国农大”&#xff0c;坐落于中国首都北京&#xff0c;由中华人民共和国教育部直属&#xff0c;中央直管副部级建制&#xff0c;水利部、农业部和北京市共建&#xff0c;位列国家“双一流…

【PostgreSQL支持中文的全文检索插件(zhparser)】

PostgreSQL本身是支持全文检索的&#xff0c;提供两个数据类型&#xff08;tsvector,tsquery&#xff09;&#xff0c;并且通过动态检索自然语言文档的集合&#xff0c;定位到最匹配的查询结果。其内置的默认的分词解析器采用空格进行分词&#xff0c;但是因为中文的词语之间没…

Vue3:分类管理综合案例实现

综合案例 实现分类管理功能 路由 在main.js中引入router 访问根路径’/后跳转到布局容器 加载布局容器后重定向到’/nav/manage’ 加载我们需要的组件 这样可以在布局容器中切换功能模块时,只对需要修改的组件进行重新加载 const router createRouter({history: create…

APP封装后防止破解的全方位策略

移动应用开发完成后&#xff0c;封装&#xff08;编译打包&#xff09;是发布前的重要步骤。然而&#xff0c;一旦APP发布&#xff0c;就可能面临被逆向工程破解的风险&#xff0c;从而导致源代码泄露、数据被盗取等严重后果。 本文将介绍一系列实用的策略和技术&#xff0c;帮…

邦注科技 工业冷水机的风冷和水冷的区别介绍

工业冷水机在工业生产中扮演着重要角色&#xff0c;特别是在需要精确控制温度的应用中。风冷式冷水机和水冷式冷水机是两种常见的类型&#xff0c;它们之间存在一些显著的区别。 热交换的来源不同&#xff1a; 风冷式冷水机&#xff1a;热交换的来源是气体。它采用空气冷却方…

STL——vector容器【动态数组】

vector基本概念 功能&#xff1a;vector数据结构和数组非常相似&#xff0c;也成为单端数组 头文件&#xff1a;<vector> vector与普通数组的区别&#xff1a;不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a; 并不是在原空间之后续…

代理IP与网络隐私

随着科技的发展&#xff0c;越来越多的人开始享受网络的便利&#xff0c;人们逐步在社交媒体上分享自己的生活。这些公开信息是可以被所有人浏览观看的&#xff0c;但是也会存在部分隐私信息我们并不想要被人知晓&#xff0c;这就牵扯到网络隐私保护问题。 代理IP对于网络隐私保…

链接表存储图(C++注释详解): 构建表 深度优先遍历 (DFS)

链接表的结构体单元: #define size 100 typedef struct node {int idx;//下一个节点的索引int wt;//权重, 也可根据实际情景存储边的信息struct node* next; }Node; Node* hd[size]; // 存储图的邻接表 链接表的的构建: int main() {int n, m;cin >> n >> m; //…

【2024系统架构设计】回顾历史,查缺补漏篇 ③

前言 hello,大家好: 💡💡💡 我们一起来备考软考高级系统架构设计师吧,本专栏提供综合知识、案例科目、论文(论点和部分示例范文)等内容,包括知识点总结和记忆小妙招哦。 🚀🚀🚀 可以减少资料查找和收集的时间,提高效率,我们一起集中精力学习干货吧! 💡…

iOS 创建pch文件

1.参考链接&#xff08;xcode8添加方法&#xff0c;之前的跟这个差不多&#xff09;&#xff1a; 参考链接 2.自我总结&#xff1a; &#xff08;1&#xff09;创建pch文件: 注意点&#xff1a;1&#xff09;注意选中所有的targets&#xff08;看图明义&#xff09; 2&…