播放网络摄像头视频支持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()
相关推荐
其实防守也摸鱼1 小时前
《SQL注入进阶实验:基于sqli-Labs的报错注入(Error-Based Injection)实战解析》
网络·数据库·sql·安全·网络安全·sql注入·报错注入
永远不会出bug1 小时前
JAVA:WebSocket 「在线状态 + 强制挤下线通知」
网络·websocket·网络协议
m0_495496411 小时前
SQL中如何获取前N个最大值并排除自己_利用窗口函数限制
jvm·数据库·python
m0_740653221 小时前
mysql如何提取日期中的年份_使用year函数从日期中截取
jvm·数据库·python
S1998_1997111609•X1 小时前
login:/-system.web,dex.dmp,b-scode:app·%
网络·数据库·百度·facebook·twitter
运气好好的1 小时前
mysql数据库日志文件过大如何清理_定期备份与重置日志文件
jvm·数据库·python
ATMQuant1 小时前
量化策略开发01:我让AI全权做交易决策 - 从提示词设计到决策执行
python·量化交易·vnpy·ai策略
站大爷IP1 小时前
如何在 Python 中使用 colorama 库来给输出添加颜色
python