问题描述
为了给半夜实验室的老鼠拍照,想给台式机按一个usb摄像头,并实现对该摄像头的实时远程访问,即将台式机(windows 10)的usb摄像头变成一个网络摄像头。
解决方法
使用已有的软件
本以为这个问题是一个很成熟的问题,windows端合适的软件应该有很多,但没想到找了一个小时只找到了收费软件,免费的也有几个
如:
但不知道为什么都不能用,而且更多的app的目的并不是将windows端的usb摄像头变为局域网内网络摄像头,而是使局域网内的网络摄像头能够被windwos电脑直接访问。
代码
没能找到现成的软件,便想使用Python和Opencv完成读取视频流、并将其共享到端口,从而实现对该摄像头的实时远程访问。
(暂时只能实现局域网内的访问)
事实上,此类任务显然是有人做过的,可以通过搜索关键字"camera-live-streaming"获得更多资料。
本文参考的下面的github库:
https://github.com/akmamun/camera-live-streaming
但该代码只能本机访问,原因是因为flask在debug模式下默认只能本机访问,因此要更改app.run()中的内容才能实现局域网内其他设备的访问。
更改后的代码如下:
app.py
from camera import VideoCamera
from flask import Flask, render_template, Response
import time
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
def gen(camera):
while True:
try:
frame = camera.get_frame()
except Exception:
print("Video is finished or empty")
#return None
frame = camera.get_heartbeat()
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')
camera.py
import cv2
class VideoCamera(object):
def __init__(self):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
#self.video = cv2.VideoCapture(0)
# If you decide to use video.mp4, you must have this file in the folder
# as the main.py.
# self.video = cv2.VideoCapture('suits_hd.mp4')
self.video = cv2.VideoCapture(0)
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
def get_heartbeat(self):
# jpeg = cv2.imread('noise-black.jpg')
image = cv2.imread('noise-green.jpg')
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
网页端
index.html
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<title>Live Streaming Demonstration</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-lg-8 offset-lg-2">
<h3 class="mt-5">Live Streaming</h3>
<img src="{{ url_for('video_feed') }}" width="100%">
</div>
</div>
</div>
</body>
</html>
但目前仍存在两个问题:
1. 每个时刻只允许一台设备访问,不允许多台设备同时访问;
2. 只能在局域网内部访问。
问题1 只允许一台设备访问
通过设置多线程app.run(host = '0.0.0.0', threaded=True)即可解决,即将app.py改为:
from camera import VideoCamera
from flask import Flask, render_template, Response
import time
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
def gen(camera):
while True:
try:
frame = camera.get_frame()
except Exception:
print("Video is finished or empty")
#return None
frame = camera.get_heartbeat()
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', threaded=True)
问题2 只能在局域网内部访问
本来想做内网渗透,但意外发现实验室电脑虽然没有公网的IPv4地址,但是有公网的Ipv6地址,而flask是支持ipv6的,于是将app.py改为如下代码即可:
from camera import VideoCamera
from flask import Flask, render_template, Response
import time
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
def gen(camera):
while True:
try:
frame = camera.get_frame()
except Exception:
print("Video is finished or empty")
#return None
frame = camera.get_heartbeat()
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 = '::', threaded=True) # ipv6
注:访问ipv6时要加[],如:
http://[xxxx:xxx:xxxx:xxx::x:xxxx]:5000/
======================================================================================================