XBMC 通知图像(来自 IP 摄像头)未更新

2 投票
1 回答
1168 浏览
提问于 2025-04-18 09:55

我有一个XBMC盒子连接在我的电视上。此外,我还有两个Foscam的网络摄像头,用来监控我的两个小女孩。

之前我觉得写几个脚本会很酷,这样当其中一个Foscam摄像头的警报响起时,我就能在XBMC上收到通知,还能看到相关摄像头的实时画面。这样我就可以一边看电视,一边关注孩子们。

所以,在XBMC盒子上我运行了一个脚本,每秒检查一次Foscam的警报状态。如果有警报触发,它就会发送一个命令给XBMC,启动一个XBMC脚本,暂停检查30秒,然后再继续检查警报状态。这个XBMC脚本会显示一个30秒的通知,里面有我女儿的名字(根据哪个摄像头触发的)和她们的可爱照片,还有相关Foscam的实时快照,每半秒更新一次。

这一切都运行得很好,真是太棒了 :) 不过,上周我升级了Foscam的固件。新固件的唯一变化(在固件说明中提到)是把摄像头的HTTP授权方式从basic改成了digest。从那以后,我的XBMC脚本就出现了问题。

首先,这是当前版本的脚本:

# Import the XBMC/XBMCGUI modules.
from requests.auth import HTTPDigestAuth
import xbmc, xbmcgui, xbmcvfs, xbmcaddon
import sys, os, requests, time


# Class to manage the notification image
class CamView(xbmcgui.WindowDialog):

    urlpath   = "/snapshot.cgi?resolution=16"
    imagename = "snapshot.jpg"

    def __init__(self, camname,camport,idx,username,password):

        # Construct correct URL
        self.baseurl = 'http://' + camname + 'cam:' + camport

        # Cams use digest authentication
        self.auth = HTTPDigestAuth(username, password)

        # Set
        path = xbmc.translatePath('special://profile/addon_data/%s' % xbmcaddon.Addon().getAddonInfo('id'))
        if not xbmcvfs.exists(path):
            xbmcvfs.mkdir(path)
        self.imagefile = os.path.join(path, self.imagename)

        # Message
        self.msg = {
            "1": camname.capitalize() + ' moved',
            "3": camname.capitalize() + ' made a sound',
        }.get(idx, camname.capitalize() + 'cam fired alarm')

        # set the initial image before the window is shown
        self.image = xbmcgui.ControlImage(870, 383, 380, 253, "")
        self.addControl(self.image)


    def update_image(self):

        f = requests.get(self.baseurl+self.urlpath, auth=self.auth)
        with open(self.imagefile, "wb") as local_file:
            local_file.write(f.content)

        self.image.setImage("")
        self.image.setImage(self.imagefile)

    def __enter__(self):
        return self

    def __exit__(self,type,value,traceback):
        os.remove(self.imagefile)


def main():

    for i in range(1,len(sys.argv)):
        str,dummy,val = sys.argv[i].partition("=")
        if str == "alarm_id":   idx      = val
        if str == "cam_id"  :   camname  = val
        if str == "cam_port":   camport  = val
        if str == "username":   username = val
        if str == "password":   password = val

    with CamView(camname,camport,idx,username,password) as viewer:

        viewer.show()

        start_time = time.time()
        firstimage = True
        while(time.time() - start_time <= 30):

            viewer.update_image()
            curr_time = round(time.time()-start_time, 0)

            if firstimage:

                firstimage = False
                nowtime = time.strftime("%I:%M %p")

                viewer.image.setAnimations([('conditional', 'effect=fade start=0 end=100 time=750 delay=125 condition=true'),
                                            ('conditional', 'effect=slide start=400,0 end=0,0 time=750 condition=true')])

                xoptions = ("Notification(\"" + viewer.msg + "\", " + nowtime + ", 29500, special://masterprofile/addon_data/" +
                            xbmcaddon.Addon().getAddonInfo('id') + "/" + camname + ".png)")
                xbmc.executebuiltin(xoptions)


            elif curr_time == 30:
                viewer.image.setAnimations([('conditional', 'effect=fade start=100 end=0 time=750 condition=true'),
                                            ('conditional', 'effect=slide start=0,0 end=400,0 time=750 condition=true')])

            else:
                viewer.image.setAnimations([('conditional', 'effect=fade start=100 end=100 time=0 condition=true')])


            xbmc.sleep(500)


if __name__ == "__main__":

    if xbmc.getInfoLabel("Window(10000).Property(foscamScriptRunning)") == "True":
        xbmc.log('Script already running', level=xbmc.LOGERROR)
    else:
        xbmc.log('Foscam alarm triggered', level=xbmc.LOGNOTICE)
        xbmcgui.Window(10000).setProperty("foscamScriptRunning", "True")
        main()
        xbmcgui.Window(10000).setProperty("foscamScriptRunning", "False")

原来的脚本使用了urllib,但我发现它并不方便支持digest认证。所以我改用了urllib2。但这并没有成功,因为我在XBMC弹出的窗口中看到的图片在第一次之后就再也没有更新过。有时候甚至根本没有图片。

于是我稍微查了一下,发现用urllib2Digest认证获取快照需要超过7秒钟!(而旧固件时,这个过程不到0.1秒)。我觉得这可能是导致图片不更新的原因,所以我把所有东西都改成了requests模块。经过分析,发现从摄像头获取单个快照现在大约需要0.25秒;虽然我觉得这还是挺慢的,但也许可以接受。不过,使用这种方法,通知中的图片还是没有更新。

我通过远程SSH触发脚本,所以我可以查看XBMC的日志等。我还检查了snapshot.jpg文件的时间戳,它们似乎与脚本触发的时间和requests的0.25秒延迟一致。在XBMC脚本中,我尝试了所有可能的顺序来清除图片和设置新快照,但都没有成功。如果我在清除和重新设置图片之间加一个延迟,我会看到图片闪烁,这表明一切都在工作。然而,它总是重新设置为完全相同的快照。

所以,我真的很困惑。我到底忽略了什么呢?

1 个回答

1

说说我的经历:

我最后“解决”了这个问题,方法是给每个快照保存一个独特的名字(这些名字是根据时间和微秒生成的),然后再把这些文件都删除掉。

这让我觉得可能是xbmcgui.ControlImage.setImage()在处理缓存方面有问题,但我找不到任何文档提到缓存的内容……

我在这个方法中遇到的一个问题是,如果在通知显示的时候按下Esc键(因为那时所有的XBMC控制都失效了),有时候这些图片没有被正确清理掉。这虽然是个小问题,但明显说明这个解决方案并不太靠谱 :)

撰写回答