居家工作後,因為筆電擺設的位置,每次會議想打開上蓋開視訊都會卡到螢幕,但又不想另外花錢買外接視訊,於是就這樣擱著不開鏡頭。
有天突發奇想,能不能用 Rasbperry pi 作一個呢?研究後發現還蠻簡單的,只需要Raspberry pi、鏡頭模組、一個簡單的 python script 以及 OBS 軟體就能實現!這篇分享我的實作經驗給大家。
以下教學假設讀者已經有基本的 Raspberry pi 使用經驗,且 Raspberry pi 的系統、Python 環境、Wifi 連線、相機模組都裝好或設定好,並使用 Bullseye 版本的 Raspberry pi OS,透過 picamera2 和 libcamera 來擷取相機影像
建立視訊串流 Server
原始碼下載:github
我用 Python flask 架設簡單的 Video Steam Server,首先建立虛擬環境,並且安裝對應套件。
特別注意的是,我們需要已經預設安裝在系統環境的 libcamera 和 picamera2 等套件,因此建立虛擬環境時,要在後面增加 --system-site-packages
參數, pip
才會幫我們自動導入,免去後續自行安裝的困擾。
$ virtualenv .env --system-site-packages
$ source .env/bin/activate
$ pip install flask
接著建立 stream.py
開始寫 code! 原始碼可參考 github,我們一步步解說程式內容
- 第一步初始化
Picamear2
- 緊接著準備 camera config,給定
- size:
1920 x 1080
,此部分可依個人情況調整 - format:給定
XBGR8888
顏色才會正確(不然會出現綠巨人…) - transform:此設定可選,因鏡頭擺設角度上下顛倒,需要在影像輸出時作 vertical flip 轉回來
- controls:此設定可選,因選用的鏡頭品質暗部雜訊多且畫質偏軟,透過 config 增加銳利度並降燥
- 更多設定可參考 Picamera2 官方文件
- size:
- 將 config 設定至 camera object 中
- 呼叫
start_recording
啟動相機,並傳入JpegEncoder()
、Quality.VERY_HIGH
將輸出編碼成高品質 JPEG,透過FileOutput
輸出至StreamingOutput
from picamera2 import Picamera2
from picamera2.encoders import JpegEncoder, Quality
from picamera2.outputs import FileOutput
cam = Picamera2()
config = cam.create_video_configuration(
{'size': (1920, 1080), 'format': 'XBGR8888'},
transform=Transform(vflip=1),
controls={'NoiseReductionMode': controls.draft.NoiseReductionModeEnum.HighQuality, 'Sharpness': 1.5}
)
cam.configure(config)
cam.start_recording(JpegEncoder(), FileOutput(output), Quality.VERY_HIGH)
疑?不對啊?我們有寫 StreamingOutput
嗎?對,要來補了,參考 Picamera2 的 Sample Code,繼承 BufferedIOBase
寫一個 StreamingOutput
,作為將 JPEG bytes data 轉給 flask 的中介
class StreamingOutput(BufferedIOBase):
def __init__(self):
self.frame = None
self.condition = Condition()
def write(self, buf):
with self.condition:
self.frame = buf
self.condition.notify_all()
output = StreamingOutput()
Camera 的部分準備完畢,開始準備 flask 的初始化
from flask import Flask
app = Flask(__name__)
寫一個 function 當進入網站時,render template
template = '''
<!DOCTYPE html>
<html lang="en">
<body>
<img src="{{ url_for('video_stream') }}" width="100%">
</body>
</html>
'''
@app.route("/", methods=['GET'])
def get_stream_html():
return render_template_string(template)
實作一個 api,從前面準備好的 StreamingOutput
instance 讀取資料,不斷丟到 Response
裡,瀏覽器就能不斷刷新 img
,實現直播的畫面
def gen_frames():
while True:
with output.condition:
output.condition.wait()
frame = output.frame
yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.route('/api/stream')
def video_stream():
return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
最後啟動 flask dev server 即可
app.run(host='0.0.0.0')
若只有這樣寫,按下 ctrl+c
中止 server 會有錯誤拋出,因沒有正確關閉相機導致。需要在後面補上 cam.stop()
,當 server 中斷後程式會往下跑並關閉相機,讓整個 process 正常退出
app.run(host='0.0.0.0')
cam.stop()
到此 code 已全部寫完,在 console 跑看看
$ python stream.py
打開瀏覽器前往 server 位置,此例為 http://192.168.68.70:5000
,就能看到即時轉播的畫面了!
延伸閱讀:如何用 OpenCV + Flask 快速搭建影像串流?
安裝 OBS 並建立虛擬相機
OBS 為 Open Broadcaster Software 的簡稱,在這裡免費下載,Windows、Mac、Linux 三平台皆支援!
安裝完開啟 OBS ,找到 來源
區塊中的 +
按鈕點下去建立新的視訊來源
跳出選單中選擇 瀏覽器
取個名稱,這邊以 pi camera
為例,按下確定
於下一個設定對話框中設定參數:
- 網址:填入剛架設好的視訊串流 server 網址,這邊以
http://192.168.68.70:5000
為例 - 寬度與高度:填入在
stream.py
中設定的值1920
和1080
按下確定回到主畫面,就能看到從 Raspberry pi 即時串流過來的影像。如果串流時 Raspberry pi 發生暫時離線或者畫面黑屏,可以點 OBS 中間的 重新整理
按紐恢復正常。
最後一步,點 OBS 右下角的 啟動虛擬相機
,就可以去 Google Meet 試試看囉!
在 Google Meet 中使用 Raspberry pi 視訊鏡頭
於 Google Meet 的 Audio & Video
設定中,將 Camera
改成 OBS Virtual Camera
即可
That’s it! 只要 Raspberry pi 和電腦在同一個 wifi 下都可以串流此土砲的視訊鏡頭,換句話說,如果在公用 wifi 任何人也能串流,因此還是建議在自家 wifi 下使用!
如果想讓 Raspberry pi 一開機就啟動串流 server,可以將 script 放入系統 service 中,作法可以求助估狗大神(或是 chatGPT XD),這邊就不贅述了
希望大家都能土砲成功!
延伸閱讀
參考資料
DIY a Webcam for MacBook with Raspberry Pi