1)ffmpeg如何判斷一幀數據是正確的?ffmpeg有沒有錯誤處理的模式,能使花屏的幀(h264格式的)不顯示出來?
2) H264網絡傳輸過程中丟包造成馬賽克問題?
原因:
1. 接收網絡數據包后沒有調整包的順序,譬如說接受包的順序是1,3,4,2,如果沒有調整順序的話,發送給解碼器的順序也是1,3,4,2,這樣肯定會出現馬賽克 ; 2. 接收網絡數據包后沒有沒有合並數據包,眾所周知,一個Video幀可能被分割成多個網絡數據包傳送,譬如說是1,2,3,如果在接受端沒有將這三個包合並成一個Video幀發送給解碼器,而是當成三個Video幀發送給解碼器,也肯定會出現馬賽克 ; 3. 沒有正確處理好網絡丟包的情況,如果所丟的數據包正好傳送的是另外一個幀所參考的的數據,這樣也會出現馬賽克 ; 4. 解碼器有問題,如果播放本地文件也出現馬賽克的話。
解決方法:
1.服務器軟件用多線程: (1)主線程:讀出(看你的圖象具體怎么上PC機了)一幀視頻數據,送給拆分線程。
(2)拆分線程:接到一幀視頻,開始拆包、做幀標記、打序列號,送給發送線程。 (3)發送線程:用RTP socket把封裝好的數據包發給客戶端。此socket是點對多點、單向 有根方式的組播套接字,實際上是基於UDP派生的,但他用到了RTP和RTCP(實時傳輸 協議和實時傳輸控制協議),如果你傳輸的不是實時數據,直接用UDP就行了。
2.客戶端軟件結構一般用多線程,線程間用事件對象進行同步,而共享數據區用臨界區對象進
行同步。 (1)主線程:接收網絡數據包的線程,優先級最高,可以盡量保證不丟數據,也采用RTP協 議,用網絡事件來觸發。主線程接收到視頻數據包后,將數據放入一個鏈表中,然后用事件對象觸發組裝線程。 (2)組裝線程:從鏈表中讀出數據包,要進行幀確認、排序等工作,當把一幀圖象的所有 包都取到時,再調用組裝模塊(可以是一個函數),將這些數據包組裝成完整的一個幀,然后送到解壓線程。 (3)若干解壓播放線程。主要考慮到如果你客戶端軟件想同時播放多畫面,比如說4畫面圖 象,就要用4個解壓播放線程。 (4)至於圖象存儲,要看你的客戶需要怎么存了,如果是手工存當然不需要單開線程,如果 是規定定時存或在某個事件發生時自動存盤,就需要單開一個線程,由定時器到時消息或此事件發生來觸發。
后來我們項目也將圖象送上了廣域網和Internet,主要就是傳輸的速率明顯慢了。把服務器軟件放在Web服務器上,用UDP點對點的方式給提出視頻服務的Internet客戶端發出相應視頻幀。還可以實現對每個客戶端的各種服務請求的響應和控制。
建議:
(1)Winsock傳輸部分最好不要用MFC提供的類,自己用API做,但是應用 程序可以用MFC做,
(2)在局域網上用點對多點的組播方式傳輸時,基於RTP和RTCP協議來做。 服務器方得拆包傳,每包最好不要大於2K,以我去年做的項目經驗, 用1.5K比較好,每包間隔1-2毫秒。發送方給每包打上時間戳和序列 號,接收方重新排序和組裝。 (3)如果是點對點傳,應該基於UDP協議,可以一幀一幀的傳,我最大傳過 30K。沒問題,我已經在實際項目中試過,JPEG格式的視頻流在局域網 中1秒15幀,一幀12K;在廣域網中1秒中4幀,一幀15K。 (4) 如果你傳輸的是監控的告警數據,要求准確性,你必須基於TCP協議,用點對點方式傳輸。
VLC 對應的解決策略:

1 static inline uint16_t rtp_seq (const block_t *block) 2 { 3 assert (block->i_buffer >= 4); 4 return GetWBE (block->p_buffer + 2); 5 } 6 7 #define GetWBE( p ) U16_AT( p ) 8 9 /* MSB (big endian)/LSB (little endian) conversions - network order is always 10 * MSB, and should be used for both network communications and files. */ 11 LIBVLC_USED 12 static inline uint16_t U16_AT( const void * _p ) 13 { 14 const uint8_t * p = (const uint8_t *)_p; 15 return ( ((uint16_t)p[0] << 8) | p[1] ); 16 } 17 18 /** State for a RTP session: */ 19 struct rtp_session_t 20 { 21 rtp_source_t **srcv; 22 unsigned srcc; 23 uint8_t ptc; 24 rtp_pt_t *ptv; 25 }; 26 27 /** State for an RTP source */ 28 struct rtp_source_t 29 { 30 uint32_t ssrc; 31 uint32_t jitter; /* interarrival delay jitter estimate */ 32 mtime_t last_rx; /* last received packet local timestamp */ 33 uint32_t last_ts; /* last received packet RTP timestamp */ 34 35 uint32_t ref_rtp; /* sender RTP timestamp reference */ 36 mtime_t ref_ntp; /* sender NTP timestamp reference */ 37 38 uint16_t bad_seq; /* tentatively next expected sequence for resync */ 39 uint16_t max_seq; /* next expected sequence */ 40 41 uint16_t last_seq; /* sequence of the next dequeued packet */ 42 block_t *blocks; /* re-ordered blocks queue */ 43 void *opaque[0]; /* Per-source private payload data */ 44 }; 45 46 /** 47 * Destroys an RTP source and its associated streams. 48 */ 49 static void 50 rtp_source_destroy (demux_t *demux, const rtp_session_t *session, 51 rtp_source_t *source) 52 { 53 msg_Dbg (demux, "removing RTP source (%08x)", source->ssrc); 54 55 for (unsigned i = 0; i < session->ptc; i++) 56 session->ptv[i].destroy (demux, source->opaque[i]); 57 block_ChainRelease (source->blocks); 58 free (source); 59 } 60 61 /** 62 * Receives an RTP packet and queues it. Not a cancellation point. 63 * 64 * @param demux VLC demux object 65 * @param session RTP session receiving the packet 66 * @param block RTP packet including the RTP header 67 */ 68 void 69 rtp_queue (demux_t *demux, rtp_session_t *session, block_t *block) 70 { 71 demux_sys_t *p_sys = demux->p_sys; 72 73 /* RTP header sanity checks (see RFC 3550) */ 74 if (block->i_buffer < 12) //如果RTP包的長度小於12,說明包傳輸有錯誤 75 goto drop; 76 if ((block->p_buffer[0] >> 6 ) != 2) /* RTP version number(rtp版本號必須為2) */ 77 goto drop; 78 79 /* Remove padding if present (判斷RTP數據包是否有填充字節,如果有填充字節,解析數據包時,必須將填充字節去掉)*/ 80 if (block->p_buffer[0] & 0x20) 81 { 82 uint8_t padding = block->p_buffer[block->i_buffer - 1]; 83 if ((padding == 0) || (block->i_buffer < (12u + padding))) 84 goto drop; /* illegal value */ 85 86 block->i_buffer -= padding; 87 } 88 89 mtime_t now = mdate (); //獲取精確的時鍾信息 90 rtp_source_t *src = NULL; 91 const uint16_t seq = rtp_seq (block); //獲取序列號 92 const uint32_t ssrc = GetDWBE (block->p_buffer + 8); //獲取SSRC 93 94 /* In most case, we know this source already 找到相同的SSRC*/ 95 for (unsigned i = 0, max = session->srcc; i < max; i++) 96 { 97 rtp_source_t *tmp = session->srcv[i]; 98 if (tmp->ssrc == ssrc) 99 { 100 src = tmp; 101 break; 102 } 103 104 /* RTP source garbage collection */ 105 if ((tmp->last_rx + p_sys->timeout) < now) //超時了 106 { 107 rtp_source_destroy (demux, session, tmp); 108 if (--session->srcc > 0) 109 session->srcv[i] = session->srcv[session->srcc - 1]; //將最后一個賦值給刪除的那個ssrc 110 } 111 } 112 113 if (src == NULL) //在原來的會話中沒有找到同樣的ssrc,說明是新來的ssrc 114 { 115 /* New source */ 116 if (session->srcc >= p_sys->max_src) //判斷是不是到達了最大的ssrc數的極限值(max) 117 { 118 msg_Warn (demux, "too many RTP sessions"); 119 goto drop; 120 } 121 122 rtp_source_t **tab; 123 tab = realloc (session->srcv, (session->srcc + 1) * sizeof (*tab)); 124 if (tab == NULL) 125 goto drop; 126 session->srcv = tab; 127 128 src = rtp_source_create (demux, session, ssrc, seq); //創建ssrc 129 if (src == NULL) 130 goto drop; 131 132 tab[session->srcc++] = src; 133 /* Cannot compute jitter yet */ 134 } 135 else 136 { 137 const rtp_pt_t *pt = rtp_find_ptype (session, src, block, NULL); 138 139 if (pt != NULL) 140 { 141 /* Recompute jitter estimate. 142 * That is computed from the RTP timestamps and the system clock. 143 * It is independent of RTP sequence. */ 144 uint32_t freq = pt->frequency; 145 int64_t ts = rtp_timestamp (block); 146 int64_t d = ((now - src->last_rx) * freq) / CLOCK_FREQ; 147 d -= ts - src->last_ts; 148 if (d < 0) d = -d; 149 src->jitter += ((d - src->jitter) + 8) >> 4; 150 } 151 } 152 src->last_rx = now; 153 block->i_pts = now; /* store reception time until dequeued */ 154 src->last_ts = rtp_timestamp (block); 155 156 /* Check sequence number */ 157 /* NOTE: the sequence number is per-source, 158 * but is independent from the payload type. */ 159 int16_t delta_seq = seq - src->max_seq; 160 if ((delta_seq > 0) ? (delta_seq > p_sys->max_dropout) 161 : (-delta_seq > p_sys->max_misorder)) 162 { 163 msg_Dbg (demux, "sequence discontinuity" 164 " (got: %"PRIu16", expected: %"PRIu16")", seq, src->max_seq); 165 if (seq == src->bad_seq) 166 { 167 src->max_seq = src->bad_seq = seq + 1; 168 src->last_seq = seq - 0x7fffe; /* hack for rtp_decode() */ 169 msg_Warn (demux, "sequence resynchronized"); 170 block_ChainRelease (src->blocks); 171 src->blocks = NULL; 172 } 173 else 174 { 175 src->bad_seq = seq + 1; 176 goto drop; 177 } 178 } 179 else 180 if (delta_seq >= 0) 181 src->max_seq = seq + 1; 182 183 /* Queues the block in sequence order, 184 * hence there is a single queue for all payload types. */ 185 block_t **pp = &src->blocks; 186 for (block_t *prev = *pp; prev != NULL; prev = *pp) //將接收到的數據插入到隊列中 187 { 188 int16_t delta_seq = seq - rtp_seq (prev); 189 if (delta_seq < 0) 190 break; 191 if (delta_seq == 0) 192 { 193 msg_Dbg (demux, "duplicate packet (sequence: %"PRIu16")", seq); 194 goto drop; /* duplicate */ 195 } 196 pp = &prev->p_next; 197 } 198 block->p_next = *pp; 199 *pp = block; 200 201 /*rtp_decode (demux, session, src);*/ 202 return; 203 204 drop: 205 block_Release (block); 206 } 207 208 209 210 211 vlc調用過程: 212 213 /** 214 * @file input.c 215 * @brief RTP packet input 216 */ 217 /***************************************************************************** 218 * Copyright 漏 2008 R茅mi Denis-Courmont 219 * 220 * This library is free software; you can redistribute it and/or 221 * modify it under the terms of the GNU Lesser General Public License 222 * as published by the Free Software Foundation; either version 2.1 223 * of the License, or (at your option) any later version. 224 * 225 * This library is distributed in the hope that it will be useful, 226 * but WITHOUT ANY WARRANTY; without even the implied warranty of 227 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 228 * GNU General Public License for more details. 229 * 230 * You should have received a copy of the GNU Lesser General Public 231 * License along with this library; if not, write to the Free Software 232 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 233 ****************************************************************************/ 234 235 #ifdef HAVE_CONFIG_H 236 # include <config.h> 237 #endif 238 239 #include <vlc_common.h> 240 #include <vlc_demux.h> 241 #include <vlc_block.h> 242 #include <vlc_network.h> 243 244 #include <unistd.h> 245 #ifdef HAVE_POLL 246 # include <poll.h> 247 #endif 248 249 #include "rtp.h" 250 #ifdef HAVE_SRTP 251 # include <srtp.h> 252 #endif 253 254 static bool fd_dead (int fd) 255 { 256 struct pollfd ufd = { .fd = fd, }; 257 return (poll (&ufd, 1, 0) > 0) && (ufd.revents & POLLHUP); 258 } 259 260 /** 261 * Gets a datagram from the network. 262 * @param fd datagram file descriptor 263 * @return a block or NULL on fatal error (socket dead) 264 */ 265 static block_t *rtp_dgram_recv (vlc_object_t *obj, int fd) 266 { 267 block_t *block = block_Alloc (0xffff); 268 ssize_t len; 269 270 block_cleanup_push (block); 271 do 272 { 273 len = net_Read (obj, fd, NULL, 274 block->p_buffer, block->i_buffer, false); 275 276 if (((len <= 0) && fd_dead (fd)) || !vlc_object_alive (obj)) 277 { /* POLLHUP -> permanent (DCCP) socket error */ 278 block_Release (block); 279 block = NULL; 280 break; 281 } 282 } 283 while (len == -1); 284 vlc_cleanup_pop (); 285 286 return block ? block_Realloc (block, 0, len) : NULL; 287 } 288 289 290 /** 291 * Gets a framed RTP packet. 292 * @param fd stream file descriptor 293 * @return a block or NULL in case of fatal error 294 */ 295 static block_t *rtp_stream_recv (vlc_object_t *obj, int fd) 296 { 297 ssize_t len = 0; 298 uint8_t hdr[2]; /* frame header */ 299 300 /* Receives the RTP frame header */ 301 do 302 { 303 ssize_t val = net_Read (obj, fd, NULL, hdr + len, 2 - len, false); 304 if (val <= 0) 305 return NULL; 306 len += val; 307 } 308 while (len < 2); 309 310 block_t *block = block_Alloc (GetWBE (hdr)); 311 312 /* Receives the RTP packet */ 313 for (ssize_t i = 0; i < len;) 314 { 315 ssize_t val; 316 317 block_cleanup_push (block); 318 val = net_Read (obj, fd, NULL, 319 block->p_buffer + i, block->i_buffer - i, false); 320 vlc_cleanup_pop (); 321 322 if (val <= 0) 323 { 324 block_Release (block); 325 return NULL; 326 } 327 i += val; 328 } 329 330 return block; 331 } 332 333 334 static block_t *rtp_recv (demux_t *demux) 335 { 336 demux_sys_t *p_sys = demux->p_sys; 337 338 for (block_t *block;; block_Release (block)) 339 { 340 block = p_sys->framed_rtp 341 ? rtp_stream_recv (VLC_OBJECT (demux), p_sys->fd) 342 : rtp_dgram_recv (VLC_OBJECT (demux), p_sys->fd); 343 if (block == NULL) 344 { 345 msg_Err (demux, "RTP flow stopped"); 346 break; /* fatal error */ 347 } 348 349 if (block->i_buffer < 2) 350 continue; 351 352 /* FIXME */ 353 const uint8_t ptype = rtp_ptype (block); 354 if (ptype >= 72 && ptype <= 76) 355 continue; /* Muxed RTCP, ignore for now */ 356 #ifdef HAVE_SRTP 357 if (p_sys->srtp) 358 { 359 size_t len = block->i_buffer; 360 int canc, err; 361 362 canc = vlc_savecancel (); 363 err = srtp_recv (p_sys->srtp, block->p_buffer, &len); 364 vlc_restorecancel (canc); 365 if (err) 366 { 367 msg_Dbg (demux, "SRTP authentication/decryption failed"); 368 continue; 369 } 370 block->i_buffer = len; 371 } 372 #endif 373 return block; /* success! */ 374 } 375 return NULL; 376 } 377 378 379 static void timer_cleanup (void *timer) 380 { 381 vlc_timer_destroy ((vlc_timer_t)timer); 382 } 383 384 static void rtp_process (void *data); 385 386 void *rtp_thread (void *data) 387 { 388 demux_t *demux = data; 389 demux_sys_t *p_sys = demux->p_sys; 390 bool autodetect = true; 391 392 if (vlc_timer_create (&p_sys->timer, rtp_process, data)) 393 return NULL; 394 vlc_cleanup_push (timer_cleanup, (void *)p_sys->timer); 395 396 for (;;) 397 { 398 block_t *block = rtp_recv (demux); 399 if (block == NULL) 400 break; 401 402 if (autodetect) 403 { /* Autodetect payload type, _before_ rtp_queue() */ 404 /* No need for lock - the queue is empty. */ 405 if (rtp_autodetect (demux, p_sys->session, block)) 406 { 407 block_Release (block); 408 continue; 409 } 410 autodetect = false; 411 } 412 413 int canc = vlc_savecancel (); 414 vlc_mutex_lock (&p_sys->lock); 415 rtp_queue (demux, p_sys->session, block); 416 vlc_mutex_unlock (&p_sys->lock); 417 vlc_restorecancel (canc); 418 419 rtp_process (demux); 420 } 421 vlc_cleanup_run (); 422 return NULL; 423 } 424 425 426 /** 427 * Process one RTP packet from the de-jitter queue. 428 */ 429 static void rtp_process (void *data) 430 { 431 demux_t *demux = data; 432 demux_sys_t *p_sys = demux->p_sys; 433 mtime_t deadline; 434 435 vlc_mutex_lock (&p_sys->lock); 436 if (rtp_dequeue (demux, p_sys->session, &deadline)) 437 vlc_timer_schedule (p_sys->timer, true, deadline, 0); 438 vlc_mutex_unlock (&p_sys->lock); 439 }