rtsp视频推流的一些总结


ffmpeg直接读取文件推流到rtsp服务器

项目中常常用到推流拉流能功能,那么除了用obs之外,是否可以直接用nodejs、python直接推流呢,答案是肯定的,这里介绍两种使用过的方法,用于个人测试,不够完善,仅仅是抛砖引玉。

首先,下面这条ffmepeg指令即可推流,

ffmpeg -re -stream_loop -1 -i G:\FFOutput\boat-1.mp4 -b:v 1000k -bufsize 1000k -maxrate 1500k -profile:v baseline -g 50 -vf "drawtext=fontfile=E:/FFOutput/simsun.ttf: text='201 channel %{localtime\:%Y\-%m\-%d %H-%M-%S}':fontsize=40:fontcolor=yellow:x=10:y=10"  -vcodec h264 -f rtsp  rtsp://127.0.0.1:8554/channel02

上面这条命令将会对本地的一个mp4文件进行循环播放,并推流到指定的rtsp服务器,rtsp服务器可以使用开源的easyDarwin。

方案一:python启用子线程使用ffmpeg推流到rtsp服务器

ffmpeg真的非常棒,几乎可以处理任何格式的视频。
下面是python下拉流并推流的一个示例,已经过了实际验证

import cv2
import subprocess
import time

'''拉流url地址,指定 从哪拉流'''
# video_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 自己摄像头
pull_url = 'rtsp://192.168.1.200:554/channel02' # "rtsp_address"
video_capture = cv2.VideoCapture(pull_url) # 调用摄像头的rtsp协议流
# pull_url = "rtmp_address"


'''推流url地址,指定 用opencv把各种处理后的流(视频帧) 推到 哪里'''
# push_url = "rtsp://192.168.107.65:8554/room55"
push_url = "rtsp://192.168.1.200:554/channel04"

width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(video_capture.get(cv2.CAP_PROP_FPS)) # Error setting option framerate to value 0. 
print("width", width, "height", height,  "fps:", fps) 


# command = [r'D:\Softwares\ffmpeg-5.1-full_build\bin\ffmpeg.exe', # windows要指定ffmpeg地址
command = ['ffmpeg', # linux不用指定
    '-y', '-an',
    '-f', 'rawvideo',
    '-vcodec','rawvideo',
    '-pix_fmt', 'bgr24', #像素格式
    '-s', "{}x{}".format(width, height),
    '-r', str(fps), # 自己的摄像头的fps是0,若用自己的notebook摄像头,设置为15、20、25都可。 
    '-i', '-',
    '-c:v', 'libx264',  # 视频编码方式
    # '-b:v', '1000k',
    '-bufsize', '1000k',
    '-maxrate', '1000k',
    '-pix_fmt', 'yuv420p',
    '-preset', 'ultrafast',
    '-f', 'rtsp', #  flv rtsp
    '-rtsp_transport', 'udp',  # 使用TCP推流,linux中一定要有这行
    push_url] # rtsp rtmp  
pipe = subprocess.Popen(command, shell=False, stdin=subprocess.PIPE)

def frame_handler(frame):
    # ...
    cv2.putText(frame, "helloworld", (300, 230), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0))
    return frame


process_this_frame = True 
while True: # True or video_capture.isOpened():
    # Grab a single frame of video
    ret, frame = video_capture.read()

    # handle the video capture frame
    start = time.time()
    
    frame = frame_handler(frame) 
    
    # Display the resulting image. linux 需要注释该行代码
    # cv2.imshow('Video', frame)

    # Hit 'q' on the keyboard to quit!
    if cv2.waitKey(delay=100) & 0xFF == ord('q'): #  delay=100ms为0.1s .若dealy时间太长,比如1000ms,则无法成功推流!
        break
    # 存入管道用于直播

    pipe.stdin.write(frame.tostring())
    # pipe.stdin.write(frame.tobytes())
    #out.write(frame)    #同时 存入视频文件 记录直播帧数据
video_capture.release()
cv2.destroyAllWindows()
pipe.terminate()

以上代码是通过管道的方式,先是通过cv2去读取源码流的图像,然后处理(包括推理、标记、打标签等一系列操作),然后处理完的帧再塞到管道中,而主线程定义了一个子线程,子线程就是将管道中的图像通过ffmpeg转码推流到rtsp服务器

方案二:python的flask框架输出动态图像到前端显示视频

这个方法非常有意思,直接输出一个页面,而不需要一个流服务器,虽然这样比较占带宽,因为直接传输的是jpg图像,但好就好在使用简单,不需要额外集成,通用性也高,用于demo预览其实是非常合适的

在目录下新建server.py文件,内容如下:

from flask import Flask, render_template, Response
import cv2
import time

class VideoCamera(object):
    def __init__(self):
        # 通过opencv获取实时视频流
        # self.video = cv2.VideoCapture(0)
        self.video = cv2.VideoCapture("rtsp://121.196.105.173:13389/channel02")

    def __del__(self):
        self.video.release()

    def get_frame(self):
        success, image = self.video.read()
        # 在这里处理视频帧
        cv2.putText(image, "helloworld", (300, 230), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0))
        #
        # 因为opencv读取的图片并非jpeg格式,因此要用motion JPEG模式需要先将图片转码成jpg格式图片
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()


app = Flask(__name__)


@app.route('/')  # 主页
def index():
    # jinja2模板,具体格式保存在index.html文件中
    return render_template('index.html')


def gen(camera):
    while True:
        start_t=time.time()
        frame = camera.get_frame()
        #print('{0}'.format(time.time()-start_t))
        # 使用generator函数输出视频流, 每次请求输出的content类型是image/jpeg
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')


@app.route('/video_feed')  # 这个地址返回视频流响应
def video_feed():
    return Response(gen(VideoCamera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')


if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, port=5000)

再新建目录和文件 ./templates/index.html ,为什么要这么建,是flask模板的默认路径,内同如下

<html>
  <head>
    <title>Video Streaming Demonstration</title>
  </head>
  <body>
    <h1>Video Streaming Demonstration</h1>
    <img src="{{ url_for('video_feed') }}">
  </body>
</html>

运行server.py,浏览器输入相应地址即可预览了,真的非常简约。推荐二次开发,可玩性还是可以的。

就我个人而言,对于自己玩玩的小项目,给人稍微展示一下AI效果之类的,可以直接使用方案三,读取-修改-展示一条龙,如果想要更加专业,在企业应用中使用,那么方案二是比较可以的,可以把rtsp流保存到nvr中,便于其它业务调用。

————分割线—–

补充一个框架工具,graio,特别适合布署ai应用,具体方法请参考官网 https://www.gradio.app/


文章作者: 无名小卒
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 无名小卒 !
  目录