利用ffmpeg推流到rtsp,再利用jmpeg在html界面上显示的解决办法

发布时间 2023-04-29 02:03:40作者: 信2005-2刘海涛

需求

最近在百度飞桨上训练了一个摔倒识别的模型,用的PaddleDetection这个模型,训练好以后我部署到了Windows,但是我看大多数人都是部署到了Linux,具体方法大家可以自行百度。

部署好以后我是使用摄像头进行实时识别的,但是我想要展示处理后的画面,这个我看了飞桨的官方介绍,是可以实现的,但是由于第一次没有好好看,所以就没有想到推流到rtsp的方式。我第一次是我的同学帮我写的,因为百度飞桨这个模型是用的帧处理,就是将结果处理成为一帧帧的图片,在一个while循环里不断的处理,我一开始通过在循环里写cv2.imshow的方式将不断加载的图片播放成视频,在我的同学的改造下变成了cv2.imwrite,把图片保存到本地然后在用python代码读取生成视频,因为图片是不断变化的,他还用qt帮我写了个界面,真的,哭死!(别问我为什么不自己写,问就是还没学)

但是解决了画面的问题又一难题来了,摔倒识别用的python代码,我的网页是javaweb,我怎么能将这个画面在web里显示出来呢?我去百度了,百度到一些解决方法,比如websocket,但是很遗憾,我没解决成功,于是我就去问老师,武老师给我提供了一些方法,老大也给我提供了一些方法。

  • 使用Flask来编写界面,但是由于时间紧迫,所以没有时间在学习新技术,所以Pass
  • 使用qt写成软件的方式,理由同上,Pass
  • 将视频保存在本地再读取,无法实现实时性,Pass
  • 保存到数据在读取,同上,Pass
  • 边保存边读取,百度了一下,实现不了我暂时,Pass
  • 武老师说搜索网络直播,在老师的帮助下找到了一条路,就是推送视频流到rtsp,在用vue来读取,武老师还帮我查阅了很多资料,他真的,我哭死!

既然路已经找到,剩下的就是如何实现的问题了。

在python代码里推流到rtsp

首先自然而然的就是去搜索如何推流到rtsp,这里要感谢武老师提供的博客:传送门

使用的工具是ffmpeg,它的作用就是将视频推送到rtsp上面,将本地视频变成一种网络流,但是我们需要下载一个工具提前,无需安装,直接打开即可:工具传送门。这个根据系统自行下载,我下载的是windwos的,里面有一个 .exe文件,双击打开就会运行,不打开这个软件你是无法使用ffmpeg推流到rtsp的。

image-20230429002812168

可以看到软件成功运行,ffmpeg需要配置环境变量,教程很多,大家自己百度,ffmpeg下载的传送门放这里了:ffmpeg官网

现在我们只需要重新打开一个cmd,输入以下命令:

ffmpeg -re -stream_loop -1 -i "D:\Code\Python\Pycharm\innovation\PaddleDetection\output_inference\down.mp4" -c copy -f rtsp rtsp://127.0.0.1:8554/test

上述指令的意思就是将本地视频推流到一个rtsp上面,rtsp地址为rtsp://127.0.0.1:8554/test,其中有一个参数的作用是循环播放,大家可以自行学习相关参数。出现这个画面就是说明推流成功了:

image-20230429003325897

接着我们可以先用vlc播放器来验证一下,vlc播放器请自行下载安装,地址:传送门。怎么用vlc播放器播放rtsp流视频的方法也请自行百度,可以看到我们成功播放出来了。

image-20230429003627623

接下来就是实现在python代码里使用ffmpeg推流视频到rtsp,本地视频的方法就不说了,上面那篇博客人家写的很清楚了,我是新建了一个python文件,然后在while循坏外调用该方法,最后在循环里将图片用这个方法推流,成功实现视频(注意不要在循环里写推流代码,不然你只会得到一张帧图片),源码就是用的上面那位大佬的博客里面的代码,我改了改,(因为处理后的视频大小是固定的,所以我写死了,有需求的小伙伴自行更改):

import time
import os
import ast
import glob

import cv2
import yaml
import copy
import numpy as np
import subprocess as sp
import imageio_ffmpeg
import ffmpeg


class Ffmpeg(object):
    def __init__(self):
        rtspUrl = 'rtsp://127.0.0.1:8554/video1'  # 这里改成本地ip,端口号不变,文件夹自定义

        # # 视频来源 地址需要替换自己的可识别文件地址
        # camera = cv2.VideoCapture(0)  # 从文件读取视频
        #
        # # 视频属性
        # size = (int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)), int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT)))
        # sizeStr = str(size[0]) + 'x' + str(size[1])
        # fps = camera.get(cv2.CAP_PROP_FPS)  # 30p/self
        fps = int(30)

        # 视频文件输出
        # fourcc = cv2.VideoWriter_fourcc(*'XVID')
        # out = cv2.VideoWriter(filePath + 'res_mv.avi', fourcc, fps, size)
        # 直播管道输出
        # ffmpeg推送rtmp 重点 : 通过管道 共享数据的方式
        self.command = [
            'ffmpeg',
            # 're',#
            # '-y', # 无需询问即可覆盖输出文件
            '-f', 'rawvideo',  # 强制输入或输出文件格式
            '-vcodec', 'rawvideo',  # 设置视频编解码器。这是-codec:v的别名
            '-pix_fmt', 'bgr24',  # 设置像素格式
            '-s', '640x480',  # 设置图像大小
            '-r', str(fps),  # 设置帧率
            '-i', '-',  # 输入
            '-c:v', 'libx264',
            '-pix_fmt', 'yuv420p',
            '-preset', 'ultrafast',
            '-f', 'rtsp',  # 强制输入或输出文件格式
            rtspUrl]

        # 管道特性配置
        # pipe = sp.Popen(command, stdout = sp.PIPE, bufsize=10**8)
        self.pipe = sp.Popen(self.command, stdin=sp.PIPE, shell=False)  #
        # pipe.stdin.write(frame.tostring())
        # while (camera.isOpened()):
        #     ret, frame = camera.read()  # 逐帧采集视频流
        #     if not ret:
        #         break
        #     ############################图片输出
        #     # 结果帧处理 存入文件 / 推流 / ffmpeg 再处理
        #     pipe.stdin.write(camera.tostring())  # 存入管道用于直播
            # out.write(frame)  # 同时 存入视频文件 记录直播帧数据

        # camera.release()
        # out.release()

然后我在运行文件里调用了这个方法,并在循环里将图片存入管道。

image-20230429004346476

image-20230429004403750

在vlc播放器里成功看到处理后的实时画面,但是有延迟,大概10s以内,可以接受。

html里播放rtsp视频流

实现了python代码推流了以后我们只需要在实现在web界面里展示推流后的rtsp画面就可以了,这个一搜就发现了很多教程,我最终选择了一个最新版,别问,问就是旧版的我没跑出来。

教程地址:传送门

根据上述教程成功实现web里展示rtsp视频流,上述博客里大佬自己写了个代码 rtsp2web,运行成功界面如下:

image-20230429010011307

我们需要用到node.js来进行下载(这里要特别纪念一下,因为我没学过node.js,导致我一直在cmd根目录运行安装ws的命令,最终淘宝花了15块钱找人解决,发现只需要在要使用的目录执行命令即可,15块钱白花)

因为我是双摄像头不同机位识别嘛,所以我做了一些修改:

<div class="content-body">
  <!-- canvas 宽高比例尽量与视频比例保持一致。 -->
  <canvas id="canvas-1" style="width: 630px ;height: 480px"></canvas>
  <canvas id="canvas-2" style="width: 630px ;height: 480px"></canvas>
</div>

<!--视频处理通过ws推流-->
<script>
  var rtsp1 = 'rtsp://127.0.0.1:8554/video1'
  var rtsp2 = 'rtsp://127.0.0.1:8554/video2'
  window.onload = () => {
    // 将rtsp视频流地址进行btoa处理一下
    new JSMpeg.Player("ws://localhost:9999/rtsp?url="+btoa(rtsp1), {
      canvas: document.getElementById("canvas-1")
    })
    new JSMpeg.Player("ws://localhost:9999/rtsp?url="+btoa(rtsp2), {
      canvas: document.getElementById("canvas-2")
    })
  }
</script>

值得一提的是,大佬加了水印,我花了25找大佬去除水印结果只需要改一个参数就可以去掉水印,只能说没读源码,25块钱又白花!

最终实现效果:

e4b9db45246f41d1c25914a6775e104