アむデアから...アむデアたでのビデオ監芖

私には䞻な仕事があり、い぀も「新しいビゞネスを始めたい」ずいう友人がいたす。 圌らは独自のものを持っおいたすが、垞に拡倧したいず考えおいたす。 そしお、圌らがそれを望むたびに、圌らはアドバむスを求めお私のずころに来たす。 アむデアは衚面にありたす。 「私たちはむンタヌネットをやっおいたすが、私たちのトピックに぀いお䜕か他のこずをしたしょう。」 そのため、電話、ネットワヌク機噚の販売、ホスティング、コロケヌションに取り組み始めたした。 問題の声明はい぀も私を喜ばせおきたした。 「電話をしたしょう」、「ホスティングをしたしょう。」 最埌に、「ビデオ監芖をしたしょう」でした。

ここで「ビデオ監芖に埓事したしょう」それが倧切なTKでした。

この蚘事ず経隓から生たれたものに぀いお説明したす。



歎史的に、私は営業担圓者が私に囲たれおいる組織で働いおおり、私はダルタニア人の技術専門家であり、長い間広く知られおいたす。

私の職業ずしおは、耇数のサヌビス技術郚門を蚭立したしたが、人々は教育レベルプログラマヌには達したせんでした。 だからこそ、私は垞に目の前にいたす。ITに関するすべおの質問は私のためです。 私は長幎にわたっおITの分野で圌ら友人に盞談しおきたした。システムおよびビゞネスアナリストであり、無料です:)。 トピックに移りたす。



人々が私のずころに来お「ビデオ監芖が欲しい」ず蚀ったら。 私は忙しかったので、この方向に発展する意欲はありたせんでした。 圌らは別の「専門家」を芋぀け、6か月間緎習したしたが、䜕も終わりたせんでした。 男は「矎しい」ず刀明し、すべおが曇っお機胜するずいう、ゟヌンマむンドず動きの力に぀いお歌いたした。 6か月間、単䞀の既補の゜リュヌションではなく、単䞀のクラむアントでもありたせん。

圌らは疲れおいたす、私のずころに来おください。 私はすでに10幎間圌らず仕事をしおいるので、もう䞀床冒険に参加しおみたせんか。 申し出は、い぀ものように、その独創性ず斬新さに感銘を受けたした。

-クラりドビデオ監芖を行いたい。 投資0、機噚を賌入したす。開発0では、お金を50/50に分割したす。

ずころで、関係者の数に応じお、お金は垞に50/50、たたは30/30/30でした。

なぜだず思いたした。 経隓は䞍芁ではありたせん。 ただし、あなたが幅広い分野の専門家であり、このプロファむルを幎に䞀床倉曎する堎合は圹に立ちたせん:)



アむデアは「クラりドビデオ監芖が欲しい」ず衚明されおいたす。



䜕かを販売したい人は、垞に䜕かを販売したす。

この「䜕か」を思い぀いおアレンゞするこずは残っおいたす。



クラりド。 お金0、負荷が倧きい。 したがっお、これらの問題を解決するプロトタむプを䜜成する必芁がありたす。

䞀芋、Openstackに萜ちたした。 その堎で蚈算胜力を拡匵するこずは可胜ですが、実行するにはいく぀かのマシンが必芁です。 さたざたなバヌゞョンのOpenstackで遊んだため、より適切な゜リュヌションを探す必芁がありたした。 私の目はProxmoxに萜ちたした。

最初は私たちのニヌズに合った玠晎らしい゜リュヌション。 そしお、それらのVMは垞にハヌドりェアたたは別のクラりドに移怍できたす。

鉄銬の賌入に参加する時間はありたせんでしたが、私の銬車銬が賌入される前にIntel Core i5-3570K @ 3.40GHz

そしお、このアむロンでは、クラりド監芖を行う必芁がありたした。 䞍条理な状況:)しかし、私たちはあきらめたせん。

ある皮の販売システムを思い぀きたす。 セキュリティシステムおよび民間譊備䌚瀟ず関係のあるすべおの皮類のアラヌトず連携したビデオ監芖州レベルでは抜け道がありたす。

未来にこだわっおこれに

1連続録画

2モヌション録画

3タむムラプス録画

4SMS通知

5動きが怜出されたずきのセキュリティセンタヌからの呌び出し。 オペレヌタヌはレコヌドを確認し、チョップを呌び出し、クラむアントに通知したす。

モスクワの民間譊備䌚瀟は幎々拡倧したす。 Mi Policeは毎幎、セキュリティビゞネスの量を枛らしおいたす。 そのため、これらすべおが埐々に個人トレヌダヌに忍び寄っおきたす。



䞀般的なシステムは次のようになりたした。



画像



カメラはあらゆるハヌドりェアに搭茉され、セキュリティに結び付けられたす。 このハヌドりェアは、OpenWrt䞊の゜フトりェアクラむアントたたはハヌドりェア゜リュヌションのいずれかです。 RPCには、理由がありたす。 アむデアは2぀のモゞュヌルを䜜成するこずでした。 1぀は完党なコン゜ヌルです。 これにより、コン゜ヌルを制埡する2番目のグラフィックモゞュヌルである鉄に埋め蟌むこずができたす。 これにより、どこからでも鉄片を制埡できたす。 ナヌザヌのコンピュヌタヌ、サポヌトコンピュヌタヌなどから たた、カメラからのストリヌムず静止フレヌムを暙準化するには、最初のモゞュヌルが必芁です。 私たちがすでに走ったような腺。 圌らはプロトタむプに適しおいた、経隓があった。 この鉄片のロゞックをサヌバヌに転送するこずを誰も犁止しおいたせん。



珟圚、h264動物園をディスクに駆動し、怜出噚ず同様のがらくたをweb +に取り付ける方法に぀いおタンバリンずダンスがあり、これはすべおi5に適合したす。

ZoneMinderは優れたシステムかもしれたせんが、4台のカメラを远加した埌、4぀のシステムコアすべおを70以䞊ロヌドしたした。 圌らはすぐに圌を捚おたした。



プロトタむプのために、圌らはVLCを取りたした。 それは長い間開発されおきおおり、倚くの人によっお実行され、いく぀かのプロゞェクトで䜕床も䜿甚しおいたす。

解決する必芁がある少なくずも2぀のタスクがありたす

1ネットワヌクぞの発行

2ストリヌムを蚘録する



カメラの動物園は、rtspストリヌムずhttpストリヌムで構成されおいたす。 VLCはすべおを実行できたす。 VLMを䜿甚するこずにしたした。 tlenetずhttpを制埡したす。

リンクは入力、出力でフィヌドしたす

"#{$transcode}std{access=http{mime={$this->mime}},mux=ts{use-key-frames},dst=*:{$this->getPort()}/{$this->path}}";





着信URLからHTTPストリヌムを取埗したす。

HTTPは私たちにずっおより䟿利ですか hginxで駆動し、負荷を分散し、暗号化できたす。 httpsに送信したす。

1クラむアント-1 vlcプロセス。

開発者は1人しかいないため、PHPでプロトタむプをスクリプト化するこずにしたした。時間が短く、資金が0です。

開始コマンド。 完成したコヌドを提䟛したす。必芁に応じおコマンドを取り出すこずは難しくありたせん

 $vlc_ifs = "-I http --http-host=0.0.0.0 --http-port {$this->getHttpPort()} -I telnet --telnet-port {$this->getTelnetPort()} --telnet-password ".TLPWD; if(VLC_USE_LOG) $vlc_logs = "--extraintf=http:logger --file-logging --log-verbose 0 --logfile {$this->getLogFile()}"; else $vlc_logs = '--extraintf=http'; //$vlc_shell = VLCBIN." --rtsp-tcp --ffmpeg-hw --http-reconnect --http-continuous --sout-keep ".VLCD." $vlc_ifs --repeat --loop --network-caching ".VLCNETCACHE." --sout-mux-caching ".VLCSOUTCACHE." $vlc_vlm --pidfile {$this->getPidFile()} $vlc_logs \n"; $vlc_shell = VLCBIN." --rtsp-tcp ".VLCD." $vlc_ifs --repeat --loop --live-caching ".VLC_LIVE_CACHE." --network-caching ".VLCNETCACHE." --sout-mux-caching ".VLCSOUTCACHE." --sout-ts-dts-delay 400 $vlc_vlm --pidfile {$this->getPidFile()} $vlc_logs "; if($this->isValgrind()){ $vlc_shell = "valgrind -v --trace-children=yes --log-file={$this->getValgrindFile()} --error-limit=no --leak-check=full $vlc_shell"; }
      
      







すでにラむブストリヌムがあるので、そこから曞き蟌むこずができたす。

return "#std{access=file{append},mux=ts{use-key-frames},dst=$filePath.avi}";







VLMは簡単に䜜成されたす

カメラで3぀のストリヌムを䜜成できたす

1ラむブストリヌム

2録画ストリヌム

3モヌションストリヌム

$this->execute("new {$this->cam} broadcast enabled loop");





プレむできたら、やめる

$command = "control {$this->cam} $command";





たずえば、10秒ごずに停止し、$ filePathを倉曎しお、再生するだけで十分です。

゚ントリを含む䞀連のファむルを取埗したす。 モヌション再生および停止甚。

VLCは、「with us」ず「with them」の䞡方で実行するこずもできたす。

動きの問題。

モヌションツヌルがありたす。 圌はJpegによっお動きを蚈算したす。 カメラはJpegで静止画像を提䟛したす。 必芁なものが埗られる出力で、フリヌズフレヌムを䜿甚しお、フィヌドを移動したす。



私たちには動物園があり、そこからシステムを䜜る必芁がありたす。

それで すべおをシステムず呌び、将来的にシステム芁玠を損倱なしに亀換できるように、疎結合システムを䜜成しようずしたす。

カメラ、Vlc、モヌション、ディスクぞの蚘録、オンラむン攟送がありたす。



VlcずモヌションはUNIXプロセスです。 それらを悪魔ず呌びたす。 抜象クラ​​スデヌモン

カメラからの2぀のストリヌムが必芁であるこずがわかりたした。 jpegおよびh264。

システムに゚ンティティを䜜成する

user-dvr-cam-stream

ナヌザヌ-ナヌザヌ

デヌモンずデヌモンに関連付けられたカメラを担圓するdvrシステム

cam-カメラ゚ンティティ。物理的なカメラ蚭定ず仮想ストリヌムを保存したす

ストリヌム-ストリヌム、任意の抜象ストリヌム。



Straemは、システムの「曎新」の瞬間に開始、停止、䞍安定、「プル」するこずができ、これらのむベントで、圌自身が䜕をすべきかを決定したす。



抜象宣蚀のみで構成されるシステム共通゚ンティティを取埗したす。



システムを構築するための䞀般的な工堎を取埗したす。
 abstract class AbstractFactory { private static $instance = null; /** * @var array */ private $commands; /** * @return AbstractFactory */ public static function getInstance(){ if(self::$instance == null) self::$instance = new static; return self::$instance; } /** * permanentCommand * @param ICommand $command */ protected function addPermanentCommand(ICommand $command){ $this->commands[] = $command; } /** * @param ISystem $system */ protected function addCommands(ISystem $system){ foreach($this->commands as $command) $system->addPermanentCommand($command); } //todo buildSystem method /** * @return ISystem */ public function createSystem(){ return System::getInstance(); } /** * @return array of Users */ public function createUsers(){ return array(AbstractFactory::getInstance()->createUser(1)); } /** * @param int $id * @return IUser */ public function createUser($id) { return new User($id); } /** * @param IUser $user * @return IDVR */ public function createDvr(IUser $user) { $dvr = new Dvr($user); $cams = $this->createCams($dvr); //new+add foreach($cams as $cam){ /** @var $cam Cam */ $dvr->addCam($cam); } $daemons = $this->createDaemons($dvr); foreach($daemons as $daemon){ /** @var $daemon Daemon */ $dvr->addDaemon($daemon); } return $dvr; } /** * @param DVR $dvr * @return array of Cams */ abstract protected function createCams(DVR $dvr); /** * @param DVR $dvr * @return array of Daemons */ abstract protected function createDaemons(DVR $dvr); /** * @param IDVR $dvr * @param ICamSettings $cs * @return ICam */ public function createCam(IDVR $dvr, ICamSettings $cs) { return new Cam($dvr, $cs); } /** * @param $from * @param $to * @return MoveToNfsCommand */ public function createMoveToNfsCommand($from, $to){ return new MoveToNfsCommand($from, $to); } /** * @param ICam $cam * @return ICamStream */ abstract public function createStream(ICam $cam); }
      
      









これにより、カメラのストリヌムを完党にハングさせお、思い぀いたこずを凊理するこずができたす。 そしお、少なくずも䜕らかの圢で動物園を制埡するこずができたす。 プロトタむプは圌です



䞻力補品はVLCであるため、システム甚にVLCをカスタマむズし始めたす。

抜象クラスVlcStreamを䜜成しおStreamを拡匵し、クラスLiveVlcStreamでVlcStreamを拡匵したす。

VlcStreamはVlmず「友達」です$ this-> vlm = new HttpVlm$ this-> getVlcName、 'localhost'、HTSTART + $ this-> cam-> getDVR-> getID;

開始、停止を凊理できたす

次ぞ

抜象クラスVlcReStreamはVlcStreamを拡匵したす

VlcReStream-LiveVlcStreamを入力ずしお受け取りたす

クラスRecVlcStreamはVlcReStreamを拡匵したす-ビデオをRamからNfsに「移動」したす



VlcはDaemonクラスを拡匵し、゚ンティティDVRに含たれたす。



DVRは開始ず停止を凊理したす

 public function start() { $this->log(__FUNCTION__); $this->startDaemons(); $this->startCams(); }
      
      





カメラを起動するず、カメラに関連付けられおいるすべおのストリヌムが開始されたす。

぀たり UNIXプロセスVlc、モヌション、HTTPを介したVLMを実行したす。

停止は逆の順序で発生したす。

VLCの代わりに、奜きなものを䜿甚できたす。



しかし、クラりドにはビデオ監芖がありたす。 したがっお、SQLおよび同様のストレヌゞシステムが必芁です。

動䜜䞭のシステムは次のずおりです。



工堎
 <?php /** * Created by PhpStorm. * User: calc * Date: 16.06.14 * Time: 10:56 */ namespace system2; /** * Class BBFactory * @package system2 */ class BBFactory extends AbstractFactory { /** * @return ISystem */ public function createSystem() { $system = parent::createSystem(); $e = new BBLogMotionEvent(Motion::EVENT_MOTION_START); $system->addEventHandler($e); $e = new BBLogMotionEvent(Motion::EVENT_MOTION_STOP); $system->addEventHandler($e); $e = new BBLogMotionEvent(Motion::EVENT_MOTION_DETECTED); $system->addEventHandler($e); $e = new BBLogMotionEvent(Motion::EVENT_CAMERA_LOSS); $system->addEventHandler($e); $recMotionEvent = new BBRecMotionEvent(Motion::EVENT_MOTION_START); $system->addEventHandler($recMotionEvent); $recMotionEvent = new BBRecMotionEvent(Motion::EVENT_MOTION_STOP); $system->addEventHandler($recMotionEvent); //   30    update //$system->addPermanentCommand(new RotateRecCommand()); $this->addPermanentCommand(new RotateRecCommand()); $this->addCommands($system); return $system; } /** * @param DVR $dvr * @return array of Daemons */ protected function createDaemons(DVR $dvr) { $vlc = new Vlc($dvr); $this->addPermanentCommand(new BBDaemonWatchdog($vlc)); //     ,    mtn $cams = $dvr->getCamIDs(); $ids = array(); foreach($cams as $id){ $cam = $dvr->getCam($id); $cs = $cam->getSettings(); /** @var $cs BBCamSettings */ if($cs->mtn) $ids[] = $id; } $motion = new Motion($dvr, $ids); if(count($cams)) $this->addPermanentCommand(new BBDaemonWatchdog($motion)); return array($vlc, $motion); } /** * @return array */ public function createUsers() { $users = array(); $db = \Database::getInstance(); $q = "select id from users where banned=0"; $r = $db->query($q); while(($row = $r->fetch_row())){ try{ $users[] = AbstractFactory::getInstance()->createUser($row[0]); } catch(\Exception $e){ Log::getInstance()->put($e->getCode().' '.$e->getMessage()."\n".$e->getTraceAsString()."\n", __CLASS__, Log::ERROR); } } return $users; } /** * @param DVR $dvr * @return array */ protected function createCams(DVR $dvr){ $db = \Database::getInstance(); $q = mysql::getQuery(mysql::CAM_SETTINGS, array('{dvr_id}' => $dvr->getID())); $r = $db->query($q); $cams = array(); while(($row = $r->fetch_object('system2\BBCamSettings')) != null){ /** @var BBCamSettings $row */ //$dvr->addCam(new BBCam($this, $row)); $cam = $this->createCam($dvr, $row); $cams[] = $cam; //    Motion     ,    $timelapse = new BBArchiveTimelapseCommand($cam); $this->addPermanentCommand($timelapse); } return $cams; } /** * @param ICam $cam * @return ICamStream */ public function createStream(ICam $cam) { $stream = new Streams($cam); $cs = $cam->getSettings(); /** @var $cs BBCamSettings */ $motion = new MotionStream($cam, $cs); $motion->setEnabled($cs->live && $cs->mtn); $stream->addStream($motion); $live = new BBLiveStream($cam); $live->setTestInputCommand(new BBTestInputFailSaveCommand($cam, $live)); $live->setEnabled($cs->live); $stream->addStream($live); $hls = new HLSVlcStream($cam, $live); $hls->setEnabled($cs->live); $stream->addStream($hls); //$this->streams[] = new FlvVlcReStream($this, $live); //nginx rtmp stream //$this->streams[] = new RtmpVlcReStream($this, $live); $rec = new BBRecStream($cam, $live); $rec->setEnabled($cs->live && $cs->rec); $rec->setTestInputCommand(new BBTestInputFailSaveCommand($cam, $rec)); $stream->addStream($rec); $mtn = new BBRecStream($cam, $live, TIME_LOCK_RECORD, Path::MOTION); $mtn->setEnabled($cs->live && $cs->mtn && BBRecMotionEvent::isMotion($cam)); $mtn->setTestInputCommand(new BBTestInputFailSaveCommand($cam, $mtn)); $stream->addStream($mtn, Path::MOTION); //motion flv stream $flv = new UrlFlvVlcStream($cam, "http://localhost:".(MOTION_STREAM_PORT + $cam->getID())); $flv->setEnabled($cs->live); $stream->addStream($flv); return $stream; } }
      
      









createSystem、興味のあるむベントを远加したす。 Systemクラスが凊理を担圓したす。

EVENT_MOTION_START、EVENT_MOTION_STOP、EVENT_MOTION_DETECTED、EVENT_CAMERA_LOSS

これはmoton configから呌び出されるものです

糞
 # Command to be executed when an event starts. (default: none) # An event starts at first motion detected after a period of no motion defined by event_gap on_event_start php ~/dvr/bin/system2/main.php event {user_id} {cam_id} motion_start 0 "%Y-%m-%d %T" # Command to be executed when an event ends after a period of no motion # (default: none). The period of no motion is defined by option event_gap. on_event_end php ~/dvr/bin/system2/main.php event {user_id} {cam_id} motion_stop 0 "%Y-%m-%d %T" # Command to be executed when a picture (.ppm|.jpg) is saved (default: none) # To give the filename as an argument to a command append it with %f ; on_picture_save value # Command to be executed when a motion frame is detected (default: none) ; on_motion_detected php ~/dvr/bin/system2/main.php event {user_id} {cam_id} motion_detected 0 "%Y-%m-%d %T" # Command to be executed when motion in a predefined area is detected # Check option 'area_detect'. (default: none) ; on_area_detected value # Command to be executed when a movie file (.mpg|.avi) is created. (default: none) # To give the filename as an argument to a command append it with %f ; on_movie_start value # Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) # To give the filename as an argument to a command append it with %f ; on_movie_end value # Command to be executed when a camera can't be opened or if it is lost # NOTE: There is situations when motion don't detect a lost camera! # It depends on the driver, some drivers dosn't detect a lost camera at all # Some hangs the motion thread. Some even hangs the PC! (default: none) on_camera_lost php ~/dvr/bin/system2/main.php event {user_id} {cam_id} motion_camera_lost 0 "%Y-%m-%d %T"
      
      







main.phpむベントuid cid event_nameパラメヌタヌぞの呌び出しがありたす

システムクラスがこれを凊理したす。

10分ごずにcronで任意の数の鉱山で1回、システム曎新が呌び出されたす。

曎新によるず、すべおのサブシステムの曎新ず

 $this->executeCommands(); $this->executePermanentCommands();
      
      





コマンドはサブシステムがスロヌするものであり、RotateRecCommandなどの定数コマンドは叀いレコヌドを削陀する圹割を果たしたす。



デヌモンを䜜成するずきに、たずえばBBDaemonWatchdogなどの远加機胜があり、デヌモンが萜ちたかどうかを監芖したす。



ナヌザヌはすでにデヌタベヌスから䜜成されおいたす。

次に、デヌタベヌスからカメラ。 氞続的なタむムラプスコマンドが远加されたす。 これは、TIME_LOCK_TIMELAPSE8時間で1回だけ機胜し、Mysqlで情報をポップしたす



最埌の-最も興味深いのはストリヌムです。

ストリヌムモヌション、ラむブ、hlsHTTPラむブストリヌム、rec、mtn、flvが䜜成されたす。

ラむブ-通垞のhttp

rec、mtn-蚘録甚のストリヌム

ブラりザぞの出力甚のhlsおよびflv。



Github github.com/Calc86/dvr/tree/master/bin/system2



ロゞックがありたす。 「物理孊」が必芁です。

倚くのカメラ、倚くのスレッド、1぀のプロセッサがありたす。

すべおの蚘録はramdiskで行われ、hlsはramdiskで行われ、vlc構成はトランスコヌディングが行われないように行われたす。 クリヌンストリヌムが削陀され、クリヌンストリヌムが保持されたした。 RAMディスクからストレヌゞプロトタむプはNFSぞの移行は、ffmpegコヌデックコピヌを通過したした

これにより、html5゚ントリを介したブラりザでの出力が蚱可されたした。

3皮類のレコヌド。 10分ごずのスラむス、8時間でのモヌション録画およびタむムラプス録画による完党録画、1秒あたり3分が衚瀺されたす。



システムの構築は、コヌドの倉曎を最小限に抑えながら、地理的にもDVR蚘録機の独立した配眮を蚈画したした。



実装されたもの

1必芁なすべおのレコヌド

2web、hls、flash、html5のレコヌドぞの出力、動物園コヌデックh264はh264に䜏んでいるので、Webに行くたで、mjpegに出力叀いノキアでオンラむンビデオを芖聎できたす

3https、nginxセキュリティURLなどを䜿甚したこの恐ろしいもののWebむンタヌフェむス。

Onvif経由でカメラを远加するのをやめたした



プロゞェクトが停止した理由。

1売れ行きの悪さ販売員ず販売システムの䞍足

2資金䞍足少なくずもこのようなシステムには管理者が必芁です

3空き時間の䞍足

4完党に無料の開発

5テスト甚にカメラを賌入したくない

6地区評議䌚ずの合意はありたしたが、テストに無料のカメラを蚭眮する意向はありたせん。

7垞甚職を倉曎したす。 これを行う機䌚はありたせん。

8デザむンの賌入ずスラむスの賌入。ただし、将来は自分で切り刻んだ。

9システムを垞に監芖する人がいないモヌションずvlcの䞡方にバグが芋぀かりたしたが、システム管理によっお解決されたす

10その幎の「セヌルスマン」は、単䞀の販売および䟡栌蚭定システムを思い぀きたせんでした。

11私は、䞻芁な仕事であるビデオ監芖ず個人的な生掻の創造ず䞊行しお倚くのこずをしたした。

12誰もが亀枉した責任に぀いおボルトを打ちたした:)



しかし、3人のクラむアントがいお、圌らはそれを気に入っおいたした。 圌らはシステムにお金をかけず、カメラを蚭眮するためだけにお金を取りたした。



それがどのように芋えたか:)

画像



トピックがうたくいけば、VLCず萜ずし穎に぀いおさらに詳しく説明したす。 そしお、「神話䞊の」鉄片ずそれを䜿った実隓に぀いおも。



All Articles