← All posts tagged bug

Ответ Axxon'а: "Нашим продукт-менеджером по IP принято решение пока не делать исправление на стороне нашего ПО, поэтому задачу закрываю."
Причем закрывают "Решено". Мухахашечка.

Уж 3.13 близится, а ath всё падает:
ath: phy0: Failed to stop TX DMA, queues=0x18f!
ath: phy0: DMA failed to stop in 10 ms AR_CR=0xffffffff AR_DIAG_SW=0xffffffff DMADBG_7=0xffffffff
ath: phy0: Could not stop RX, we could be confusing the DMA engine when we start RX up
ath: phy0: Chip reset failed
ath: phy0: Unable to reset channel, reset status -22
ath: phy0: Unable to set channel

Н-да. Запустил локальный прокси на той же машине, начал высасывать всё из TCP потока принудительно на высочайшем приоритете.
Дождался "тормоза". В этот момент поток от камеры шел, его вычитывал (без задержек), всё буферизовалось. Но AxxonNext порвал коннект и пересоеденился. Выходит, что рвёт он сам, видимо, поймав некий таймаут...

Так, начал усиленно гонять вторую камеру. И знаете таки шо? Никаких потерь по TCP с ней нет.
Но и поток какой-то ненормальный — 33.6 RTP пакета в секунду, и 44100 кбайт в секунду.
На камере, на которой всё помирает постоянно и быстро — 250 пакетов и 330кбайт в секунду...

Заодно стало понятно, почему иногда бьётся поток целиком, иногда теряется пакет, а иногда вроде всё ок — но пакет вообще левый.
все уши растут из-за того, что пакеты шлются в несколько send()'ов, какой и в каком месте насколько поломается — один большой вопрос..

// Ага, гляжу в сырцы LIVE555. Дифф между live.2013.10.09.tar.gz и live.2014.02.19.tar.gz:
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 - i.e., writing some, but not all, of its data.
- Fixed a couple of minor bugs.  (Thanks to "maksqwe1@ukr.net".)

// Вывод? Матерный.

Что-то мысль появилась, может надо стучаться не китайцам, а производителям софта?
mvista.com
Там вообще ВЕСЬ софт собран МонтаВистой.
линух — MontaVista(R) Linux(R) Professional Edition 5.0.0 (0702774)
rtsp_streamer — MontaVista Software, LIVE555 Streaming Media v 2011.05.25
webserver — MontaVista Software, boa server

Так. Это 100% не IO. Пишу на HDD на том же компе всё, что приходит.
Пишет уже 30 минут. Ни одного не то что разрыва — ни одного сбоя, не потеряно ни одного пакета.

За это время Axxon потерял коннект до этой камеры 7 раз (и 31 реконнект всего).
Теперь я точно уверен, что проблема не в камере, а в неправильной организации обработки входящего потока.

Интересно. @maxlapshin — а не замечал ли обрывы, вызванные самой камерой?
Я сейчас выключил всё — и задержки и залипы, выкрутил буфера на максимум.
Наблюдаю за процессом, и стабильно помирает на 107 метрах записанного RTP потока.
last known: 82435 110583176 457.994058654 614380.270457 last 58340 loss 0 0.0
last known: 82303 110738104 457.295304059 615287.595101 last 63204 loss 0 0.0

ровно на 180 секунд (3 минуты).
беру вторую такую же камеру:
last known: 44961 60523326 249.807242507 336272.884842 last 45625 loss 0 0.0
тоже ровно 180 секунд.

я слегка не понимаю что за фигня O_O
но это... гхм... пиндец.

В принципе, мне удалось поймать свал без sleep() воткнув запись на диск f.write(rtppack) в том же потоке, вызывая write для каждого RTP пакета.
причем я поймал этот свал на ноутбуке, где пишу на SSD (так что жаловаться на нехватку IO нельзя).
Если включить буферизацию и писать на диск блоками по 128k — отвалов нет.

Градус странности растёт.

Итак, решил потестировать способы восстановления из поломки потока китайских камер из-за неуспевания выгребания.
Workaround #1: тупой reconnect поймав первый же garbage в канале.
потери при этом = примерно времени, на которое замирал поток (где-то 600 RTP фреймов на каждую секунду лага для камеры fullhd)
Workaround #2: reconnect сразу после sleep()'а (прервать со своей стороны, превентивно): эффект полностью аналогичен WA#1
Workaround #3: попытаться ресинхронизоваться внутри потока. Метод показал самый лучший эффект! Во-1х: абсолютные потери синхронизации при этом примерно втрое меньше, чем при реконнекте (потери при реконнекте обычно около 1500-1800 RTP пакетов, 600 пакетов на 1сек задержки (при потоке 240 пакетов/сек); при ресинхронизации потери составляют 300-400 пакетов на 1сек, и в абсолютных цифрах где-то вдвое меньше,чем при реконнекте.
А самое главное — соединение НЕ ПАДАЕТ, а значит, алгоритм адаптации TCP Window и Receive Buffer'ов работает без потерь, и практически моментально наращивает буфера так, что потери просто исчезают! Код всё так же регулярно залипает на 1-2-3 секунды, но поток не рвётся!
Это просто фантастика!

Причем это реально ЛУЧШЕ (имхо), чем рвать соединение целиком: номера RTP пакетов в коннекте есть, так что без проблем потерянный объём можно замерить.
Если канал тоньше, чем пропускная способность сети — то потери будут расти, таким образом, обнаружив потери больше чем на 5 сек — можно спокойно жаловаться на толщину канала (реконнектиться с меньшим качеством, попросить поменять кабель, взорвать АЭС, или любой другой способ реакции на эту проблему); если же проблема в том, что приёмник просто не успевает по какой-то причине выгребать — resync поведение = наше щасте. Получаем объединение плюсов одновременно UDP и TCP подходов: если совсем что-то плохо — кусочек потеряли; в остальных случаях — ретрансмиты автоматические и проблем не доставляют.

Пойду еще потыкаю палочкой с сервера виндового.
2 @maxlapshin: уродский наколеночный код можно глянуть тут: github.com
потыкай этой палочкой камеры, которые у тебя под ногами есть — сработает подход? или это только на моих экземплярах паштет?