Wang's blog

架设RTMP流媒体服务器并使用python进行推流

Published on

1、服务器架设

服务器不一定要安装在推流程序所在的机器上,也可以安装在发送、接收双方均可访问到的第三台机器上。

安装方式可以使用docker镜像或直接编译安装。

使用docker镜像

github地址:https://github.com/alfg/docker-nginx-rtmp

安装并运行:

docker pull alfg/nginx-rtmp
docker run -it -p 1935:1935 -p 8080:80 --rm alfg/nginx-rtmp

之后即可推流至:

rtmp://<server ip>:1935/stream/$STREAM_NAME

自行编译安装

# 安装依赖库
sudo apt install -y libpcre3 libpcre3-dev openssl libssl-dev zlib1g-dev
# 下载nginx源码并解压
wget http://nginx.org/download/nginx-1.17.8.tar.gz
tar -xf nginx-1.17.8.tar.gz
# 下载nginx-rtmp-module模块源码
git clone https://github.com/arut/nginx-rtmp-module.git
# 编译并安装带nginx-rtmp-module模块的nginx
cd nginx-1.17.8
./configure --add-module=../nginx-rtmp-module
sudo make install

配置nginx,配置文件地址为:/usr/local/nginx/conf/nginx.conf。由于目前浏览器已经禁止使用Flash,不能直接播放rtmp流,因此可以选择添加hls配置。

worker_processes  1;

events {
    worker_connections  1024;
}

rtmp {          
        server {    
                listen 1935;    
                chunk_size 4096;
 
                application stream {
                        live on;   
                        record off;
                        # hls配置
                        hls on;               
                        hls_path /var/www/hls;
                        hls_fragment 3;        
                        hls_playlist_length 60;
                }
        }
}
       
http {
        server {   
                listen 80;

                # hls配置
                location /hls {
                    # Disable cache 
                    add_header Cache-Control no-cache;

                    # CORS setup 
                    add_header 'Access-Control-Allow-Origin' '*' always;
                    add_header 'Access-Control-Expose-Headers' 'Content-Length';

                    # allow CORS preflight requests 
                    if ($request_method = 'OPTIONS') {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Max-Age' 1728000;
                        add_header 'Content-Type' 'text/plain charset=UTF-8';
                        add_header 'Content-Length' 0;
                        return 204;
                    }

                    types {
                        application/vnd.apple.mpegurl m3u8;
                        video/mp2t ts;
                    }

                    root /var/www;
                }
        }
}

运行nginx:

sudo /usr/local/nginx/sbin/nginx

之后即可推流至:

rtmp://<server ip>:1935/stream/$STREAM_NAME

可以直接播放上面的rtmp流,也可以播放hls流:

http://<server ip>/hls/$STREAM_NAME

停止nginx:

sudo /usr/local/nginx/sbin/nginx -s stop

2、使用python推流

python可以使用ffmpeg命令进行推流,有两种方式:直接调用ffmpeg命令,或使用ffmpeg-python库。

安装ffmpeg:

sudo apt install -y ffmpeg

直接调用ffmpeg命令

使用subprocess开启ffmpeg子进程,在python中读取每一帧并进行处理,之后通过管道将图像交给子进程进行推流。

import cv2
import subprocess

# 设置视频文件与推流地址
filename = 'test.mp4'
push_url = 'rtmp://localhost:1935/stream/test'

# 读取视频属性
cap = cv2.VideoCapture(filename)
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# ffmpeg命令
command = ['ffmpeg',
           '-y',
           '-f', 'rawvideo',
           '-vcodec', 'rawvideo',
           '-pix_fmt', 'bgr24',
           '-s', "{}x{}".format(width, height),
           '-r', str(fps),
           '-i', '-',
           '-c:v', 'libx264',
           '-pix_fmt', 'yuv420p',
           '-preset', 'ultrafast',
           '-f', 'flv',
           push_url]

# 开启子进程
process = subprocess.Popen(command, stdin=subprocess.PIPE)

while True:
    # 读取每一帧
    ret, frame = cap.read()
    if not ret:
        break
    # 处理
    # frame = handle(frame)
    # 交给子进程推流
    process.stdin.write(frame.tobytes())

使用ffmpeg-python库

ffmpeg-python库将ffmpeg命令进行封装,实际仍会启动ffmpeg子进程,但是代码更加简洁。

github地址:https://github.com/kkroening/ffmpeg-python

安装

pip install ffmpeg-python

使用

import cv2
import ffmpeg

# 设置视频文件与推流地址
filename = 'test.mp4'
push_url = 'rtmp://localhost:1935/stream/test'

# 读取视频属性
cap = cv2.VideoCapture(filename)
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

# 开启ffmpeg子进程
process = (
    ffmpeg
    .input('pipe:', format='rawvideo', pix_fmt='bgr24', s='{}x{}'.format(width, height), r=fps)
    .output(push_url, vcodec='libx264', pix_fmt='yuv420p', preset='ultrafast', format='flv')
    .run_async(pipe_stdin=True)
)

while True:
    # 读取每一帧
    ret, frame = cap.read()
    if not ret:
        break
    # 处理
    # frame = handle(frame)
    # 交给子进程推流
    process.stdin.write(frame.tobytes())

# 关闭子进程输入并等待结束
process.stdin.close()
process.wait()