Ubuntu 18.04 利用ffmpeg+nginx 搭建Hls/rtmp/http_flv服务器 python

1:环境

Ubuntu 18.04
nginx 1.12.2(之前是用1.18.0,发现版本不支持flv_live)
ffmpeg 4.4.1
python 3.6

2:安装nginx-rtmp版本

2.1:安装依赖包

sudo apt-get update sudo apt-get install libxml2-dev sudo apt-get
install build-essential sudo apt-get install openssl sudo apt-get
install libssl-dev sudo apt-get install make sudo apt-get install curl
sudo apt-get install libcurl4-gnutls-dev sudo apt-get install
libjpeg-dev sudo apt-get install libpng-dev sudo apt-get install
libtool-bin sudo apt-get install bison sudo apt-get install zlib1g-dev
libpcre3 libpcre3-dev libssl-dev libxslt1-dev libgeoip-dev
libgoogle-perftools-dev libperl-dev libtool sudo apt-get install
libxml2-dev build-essential openssl libssl-dev make curl
libcurl4-gnutls-dev libjpeg-dev libpng-dev libtool-bin bison
zlib1g-dev libpcre3 libpcre3-dev libssl-dev libxslt1-dev libgeoip-dev
libgoogle-perftools-dev libperl-dev libtool

2.2:下载ngnix+nginx-rtmp-module

1、下载新版本,到官网复制下载链接

wget http://nginx.org/download/nginx-1.12.2.tar.gz

2、解压 tar -zxvf nginx-1.12.2.tar.gz

tar -zxvf nginx-1.12.2.tar.gz

3、下载nginx-http-flv-module(该模块包含nginx-rtmp-module所有的功能,为了解决flash不能用的问题)(下载到nginx-1.18.0同目录)

https://github.com/winshining/nginx-http-flv-module #nginx-http-flv-module下载地址
sudo unzip nginx-http-flv-module-master.zip # 解压zip文件

4、配置并编译安装nginx

cd nginx-1.12.2
# 设置编译参数,
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-stream --with-mail=dynamic --add-module=../nginx-http-flv-module-master
sudo make
sudo make install
mask结束过程中出现离开目录,不用管,直接跳过

5、启动nginx

sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
#注意:-c 指定配置文件的路径,不加的话,nginx会自动加载默认路径的配置文件,可以通过 -h查看帮助命令。

6、查看是否启动成功

cd /usr/local/nginx/sbin
./nginx -t

出现以下内容,则启动成功

python媒体流服务器部署到手机 python搭建流媒体服务器_python


设置nginx开启自启服务,请查阅:

3:配置启动服务支持hls

sudo vim /usr/local/nginx/conf/nginx.conf
# 1:将以下代码加到http 同级
rtmp {

    server {

        listen 1935;  #监听的端口

        chunk_size 128;  # 数据传输的大小,不能低于128

        # 该块为手动添加,以支持http-flv直播
        application http_flv {
            live on;
            gop_cache on; #open GOP cache for reducing the wating time for the first picture of video
        }

        # 该块为手动添加,以支持http-hls直播,若无需hls直播,该块可不用添加
        application hls {
            live on;  # 直播模式
            hls on;  # 这个参数把直播服务器改成实时回放服务器
            hls_path /home/taoxifa/www/hls;  #视频流存放地址
            hls_fragment 5s;
            hls_playlist_length 15s;
            hls_continuous on; #连续模式。
            hls_cleanup on;    #对多余的切片进行删除。
            hls_nested on;     #嵌套模式。
        }

    }
}
# 2:将以下代码加入到http下的service下,与原有的location同级
        # 该块为手动添加,HTTP播放flv直播流功能
        location /live0 {
            flv_live on;     #open flv live streaming (subscribe)
            chunked_transfer_encoding  on; #open 'Transfer-Encoding: chunked' response
            add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
            add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
        }
        
        # 该块为手动添加,Http播放HLS功能,若无需HLS,则该块省略
        location /live {  #添加视频流存放地址。
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
            #访问权限开启,否则访问这个地址会报403
            autoindex on;
            alias /home/taoxifa/www/hls;#视频流存放地址,与上面的hls_path相对应,这里root和alias的区别可自行百度
            expires -1;
            add_header Cache-Control no-cache;
            #防止跨域问题
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }

修改完配置文件后,由于rtmp使用了1935端口,所以需要开启1935端口

sudo iptables -I INPUT -p tcp --dport 1935 -j ACCEPT # 若需要开放其他端口,则将1935替换成其他的即可
sudo iptables-save  # 临时保存生效
# 如需长期生效,则使用以下命令
sudo apt-get install iptables-persistent
sudo netfilter-persistent save
sudo netfilter-persistent reload

重启nginx服务
sudo /usr/local/nginx/sbin/nginx -s reload -c /usr/local/nginx/conf/nginx.conf

4:使用python推流,代码

import cv2 as cv
import time
import subprocess as sp
import multiprocessing
import platform
import psutil


class stream_pusher(object):
    def __init__(self, rtmp_url=None, raw_frame_q=None):  # 类实例化的时候传入rtmp地址和帧传入队列
        self.rtmp_url = rtmp_url  # 播放的地址
        self.raw_frame_q = raw_frame_q  # 公共队列
        fps = 30  # 设置帧速率
        # 设置分辨率
        width = 640  # 宽
        height = 480  # 高
        # 设置FFmpeg命令文本
        # self.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',
        #                 '-tune', 'zerolatency',  # 加了这一行设置零延时后又变快了2.5->1.3
        #                 '-f', 'flv',
        #                 self.rtmp_url]

        # self.command = ['ffmpeg',
        #            '-y',
        #            '-f', 'rawvideo',
        #            '-vcodec', 'rawvideo',
        #            '-pix_fmt', 'bgr24',
        #            '-s', "{}x{}".format(width, height),
        #            '-i', '-',
        #            '-c:v', 'libx264',
        #            '-preset', 'faster',
        #            '-f', 'flv',
        #            self.rtmp_url]
        self.command = ['ffmpeg',
                        '-re', '-y', '-an',
                        '-f', 'rawvideo',
                        '-vcodec', 'rawvideo',
                        '-pix_fmt', 'bgr24',
                        '-s', "{}x{}".format(width, height),
                        '-r', '25',
                        '-i', '-',
                        '-c:v', 'h264',
                        '-pix_fmt', 'yuv420p', '-flvflags', 'no_duration_filesize',
                        '-f', 'flv',
                        '-preset:v', 'ultrafast',  # 增加这一行后延时少了一秒,4->2.5
                        '-tune', 'zerolatency',  # 加了这一行设置零延时后又变快了2.5->1.3
                        # '-stream_loop','-1',
                        '-flvflags', 'add_keyframe_index',
                        self.rtmp_url]


    # 对获取的帧做一些画面处理的方法,返回完成处理的帧。
    def __frame_handle__(self, raw_frame, text, shape1, shape2):
        #帧用cv2进行一些处理,比如写上文本,画矩形等
        this_time = time.time()
        return(raw_frame)

    # 向服务器推送
    def push_frame(self):
        # 指定在哪些cpu核上运行。我的ARM有6核,前4核较慢做辅助处理。后2核较快,做核心程序的处理。这里指定推流动作在慢的4个核中运行
        p = psutil.Process()
        # p.cpu_affinity([0,1,2,3])
        p.cpu_affinity([0])
        # 配置向os传递命令的管道
        p = sp.Popen(self.command, stdin=sp.PIPE)
        num = 0
        while True:
            if not self.raw_frame_q.empty():  # 如果输入管道不为空
                num += 1
                # poll()返回该子进程的状态,0正常结束,1sleep,2子进程不存在,-15 kill,None正在运行
                # if p.poll() is not None:
                #     print(p.poll())
                #     # pipe = sp.Popen(command, stdin=sp.PIPE, env=my_env)
                #     time.sleep(3)+6
                # 把帧和相关信息从输入队列中取出
                raw_frame, text, shape1, shape2 = self.raw_frame_q.get()
                # if num == 100:
                #     print("start sleep 50")
                #     time.sleep(50)
                #     print("end sleep 50")
                # if num >= 100:
                #     print("超时后的,第%d次写入" % (num - 99))
                #     time.sleep(5)

                # 对获取的帧进行画面处理
                frame = self.__frame_handle__(raw_frame, text, shape1, shape2)

                # 把内容放入管道,放入后有os自己去执行
                p.stdin.write(frame.tostring())
            else:
                time.sleep(0.01)


    # 启动运行
    def run(self):
        # 定义一个子进程
        push_frame_p = multiprocessing.Process(target=self.push_frame, args=())
        push_frame_p.daemon = True # 把子进程设置为daemon方式
        push_frame_p.start() # 运行子进程


if __name__ == '__main__':
    # 根据不同的操作系统,设定读取哪个摄像头
    if platform.system() == 'Linux': # 如果是Linux系统
        cap = cv.VideoCapture(10) # 绑定编号为10的摄像头
        cap.set(3, 640) # 设置摄像头画面的宽
        cap.set(4, 480) # 设置摄像头画面的高
    elif platform.system() == 'Windows': # 如果是苹果的OS X系统
        # cap = cv.VideoCapture("C:/Users/86187/Desktop/test2.mp4")   # 绑定编号为0的摄像头
        cap = cv.VideoCapture(0)   # 绑定编号为0的摄像头
        cap.set(3, 640)
        cap.set(4, 480)
    else: # 没有windows系统,所以就不判断了
        exit(0)

    rtmpUrl = "rtmp://ip:1935/application名称/码流名称(子定义)"  #例:192.168.0.2:1935/http_flv/stream0
    # 需要什么格式的直播流,application名称就设置为nginx一致的application名称。目前之前rtmp、hls、http_flv
    raw_q = multiprocessing.Queue()  # 定义一个向推流对象传入帧及其他信息的队列
    my_pusher = stream_pusher(rtmp_url=rtmpUrl, raw_frame_q=raw_q)  # 实例化一个对象
    my_pusher.run()  # 让这个对象在后台推送视频流
    for i in range(100000):
        _, raw_frame = cap.read()
        print("frame_shape", raw_frame.shape)
        info = (raw_frame, '2', '3', '4') # 把需要送入队列的内容进行封装
        if not raw_q.full(): # 如果队列没满
            raw_q.put(info) # 送入队列
        cv.imshow("img", raw_frame)
        cv.waitKey(1)
    cap.release()
    cv.destroyAllWindows()
    print('finish')

注意,Python代码中的rtmpUrl的路径需要对应nginx.conf中的application 和location

python媒体流服务器部署到手机 python搭建流媒体服务器_nginx_02

5:拉流测试(启动nginx和python后,即可使用以下链接测试)

rtmp地址:rtmp://ip:1935/hls/stream0
http地址:http://ip:80/live/stream0/index.m3u8 (如果不想使用80端口,则在nginx.conf文件中修改http-service下的listen为自己想要的端口即可,同时也需要同步开放该端口)
http_flv播放地址:http://192.168.xx.xxx/live0?port=1935&app=http_flv&stream=stream0

我是用vlc工具打开的

python媒体流服务器部署到手机 python搭建流媒体服务器_ide_03

python媒体流服务器部署到手机 python搭建流媒体服务器_python媒体流服务器部署到手机_04