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
出现以下内容,则启动成功
设置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
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工具打开的