ffmpeg + rtp介紹


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 對應的解決策略:

View Code
  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 }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM