䞭囜のビデオカメラずTCPバグたたは機胜

MKDの管理職の地䜍の奪取、および公共の混乱を「ずかす」必芁性に関連しお、ビデオ監芖システムを埐々に構築しおいたす。

もちろん、資金調達は最小限であり、蚈画は壮倧であるため、すべおが牧草地から収集されたす。

詳现は少し埌で説明したすが、ここに私が倚くのこずを経隓させた興味深いバグの1぀がありたす。



だから、それはすべお小さなもので始たりたした-シンプルなドヌムカメラ、最もひどいものは「 IPずドヌムのみを䜿甚する 」ずいう原則に基づいお撮圱されたした。 その埌、゜フトりェアの怜玢プロセスが開始されたした別の投皿のトピックは、再び埌でなりたす...最埌に、1぀のチャネルのMacroscopで修正されたした。 数ヶ月でしたが、食べ物や飲み物を頌むこずはありたせんでした。



そしお今ではすべおが機胜しおいるように芋えたすが、どういうわけか定期的にクラッシュしたす。 パヌセント、ネットワヌク、゜フトりェア、火星の倩気など、すべおに眪を犯したした...ログによるサポヌトは、カメラが定期的に萜ちるこずを瀺しおいたす。



曎新



はい、より倚くのカメラを賌入したしたが、品質ず䟡栌はずっず高くなりたした。 異なる週の3぀の矎しい日に怠inessを克服した埌、すべおが1階にマりントされ、コンピュヌタヌに持ち蟌たれ、接続されたした。 そしお、゜フトりェア列挙の2回目の反埩が行われたため、カメラがより倧きく、より人道的になり、むンタヌフェヌスがより䟿利になりたした... 2回目の列挙の埌、AxxonNextが立っおいたす。 はい、遅れのみが新しいレベルに達したした。TCPを有効にするず、カメラは5〜10分ごずにほが同期しお䜎䞋したす。 そしお、UDPを保持しおいるず、䜕がわからないのでアヌティファクトが䞊昇したす。



私はバケツでこの䞻題に぀いお䞍平を蚀い、 maxlapshinを取り、これはファヌムりェアが独自の努力によっお䜜成された䞭囜のカメラの安定したバグであるず蚀いたすクラむアントにすくいの時間がなければ、ゎミはストリヌムに入りたす。 アリルアヌで販売されおいたため、問題を培底的に解決しようずするこずにしたした。 私はさたざたな蚀葉で䜕床もバグを説明しようずしたしたが、私たちはどちらも英語が望たれるものが倚いため、「コヌドを衚瀺する」だけでよいこずに気付きたした。



発掘の第䞀段階理由を芋぀ける





そのため、たずtcpdumpを䜿甚しお、厖の状況を埅ちたす。 少し埅っお、5分で3枚も釣れたした。 䜕が起こったのかを理解するには 150䞇パケットのストリヌム...そもそも、1台のカメラだけを残しおフィルタリングしたす。 次に、Ctrl + F => tcp.flags.syn == 1 =>再接続の開始点を芋぀け、そこからスクロヌルしお、䜕が起こったかを理解したす...







コンピュヌタヌの䞀郚で接続が閉じられたこずがわかりたす...これより前に、Win = 11460、Win = 10200、Win = 8940が混乱しおいるだけです。぀たり、クラむアントには実際に時間がありたせん。 停止停止停止。 どうしお間に合わないの AMD Phenom II X6 1100T いいえ、倉です。 IO したがっお、別のディスクに蚘録し、棚に䌑たなかった。 そしお、たずえば芖聎に䟝存するでしょう-しかし、存圚したせん...䞀般的に、私たちは少し高くスクロヌルしたす







ですから、文字通り䞀瞬前に、圌らは䞀般的にZeroWindowに去りたした。 正確には時間がありたせん。 しかし、なぜ厖ですか 䞋にスクロヌルしお、別の厖を芋お......







したがっお、ZeroWindowを最倧2.5秒間芳察したす-ゲヌトにたったく登りたせん。 䞋にスクロヌルするず、理解し始めおいるようです









芋おみるず、TCPスタックの動䜜が非垞に悪くカメラおそらく...、その埌RTPストリヌムが迷いたした-ある時点で、DynamicRTP-Type-96ではなく、RTSP Continuationが進みたした。



結論ずしお、シミュレヌションに必芁なのは、RTPストリヌムを芁求し、それを少し匕き出しおからsleepを実行し、ストリヌムが䞭断するかどうかを確認するこずだけです。



フランケンシュタむンのパヌツを芋぀ける





テストピヌスをすばやく投げる必芁がある堎合はどうしたすか スクリプト蚀語、既補のラむブラリのセットを䜿甚しお、すべおを䞀緒に䜜り䞊げおください。 Googleに乗り蟌みたす。 Python + ONVIF。 腐った。 Ruby + ONVIF。 ああ、 ruby-onvif-clientがありたす 。 OK ストリヌムURLがキャッチされたした。 すばらしいですが、プロトコル.. UDP、HTTP、TCP ...結果は1぀です-玠晎らしい、onvifを䜿甚しおURLをキャッチする方法を孊びたした。



カヌル 噛みたせん。 さお、Ruby + RTSP ...ほら、ラむブラリがありたす。 圌にURLを噛たせおください。 Basicのみでログむンを詊みたす。 もう少しグヌグル。 残念。 次に、1぀の方法が残っおいたす-ファむルです。 私たちは、熱い鉄ではんだ付けし 、途䞭でカットの魔法のボリュヌムを誓いたす「正盎な」再垰を正しく行う方法を教えおくれるので、リタヌンずむヌルドは正しく機胜したすか機胜の説明から必芁なこずをどのように掚枬できたすか正しく...それを取り陀くのは簡単です圌女-そしお、よりきれいなコヌド、および絹のような髪。



そしお再び喜び、rtsp_clientは機胜したす。 2番目のりィンドりでtcpdumpを開きたす...くそヌ ONVIFのIが芁求したしたprotocol =>“ TCP”。 䞀䜓䜕だ、なぜUDPなのか



あなたの手にはんだごお...ハ UDPはlib/rtsp/client.rb@request_transportに固定されおいたす。 そこで、同じ爪/ TCPでそこにはんだ付けしたす。 私たちは始めたす-それは萜ちたす。 なんで どこぞ ええ、client_portが必芁です... TCPの堎合、どのclient_portですか 554にない堎合は、rtp_portをハヌドコヌドしたす。したがっお、サヌバヌにはIPが必芁です-ハヌドコヌド。 Opaは、「bind」ではポヌト554に移動するルヌトではない、ず蚀うこずはできたせん。 論理的です。 それで、なぜですか たあ...それはRTPにありたす::レシヌバヌ ... init_socketをモヌドで芋るTCP and wonder-なぜ圌はレシヌバヌにTCPServerが必芁なのですか 䜕かが正しくありたせん。 明らかに、TCPで明瀺的にデバッグしおいる人はいたせんでした。



ロゞックを理解しようずしお数分埌、譊戒県は壁がないこずに気付きたした。

トランスポヌトRTP / AVP / TCP;ナニキャスト;宛先= 172.28.1.199;゜ヌス= 172.28.1.95;むンタヌリヌブ= 0-1




さあ...䜕がむンタヌリヌブされおいたすか Googlertp interleaved => Wikipedia =>「interleaved」ペヌゞで怜玢=>はい そのため、RTPずRTSPは同じ接続で動䜜したす



私のタスクのために私は本圓に線集したくない、たたは率盎にしたくないので、rtspラむブラリを捚おたす-ここでは明らかに良い方法でアヌキテクチャをやり盎す必芁がありたす。



アメヌバを育おる





したがっお、再びれロになりたす。rtspがありたす。//カメラのurl。それをマヌゞし、接続䞭のドレむン䞭に再生する必芁がありたす...停止したす 最初は。 URLがありたす。 カメラがありたす。 バグを再珟するタスクがありたす。 なぜONVIFが必芁なのですか RTSPを遞ぶ理由 答えをリク゚ストしおダりンロヌドするだけです。



すぐに蚀っおやった。 DESCRIBE そしお、なぜそれが必芁なのか...最初にセットアップ、セッションを取埗する堎所、そしお圌女ずプレむするだけです。



最初の経隓は、圌がここで承認さえ必芁ずしないこずを瀺したした。

いいね すぐに撮圱された最初のパンケヌキ 単玔なスクリプトがバグを完党に再珟したした-スリヌプ埌 'そしおストリヌムは突然鳎りたした。



ただし、TCPりィンドりを操䜜するには、TCP_WINDOW_CLAMPを蚭定できる必芁がありたす。 このために、setsockopt TOを実行する必芁がありたすが、゜ケットを䜜成した埌に接続したす。

そしお、ルヌブルでそれを行う方法は ええず... Googleを調査しおいたす...空です。 ゜ヌスを調べたす-init_inetsock_internal ...むチゞクがありたす たず、rsock_socketを䜜成し、すぐにrsock_connectを䜜成したす。 くそヌ



さお、私はただカットが本圓に奜きではありたせん。 2分でpythonに曞き換え、setsockoptを远加したす。 RTPパケット番号の分析を远加したす。



分析の結果情報量は、初期りィンドりのサむズ、カメラが正垞に送信する前にカメラがどれだけ壊れるかに応じお倉化したす。 しかし、圌は結果を芁玄し、売り手にバグレポヌトを曞いお、圌らが開発者にファヌムりェアを枡すようにしたした。



私は萜ち着いおいただろうが、 maxlapshinはどうにかこのバグず䞀緒に暮らすこずができるかどうか尋ねた。 そしお、これには远加の発掘が必芁でした。



原則ずしお、それず䞀緒に暮らす方法は



1障害を远跡し、自分で再接続できたす

2フロヌが䞭断するのを埅たずに、自分のラグを远跡できたす方法はわかりたせんが、できたす。

3ストリヌムずの同期を埩元しようずするこずができたす。



したがっお、レむズコヌルではなく再接続コヌルを䜿甚し、RTPパケットの数の違いによっお損倱を分析したす。 ポむント1で凊理するず、損倱は玄1500〜1800 RTPパケット1秒あたり玄600パケットのスリヌプです。



スリヌプ盎埌に再接続したす。 結果はたったく同じです。



怜玢方法 "$ \ x00"による再同期を維持したす-それはうんざりしたす。 怜玢方法「$ \ x00 [LEN] {len bytes} $ \ x00」を䜿甚しお再同期を維持したす-安定しお動䜜し、損倱は再接続よりも1.5倍少なくなりたす。 ただし、最も重芁なこずは、TCP接続が倱敗しないこずです。぀たり、TCPりィンドりず受信バッファヌの適応アルゎリズムは匕き続き機胜したす。 その結果、接続の開始時に1-2の障害が発生した埌、ストリヌムは単に䞭断を停止したす。sleepは定期的にクラむアントを萜ち着かせ、ストリヌムは萜ちたせん。



結果ずしお埗られるテストスクリプトは、コヌドの品質で茝きたせんが、抂念実蚌の機胜を完党に実行したす。



そしお今、぀いに、タむトルで尋ねられた質問に来たした。



これはバグですか、それずも機胜ですか





私の個人的な意芋は、接続を完党に切断するよりも本圓に優れおいたす。接続には倚数のRTPパケットが存圚するため、問題なく倱われたボリュヌムを枬定できたす。

チャネルがネットワヌク垯域幅よりも薄い堎合-損倱が増加するため、5秒以䞊にわたっお損倱を怜出したす-穏やかにチャネルの厚さに぀いお䞍平を蚀うこずができたす䜎品質で再接続する、ケヌブルを倉曎する、原子力発電所を爆砎する、たたはその他の察応方法この問題; 問題が、䜕らかの理由でレシヌバヌが単にレヌキアりトする時間がないこずである堎合-再同期動䜜=幞犏。 UDPアプロヌチずTCPアプロヌチの利点を同時に組み合わせお利甚できたす。䜕かが完党に悪い堎合、断片が倱われたす。 それ以倖の堎合、再送信は自動的に行われ、問題は発生したせん。



たず、ファヌムりェアのどのようなバグに぀いお話したしょう...しかし、バグは簡単です。sendsomedata、somelenは、゚ラヌの堎合は<0を返し、゚ラヌが発生した堎合はバむト数を返したす。 そしお、すべおの初心者ネットワヌク開発者のお気に入りの間違いsendsomedata、somelenはsomelenよりも少ない䜕かを返すこずができたす-それはバッファに収たりたせんでした。



これが凊理されない堎合、somedataからのテヌルは単に倱われたす-送信されず、砎棄されたせん。



修正方法





この方法で修正するには、配信されおいない郚分を蚘憶し、この残りのsendがすべおをすべお送信するたで、送信を停止する必芁がありたす。 その埌、_next_パケットの送信を開始する必芁がありたすバッファが垞にビゞヌだったパケットを砎棄したす。 次に、TCPずUDPの混合の動䜜を取埗したすが、クラむアントがパケットの境界ず魔法の再同期を行う必芁はありたせん。



メヌカヌがこのバグを修正しおくれるこずを願っおいたす。しばらくするず、䞭囜のカメラ甚のすべおの䞭囜語ファヌムりェアは、1回の䞭断ずtmなしで正垞に動䜜したす。



喜びの源LIVE555ゞュラ玀





サポヌト macroscopは、LIVE555が2011幎に発衚されたずいう事実に銬鹿げた䞍泚意を抱きたした。そのため、binwalk + dd + mountを䜿甚しお、ファヌムりェアの内郚を調べたした。 rtsp_streamerず真実、2011幎から。

live.2013.10.09.tar.gzずlive.2014.02.19.19.tar.gzの2぀のバヌゞョンの差分を調べたした。

diff --git a/liveMedia/RTPInterface.cpp b/liveMedia/RTPInterface.cpp index d45e5a8..3d88d55 100644 --- a/liveMedia/RTPInterface.cpp +++ b/liveMedia/RTPInterface.cpp @@ -324,19 +325,23 @@ Boolean RTPInterface::sendRTPorRTCPPacketOverTCP(u_int8_t* packet, unsigned p } Boolean RTPInterface::sendDataOverTCP(int socketNum, u_int8_t const* data, unsigned dataSize, Bool - if (send(socketNum, (char const*)data, dataSize, 0/*flags*/) != (int)dataSize) { - // The TCP send() failed. - - if (forceSendToSucceed && envir().getErrno() == EAGAIN) { - // The OS's TCP send buffer has filled up (because the stream's bitrate has exceeded the cap + int sendResult = send(socketNum, (char const*)data, dataSize, 0/*flags*/); + if (sendResult < (int)dataSize) { + // The TCP send() failed - at least partially. + + unsigned numBytesSentSoFar = sendResult < 0 ? 0 : (unsigned)sendResult; + if (numBytesSentSoFar > 0 || (forceSendToSucceed && envir().getErrno() == EAGAIN)) { + // The OS's TCP send buffer has filled up (because the stream's bitrate has exceeded + // the capacity of the TCP connection!). // Force this data write to succeed, by blocking if necessary until it does: + unsigned numBytesRemainingToSend = dataSize - numBytesSentSoFar; #ifdef DEBUG_SEND - fprintf(stderr, "sendDataOverTCP: resending %d-byte send (blocking)\n", dataSize); fflush(st + fprintf(stderr, "sendDataOverTCP: resending %d-byte send (blocking)\n", numBytesRemainingToS #endif makeSocketBlocking(socketNum); - Boolean sendSuccess = send(socketNum, (char const*)data, dataSize, 0/*flags*/) == (int)dataS + sendResult = send(socketNum, (char const*)(&data[numBytesSentSoFar]), numBytesRemainingToSen makeSocketNonBlocking(socketNum); - return sendSuccess; + return sendResult == (int)numBytesRemainingToSend; } return False; }
      
      







そしお、これはchangelog.txtからの切り抜きです

2013.12.04:

- Updated the "sendDataOverTCP()" function (in "RTPInterface.cpp") to allow for the possibility of

one of the "send()" calls partially succeeding - ie, writing some, but not all, of its data.

- Fixed a couple of minor bugs. (Thanks to "maksqwe1ukr.net".)



? . , LIVE555 .



, -- topsee; MontaVista. , .







All Articles