【JS】基于node-media-server搭建流媒体服务器示例

😏★,°:.☆( ̄▽ ̄)/$:.°★ 😏
这篇文章主要介绍基于node-media-server搭建流媒体服务器示例。
学其所用,用其所学。——梁启超
欢迎来到我的博客,一起学习,共同进步。
喜欢的朋友可以关注一下,下次更新不迷路🥞

文章目录

    • :smirk:1. node-media-server介绍
    • :blush:2. 环境安装与配置
    • :satisfied:3. 应用示例

😏1. node-media-server介绍

node-media-server 是一个基于 Node.js 的流媒体服务器,它提供了构建和管理实时音视频流媒体应用程序所需的功能。它是一个开源项目,具有灵活性和可扩展性,适用于各种流媒体应用场景。

以下是一些 node-media-server 的特点和功能:

1.RTMP支持:node-media-server 支持 RTMP(Real-Time Messaging Protocol)协议,用于接收和传输实时的音视频流。RTMP 适用于实时直播和互动应用等场景。

2.多路并发流支持:node-media-server 具有多路并发流处理能力,可以同时处理多个流媒体的接收、转码、推流和录制等操作。

3.高性能和低延迟:node-media-server 的设计注重高性能和低延迟,使其适用于实时应用场景,如实时直播、互动直播和视频聊天等。

4.支持多种编码格式:node-media-server 支持多种常用的音视频编码格式,如 H.264、AAC、VP8 等,使其能够处理不同类型的流媒体数据。

5.功能丰富的 API:node-media-server 提供了丰富的 API,方便开发人员进行配置和管理。你可以通过编写代码来定制和扩展服务器的功能。

6.高度可配置:node-media-server 具有灵活的配置选项,允许你根据特定需求进行定制。你可以配置服务器的端口、流媒体路径、认证方式等。

😊2. 环境安装与配置

# 安装nodejs和ffmpeg
sudo apt install nodejs ffmpeg
# 安装node-media-server
npm install node-media-server

😆3. 应用示例

创建app.js,写入:

const NodeMediaServer= require('node-media-server');
const config = {
    rtmp: {
        port: 1935,
        chunk_size: 60000,
        gop_cache: true,
        ping: 60,
        ping_timeout: 30
    },
    http: {
        port: 8000,
        allow_origin: '*',
    }
};
 
var nms = new NodeMediaServer(config)
nms.run();

运行该程序:node app.js

准备好一个mp4视频,用ffmpeg命令行推流(也可自己写程序):

ffmpeg -re -i input.mp4 -c:v copy -c:a copy -f flv rtmp://localhost:1935/live/stream_name

最后效果示例,地址在http://localhost:8000/admin/

在这里插入图片描述

另外,也可以用C++程序来实现视频推流,下面是一个示例:

// defer.h
#ifndef FFMPEG_EXAMPLES_DEFER_H
#define FFMPEG_EXAMPLES_DEFER_H

#include <utility>

template<typename F> class defer_raii
{
public:
    // copy/move construction and any kind of assignment would lead to the cleanup function getting
    // called twice. We can't have that.
    defer_raii(defer_raii&&)                 = delete;
    defer_raii(const defer_raii&)            = delete;
    defer_raii& operator=(const defer_raii&) = delete;
    defer_raii& operator=(defer_raii&&)      = delete;

    // construct the object from the given callable
    template<typename FF> defer_raii(FF&& f) : cleanup_function(std::forward<FF>(f)) {}

    // when the object goes out of scope call the cleanup function
    ~defer_raii() { cleanup_function(); }

private:
    F cleanup_function;
};

template<typename F> defer_raii<F> defer_func(F&& f) { return { std::forward<F>(f) }; }

#define DEFER_ACTUALLY_JOIN(x, y) x##y
#define DEFER_JOIN(x, y)          DEFER_ACTUALLY_JOIN(x, y)
#ifdef __COUNTER__
#define DEFER_UNIQUE_VARNAME(x) DEFER_JOIN(x, __COUNTER__)
#else
#define DEFER_UNIQUE_VARNAME(x) DEFER_JOIN(x, __LINE__)
#endif

#define defer(lambda__)                                                                                    \
    [[maybe_unused]] const auto& DEFER_UNIQUE_VARNAME(_defer_) = defer_func([&]() { lambda__; })

#endif // !FFMPEG_EXAMPLES_DEFER_H
// main.cpp
extern "C" {
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/time.h>
}

#define __STDC_CONSTANT_MACROS
#include <stdint.h>

#include "defer.h"
// #include "logging.h"
#include <iostream>
#include <chrono>
#include <thread>
#include "fmt/format.h"

int main(int argc, char* argv[])
{
    // Logger::init(argv[0]);

    if (argc < 3) {
        std::cout << "pushing <input_video> <rtmp_name>" << std::endl;
        return -1;
    }

    const char * in_filename = argv[1];
    const char * rtmp_name = argv[2];

    AVFormatContext * decoder_fmt_ctx = nullptr;
    avformat_open_input(&decoder_fmt_ctx, in_filename, nullptr, nullptr);
    avformat_find_stream_info(decoder_fmt_ctx, nullptr);

    int video_stream_idx = av_find_best_stream(decoder_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    // CHECK(video_stream_idx >= 0);

    // decoder
    auto decoder = avcodec_find_decoder(decoder_fmt_ctx->streams[video_stream_idx]->codecpar->codec_id);
    // CHECK_NOTNULL(decoder);

    // decoder context
    AVCodecContext * decoder_ctx = avcodec_alloc_context3(decoder);
    // CHECK_NOTNULL(decoder_ctx);

    avcodec_parameters_to_context(decoder_ctx, decoder_fmt_ctx->streams[video_stream_idx]->codecpar);
    avcodec_open2(decoder_ctx, decoder, nullptr);

    av_dump_format(decoder_fmt_ctx, 0, in_filename, 0);

    //
    // output
    //
    AVFormatContext * encoder_fmt_ctx = nullptr;
    avformat_alloc_output_context2(&encoder_fmt_ctx, nullptr, "flv", nullptr);
    avformat_new_stream(encoder_fmt_ctx, nullptr);

    // encoder
    auto encoder = avcodec_find_encoder_by_name("libx264");
    // CHECK_NOTNULL(encoder);

    AVCodecContext *encoder_ctx = avcodec_alloc_context3(encoder);
    // CHECK_NOTNULL(encoder_ctx);

    AVDictionary* encoder_options = nullptr;
    av_dict_set(&encoder_options, "crf", "23", AV_DICT_DONT_OVERWRITE);
    av_dict_set(&encoder_options, "threads", "auto", AV_DICT_DONT_OVERWRITE);
    defer(av_dict_free(&encoder_options));

    // encoder codec params
    encoder_ctx->height = decoder_ctx->height;
    encoder_ctx->width = decoder_ctx->width;
    encoder_ctx->pix_fmt = decoder_ctx->pix_fmt;
    encoder_ctx->sample_aspect_ratio = decoder_ctx->sample_aspect_ratio;
    encoder_ctx->framerate = av_guess_frame_rate(decoder_fmt_ctx, decoder_fmt_ctx->streams[video_stream_idx], nullptr);

    // time base
    encoder_ctx->time_base = av_inv_q(encoder_ctx->framerate);
    encoder_fmt_ctx->streams[0]->time_base = encoder_ctx->time_base;

    avcodec_open2(encoder_ctx, encoder, &encoder_options);
    avcodec_parameters_from_context(encoder_fmt_ctx->streams[0]->codecpar, encoder_ctx);

    if (!(encoder_fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
        avio_open(&encoder_fmt_ctx->pb, rtmp_name, AVIO_FLAG_WRITE);
    }

    avformat_write_header(encoder_fmt_ctx, nullptr);

    av_dump_format(encoder_fmt_ctx, 0, rtmp_name, 1);

    AVPacket * in_packet = av_packet_alloc();
    defer(av_packet_free(&in_packet));
    AVPacket * out_packet = av_packet_alloc();
    defer(av_packet_free(&out_packet));
    AVFrame * in_frame = av_frame_alloc();
    defer(av_frame_free(&in_frame));

    int64_t first_pts = AV_NOPTS_VALUE;

    while(av_read_frame(decoder_fmt_ctx, in_packet) >= 0) {
        if (in_packet->stream_index != video_stream_idx) {
            continue;
        }

        int ret = avcodec_send_packet(decoder_ctx, in_packet);
        while(ret >= 0) {
            av_frame_unref(in_frame);
            ret = avcodec_receive_frame(decoder_ctx, in_frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                std::cout << "[PUSHING] avcodec_receive_frame()";
                return ret;
            }

            in_frame->pict_type = AV_PICTURE_TYPE_NONE;

            // sleep @{
            first_pts = first_pts == AV_NOPTS_VALUE ? av_gettime_relative() : first_pts;

            int64_t ts = av_gettime_relative() - first_pts;

            int64_t pts_us = av_rescale_q(in_frame->pts, encoder_fmt_ctx->streams[0]->time_base, { 1, AV_TIME_BASE });
            int64_t sleep_us = std::max<int64_t>(0, pts_us - ts);

            std::cout << fmt::format("[PUSHING] pts = {:>6.3f}s, ts = {:>6.3f}s, sleep = {:>4d}ms, frame = {:>5d}, fps = {:>5.2f}",
                                     pts_us / 1000000.0, ts / 1000000.0, sleep_us / 1000,
                                     encoder_ctx->frame_number, encoder_ctx->frame_number / (ts / 1000000.0));
            av_usleep(sleep_us);
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            // @}

            ret = avcodec_send_frame(encoder_ctx, in_frame);
            while(ret >= 0) {
                av_packet_unref(out_packet);
                ret = avcodec_receive_packet(encoder_ctx, out_packet);

                if(ret == AVERROR(EAGAIN)) {
                    break;
                } else if(ret == AVERROR_EOF) {
                    std::cout << "[PUSHING] EOF";
                    break;
                } else if(ret < 0) {
                    std::cout << "[PUSHING] avcodec_receive_packet()";
                    return ret;
                }

                out_packet->stream_index = 0;
                av_packet_rescale_ts(out_packet, decoder_fmt_ctx->streams[video_stream_idx]->time_base, encoder_fmt_ctx->streams[0]->time_base);

                if (av_interleaved_write_frame(encoder_fmt_ctx, out_packet) != 0) {
                    std::cout <<"[PUSHING] av_interleaved_write_frame()";
                    return -1;
                }
            }
        }
        av_packet_unref(in_packet);
    }

    avformat_close_input(&decoder_fmt_ctx);
    avformat_free_context(encoder_fmt_ctx);

    avcodec_free_context(&decoder_ctx);
    avcodec_free_context(&encoder_ctx);

    return 0;
}

编译与运行:

g++ -o main main.cpp -lavformat -lavcodec -lavutil -lfmt -D__STDC_CONSTANT_MACROS
./main test.mp4 rtmp://127.0.0.1:1935/live/test
# 还不太完善,可改进

请添加图片描述

以上。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】常见算法详解第2篇:KNN之kd树介绍(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习&#xff0c;伴随浅显易懂的数学知识&#xff0c;让大家掌握机器学习常见算法原理&#xff0c;应用Scikit-learn实现机器学习算法的应用&#xff0…

Windows Server安装部署FTP服务

文章目录 建立FTP目录通过IIS在Server上安装FTP服务配置FTP站点配置身份验证和授权测试FTP服务FTP软件推荐FTP客户端软件FTP服务器软件适合Ubuntu的FTP软件 推荐阅读 在Windows操作系统中安装和配置FTP服务&#xff0c;主要是基于Internet Information Services (IIS)的FTP服务…

ABAP 笔记--内表结构不一致,无法更新数据库MODIFY和UPDATE

目录 ABAP 笔记内表结构不一致&#xff0c;无法更新数据库MODIFY和UPDATE ABAP 笔记 内表结构不一致&#xff0c;无法更新数据库 MODIFY和UPDATE 如果是使用MODIFY或者UPDATE

【2024.2.3练习】修剪灌木

题目描述 题目分析 数学思维题。首先容易看出从左往右树的最大高度是对称的&#xff0c;不妨只看前棵树&#xff0c;由于此时右边的灌木数量不少于左边灌木数量&#xff0c;所以要想长到最高一定是修剪到最右边再剪回来&#xff0c;设该树右边共有棵树&#xff0c;那么它能长到…

python基于django的公交线路查询系统mf383

1.个人信息的管理&#xff1a;对用户名&#xff0c;密码的增加、删除等 2.线路信息的管理&#xff1a;对线路的增加、修改、删除等 3.站点信息的管理&#xff1a;对站点的增加、修改、删除等 4.车次信息的管理&#xff1a;对车次的增加、修改、删除等 5.线路查询、站点查询 …

JAVASE进阶:Collection高级(1)——源码分析contains方法、lambda遍历集合

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;JAVASE进阶&#xff1a;函数式编程——lambda表达式替代匿名内部类 &#x1f4da;订阅专栏&#xff1a;JAVASE进阶 希望文章对你…

2024 高级前端面试题之 HTTP模块 「精选篇」

该内容主要整理关于 HTTP模块 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTTP模块精选篇 1. HTTP 报文的组成部分2. 常见状态码3. 从输入URL到呈现页面过程3.1 简洁3.2 详细 4. TCP、UDP相关5. HTTP2相关6. https相关7. WebSocket的…

docker-compose Install HertzBeat

HertzBeat前言 HertzBeat 赫兹跳动 是一个拥有强大自定义监控能力,高性能集群,兼容 Prometheus,无需 Agent 的开源实时监控告警系统。 易用友好的开源实时监控告警系统,无需Agent,高性能集群,兼容Prometheus,强大自定义监控能力。​ 集 监控+告警+通知 为一体,支持对…

ToF传感器在移动机器人中的作用

原创 | 文 BFT机器人 在日新月异的机器人技术领域&#xff0c;技术的无缝整合正引领着人类与机器交互方式的革新潮流。ToF传感器作为变革性创新的一个例子&#xff0c;对移动机器人更好地感知周围环境起到了决定性的作用。 ToF传感器与激光雷达技术在创建深度图方面有着异曲同…

SpringBoot实战2

目录 1.如何返回两个类型的数据&#xff1f;User和Booth 2.如何使用MyBatis遍历一个数组进行查询&#xff1f; 3.前端要的数据太多太杂&#xff0c;我们拼接多个List&#xff0c;前端找数据困难&#xff0c;浪费时间。因此我们进行三表联表查询。 1.首先创建一个vo包&#x…

c++ STL less 的视角

c less 函数在不同的地方感觉所起的作用是不一样的&#xff0c; 这中间原因是 less 的视角不一样&#xff0c; 下面尝试给出解释下&#xff0c; 方便记忆 1、 左右视角 符合 排序sort less(value, element&#xff09; less 表示一种 “符合关系“&#xff0c; 表示sort 后…

大数据环境搭建(一)-Hive

1 hive介绍 由Facebook开源的,用于解决海量结构化日志的数据统计的项目 本质上是将HQL转化为MapReduce、Tez、Spark等程序 Hive表的数据是HDFS上的目录和文件 Hive元数据 metastore&#xff0c;包含Hive表的数据库、表名、列、分区、表类型、表所在目录等。 根据Hive部署模…

蓝桥杯第九届省赛题-----彩灯控制系统笔记

题目要求&#xff1a; 一、 基本要求 1.1 使用 CT107D 单片机竞赛板&#xff0c;完成“彩灯控制器”功能的程序设计与调 试&#xff1b; 1.2 设计与调试过程中&#xff0c;可参考组委会提供的“资源数据包”&#xff1b; 1.3 Keil 工程文件以准考证号命名&#xff0c…

在VM虚拟机搭建NFS服务器

NFS共享要求如下&#xff1a; &#xff08;1&#xff09;共享“/mnt/自已姓名的完整汉语拼音”目录&#xff0c;允许XXX网段的计算机访问该共享目录&#xff0c;可进行读写操作。&#xff08;说明&#xff1a;XXX网段&#xff0c;请根据你的规划&#xff0c;再具体指定&#xf…

VUE开发记录

1、VUE模板传递参数到JS方法 <select-language :value"item.language" change"selectLanguage($event, key)"></select-language>selectLanguage(value, key){console.log(value, key) }, 2、Element框架el-form-item自定义label和内容 <…

远秋医学培训系统未授权查看密码

指纹特征 title"远秋医学培训报名系统v1.0"漏洞复现 POC&#xff1a;/User/ManagerList.aspx?ty1&ty1

好的问卷设计标准:确保数据质量与准确性的关键要素

问卷的主要由三个部分组成&#xff1a;问卷说明、问卷主题、问卷结束。而这三个部分又包含了很多因素&#xff0c;比如问卷主题、问卷标题、问卷题目、问卷调查对象等。制作问卷不仅仅是简单的问题罗列&#xff0c;然后进行发放。不同质量的调查问卷会反馈出不一样的效果&#…

高级变量赋值和变量的间接引用

1.高级变量赋值 var${str-lucky} 变量配置方式 var${str:-lucky} 变量配置方式 var${strlucky} 变量配置方式 2.变量的间接引用 eval 命令 eval命令将会首先扫描命令行进行所有的置换&#xff0c;然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变…

MySQL学习记录——일 MySQL 安装、配置

文章目录 1、卸载内置环境2、安装MySQL3、启动4、登录5、配置my.cnf 当前环境是1核2G云服务器&#xff0c;CentOS7.6。要在root用户下进行操作 1、卸载内置环境 云服务器中有可能会自带mysql还有mariadb这样的数据库服务&#xff0c;在安装我们mysql前&#xff0c;得先查找一下…

Java学习六、数组的定义与使用

一、数组的创建及初始化 数组是相同类型元素的集合&#xff0c;在内存中是一段连续的空间。 1.数组的创建 T[] 数组名 new T[N]; T&#xff1a;表示数组中存放元素的类型 N:表示数组长度 int[] array1 new int[10];//创建一个可以容纳10个int类型元素的数组 double[] array…