播放网络摄像头视频支持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()
相关推荐
青少儿编程课堂6 小时前
威佐夫博弈(双堆取子游戏)解析
c++·python·算法·bfs·信息学竞赛
hhzz6 小时前
【OpenCV 入门到精通 03】图像入门:读取、显示、保存完全指南
人工智能·python·opencv·计算机视觉
金融小师妹7 小时前
AI趋势识别:黄仁勋宣布“AGI时代”到来,Astra能力跃迁背后的AI安全边界
大数据·python·深度学习·重构·逻辑回归
2501_9418752810 小时前
从配置中心到动态管理的互联网工程语法演进与多语言实践分享
开发语言·python
hfywmsj10 小时前
广州餐饮铺位招租决策模型:多因子选址系统设计
开发语言·人工智能·python·广州餐饮铺位招租
泡海椒13 小时前
内置SPI函数库详解:JQuick-Java Builtin工具类实战用法
java·开发语言·python
hqyjzsb13 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
sanjiaomao33314 小时前
从论文公式到Python实现:用单元测试校验滑动平均算法
python·算法·单元测试
Cx330❀15 小时前
【Linux网络】网络层协议 IP :从网络层原理到 Linux 内核源码
linux·运维·服务器·网络·tcp/ip·ai·ai编程
linweidong16 小时前
中科闻歌Java面试题及参考答案
java·python·大模型·提示词·ai agent·mcp·rag原理