当前位置: 首页 > news >正文

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)

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

下周

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

相关文章:

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

总结 最近在写直播项目 目前比较重要的点就是推拉流 自己也去了解了一下 ffmpeg FFmpeg 是一个开源项目&#xff0c;它提供了一个跨平台的命令行工具&#xff0c;以及一系列用于处理音频和视频数据的库。FFmpeg 能够执行多种任务&#xff0c;包括解封装、转封装、视频和音频…...

Django信号与扩展:深入理解与实践

title: Django信号与扩展&#xff1a;深入理解与实践 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 后端开发 tags: Django信号松耦合观察者扩展安全性能 第一部分&#xff1a;Django信号基础 Django信号概述 一. Django信号的定义与作用 Django信…...

使用Docker创建verdaccio私服

verdaccio官网 1.Docker安装 这边以Ubuntu安装为例Ubuntu 安装Docker​&#xff0c;具体安装方式请根据自己电脑自行搜索。 2.下载verdaccio docker pull verdaccio/verdaccio3.运行verdaccio 运行容器&#xff1a; docker run -it -d --name verdaccio -p 4873:4873 ver…...

Spring 使用 Groovy 实现动态server

本人在项目中遇到这么个需求,有一个模块的server方法需要频繁修改 经阅读可以使用 Groovy 使用java脚本来时pom坐标 <dependency><groupId>org.codehaus.groovy</groupId><artifactId>groovy</artifactId><version>3.0.9</version>…...

oracle不得不知道的sql

一、oracle 查询语句 1.translate select translate(abc你好cdefgdc,abcdefg,1234567)from dual; select translate(abc你好cdefgdc,abcdefg,)from dual;--如果替换字符整个为空字符 &#xff0c;则直接返回null select translate(abc你好cdefgdc,abcdefg,122)from dual; sel…...

算法-卡尔曼滤波之卡尔曼滤波的第二个方程:预测方程(状态外推方程)

在上一节中&#xff0c;使用了静态模型&#xff0c;我们推导出了卡尔曼滤波的状态更新方程&#xff0c;但是在实际情况下&#xff0c;系统都是动态&#xff0c;预测阶段&#xff0c;前后时刻的状态是改变的&#xff0c;此时我们引入预测方程&#xff0c;也叫状态外推方程&#…...

刘邦的创业团队是沛县人,朱元璋的则是凤阳;要创业,一个县人才就够了

当人们回顾刘邦和朱元璋的创业经历时&#xff0c;总是会感慨他们起于微末&#xff0c;都创下了偌大王朝&#xff0c;成就无上荣誉。 尤其是我们查阅史书时&#xff0c;发现这二人的崛起班底都是各自的家乡人&#xff0c;例如刘邦的班底就是沛县人&#xff0c;朱元璋的班底是凤…...

【Unity之FairyGUI】你了解FGUI吗,跨平台多功能高效UI插件

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;就业…...

基于51单片机的自动浇花器电路

一、系统概述 自动浇水灌溉系统设计方案&#xff0c;以AT89C51单片机为控制核心&#xff0c;采用模块化的设计方法。 组成部分为&#xff1a;5V供电模块、土壤湿度传感器模块、ADC0832模数转换模块、水泵控制模块、按键输入模块、LCD显示模块和声光报警模块&#xff0c;结构如…...

2024中国(重庆)商旅文化川渝美食暨消费品博览会8月举办

2024中国(重庆)商旅文化川渝美食暨消费品博览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 展会背景&#xff1a; 2024中国航空科普大会暨第八届全国青少年无人机大赛在重庆举办&#xff…...

MacOS docker 安装与配置

orbstack 安装 官网&#xff1a; https://orbstack.dev 下载链接&#xff1a;Download OrbStack Fast, light, simple Docker Desktop alternative 选择是Apple M系列处理器&#xff0c; 或 Intel系列处理器 到这里就安装好了Orbstack软件&#xff0c;下面开始配置docker 下…...

【嵌入式大赛应用赛道】机械手臂

电机 进步电机&#xff1a;它的转动是以确定的步数进行的&#xff0c;只要计算好脉冲数量和频率&#xff0c;就可以准确预测和控制电机的转动角度、速度以及停止的位置 伺服电机&#xff1a;将输入的电信号&#xff08;如电压或电流指令&#xff09;转换成轴上的精确旋转运动…...

MES系统主要包括那些功能?

一开始接触MES系统&#xff0c;对MES细条的功能不清楚&#xff0c;这样很正常&#xff0c;因为MES系统相对于其他系统来讲&#xff0c;功能有多又复杂! 作为曾参与200企业MES系统架构的资深从业人员&#xff0c;我给大家选出了一款优秀模板——简道云MES系统&#xff0c;给大家…...

git 合并commit

操作步骤 合并commit cd xxx/ git checkout a8c0efegfwgtw # 最新commit git reset rhgertheryhg --soft # 最初的commit git status git checkout -b test1 git commit -m "test1" git branch git push origin test1 git tag test1_v0.0.1 git push origin test1_…...

【ARMv8/v9 系统寄存器 5 -- CPU ID 判断寄存器 MPIDR_EL1 使用详细介绍】

文章目录 寄存器名称: MPIDR_EL1寄存器结构:主要功能和用途亲和级别&#xff08;Affinity Levels&#xff09;简介CORE ID 获取函数 在ARMv8-A架构中&#xff0c; MPIDR_EL1寄存器是一个非常重要的系统寄存器&#xff0c;它提供了关于处理器在其物理和逻辑配置中的位置的信息。…...

软件工程课程设计之酒店管理系统的设计与实现

这是一个简化的酒店管理系统的需求分析文档、系统设计文档、测试文档的结构概述&#xff0c;以及部分实现阶段的代码示例。详细设计阶段的数据字典、ER图、模块分类图将以文字描述形式给出&#xff0c;而完整的代码未完全实现。这里只做软件工程部分的设计需求说明哈&#xff5…...

函数递归练习

目录 1.分析下面选择题 2.实现求第n个斐波那契数 3.编写一个函数实现n的k次方&#xff0c;使用递归实现。 4.写一个递归函数DigitSum(n)&#xff0c;输入一个非负整数&#xff0c;返回组成它的数字之和 5.递归方式实现打印一个整数的每一位 6.实现求n的阶乘 1.分析下面选择…...

公有云Linux模拟TCP三次挥手与四次握手(Wireshark抓包验证版)

目录 写在前面环境准备实验步骤1. 安装nc工具2. 使用nc打开一个连接2.1 公有云-安全组放行对应端口&#xff08;可选&#xff09; 3. 打开Wireshark抓包工具4. 新开终端&#xff0c;进行连接5. 查看抓包文件&#xff0c;验证TCP三次握手与四次挥手TCP三次握手数据传输TCP四次挥…...

【Day3:JAVA运算符、方法的介绍】

目录 1、运算符1.1 赋值运算符1.2 比较运算符1.3 逻辑运算符1.3.1 逻辑运算符概述1.3.2 逻辑运算符分类1.3.3 短路的逻辑运算符 1.4 三元运算符1.5 运算符优先级 2、方法2.1 方法介绍2.2 方法的定义和调用格式2.2.1 方法的调用2.2.2 带参数方法的调用2.2.3 带返回值方法的调用2…...

Chrome查看User Agent的实战教程

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

Linux 第三十四章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…...

国际化日期(inti)

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

【论文阅读笔记】jTrans(ISSTA 22)

个人博客地址 [ISSTA 22] jTrans&#xff08;个人阅读笔记&#xff09; 论文&#xff1a;《jTrans: Jump-Aware Transformer for Binary Code Similarity》 仓库&#xff1a;https://github.com/vul337/jTrans 提出的问题 二进制代码相似性检测&#xff08;BCSD&#xff0…...

单位个人如何向期刊投稿发表文章?

在单位担任信息宣传员一职以来,我深感肩上的责任重大。每月的对外信息宣传投稿不仅是工作的核心,更是衡量我们部门成效的重要指标。起初,我满腔热血,以为只要勤勉努力,将精心撰写的稿件投至各大报社、报纸期刊的官方邮箱,就能顺利登上版面,赢得读者的青睐。然而,现实远比理想骨…...

Redis数据结构-RedisObject

1.7 Redis数据结构-RedisObject Redis中的任意数据类型的键和值都会被封装为一个RedisObject&#xff0c;也叫做Redis对象&#xff0c;源码如下&#xff1a; 1、什么是redisObject&#xff1a; 从Redis的使用者的角度来看&#xff0c;⼀个Redis节点包含多个database&#xff…...

Vue 中使用 el-date-picker 限制只能选择当天、当天之前或当天之后日期的方法详解

网上很多都是不完整的&#xff0c;我这里发布一个完整的 - 8.64e7 表示可选择当天时间&#xff08;注&#xff1a;小于当前时间&#xff0c;- 8.64e7 则是禁用日期不包含当前日&#xff0c;若大于当前日期&#xff0c; 8.64e7 则是禁用日期包含当前日&#xff09; time.getTi…...

系列介绍:《创意代码:Processing艺术编程之旅》

系列介绍&#xff1a;《创意代码&#xff1a;Processing艺术编程之旅》 标题创意&#xff1a; “代码绘梦&#xff1a;Processing艺术编程入门”“数字画布&#xff1a;用Processing创造视觉奇迹”“编程美学&#xff1a;Processing艺术创作指南”“创意编程&#xff1a;Proc…...

深度学习设计模式之抽象工厂模式

文章目录 前言一、介绍二、详细分析1.核心组成2.实现步骤3.代码示例4.优缺点优点缺点 5.使用场景 总结 前言 本文主要学习抽象工厂模式&#xff0c;抽象工厂模式创建的是对象家族&#xff0c;比如&#xff1a;苹果是一个产品&#xff0c;但是他不单单只生产手机&#xff0c;还…...

K8s是什么?

url address K8s是一个开源的容器编排平台&#xff0c;可以自动化&#xff0c;在部署&#xff0c;管理和扩展容器化应用过程中涉及的许多手动操作。 Kubernetes最初是由Google工程师作为Borg项目开发和设计的&#xff0c;后于2015年捐赠给云原生计算基金会&#xff08;CNCF&a…...

【网站项目】SpringBoot796水产养殖系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...