播放网络摄像头视频支持ONVIF/RTSP

获取视频地址:

复制代码
f"rtsp://{u}:{p}@{ip}:554/Streaming/Channels/101",
            f"rtsp://{u}:{p}@{ip}:554/h264/ch1/main/av_stream",
            f"rtsp://{ip}:554/user={u}&password={p}&channel=1&stream=0.sdp"
        ]
        sub = [
            f"rtsp://{u}:{p}@{ip}:554/Streaming/Channels/102",
            f"rtsp://{u}:{p}@{ip}:554/h264/ch1/sub/av_stream",
            f"rtsp://{ip}:554/user={u}&password={p}&channel=1&stream=1.sdp"
复制代码
import tkinter as tk
from tkinter import ttk, messagebox
import cv2
from PIL import Image, ImageTk
import socket

class CameraApp:
    def __init__(self, root):
        self.root = root
        self.root.title("网络摄像头专用播放器")
        self.root.geometry("640x480")
        self.root.resizable(False, False)

        self.cap = None
        self.after_id = None
        self.btn_lock = False

        self.create_ui()

    def create_ui(self):
        
        top_frame = ttk.Frame(self.root)
        top_frame.place(x=5, y=5, width=630, height=45)

        ttk.Label(top_frame, text="IP:").grid(row=0, column=0, padx=3)
        self.ent_ip = ttk.Entry(top_frame, width=18)
        self.ent_ip.grid(row=0, column=1, padx=3)
        
        self.ent_ip.insert(0, "192.168.111.234")

        ttk.Label(top_frame, text="账号:").grid(row=0, column=2, padx=3)
        self.ent_user = ttk.Entry(top_frame, width=12)
        self.ent_user.grid(row=0, column=3, padx=3)
        self.ent_user.insert(0, "admin")

        ttk.Label(top_frame, text="密码:").grid(row=0, column=4, padx=3)
        self.ent_pwd = ttk.Entry(top_frame, width=12, show="*")
        self.ent_pwd.grid(row=0, column=5, padx=3)
        self.ent_pwd.insert(0, "1q2w3e4r")

        
        self.video_w = int(640 * 0.95)
        self.video_h = int(345 * 0.95)
        self.video_lab = ttk.Label(self.root, text="等待播放...")
        self.video_lab.place(x=16, y=58, width=self.video_w, height=self.video_h)

        
        btn_frame = ttk.Frame(self.root)
        btn_frame.place(x=0, y=400, width=640, height=80)

        self.btn_main = ttk.Button(btn_frame, text="主码流", command=self.play_main)
        self.btn_sub = ttk.Button(btn_frame, text="子码流", command=self.play_sub)
        self.btn_stop = ttk.Button(btn_frame, text="停止播放", command=self.stop_all)
        self.btn_exit = ttk.Button(btn_frame, text="退出程序", command=self.exit_app)

        self.btn_main.pack(side=tk.LEFT, padx=10)
        self.btn_sub.pack(side=tk.LEFT, padx=10)
        self.btn_stop.pack(side=tk.LEFT, padx=10)
        self.btn_exit.pack(side=tk.LEFT, padx=10)

    def lock_btn(self):
        self.btn_lock = True
        for btn in [self.btn_main, self.btn_sub, self.btn_stop]:
            btn.config(state="disabled")

    def unlock_btn(self):
        self.btn_lock = False
        for btn in [self.btn_main, self.btn_sub, self.btn_stop]:
            btn.config(state="normal")

    
    def check_camera(self):
        ip = self.ent_ip.get().strip()
        if not ip:
            messagebox.showwarning("提示", "请输入IP")
            return False
        try:
            for p in [554, 80]:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(1)
                if s.connect_ex((ip, p)) == 0:
                    s.close()
                    return True
                s.close()
        except:
            pass
        messagebox.showerror("错误", "摄像头不在线或未开启RTSP/ONVIF")
        return False

    
    def stop_all(self):
        self.lock_btn()
        if self.after_id:
            self.root.after_cancel(self.after_id)
            self.after_id = None
        if self.cap:
            try:
                self.cap.release()
            except:
                pass
            self.cap = None
        self.video_lab.config(image="", text="已停止")
        self.unlock_btn()

    
    def get_rtsp_urls(self, is_main):
        ip = self.ent_ip.get().strip()
        u = self.ent_user.get().strip()
        p = self.ent_pwd.get().strip()
        main = [
            f"rtsp://{u}:{p}@{ip}:554/Streaming/Channels/101",
            f"rtsp://{u}:{p}@{ip}:554/h264/ch1/main/av_stream",
            f"rtsp://{ip}:554/user={u}&password={p}&channel=1&stream=0.sdp"
        ]
        sub = [
            f"rtsp://{u}:{p}@{ip}:554/Streaming/Channels/102",
            f"rtsp://{u}:{p}@{ip}:554/h264/ch1/sub/av_stream",
            f"rtsp://{ip}:554/user={u}&password={p}&channel=1&stream=1.sdp"
        ]
        return main if is_main else sub

    
    def try_play(self, is_main):
        urls = self.get_rtsp_urls(is_main)
        for url in urls:
            try:
                cap = cv2.VideoCapture(url, cv2.CAP_FFMPEG)
                cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
                if cap.isOpened():
                    self.cap = cap
                    return True
                cap.release()
            except:
                continue
        return False

    def play_main(self):
        if self.btn_lock: return
        self.lock_btn()
        if not self.check_camera():
            self.unlock_btn()
            return
        self.stop_all()
        if self.try_play(is_main=True):
            self.update_frame()
        else:
            messagebox.showerror("失败", "无法打开视频,请核对账号密码与码流设置")
            self.unlock_btn()

    def play_sub(self):
        if self.btn_lock: return
        self.lock_btn()
        if not self.check_camera():
            self.unlock_btn()
            return
        self.stop_all()
        if self.try_play(is_main=False):
            self.update_frame()
        else:
            messagebox.showerror("失败", "无法打开视频,请核对账号密码与码流设置")
            self.unlock_btn()

    
    def update_frame(self):
        if not self.cap or not self.cap.isOpened():
            self.unlock_btn()
            return

        ret, frame = self.cap.read()
        if ret:
            frame = cv2.resize(frame, (self.video_w, self.video_h))
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            imgtk = ImageTk.PhotoImage(Image.fromarray(frame))
            self.video_lab.imgtk = imgtk
            self.video_lab.config(image=imgtk, text="")

        self.unlock_btn()
        self.after_id = self.root.after(30, self.update_frame)

    def exit_app(self):
        self.stop_all()
        self.root.destroy()

if __name__ == "__main__":
    win = tk.Tk()
    app = CameraApp(win)
    win.mainloop()
相关推荐
Escalating_xu1 小时前
【Linux 网络编程】connect 的断线重连:用状态机写一个能恢复连接的 TCP 客户端
linux·网络·tcp/ip
2401_868534783 小时前
局域网络中信息安全方案设计及攻击防范技术(制造企业园区网)
服务器·网络
2601_965449083 小时前
开源工具做推广没预算?AI自动生成宣传视频的方案
人工智能·开源·音视频
2601_962885723 小时前
如何用 Python 计算 VaR 和 CVaR(在险价值)?
开发语言·python
李航19833 小时前
DeepSeekAI建模三 - 实战建模一组柜子
人工智能·python·计算机视觉·ai·ai编程
懂点Tech3 小时前
ctfshow MISC入门图片篇(信息附加):misc21解题思路
网络
揽秀亭长3 小时前
视频转文字如何实现?四种方式的工作流与对比
音视频
AI视频工具指南4 小时前
文字怎么变成视频?同一地点的几个画面怎样接起来
人工智能·aigc·音视频
CS_Zero4 小时前
计算机网络通信基础2:带宽和吞吐量、客户端和服务器、网络基础设施
网络·计算机网络
盛世宏博智慧档案4 小时前
算力机房多协议融合:Modbus TCP 温湿度变送器与 SNMP 设备混合组网方案
网络·网络协议·tcp/ip·传感器·温湿度