• 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 }

     

     

  • 相关阅读:
    uva 10918 Tri Tiling
    uva 10943 How do you add?
    uva 10518 How Many Calls?
    convert函数用法小结转载
    GridView 实现服务器端和客户端全选的两种方法
    vs2008和vs10以及Windows Phone自带的1000多个 Windows 系统使用的各种图标、光标和动画文件
    添加滚动条的几个样式未完待续
    asp.net页面绑定数据的方式未完待续
    读取数据库中空字段的处理方法如下
    遇到一段让我尴尬的代码,有增长了点见识。
  • 原文地址:https://www.cnblogs.com/wyqfighting/p/2943082.html
Copyright © 2020-2023  润新知