1)ffmpeg如何判断一帧数据是正确的?ffmpeg有没有错误处理的模式,能使花屏的帧(h264格式的)不显示出来?
2) H264网络传输过程中丢包造成马赛克问题?
原因:
- 接收网络数据包后没有调整包的顺序,譬如说接受包的顺序是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 }
原文链接: https://www.cnblogs.com/wyqfighting/archive/2013/03/04/2943082.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/79693
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!