本帖最后由 gray6666 于 2021-4-16 09:07 编辑
一、知识储备
1.什么是流媒体?(小车视频观看的数据格式)
流媒体(streaming media)是指将一连串的媒体数据压缩后,经过网上分段发送数据,在网上即时传输影音以供观赏的一种技术与过程,此技术使得数据包得以像流水一样发送;如果不使用此技术,就必须在使用前下载整个媒体文件。流式传输可传送现场影音或预存于服务器上的影片,当观看者在收看这些影音文件时,影音数据在送达观看者的计算机后立即由特定播放软件播放。
2.如何把视频流输出到浏览器?(获取视频的方法)
opencv通过获取本地实时视频流,利用flask框架构建一个实时视频流服务器,然后其他机器可以通过向这个服务器发送请求来获取这台机器上的实时视频流。常见网络摄像机采用此方法传输视频。
二、部分构建实时视频流服务器资料(引用前辈写的博客和代码,地址附后)
资料来源 https://blog.miguelgrinberg.com/post/video-streaming-with-flask
构建一个完整的应用程序,将实时视频流传输到Web浏览器。
流视频到浏览器的方法有很多,每种方法都有其优点和缺点。与Flask的流传输功能很好配合的方法是流传输一系列独立的JPEG图片。这称为Motion JPEG,并且被许多IP安全摄像机使用。这种方法的延迟时间很短,但是质量并不是最好的,因为JPEG压缩对于运动视频不是很有效。
在下面,您可以看到一个非常简单但完整的Web应用程序,可以为Motion JPEG流提供服务:
- <font size="4" face="宋体">#!/usr/bin/env python
- from flask import Flask, render_template, Response
- from camera import Camera
-
- app = Flask(__name__)
-
- @app.route('/')
- def index():
- return render_template('index.html')
-
- def gen(camera):
- while True:
- frame = camera.get_frame()
- yield (b'--frame\r\n'
- b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
-
- @app.route('/video_feed')
- def video_feed():
- return Response(gen(Camera()),
- mimetype='multipart/x-mixed-replace; boundary=frame')
-
- if __name__ == '__main__':
- app.run(host='0.0.0.0', debug=True)</font>
复制代码
网页显示
该应用程序导入一个Camera库(该文件在后面附下载地址)负责提供帧序列的类。在这种情况下,将摄像机控制部分放在单独的模块中是个好主意,这样,Web应用程序便保持干净,简单和通用。
该应用程序有两条路线。该/路线将服务于在index.html模板中定义的主页。
您可以在下面看到此模板文件的内容:
- <font size="4" face="宋体"><html>
- <head>
- <title>Video Streaming Demonstration</title>
- </head>
- <body>
- <h1>Video Streaming Demonstration</h1>
- <img src="{{ url_for('video_feed') }}">
- </body>
- </html></font>
复制代码
这是一个简单的HTML页面,仅包含标题和图像标签。请注意,图像标签的src属性指向此应用程序的第二条路线,这就是魔术发生的地方。
该/video_feed路线返回流响应。因为此流返回要在网页中显示的图像,所以此路由的URLsrc在image标记的属性中。浏览器将通过在其中显示JPEG图像流来自动更新图像元素,因为大多数/所有浏览器都支持多部分响应(如果您发现不喜欢这种浏览器的话,请告诉我)。
/video_feed路由中使用的生成器函数称为gen(),并将Camera类的实例作为参数。在mimetype如上述所示,用参数设定multipart/x-mixed-replace的内容类型和边界设置为字符串"frame"。
该gen()函数进入一个循环,在该循环中,该函数不断从相机返回帧作为响应块。该函数通过调用camera.get_frame()方法要求相机提供一个帧,然后以该帧的形式格式化为内容类型为的响应块image/jpeg。
三、如何整合到小车
1.根目新建 camera_pi.py,在git下载代码或是粘贴下面的文件
- <font size="4" face="宋体">#!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # camera_pi.py
- #
- #
- #
- import time
- import io
- import threading
- import picamera
-
-
- class Camera(object):
- thread = None # background thread that reads frames from camera
- frame = None # current frame is stored here by background thread
- last_access = 0 # time of last client access to the camera
-
- def initialize(self):
- if Camera.thread is None:
- # start background frame thread
- Camera.thread = threading.Thread(target=self._thread)
- Camera.thread.start()
-
- # wait until frames start to be available
- while self.frame is None:
- time.sleep(0)
-
- def get_frame(self):
- Camera.last_access = time.time()
- self.initialize()
- return self.frame
-
- @classmethod
- def _thread(cls):
- with picamera.PiCamera() as camera:
- # camera setup
- camera.resolution = (320, 240)
- camera.hflip = True
- camera.vflip = False #是否进行垂直翻转,默认是True
-
- # let camera warm up
- camera.start_preview()
- time.sleep(2)
-
- stream = io.BytesIO()
- for foo in camera.capture_continuous(stream, 'jpeg',
- use_video_port=True):
- # store frame
- stream.seek(0)
- cls.frame = stream.read()
-
- # reset stream for next frame
- stream.seek(0)
- stream.truncate()
-
- # if there hasn't been any clients asking for frames in
- # the last 10 seconds stop the thread
- if time.time() - cls.last_access > 10:
- break
- cls.thread = None</font>
复制代码
2.部分小车代码- <font size="4" face="宋体">from flask import Flask,request,render_template,Response # 导入flask框架所需要模块
- import RPi.GPIO as GPIO #导入RPi.GPIO库
- import time #计时器
- from camera_pi import Camera
- #设置GPIO模式
- GPIO.setmode(GPIO.BOARD)
- #设置in1到in4接口
- IN1 = 11
- IN2 = 13
- IN3 = 15
- IN4 = 16
- GPIO.setup(IN1,GPIO.OUT)
- GPIO.setup(IN2,GPIO.OUT)
- GPIO.setup(IN3,GPIO.OUT)
- GPIO.setup(IN4,GPIO.OUT)
-
- '''
- 小车运动函数
- '''
- #前进
- def front1():
- GPIO.output(IN1,GPIO.HIGH)
- GPIO.output(IN2,GPIO.LOW)
- GPIO.output(IN3,GPIO.HIGH)
- GPIO.output(IN4,GPIO.LOW)
-
- #后退
- def back1():
- GPIO.output(IN1,GPIO.LOW)
- GPIO.output(IN2,GPIO.HIGH)
- GPIO.output(IN3,GPIO.LOW)
- GPIO.output(IN4,GPIO.HIGH)
-
- app = Flask(__name__)
- ###############流媒体服务器#######################################
- def gen(camera):
- """Video streaming generator function."""
- while True:
- frame = camera.get_frame()
- yield (b'--frame \r\n'
- b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
-
-
- @app.route('/video_feed')
- def video_feed():
- """Video streaming route. Put this in the src attribute of an img tag."""
- return Response(gen(Camera()),
- mimetype='multipart/x-mixed-replace; boundary=frame')
- ###################################################################
- # 进入的首个网页
- @app.route('/',methods=['GET','POST'])
- def main():
- return render_template("index.html") # 返回网页
-
- # 点击按钮,通过post方法获取运动命令
- @app.route('/front',methods=['POST'])
- def front():
- front1()
- return render_template("index.html") #返回一开始的页面
-
- # 点击关闭
- @app.route("/back",methods=['POST'])
- def back():
- back1()
- return render_template("index.html") # #返回一开始的页面
-
- if __name__=="__main__":
-
- app.run(host='0.0.0.0',port=5000)
-
- GPIO.clearnup()</font>
复制代码
四、测试视频
|