【Ubuntu 上搭建 Nginx-RTMP 服务】

本章目录:

环境

  • 服务端系统:Ubuntu
  • 播放器 :Windows 下使用 ffplay 命令

1. 安装依赖

在 Ubuntu 系统中,执行以下命令安装必要的依赖项:

bash 复制代码
sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev

2. 创建 Nginx 编译目录

bash 复制代码
$ mkdir my_nginx_rtmp
$ cd my_nginx_rtmp/
$ pwd
/home/togevision/wzt/my_nginx_rtmp

3. 下载 Nginx 和 Nginx-RTMP-Module

bash 复制代码
wget http://nginx.org/download/nginx-1.21.6.tar.gz
wget https://github.com/arut/nginx-rtmp-module/archive/master.zip

# 解压压缩包
tar -xf nginx-1.21.6.tar.gz
unzip master.zip

4. 编译 Nginx 并添加 RTMP 模块

进入解压后的 Nginx 源码目录,编译 Nginx,并添加 RTMP 模块:

bash 复制代码
cd nginx-1.21.6/
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-master
make -j4 && sudo make install

成功输出示例

bash 复制代码
checking for C compiler ... found
...
creating objs/Makefile
Configuration summary
  + using system PCRE library
  + using system OpenSSL library
  + RTMP module added
...

5. 验证 Nginx 安装成功

bash 复制代码
togevision@TG:~/wzt/my_nginx_rtmp$ /usr/local/nginx/sbin/nginx -v
nginx version: nginx/1.21.6

6. 配置环境变量

将 Nginx 添加到环境变量中:

bash 复制代码
sudo vi ~/.bashrc

在文件末尾添加:

bash 复制代码
export PATH=$PATH:/usr/local/nginx/sbin/

执行以下命令生效:

bash 复制代码
source ~/.bashrc

7. 修改 Nginx 配置文件

编辑 Nginx 配置文件:

bash 复制代码
sudo vim /usr/local/nginx/conf/nginx.conf

在文件末尾添加以下内容以启用 RTMP 模块:

css 复制代码
rtmp {
    server {
        listen 1935;
        chunk_size 4096;

        application live {
            live on;
            record off;
        }
    }
}

8. 启动 Nginx 服务

bash 复制代码
nginx

启动后无提示,但可以通过以下命令确认状态


查看 Nginx 是否启动成功

bash 复制代码
ps -ef | grep nginx | grep -v grep

输出示例

plain 复制代码
rog      1312637    2257  0 15:51 ?        00:00:00 nginx: master process nginx
rog      1314761 1312637  0 16:01 ?        00:00:06 nginx: worker process

查看端口监听状态

bash 复制代码
netstat -anp | grep nginx

输出示例

plain 复制代码
tcp        0      0 0.0.0.0:1935            0.0.0.0:*               LISTEN      1312637/nginx: mast
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1312637/nginx: mast
  • 端口 80:HTTP 服务默认监听端口
  • 端口 1935:RTMP 服务默认监听端口

8. 常见问题及解决方法

1. 缺少 zlib

执行以下命令安装:

bash 复制代码
sudo apt-get install zlib1g-dev

输出示例

plain 复制代码
Reading package lists... Done
...

2. 找不到 openssl

~/.bashrc 文件中添加 OpenSSL 库位置:

bash 复制代码
export LD_LIBRARY_PATH=/home/rog/anaconda3/lib:$LD_LIBRARY_PATH
source ~/.bashrc

3. Nginx 端口冲突

如果出现以下错误:

plain 复制代码
nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)

修改 /usr/local/nginx/conf/nginx.conf 中的端口号为 1024 以上的端口,例如 8090


9. 测试推流与播放

必要资源下载:

测试视频下载

Linux下的x86版本 ffmpeg现成库下载

推流命令示例

服务端Ubuntu中推流:

ffmpeg -re -i ../res/video.mp4 -c copy -f flv rtmp://192.168.194.129:1935/live/1

客户端Windows中使用ffplay命令拉流:

ffplay.exe rtmp://192.168.194.129:1935/live/1

播放效果如下:

10. 使用C++ 和FFmpeg库实现相同RTMP效果:

目录结构如下

shell 复制代码
togevision@TG:~/wzt/av/rtmp_demo$ tree -L 2
.
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── Makefile
│   └── rtmp_demo
├── CMakeLists.txt
├── ffmpeg
│   ├── include
│   ├── lib
│   └── Makefile
├── ffmpeg_x86
│   ├── inc
│   └── lib
├── main.cpp
└── res
    └── video.mp4

9 directories, 8 files

main.cpp源码如下,FFmpeg实现RTMP:

cpp 复制代码
#include <iostream>
#include <chrono>
#include <thread>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/time.h>
}

// ffmpeg -re -i ../res/video.mp4 -c copy -f flv rtmp://192.168.194.129:1935/live/1

int main() {
    const char *input_file = "../res/video.mp4";
    const char *output_url = "rtmp://192.168.194.129:1935/live/1";

    // 初始化网络组件
    avformat_network_init();

    AVFormatContext *input_ctx = nullptr;
    if (avformat_open_input(&input_ctx, input_file, nullptr, nullptr) < 0) {
        std::cerr << "Could not open input file." << std::endl;
        return -1;
    }

    if (avformat_find_stream_info(input_ctx, nullptr) < 0) {
        std::cerr << "Failed to retrieve input stream information." << std::endl;
        avformat_close_input(&input_ctx);
        return -1;
    }

    AVFormatContext *output_ctx = nullptr;
    if (avformat_alloc_output_context2(&output_ctx, nullptr, "flv", output_url) < 0) {
        std::cerr << "Could not create output context." << std::endl;
        avformat_close_input(&input_ctx);
        return -1;
    }

    for (unsigned int i = 0; i < input_ctx->nb_streams; i++) {
        AVStream *in_stream = input_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(output_ctx, nullptr);
        if (!out_stream) {
            std::cerr << "Failed to allocate output stream." << std::endl;
            avformat_close_input(&input_ctx);
            avformat_free_context(output_ctx);
            return -1;
        }

        if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
            std::cerr << "Failed to copy codec parameters." << std::endl;
            avformat_close_input(&input_ctx);
            avformat_free_context(output_ctx);
            return -1;
        }
        out_stream->codecpar->codec_tag = 0;
    }

    if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&output_ctx->pb, output_url, AVIO_FLAG_WRITE) < 0) {
            std::cerr << "Could not open output URL." << std::endl;
            avformat_close_input(&input_ctx);
            avformat_free_context(output_ctx);
            return -1;
        }
    }

    if (avformat_write_header(output_ctx, nullptr) < 0) {
        std::cerr << "Error occurred when writing header to output." << std::endl;
        avformat_close_input(&input_ctx);
        if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
            avio_closep(&output_ctx->pb);
        avformat_free_context(output_ctx);
        return -1;
    }

    int64_t start_time = av_gettime_relative();
    AVPacket pkt;

    while (true) {
        if (av_read_frame(input_ctx, &pkt) < 0)
            break;

        AVStream *in_stream = input_ctx->streams[pkt.stream_index];
        AVStream *out_stream = output_ctx->streams[pkt.stream_index];

        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;

        // 计算每帧的发送时间
        int64_t pts_time = av_rescale_q(pkt.pts, out_stream->time_base, AVRational{1, AV_TIME_BASE});
        int64_t now_time = av_gettime_relative() - start_time;
        if (pts_time > now_time) {
            av_usleep(pts_time - now_time);
        }

        if (av_interleaved_write_frame(output_ctx, &pkt) < 0) {
            std::cerr << "Error writing frame." << std::endl;
            av_packet_unref(&pkt);
            break;
        }
        av_packet_unref(&pkt);
    }

    // 写入尾部信息
    if (av_write_trailer(output_ctx) < 0) {
        std::cerr << "Error occurred when writing trailer to output." << std::endl;
    }

    // 释放资源
    avformat_close_input(&input_ctx);
    if (!(output_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&output_ctx->pb);
    avformat_free_context(output_ctx);

    avformat_network_deinit();

    std::cout << "Streaming finished successfully!" << std::endl;

    return 0;
}

cmake代码如下:

bash 复制代码
cmake_minimum_required(VERSION 3.10)
project(FFmpegRTMPStreaming)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# SET(CMAKE_CXX_COMPILER "/home/togevision/toolchain/arm-AX620E-linux-uclibcgnueabihf/bin/arm-AX620E-linux-uclibcgnueabihf-g++")

# 指定头文件和库文件的路径
include_directories(${CMAKE_SOURCE_DIR}/ffmpeg_x86/inc)
link_directories(${CMAKE_SOURCE_DIR}/ffmpeg_x86/lib)
# include_directories(${CMAKE_SOURCE_DIR}/ffmpeg/include)
# link_directories(${CMAKE_SOURCE_DIR}/ffmpeg/lib/arm/uclibc)

# 添加可执行文件
add_executable(rtmp_demo main.cpp)

# 链接FFmpeg库
target_link_libraries(rtmp_demo avformat avcodec avutil swresample)

# 执行之前需要指定动态库路径:
# export LD_LIBRARY_PATH=/home/togevision/wzt/av/rtmp_demo/ffmpeg_x86/lib:$LD_LIBRARY_PATH

构建和执行步骤:

bash 复制代码
# 进入build目录,准备构建makefile
cd build/

# 执行cmake
cmake ..

# 执行make
make

# 查看生成
ls

# 指定动态库路径:
export LD_LIBRARY_PATH=/home/togevision/wzt/av/rtmp_demo/ffmpeg_x86/lib:$LD_LIBRARY_PATH

# 执行生成的目标程序
./rtmp_demo 

# windows下使用ffplay或其它播放器播放rtmp
ffplay.exe rtmp://192.168.194.129:1935/live/1

其它

Nginx相关源码库的官网链接:
软件名称 下载链接 个人使用版本 描述
Nginx http://nginx.org/en/download.html nginx-1.21.6.tar.gz 一个高性能的HTTP和反向代理服务器。
Pcre https://sourceforge.net/projects/pcre/files/pcre/8.45/ pcre-8.45.tar.gz 一个正则表达式库。(必须)
Zlib http://www.zlib.net/ zlib-1.3.1.tar.gz 一个开源的数据压缩库,提供了对数据的无损压缩和解压功能。(必须)
Openssl https://www.openssl.org/source/ openssl-1.1.0l.tar.gz 一个开源的加密库,提供了各种加密算法和安全协议的实现。(必须)
相关推荐
14_1113 分钟前
Cherno C++学习笔记 P51 创建并使用库
c++·笔记·学习
孙尚香蕉38 分钟前
Hadoop集群之间实现免密登录
linux·服务器
日拱一卒无有尽, 功不唐捐终入海1 小时前
springCloudGateWay使用总结
运维·服务器
云中飞鸿1 小时前
Ubuntu网络连接问题(笔记本更换wifi后,虚拟机连不上网络)
linux·运维·ubuntu
Niuguangshuo1 小时前
音频数据增强:提升音频信号质量的多种技术
人工智能·音视频·语音识别
zyplanke1 小时前
修改sshd默认配置,提升安全
linux·安全·ssh
开疆智能2 小时前
机器人技术:ModbusTCP转CCLINKIE网关应用
java·服务器·科技·机器人·自动化
就叫飞六吧2 小时前
51 单片机和 STM32 引脚命名对照表与解析
c++·stm32·单片机·嵌入式硬件·51单片机
霜雪殇璃2 小时前
c++对结构体的扩充以及类的介绍
开发语言·c++·笔记·学习
冉佳驹2 小时前
C++ ——— 匿名对象
c++·学习·类和对象·匿名对象