这是一只鸟。这是另一只鸟!

作者:Shawn Powers

编者注:Shawn 将在 Linux Journal 十二月刊中再次讨论他的鸟摄像头,所以这是该系列中的原始文章,以唤醒您的记忆。

我的新全职工作是我可以在家里的办公室完成的工作。在家办公的好处之一是几乎可以保证有一个带窗户的办公室。因为这是我职业生涯中第一次拥有带窗户的办公室(不包括我有一年兼职办公室面向垃圾箱),所以我觉得这是一个放置一些喂鸟器的绝佳机会。

不幸的是我的家人,但非常幸运的是当地的鸟类,当我决定做某事时,我通常会全力以赴。我没有选择简单的混合鸟食喂食器,而是决定购买各种类型的喂食器、专用种子、带流水的鸟浴盆以及为遮阴和掩护而种植的树木。我的家人将我办公室窗外的区域称为“BirdTopia”(图 1)。他们甚至还没有见过我计划在冬天使用的加热鸟浴盆和花生喂食器!

图 1. 通过 BirdCam 看到的 BirdTopia

那么,我对观鸟的痴迷与 Linux 有什么关系呢?好吧,痴迷要求我要么整天盯着窗外而丢掉工作,要么我想办法在盯着电脑屏幕的同时观察我的鸟。进入:BirdCam。我需要一种方法来直播 BirdTopia 的实时视频,而无需花费更多钱。(“不花钱”部分是我妻子暗示的。)

摄像头

因为我不与任何人共用办公室,所以我的摄像头选择不必漂亮。我考虑过 USB 网络摄像头,但我拥有的所有网络摄像头质量都很低。幸运的是,我有一个装满旧手机的抽屉,这些手机已被更新的型号取代。我有三部 iPhone 3GS 手机和一部屏幕破裂的 Samsung Galaxy S2。iPhone 看起来状况更好,所以我首先尝试使用其中一部。我购买了一个名为 iWebcamera 的 5 美元应用程序,该应用程序将 iOS 设备变成带有内置 Web 服务器的 IP 摄像头。不幸的是,iPhone 3GS 的摄像头非常糟糕,所以虽然应用程序运行良好,但我并不满意。

接下来是我屏幕破裂的 Galaxy S2 手机。显然裂缝无关紧要,而且摄像头好得多。此外,Google Play 商店有一个名为 IP Webcam 的应用程序,它是完全免费且非常棒的。该应用程序在手机屏幕上放置了一个巨大的丑陋广告,但远程观看的视频完全没有广告。如果您恰好有选择,我强烈建议使用旧的 Android 设备而不是使用旧的 iOS 设备。我使用专为汽车设计的吸盘支架将手机安装在办公室窗户的内侧(图 2)。

图 2. 这种吸盘支架比我最初的“靠在窗户上”设计有所改进。

观看

iOS 应用程序和 Android 应用程序都内置了 Web 服务器,可以直接观看视频流(图 3 和 4)。iOS 应用程序的界面远不如免费的 Android 程序先进,但它们都允许观看 mjpeg 视频流或实时快照。使用 Android 应用程序(这是我接下来关注的重点,因为它免费、基于 Linux 且更好),全动态视频的分辨率低于照片快照的分辨率。

图 3. iOS Web 界面功能齐全,但很简陋。

图 4. Android 网络摄像头软件更加强大。

如果您只想从网络上的一两台计算机观看,则手机上的内置 Web 服务器可能就足够了。然而,对我来说,这还不够。我想从多台计算机(包括内部网络和互联网上的计算机)观看我的喂鸟器。我还想与世界分享我的 BirdCam,但我想自己提供所有内容,而不是依赖像 Ustream 这样的服务。而且,事情开始变得非常非常有趣。

视频是给傻瓜看的

好吧,它也是给那些带宽疯狂的傻瓜看的。虽然我家庭办公室的商业互联网连接有 5Mbit 的上传速度,但事实证明,流式传输多个视频源会很快饱和这种类型的带宽。我还遇到了使用超过一两个连接来增加手机上嵌入式 Web 服务器负担的问题。我仍然没有放弃完全流式传输,所以我对“全球 BirdCam”的第一次尝试是在我的 Linux 服务器上重新编码手机的视频,这样服务器就可以处理比旧 Android 手机更多的连接。

值得庆幸的是,VLC 将以无头模式运行并愉快地重新广播视频流。获得正确的命令行选项来正确流式传输 mjpeg 证明是一个挑战,但最终,这个长长的单行代码奏效了


cvlc http://PHONE_IP:8080/videofeed --sout \
'#std{access=http{mime=multipart/x-mixed-replace; \
boundary=7b3cc56e5f51db803f790dad720ed50a}, \
mux=mpjpeg,dst=0.0.0.0:2000/}'

cvlc 别名只是启动无头 VLC。mimeboundary 的东西花了最长时间才弄清楚。基本上,我必须将其正确设置,否则 Web 浏览器只会尝试下载文件而不是播放流。这种方法确实有效,实际上,我可以将多个客户端连接到端口 2000 上的服务器,并在不增加手机负担的情况下获得重新混合的流。(手机仅向服务器提供单个源,而服务器功能强大得多。)不幸的是,这并没有解决我的带宽问题。

我的翻页书解决方案

虽然 VLC 解决方案确实有效,但它并不真正符合我的需求。由于缺乏带宽,我无法流式传输到互联网,即使可以,我的服务器也只能处理少数客户端,然后也会逐渐消失。我最终得到的最终解决方案非常优雅且非常高效。

您可能还记得我说过 Android 应用程序允许拍摄高分辨率快照以及直接视频源。与其流式传输视频,我想如果我每秒拍摄一张高分辨率照片,我可以获得更好的图像,并且还可以节省大量带宽。我仍然想要类似视频的体验,所以我编写了一些脚本并学习了一些 JavaScript,以便在常规网页上制作一种“翻页书视频”流。这是一个分为两部分的过程。我必须不断获取更新的照片,而且我必须构建一个网页来正确显示它们。

第一步:获取照片

我的第一反应是使用 cron 作业定期从 Android 手机获取照片。因为 cron 作业每分钟才运行一次,所以我立即放弃了我的第一个计划。我不需要全动态视频,但“每分钟一帧”无论如何都很可悲。我最终制作了一些脚本,其中一个脚本我在系统启动时通过 rc.local 启动(列表 1 和 2)。

列表 1. bird_update 脚本

#!/bin/bash
while true
do
   bird_getphoto
   sleep 1
done
列表 2. bird_getphoto 脚本

#!/bin/bash
#Variables -- change to fit your needs
ORIGINAL_PHOTO=/dev/shm/birdtemp.jpg
MODIFIED_PHOTO=/dev/shm/birdmod.jpg
FINISH_PHOTO=/dev/shm/birds.jpg
CAMERA_IP=192.168.1.201
PHOTO_URL=http://192.168.1.201:8080/photo.jpg

if eval "ping -c 1 $CAMERA_IP > /dev/null"
then
   /usr/bin/wget -r --timeout=10 --quiet -O \
     $ORIGINAL_PHOTO \
     "$PHOTO_URL"
  
   convert $ORIGINAL_PHOTO \
     -quality 70% \
     -pointsize 64 \
     -fill white \
     -annotate +675+60 "    `date +"%I:%M:%S %p"`" \
     $MODIFIED_PHOTO

   rm $ORIGINAL_PHOTO
   mv $MODIFIED_PHOTO $FINISH_PHOTO
        fi

第一个脚本 bird_update(列表 1)通过 rc.local 在我的服务器上启动。我可以直接从 rc.local 调用更大的脚本并使其循环,但这样,我可以更改主脚本(bird_getphoto,列表 2),而不必担心重新启动 rc.local 的东西。bird_update 运行 bird_getphoto,休眠一秒钟然后重新开始。这意味着如果我对 bird_getphoto 进行更改,这些更改将反映在循环的下一次迭代中,而无需重新启动任何内容。由于我调整了 bird_getphoto 大约 6,000 次,因此这种方法非常理想。

bird_getphoto 是“脏活”,可以这么说。逐步执行命令应该相当容易理解,但基本上

  1. 查看手机是否在线。

  2. 从手机获取照片(超时时间很短——默认情况下,超时时间非常长,偶尔的故障不应使整个系统停顿)。

  3. 将照片存储在内存盘中。我这样做是为了节省硬盘磨损。我认为我每秒保存一个文件,并且每次都对旋转介质执行此操作是很愚蠢的。

  4. 压缩并注释照片。起初我的手机处于纵向模式,所以我也不得不旋转。convert 程序是 ImageMagick 软件包的一部分,非常强大。我在照片中添加了时间戳,主要是因为我可以。

  5. 下载和转换完成后,将临时文件 mv 到实时图像。我添加了这个额外的步骤,因为如果 convert 直接存储到最终文件名,则如果 Web 服务器尝试在转换过程中提供它,它将显示为损坏的图像。mv 命令几乎是瞬间完成的,因此在添加额外的步骤后,我没有看到任何奇怪的损坏。

我几乎不好意思承认,在我提出此处显示的脚本之前,我花了多长时间摆弄命令、想法、循环等等。与我的所有文章一样,请随意更改和/或改进我的想法,以最好地满足您的需求。这些脚本已经平稳运行了数周,并且在网络故障等方面似乎非常可靠。然而,定期更新照片只是问题的一半——事实证明,这是更容易的一半。

第二步:JavaScript 和破坏互联网

为了显示不断更新的鸟类照片,从 /dev/shm/birds.jpg 到 /var/www/birds/ 创建一个符号链接很容易,我的 Apache 虚拟主机文件夹位于该位置。我创建了一个带有 img 标签的简单 HTML 文件,我可以从任何地方看到我的喂鸟器。但是,为了获得刷新的图像,我必须刷新整个网页。它可以工作,但很丑陋。我不得不寻求一些 JavaScript 帮助。

在介绍最终的 HTML 文件之前,重要的是要解释一下,虽然让 JavaScript 刷新页面上的单个图像并不是非常困难,但浏览器被设计为尽可能多地缓存,因此确保图像实际上是从我的服务器每隔几秒钟获取一次被证明具有挑战性。列表 3 显示了我的最终 HTML 文件。

列表 3. birds.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 ↪"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<h3>The birds. Or not.</h3>
<script type="text/javascript">
refreshImage = function()
  {
    img = document.getElementById("cam");
    img.src = "http://example.com/birds.jpg?rand=" 
     ↪+ Math.random();
  }
</script>
<meta http-equiv="Content-Type" content="text/html; 
 ↪charset=iso-8859-1" />
</head>
<body onload="window.setInterval(refreshImage, 1*2500);">
<center>
<img style="width:100%;max-width:2048px" 
 ↪src="http://example.com/birds.jpg" id="cam" />
<br />
<small><em>This should constantly refresh</em></small>
</center>
</body>
</html>

列表 3 中显示的文件顶部的大部分只是定义正在使用的特定 HTML 标准。老实说,其中大部分超出了我的理解范围。脚本的关键部分是 JavaScript 代码,它定义了具有特定 ID 的图像的操作。您可以在 JavaScript 和下面的 img 标签中看到 ID 是“cam”。脚本的特殊部分是照片 URL 之后的一点随机信息。这实际上是脚本的一部分,它不仅每 2.5 秒重新加载图像,而且还加载图像,并在末尾带有 ?rand=RANDOMNUMBER。这基本上是在欺骗浏览器认为每次都有新图像要下载,所以我不会看到缓存的图像。有很多方法可以做到这一点,但事实证明,在我的测试中,这是最简单且跨浏览器友好的方法。有人担心会填满服务器上的缓存或缓冲区,但到目前为止,我还没有遇到任何问题。

结束了!还是没结束?

为了满足我的个人需求,bash 脚本和少量 JavaScript 确实完成了我需要的一切。我可以在家中的多台计算机上,甚至从互联网上观看 BirdCam。每个“视频帧”大约为 600K,因此虽然它仍然使用大量带宽,但与尝试流式传输完整视频相比,这不算什么。我注意到,在蜂窝网络连接速度较慢的情况下,有时图像会冻结,因为它会在原始图像加载之前尝试刷新。我最终确定 2500 毫秒作为刷新时间,因为它似乎在大多数位置都有效(图 5)。

图 5. 凭借远程观看 BirdCam 的能力,即使我不在家,我也可以拍到一些很棒的照片!

如果您现在访问我的 BirdCam,您可能会注意到我做了一些更多的调整。我添加了对当地气象站的查询,并将当前温度添加到注释中。然而,最大的变化是您希望不会注意到的变化。因为我知道我会将其发送给成千上万潜在的观鸟者,所以我认为我可能应该将我的解决方案扩展到云端。诚然,5Mbit 的上传速度还不错,但如果有 100 人试图查看我的后院,那就另当别论了!

我的简单解决方案是将 bird_update 脚本中的 sleep 命令替换为 scp 命令,该命令将 bird.jpg 文件上传到我的 Web 托管提供商。我仍然努力成为一名优秀的网民,并将实际文件上传到我的提供商服务器上的内存盘,但由于 .jpg 文件是从云端提供的,因此我不担心观鸟者的涌入。如果您担心我的家庭连接会饱和,请不要害怕;我已经提前计划好了。

冬天来了

如果您设置了类似的 Web 摄像头,我很乐意听到您的消息。您也可以观看我随着冬天临近而对鸟类痴迷的各种行为。冒泡的鸟浴盆很快将被加热版本取代,我将为冬季鸟类添加更多的动物油脂和花生。如果您碰巧在观看,并且看到一只很酷的鸟,请随时给我发送照片!有一天,我通过 BirdCam 看到了一只靛蓝彩鹀来访,但那是我使用旧 iPhone 时看到的,所以图像质量非常差。BirdTopia 也吸引了不仅仅是鸟类的动物。虽然偶尔会有松鼠大胆地来访,但最常见的非鸟类访客是 Zoey(图 6)。

图 6. 鸟类饮用水味道更好。

祝您周末项目顺利!我喜欢玩 BirdCam,几乎就像多年前我喜欢制作 MAME 街机一样。用 Linux 构建东西总能让我内心感到温暖。

Shawn 是 Linux Journal 的副编辑,并且从一开始就接触 Linux。他对开源充满热情,并且热爱教学。他还喝太多咖啡,这经常在他的写作中表现出来。

加载 Disqus 评论