1、簡介
隨著物聯(lián)網(wǎng)(IoT)和智能設(shè)備的快速發(fā)展,邊緣計算技術(shù)已成為高效數(shù)據(jù)處理和服務(wù)交付的重要組成部分。當(dāng)我們考慮利用邊緣端設(shè)備進(jìn)行實(shí)時監(jiān)控時,一個常見的需求是通過攝像頭捕捉視頻,并在局域網(wǎng)內(nèi)實(shí)現(xiàn)視頻流的傳輸。這種設(shè)置不僅適用于家庭和小型企業(yè)的安全監(jiān)控,也能滿足遠(yuǎn)程教育、醫(yī)療監(jiān)護(hù)等多個領(lǐng)域的需要。
面對局域網(wǎng)內(nèi)的視頻流傳輸挑戰(zhàn),有多種方法可以實(shí)現(xiàn)從攝像頭到顯示終端的數(shù)據(jù)傳遞,每種方法都有其特點(diǎn)和適用場景。本文將介紹一種基于TCP/IP協(xié)議棧和Socket編程的方法,這種方法因其穩(wěn)定性和易用性而被廣泛采用。
選擇TCP/IP與Socket編程的理由:
穩(wěn)定性:TCP(傳輸控制協(xié)議)作為面向連接的協(xié)議,確保了數(shù)據(jù)包在網(wǎng)絡(luò)中的有序傳遞,并提供了錯誤檢測與糾正機(jī)制,這對于視頻流這類對丟包敏感的數(shù)據(jù)尤為重要。
靈活性:使用Socket API可以在應(yīng)用層直接操作網(wǎng)絡(luò)通信,給予開發(fā)者更大的自由度來定制視頻流的傳輸邏輯,比如調(diào)整幀率、分辨率或?qū)嵤┳远x的安全措施。
跨平臺支持:無論是Windows、Linux還是macOS,大多數(shù)操作系統(tǒng)都內(nèi)置了對TCP/IP和Socket的支持,這意味著開發(fā)的應(yīng)用程序具有良好的兼容性和移植性。
資源效率:對于邊緣端設(shè)備而言,優(yōu)化后的TCP/IP服務(wù)和Socket編程可以幫助節(jié)省寶貴的計算資源和帶寬,保證即使在網(wǎng)絡(luò)條件有限的情況下也能維持視頻流的流暢性。
接下來,我們將探討如何利用這些技術(shù)構(gòu)建一個簡易的局域網(wǎng)視頻流傳輸系統(tǒng),包括具體的實(shí)現(xiàn)步驟和技術(shù)細(xì)節(jié)。您將看到,通過合理的設(shè)計和配置,即使是普通用戶也能夠在自己的環(huán)境中輕松搭建起一套實(shí)用的實(shí)時監(jiān)控解決方案。如果您對視頻流傳輸有著更高的要求,如低延遲、高清晰度或更復(fù)雜的安全特性,后續(xù)部分還會提及一些進(jìn)階技術(shù)和最佳實(shí)踐建議。
2、相關(guān)知識
2.1 TCP/IP協(xié)議簡介
TCP/IP(Transmission Control Protocol/Internet Protocol,傳輸控制協(xié)議/網(wǎng)際協(xié)議)是指能夠在多個不同網(wǎng)絡(luò)間實(shí)現(xiàn)信息傳輸?shù)膮f(xié)議簇。TCP/IP協(xié)議不僅僅指的是TCP 和IP兩個協(xié)議,而是指一個由FTP、SMTP、TCP、UDP、IP等協(xié)議構(gòu)成的協(xié)議簇, 只是因為在TCP/IP協(xié)議中TCP協(xié)議和IP協(xié)議最具代表性,所以被稱為TCP/IP協(xié)議。
TCP/IP傳輸協(xié)議是嚴(yán)格來說是一個四層的體系結(jié)構(gòu),應(yīng)用層、傳輸層、網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層都包含其中。
層級名稱 | 主要協(xié)議 | 功能描述 |
---|---|---|
應(yīng)用層 | Telnet, FTP, SMTP, HTTP, HTTPS, DNS 等 | 接收來自傳輸層的數(shù)據(jù)或按不同應(yīng)用要求與方式將數(shù)據(jù)傳輸至傳輸層;提供用戶接口和應(yīng)用服務(wù)。 |
傳輸層 | TCP (傳輸控制協(xié)議), UDP (用戶數(shù)據(jù)報協(xié)議) | 提供端到端的通信服務(wù),確保數(shù)據(jù)可靠地從一臺機(jī)器傳輸?shù)搅硪慌_機(jī)器(TCP)或不保證順序和可靠性但更快速的數(shù)據(jù)傳輸(UDP)。 |
網(wǎng)絡(luò)層 | IP (網(wǎng)際協(xié)議), ICMP, IGMP | 負(fù)責(zé)網(wǎng)絡(luò)中數(shù)據(jù)包的傳送,包括路由選擇、數(shù)據(jù)包轉(zhuǎn)發(fā)等;ICMP用于報告錯誤并交換有限的控制消息;IGMP用于管理組播成員關(guān)系。 |
網(wǎng)絡(luò)訪問層/鏈路層 | ARP (地址解析協(xié)議), RARP, Ethernet, Wi-Fi | 提供鏈路管理和錯誤檢測;處理對不同通信媒介有關(guān)的信息細(xì)節(jié)問題,如物理地址的解析(ARP),以及在局域網(wǎng)中直接傳遞數(shù)據(jù)幀。 |
2.2 TCP
TCP(傳輸控制協(xié)議)是TCP/IP模型傳輸層中最重要的協(xié)議之一,它提供了一種面向連接、可靠的字節(jié)流服務(wù)。這意味著在兩個應(yīng)用程序之間建立通信之前,必須先通過三次握手過程來建立一個連接;而當(dāng)數(shù)據(jù)傳輸完成后,則需要通過四次揮手過程來斷開這個連接。TCP確保了數(shù)據(jù)包按序到達(dá),并且能夠檢測并重傳丟失或損壞的數(shù)據(jù)包,從而保證了數(shù)據(jù)傳輸?shù)目煽啃浴?/p>
TCP的主要特點(diǎn):
面向連接:在發(fā)送數(shù)據(jù)前,雙方必須建立一個連接。這種連接類似于電話呼叫,在通話開始前需要撥號建立連接。
可靠性:TCP使用確認(rèn)機(jī)制和超時重傳來確保所有發(fā)送的數(shù)據(jù)都被接收方正確接收。如果接收方?jīng)]有收到某個數(shù)據(jù)包或者接收到的是損壞的數(shù)據(jù)包,它會請求發(fā)送方重新發(fā)送該數(shù)據(jù)包。
流量控制:為了防止快速的發(fā)送方淹沒慢速的接收方,TCP實(shí)現(xiàn)了滑動窗口機(jī)制來進(jìn)行流量控制,根據(jù)接收方的能力調(diào)整發(fā)送速率。
擁塞控制:TCP還包含了一系列算法來避免網(wǎng)絡(luò)擁堵,例如慢啟動、擁塞避免、快重傳和快恢復(fù)等。
全雙工通信:TCP支持同時雙向的數(shù)據(jù)傳輸,即可以同時作為客戶端和服務(wù)端進(jìn)行數(shù)據(jù)交換。
錯誤檢查與糾正:利用校驗和機(jī)制對每個數(shù)據(jù)段進(jìn)行完整性驗證,以確保數(shù)據(jù)的準(zhǔn)確性。
TCP適用場景:
文件傳輸(如FTP)
電子郵件(如SMTP, POP3, IMAP)
Web瀏覽(如HTTP, HTTPS)
遠(yuǎn)程登錄(如SSH)
TCP工作流程概述
三次握手:用于建立連接??蛻舳税l(fā)送SYN(同步序列編號),服務(wù)器回應(yīng)ACK(確認(rèn)信息)和自己的SYN,然后客戶端再次回應(yīng)ACK確認(rèn)。
數(shù)據(jù)傳輸:一旦連接建立,雙方就可以開始傳輸數(shù)據(jù)。TCP負(fù)責(zé)將數(shù)據(jù)分割成合適大小的數(shù)據(jù)段,并為每個數(shù)據(jù)段添加頭部信息,包括序列號以便接收方重組數(shù)據(jù)。
四次揮手:當(dāng)一方完成數(shù)據(jù)發(fā)送后,它會發(fā)送FIN(結(jié)束標(biāo)志)給另一方表示希望關(guān)閉連接。接收方會回應(yīng)ACK確認(rèn)收到FIN,并在準(zhǔn)備好關(guān)閉連接時也發(fā)送自己的FIN。發(fā)送方再回應(yīng)ACK,最終完成連接的終止。
綜上所述,TCP因其高可靠性和安全性,廣泛應(yīng)用于那些對數(shù)據(jù)完整性和順序有嚴(yán)格要求的應(yīng)用程序中。
2.3 UDP
UDP(用戶數(shù)據(jù)報協(xié)議)是TCP/IP模型傳輸層的一個重要成員,它提供了一種無需建立連接即可發(fā)送和接收數(shù)據(jù)包的通信方式。與TCP不同的是,UDP不保證數(shù)據(jù)的順序性和可靠性,也不進(jìn)行流量控制或擁塞控制。這意味著使用UDP時,數(shù)據(jù)可能會丟失、重復(fù)或者亂序到達(dá)。
UDP的主要特點(diǎn):
無連接:在發(fā)送數(shù)據(jù)之前不需要建立連接,因此減少了建立連接所需的時間。
盡力而為的服務(wù):UDP不會對數(shù)據(jù)包的傳輸做任何保證,也不會重傳丟失的數(shù)據(jù)包。
低開銷:由于缺少確認(rèn)機(jī)制和其他復(fù)雜的功能,UDP具有較低的頭部開銷,這使得它非常適合實(shí)時應(yīng)用。
高效性:因為沒有復(fù)雜的握手過程,所以UDP可以更快地發(fā)送數(shù)據(jù)。
適用于廣播或多播:UDP支持向多個目的地同時發(fā)送數(shù)據(jù)的能力,這對于視頻會議等應(yīng)用場景非常有用。
UDP適用場景:
實(shí)時音頻/視頻流媒體服務(wù)
在線游戲
DNS查詢
SNMP(簡單網(wǎng)絡(luò)管理協(xié)議)
其他對延遲敏感的應(yīng)用程序
2.4 TCP vs UDP
特性 | TCP (Transmission Control Protocol) | UDP (User Datagram Protocol) |
---|---|---|
連接類型 | 面向連接,需要三次握手建立連接 | 無連接,即發(fā)即用 |
可靠性 | 可靠的數(shù)據(jù)傳輸,確保數(shù)據(jù)按序完整到達(dá) | 不可靠,不保證數(shù)據(jù)包會到達(dá),可能丟失或亂序 |
速度 | 較慢,因為有握手過程和錯誤檢查 | 更快,因為它沒有這些額外的過程 |
流量控制 | 支持,通過滑動窗口機(jī)制 | 不支持 |
擁塞控制 | 支持 | 不支持 |
頭部開銷 | 較大,因為包含更多的控制信息 | 較小,只有必要的控制信息 |
用途 | 適合需要高可靠性的應(yīng)用程序,如文件傳輸、電子郵件 | 適合對時間敏感但可容忍一定數(shù)據(jù)損失的應(yīng)用,如視頻流 |
2.5 Socket
Socket(套接字)是網(wǎng)絡(luò)編程中的一個重要概念,它提供了一種跨進(jìn)程通信的方式,使得不同計算機(jī)上的應(yīng)用程序能夠通過網(wǎng)絡(luò)交換數(shù)據(jù)。在實(shí)現(xiàn)TCP/IP服務(wù)時,Socket扮演著至關(guān)重要的角色,它是程序員用來編寫客戶端和服務(wù)器端程序的接口。
Socket的基本特性:
地址家族:定義了通信協(xié)議類型,例如IPv4(AF_INET)或IPv6(AF_INET6)。
類型:指定了傳輸層使用的協(xié)議,如TCP(SOCK_STREAM,流式套接字)或UDP(SOCK_DGRAM,數(shù)據(jù)報套接字)。
協(xié)議:通常設(shè)置為0,表示使用默認(rèn)協(xié)議;對于某些特殊情況可以指定特定協(xié)議編號。
使用Socket實(shí)現(xiàn)TCP/IP服務(wù)的步驟:
創(chuàng)建Socket:在服務(wù)器端和客戶端都需要調(diào)用socket()函數(shù)來創(chuàng)建一個套接字對象,該對象將用于發(fā)送和接收數(shù)據(jù)。
綁定(僅限服務(wù)器端):服務(wù)器需要調(diào)用bind()函數(shù)將其套接字與特定的IP地址和端口號關(guān)聯(lián)起來,以便其他客戶端可以通過這些信息找到并連接到服務(wù)器。
監(jiān)聽(僅限服務(wù)器端):服務(wù)器調(diào)用listen()函數(shù)開始監(jiān)聽來自客戶端的連接請求。這一步驟會將套接字轉(zhuǎn)換為被動模式,準(zhǔn)備接受連接。
接受連接(僅限服務(wù)器端):當(dāng)有客戶端嘗試連接時,服務(wù)器調(diào)用accept()函數(shù)來接受這個連接,并返回一個新的套接字,專門用于與那個特定客戶端之間的通信。
連接服務(wù)器(客戶端操作):客戶端調(diào)用connect()函數(shù)發(fā)起對服務(wù)器的連接請求,指定要連接的服務(wù)器IP地址和端口號。
發(fā)送/接收數(shù)據(jù):一旦建立了連接,雙方都可以使用send()和recv()函數(shù)來進(jìn)行數(shù)據(jù)的發(fā)送和接收。對于TCP來說,這意味著可以進(jìn)行可靠的數(shù)據(jù)流傳輸。
關(guān)閉連接:數(shù)據(jù)交換完成后,雙方應(yīng)該調(diào)用close()函數(shù)來關(guān)閉各自的套接字,釋放資源。
以上代碼展示了如何使用Python內(nèi)置的socket庫來實(shí)現(xiàn)一個簡單的TCP回顯服務(wù)器和客戶端。
3 邊緣端設(shè)備實(shí)現(xiàn)
我此次使用的設(shè)備是凌智視覺模塊,但是該設(shè)備是沒有帶WiFi的,如果需要使用WiFi,需要外界一塊WiFi模塊。
3.1 服務(wù)端
下面我將搭建一個簡易的服務(wù)器,用于通過TCP連接向客戶端發(fā)送視頻幀。它使用OpenCV庫捕獲圖像,并將圖像編碼為JPEG格式后通過網(wǎng)絡(luò)傳輸給客戶端。此外,它還支持命令控制和視頻流模式,允許客戶端發(fā)送命令來控制視頻幀的傳輸。
以下是代碼的主要功能模塊和邏輯:
導(dǎo)入必要的庫:
cv2:來自lockzhiner_vision_module的OpenCV庫,用于視頻捕獲和圖像處理。
Thread, Event:來自threading模塊,用于創(chuàng)建線程和事件對象以同步線程間的操作。
socket:用于網(wǎng)絡(luò)通信。
os, time:分別用于操作系統(tǒng)相關(guān)的功能和時間測量。
定義輔助函數(shù):
send_image(conn, frame):負(fù)責(zé)將一幀圖像編碼成JPEG格式并通過TCP連接發(fā)送出去。
定義線程處理函數(shù):
handle_client_send(conn, start_event, shutdown_event, confirm_event, streaming_event, cap):此函數(shù)在一個獨(dú)立線程中運(yùn)行,負(fù)責(zé)讀取攝像頭圖像并根據(jù)事件狀態(tài)決定是否發(fā)送圖像。它還會計算和打印幀率。
handle_client_receive(conn, addr, cap, start_event, shutdown_event, confirm_event, streaming_event):此函數(shù)也在一個獨(dú)立線程中運(yùn)行,負(fù)責(zé)接收來自客戶端的命令并對這些命令作出響應(yīng),比如開始發(fā)送單個圖像、確認(rèn)接收到圖像、關(guān)閉連接或切換視頻流模式等。
主程序邏輯:
設(shè)置了服務(wù)器的IP地址(HOST)和端口(PORT),并初始化了OpenCV的視頻捕獲對象(cap)。
創(chuàng)建了一個TCP/IP套接字,綁定了指定的主機(jī)和端口,并開始監(jiān)聽傳入的連接請求。
當(dāng)有新的客戶端連接時,會啟動兩個新線程:一個用于處理發(fā)送到客戶端的數(shù)據(jù),另一個用于接收來自客戶端的命令。
主循環(huán)等待客戶端連接,并在接收到中斷信號(如KeyboardInterrupt)時優(yōu)雅地關(guān)閉所有資源。
事件機(jī)制:
使用了多個Event對象來協(xié)調(diào)不同線程之間的交互,確保了正確的順序和狀態(tài)管理。
視頻流模式:
支持一種持續(xù)發(fā)送視頻幀的“視頻流模式”,這可以通過發(fā)送特定命令開啟或關(guān)閉。
import lockzhiner_vision_module.cv2as cv2fromthreading import Thread, Eventimport socketimport osimporttimedef send_image(conn, frame): try:
# 使用imencode將圖像編碼為JPEG格式
ret, img_encode = cv2.imencode('.jpg', frame)
if not ret:
print("Failed to encode image")
return # 假設(shè)img_encode已經(jīng)是bytes類型,直接使用
data = img_encode # 發(fā)送圖像大小和圖像數(shù)據(jù)
conn.sendall(len(data).to_bytes(4, byteorder='big'))
conn.sendall(data) print("Image sent.") except (ConnectionResetError, OSError) as e:
print(f"Error sending image: {e}")defhandle_client_send(conn, start_event, shutdown_event, confirm_event, streaming_event, cap): frame_counter =0 start_time = time.time() while not shutdown_event.is_set():
if start_event.is_set() or streaming_event.is_set(): # 檢查開始或流式事件是否被設(shè)置 ret, frame = cap.read()
if ret: send_image(conn, frame) frame_counter +=1 if not streaming_event.is_set(): # 如果不是流式傳輸,則等待客戶端確認(rèn)
confirm_event.wait() # 等待客戶端確認(rèn)
confirm_event.clear() # 清除確認(rèn)事件
start_event.clear() # 圖像發(fā)送完成并且已確認(rèn)后清除事件
# 計算幀率 elapsed_time = time.time() - start_time if elapsed_time >=1: # 每一秒打印一次幀率 fps = frame_counter / elapsed_time
print(f"FPS: {fps}") # 重置計數(shù)器和時間戳
frame_counter =0
start_time = time.time()# 接收線程處理函數(shù),接收命令并根據(jù)命令執(zhí)行操作defhandle_client_receive(conn, addr, cap, start_event, shutdown_event, confirm_event, streaming_event): print(f"Connected by {addr}") try: while not shutdown_event.is_set(): try:
data = conn.recv(1024) except ConnectionResetError: print("Connection reset by peer.")
break if not data or shutdown_event.is_set(): print("Connection closed.") break command = data.decode().strip() print(f"Received command: {command}") if command =='0': ret, frame = cap.read()
if ret:
if streaming_event.is_set(): # 如果處于流式傳輸模式,則不需要設(shè)置start_event
pass else: start_event.set() # 設(shè)置開始事件以通知發(fā)送線程 elif command =='1': confirm_event.set() # 設(shè)置確認(rèn)事件 elif command =='2': shutdown_event.set()
break elif command =='3': # 開始視頻流模式 streaming_event.set() print("Video stream mode started.") elif command =='q': # 停止視頻流模式 streaming_event.clear() print("Video stream mode stopped.") else: conn.sendall("Unknown command".encode()) finally: conn.close()if __name__ =="__main__": HOST ='172.32.0.144' PORT =6810 cap = cv2.VideoCapture() # if not cap.isOpened(): if cap.open(0) is False: print("Failed to open capture")
exit(1) print("video is all ready") start_event =Event() shutdown_event =Event() confirm_event =Event() streaming_event =Event() # 新增流式傳輸事件 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen() print("Server started, waiting for connections...") try:
while not shutdown_event.is_set():
conn, addr = server_socket.accept()
client_send_thread =Thread(target=handle_client_send,
args=(conn, start_event, shutdown_event, confirm_event, streaming_event, cap))
client_receive_thread =Thread(target=handle_client_receive,
args=(conn, addr, cap, start_event, shutdown_event, confirm_event, streaming_event))
client_send_thread.start() client_receive_thread.start() except KeyboardInterrupt: print("Server interrupted.") finally:
cap.release() server_socket.close()
print("Server stopped and resources released.")
3.2 客戶端
客戶端通過TCP連接接收圖像幀,并根據(jù)命令執(zhí)行不同操作的功能。以下是代碼的詳細(xì)解釋:
客戶端功能概述
接收圖像:
receive_image(conn, timeout=5)函數(shù)用于從服務(wù)器接收圖像數(shù)據(jù)。
它首先接收表示圖像大小的4個字節(jié),然后循環(huán)接收直到收到完整的圖像數(shù)據(jù)。
接收到的數(shù)據(jù)被轉(zhuǎn)換為NumPy數(shù)組并使用OpenCV解碼成圖像格式返回。
主程序邏輯 (main函數(shù)):
'0'請求單張圖像。
'1'不做任何操作(假設(shè)用作確認(rèn)信號)。
'2'發(fā)送退出命令給服務(wù)器并終止程序。
'3'啟動視頻流模式,允許連續(xù)接收圖像幀,并將這些幀顯示出來,同時保存為本地視頻文件。
設(shè)置了服務(wù)器的IP地址(HOST)和端口(PORT),與服務(wù)端保持一致。
創(chuàng)建了一個TCP/IP套接字并嘗試連接到服務(wù)器。
根據(jù)用戶輸入的不同命令來控制視頻幀的請求和處理:
視頻流模式:
當(dāng)用戶選擇啟動視頻流模式時,客戶端會創(chuàng)建一個以當(dāng)前時間戳命名的視頻文件。
它會持續(xù)請求圖像幀并將接收到的每一幀添加到視頻文件中,同時在窗口中顯示。
用戶可以按q鍵停止視頻流模式,并保存錄制的視頻文件。
異常處理:
在關(guān)鍵位置添加了異常處理邏輯,以確保在發(fā)生錯誤時能夠適當(dāng)響應(yīng),例如網(wǎng)絡(luò)連接超時或失敗時。
資源管理:
使用with語句確保即使出現(xiàn)異常,套接字也會被正確關(guān)閉。
確保視頻寫入器在異常情況下也能夠釋放資源。
注意事項
命令同步:客戶端發(fā)送命令后,通常需要等待服務(wù)器的響應(yīng)。這里的實(shí)現(xiàn)假設(shè)服務(wù)器會在接收到命令后立即采取行動,因此客戶端緊接著就會嘗試接收圖像數(shù)據(jù)。
超時設(shè)置:對于圖像接收設(shè)置了超時參數(shù),以防止程序在無響應(yīng)的情況下卡住。
視頻編碼格式:選擇了XVID作為視頻編碼格式,這是一個常見的選擇,但并不是唯一可用的選項??梢愿鶕?jù)需要更改。
視頻保存:視頻流模式下,視頻會被保存到本地磁盤。每次開始新的視頻流都會創(chuàng)建一個新的文件名,以避免覆蓋舊文件。
代碼
importsocketimportcv2importnumpyasnpimporttimedef receive_image(conn, timeout=5): conn.settimeout(timeout) # 設(shè)置接收超時 try: data_length = int.from_bytes(conn.recv(4), byteorder='big') image_data = b'' received =0 whilereceived < data_length:? ? ? ? ? ? packet = conn.recv(min(1024, data_length - received))? ? ? ? ? ??
ifnot packet:
break image_data += packet received += len(packet) # 將接收到的數(shù)據(jù)轉(zhuǎn)換為NumPy數(shù)組,并使用OpenCV解碼為圖像
image_np = np.frombuffer(image_data, dtype=np.uint8) frame = cv2.imdecode(image_np, cv2.IMREAD_COLOR) returnframe except socket.timeout: print("Timeout waiting for image from server.") returnNone except Exceptionase: print(f"Error receiving image: {e}") returnNonedef main(): HOST ='172.32.0.144' PORT =6810# 應(yīng)與服務(wù)端使用的端口號相同 with socket.socket(socket.AF_INET, socket.SOCK_STREAM)ass: try:
s.connect((HOST, PORT)) video_writer = None # 初始化視頻寫入器為None whileTrue:
command = input("Enter command (0 to request image, 1 to do nothing, 2 to exit, 3 for video stream): ") s.sendall(command.encode()) ifcommand =='2':
s.sendall(b'2') # 發(fā)送退出命令給服務(wù)器
break elif command =='3':
print("Starting video stream mode. Press 'q' to stop.")
timestamp = int(time.time() *1000) # 毫秒級時間戳
video_filename = f"video_stream_{timestamp}.avi"
fourcc = cv2.VideoWriter_fourcc(*'XVID') # 視頻編碼格式
video_writer = None # 重置視頻寫入器
# 請求第一幀以獲取尺寸信息
s.sendall(b'0') # 請求圖像幀
frame = receive_image(s, timeout=5) # 增加超時參數(shù) ifframeisNone:
print("Failed to receive first image frame.")
continue
height, width, _ = frame.shape
video_writer = cv2.VideoWriter(video_filename, fourcc,20.0, (width, height)) # 創(chuàng)建視頻寫入器 whileTrue:
ifvideo_writerisNone:
break # 如果視頻寫入器未初始化,則跳出循環(huán)
frame = receive_image(s, timeout=1) # 對每一幀都設(shè)置較短的超時時間 ifframeisNone: print("No new frame received. Waiting...")
time.sleep(1) # 等待一段時間再嘗試重新請求
continue
cv2.imshow('Video Stream', frame)
video_writer.write(frame) # 將幀寫入視頻文件
key = cv2.waitKey(1) &0xFF
ifkey == ord('q'):
print("Stopping video stream and saving video.")
s.sendall(b'q') # 發(fā)送停止信號給服務(wù)器 break ifvideo_writerisnot None:
video_writer.release() # 關(guān)閉視頻寫入器
print(f"Video saved as {video_filename}")
cv2.destroyAllWindows() elif command =='0':
frame = receive_image(s, timeout=5) # 增加超時參數(shù)
ifframeisnot None: cv2.imshow('Received Frame', frame)
cv2.waitKey(3000) # 顯示圖像3秒后自動關(guān)閉窗口
cv2.destroyAllWindows() else:
print("No image received.") s.sendall(b'1') # 假設(shè)'1'是確認(rèn)信號 except Exceptionase: print(f"Socket error occurred: {e}") ifvideo_writerisnot None: video_writer.release() # 確保異常情況下也釋放視頻寫入器資源if__name__ =="__main__": main()
3.3 執(zhí)行結(jié)果
-
物聯(lián)網(wǎng)
+關(guān)注
關(guān)注
2909文章
44726瀏覽量
374374 -
局域網(wǎng)
+關(guān)注
關(guān)注
5文章
757瀏覽量
46308 -
AI
+關(guān)注
關(guān)注
87文章
31054瀏覽量
269406
發(fā)布評論請先 登錄
相關(guān)推薦
評論