播放网络摄像头视频支持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 分钟前
藏在TCP握手里的暗号:一种基于序列号触发的加密回连后门
linux·网络·网络协议·tcp/ip
运维行者_1 分钟前
ITOps自动化:全面解析
java·服务器·开发语言·网络·云计算
minji...1 分钟前
Linux 网络基础之传输层协议TCP(八)拥塞控制,延迟应答,捎带应答,TCP粘包问题,异常退出问题
linux·服务器·网络·网络协议·tcp/ip·http·智能路由器
Chase_______3 分钟前
【Java杂项】为什么 b += 1 可以,但 b = b + 1 会报错?类型提升与复合赋值详解
java·开发语言·python
one day3214 分钟前
软考网络工程师第三部
网络·安全·web安全
Wiktok5 分钟前
【Wit智慧引擎】亲测可用国内pytorch镜像
人工智能·pytorch·python
ZFSS6 分钟前
MultiNLI 多种类自然语言推理数据集介绍
人工智能·ai·ai作画·音视频·ai编程
yqzyy14 分钟前
C#如何优雅处理引用类型的深拷贝(十一)
java·网络·nginx
旦莫15 分钟前
一个完美的AI测试Agent应该是什么样的
人工智能·python·测试开发·pytest·ai测试
风风筝吖21 分钟前
ACL + NAT 综合配置HCL
运维·网络