čtvrtek 28. března 2024

Kamerový systém

Když jsem dokončoval dům, pořídil jsem kamerový systém nepříliš známé značky Partizan. Nebyla to dobrá volba. Systém fungoval, ale byl hrozně uzavřený, většina funkcí byla dostupná pouze fyzicky na NVR přes připojený monitor a myš, obsluha nepohodlná, optika kamer průměrná a detekce pohybu trpící množstvím falešných alarmů. Pravda, je to už přes 5 let. O důvod víc systém inovovat!

Strefil jsem skvělou dobu výprodejů značky HiLook, což je low-end od renomovaného Hikvision. Na Alze jsem pořídil tři kusy 4MP kamer IPC-B149HA (4mm), k tomu z CZC přidal další 4MP kameru IPC-B449HA (6mm) a rekordér NVR-108MH-D(C). Do NVR jsem osadil mírně starší HDD řady WD Purple. Celá sranda nakonec stála jen pár tisíc... a mnoho hodin času.

První dojmy z kamer byly skvělé. Obraz vynikající, k tomu perfektní detekce pohybu. Rozhodně se vyplatí volit HA verze, které mají Motion Detection 2.0, spolehlivost detekce je výborná. Také NVR a k němu rozumný přístup přes webové rozhraní mě potěšilo. Potíže začaly, když jsem se to celé snažil rozumně nastavit. NVR sice umí samo konfigurovat kamery, ale ne perfektně. Hlavní problém byl však se záznamem. Nepodařilo se mi rozběhat kontinuální záznam s označením detekce pohybu v časové ose, tuto funkcionalitu prý Hikvision z webové verze rozhraní v minulosti odstranil (?!). Následně jsem zjistil, že mám i problém s ukládáním pouze při detekci pohybu.

Všechny návody zmiňují nutnost zapnout funkci Dual VCA na kamerách, ta ale v těchto HiLook low-end verzích chybí. Vlastně celý firmware je neuvěřitelně osekaný, zatímco hardware je parádní, tak funkce (i kritické) prostě nejsou. Po cvičené aktualizaci firmwaru jedné z kamer se to ještě zhoršilo - zmizela položka FTP uploadu snímků při detekci pohybu... downgrade samozřejmě nemožný, starší firmwary nedostupné, ještě že výměnná politika Alzy je v tomto velmi vstřícná.

Naštěstí se funkčního očesání nedočkalo ONVIF rozhraní. Takže nakonec jsou HiLook kamery připojený k HiLook rekordéru nikoliv přes nativní Hikvision protokol, ale prostřednictvím ONVIF. Vyrobil jsem pro tento účel na kamerách uživatele s omezenými právy, aby rekordér nemohl do nastavení kamer zasahovat, takže to konečně mám celé spokojeně pod kontrolou.

Záznam při detekci pohybu funguje spolehlivě, napájení kamer je vyřešené běžným PoE switchem TP-Link. Kamera s 6mm ohniskem, tj. úzkým úhlem záběru, perfektně sedla dovnitř garáže a je namířená na vrata. Funkce WDR (Wide Dynamic Range) si uspokojivě poradila s kontrasty. Ostatní kamery zabírají okolí domu. Za zmínku stojí také ColorVu, což je funkce přirozených barev i při nízkém okolním osvětlení. Znamená to, že kamery nemají klasický neviditelný IR přísvit, ale slušně svítí bílou LEDkou. Pokud je v okolí pouliční lampa, bude stačit. Pokud ne, bude holt na zahradě nové světlýko.

Protože jsem paranoidní, mají kamery na firewallu zakázanou veškerou síťovou komunikaci s okolním světem s výjimkou synchronizace času. Nějaký přístup "zvenku" jsem k nim ale chtěl. U předchozích kamer byla jediná možnost získat RTSP stream a z něj udělat pomocí ffmpeg snímek formátu JPEG, předaný webovému serveru. Pomalé, náročné. Vybrané kamery HiLook mají přes ONVIF dostupný snapshot, jak prozradil ONVIF Device Manager. Získat odpovídající URL pomocí Wiresharku (http://192.168.1.64/onvif-http/snapshot?Profile_1) bylo dílem okamžiku. Krása. No a když už ho máme, co streamovat takové pseudo-video pomocí MJPEG? Celé to jde přece snadno udělat v PHP!

header("Content-Type: multipart/x-mixed-replace; boundary=cam-boundary");
header(
"Cache-Control: no-cache");
header(
"Pragma: no-cache");

$ch = curl_init();
curl_setopt(
$ch, CURLOPT_URL, $cam_url);
curl_setopt(
$ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(
$ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_setopt(
$ch, CURLOPT_USERPWD, $cam_user . ':' . $cam_password]);

$ts = microtime(true);
while (!connection_aborted()) {
    $data = curl_exec($ch);
    echo "--cam-boundary\r\n";
    echo "Content-Type: image/jpeg\r\n";
    echo "Content-Length: " . strlen($data) . "\r\n\r\n";
    echo $data;
    ob_flush();
    $ts = $ts + 0.5;
    @time_sleep_until($ts);
}
curl_close(
$ch);