DSP

RTP通话:视频流(H.264)的传输

2019-07-13 16:54发布

     从摄像头获取的视频数据,经过编码后(当然,也可以不编码,如果你觉得也很ok的话),既可以视频录制,同时如果需要,当然也可以视频远程传输咯,而实时传输协议(Real-time Transport Protocol,RTP)是在Internet上处理多媒体数据流的一种网络协议,利用它能够在一对一(unicast,单播)或者一对多(multicast,多播)的网络环境中实现传流媒体数据的实时传输(不需要下载完毕后才能看视频)。RTP通常使用UDP来进行多媒体数据的传输,但如果需要的话可以使用TCP等其它协议,整个RTP协议由两个密切相关的部分组成:RTP数据协议和RTCP控制协议。      RTP数据协议负责对流媒体数据进行封包并实现媒体流的实时传输,每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前12个字节的含义是固定的,而负载则可以是音频或者视频数据。       RTCP 控制协议需要与RTP数据协议一起配合使用,当应用程序启动一个RTP会话时将同时占用两个端口,分别供RTP和RTCP使用。RTP本身并不能为按序传输数据包提供可靠的保证,也不提供流量控制和拥塞控制,这些都由RTCP来负责完成。通常RTCP会采用与RTP相同的分发机制,向会话中的所有成员周期性地发送控制信息,应用程序通过接收这些数据,从中获取会话参与者的相关资料,以及网络状况、分组丢失概率等反馈信息,从而能够对服务质量进行控制或者对网络状况进行诊断。     实时流协议(RealTime Streaming ProtocolRTSP,它的意义在于使得实时流媒体数据的受控和点播变得可能。总的说来,RTSP是一个流媒体表示协议,  主要用来控制具有实时特性的数据发送,但它本身并不传输数据,而是必须依赖于下层传输协议所提供的某些服务。RTSP 可以对流媒体提供诸如播放、暂停、快进等操作,它负责定义具体的控制消息、操作方法、状态码等,此外还描述了与RTP间的交互操作。   一、JRTPLIB库的安装 Linux: rtp的运行当然少不了JRTPLIB库的支持,JRTPLIB是一个面向对象的RTP封装库,安装过程如下:     1)下载开发包解压。这里用的是jrtplib-3.7.1,下载地址:http://download.csdn.net/detail/huangminqiang201209/4925142 。     2)  解压后出现两个目录,一个是jrtplib-3.7.1,一个是jthread-1.2.1。JRTPLib是一个开源的RTP库。JThread是一个开源的线程类。     3)进入jthead解压目录,运行./configure配置环境. 配置完毕后运行make,  接着安装make install。     4)jrtplib安装同上。装好以后系统环境如下,静态动态库安装到了/usr/local/lib目录下,包括libjrtplib-3.7.1.so和libjthread-1.2.1.so等。头文件在/usr/local/include目录jrtplib*目录下。     5)Linux默认会在路径为/lib和/usr/lib下的库文件收缩,而上面的库文件在/usr/local/lib下,可以在/lib或者在/usr/lib下创建该库的快捷方式: ln -s /usr/local/lib/libjrtp-3.7.1.so  /usr/lib/libjrtp-3.7.1.so
    6)在jrtplib源代码目录里有例子程序,make文件都是写好的,试验一下编译example1.cpp,使用静态库libjrtp.a链接,编译语句如下:g++ -o example1 example1.cpp -I /usr/local/include/jrtplib3/ -ljrtp
    7)最后执行命令:ldconfig,更新库的信息,这样执行文件./example1,就可以了 Windows:      1)解压 jrtplib-3.7.1和 jthread-1.2.1     2)用 VC打开工程文件jthread.dsw     3)编译 jrtplib.lib和jthread.lib需要注意VC6要求安装Vs6sp6,在编译jrtplib.lib和jthread.lib前,在  project——settings——C/C++——Codegeneration:use run-time library中,对于 debug,选择:DebugMultithreaded DLL,对于release,则选择:Multithreaded DLL。     4)首先编译 jthread 库,然后将 jthread-1.2.1src内的"jmutex.h"和"jthread.h"两个头文件放入jrtplib-3.7.1src目录下,然后将 jrtplib-3.7.1src 文件夹下所有头文件中的语句修改为"jmutex.h"和"jthread.h",需要修改的文件为 rtpudpv4transmitter.h、rtpsession.h和 rtppollthread.h。编译时注意编译方式和 jthread.lib一致。     5)编译生成的 jthread.lib 和 jrtplib.lib 拷贝到系统目录:C:Program  FilesMicrosoft  Visual StudioVC98Lib 下,将 jrtplib-3.7.1src下所有的.h 头文件复制到 C:Program  FilesMicrosoftVisual StudioVC98Include,以便以后使用。     6)现在我们就可以编译 jrtplib-3.7.1examples 下的实例程序了。建立 VC 工程,打开example1.c,在 Project  Settings 的 link 页添加 jthread.lib  jrtplib.lib ws2_32.lib,在project——settings——C/C++——Code  generation:use  run-time library 中,对于 debug,选择:DebugMultithreaded DLL,对于 release,则选择:Multithreaded DLL。     7)编译源程序,运行就OK 啦
二、H.264 RTP PAYLOAD     在传输前,先要了解H.264 RTP PAYLOAD 格式(负载格式):
2.分片封包模式(核心代码即为该模式,以及单个NAL单元包):    而当 NALU 的长度超过 MTU (1024)时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).
详情见本文作者原文http://www.cppblog.com/czanyou/archive/2009/12/25/67940.html
三、数据(无摄像头,即黑屏数据) 1)H.246部分数据:
    这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 NALU , 42 开始的数据是NALU内容. 2)rtp,即sendpacket()发送的部分数据:         FU indicator0x7c  (NALU&0x60)|28  ==(0110 0111 & 0110 0000) | 28 == 01100000 | 0001 1100 == 0111 1100 ==0x7c       FU header0x87   (NALU&0x1f)|0x80 == (0110 0111 &0001 1111) | 0x80 == 0000 0111 | 1000 0000 == 1000 0111 == 0x87       这2个字节取代了开始码[00 00 00 01] 。其他的都一样。      NALU:0x67:  0 11 00111(7)      FU indicator:0x7C:0 11 11100(28) 3)网络抓包数据
四、RTP视频传输代码 #define PLOAD_TYPE 98
#define DefaultTimestampIncrement 90000/25
static RTPSession sess;
//创建rtp会话
static int  RtpSetup( uint16_t portbase)
{
    int status;
    ******************************* 
    *******************************
    status = sess.Create(sessparams,&transparams);
    checkerror(status);
    return status;
}
  //错误判断
void checkerror(int err)

      if (err < 0)
      {   
         char* errstr = RTPGetErrorString(err);  
         printf("Error:%s\n", errstr);   
         exit(-1); 
      }
}
  //增加rtp传输目标ip地址,参数为目标ip和端口
int AddDestination(uint32_t ipaddr, uint16_t destport)
{
    int status;
 
    RTPIPv4Address addr(ipaddr,destport);
    status = sess.AddDestination(addr);
    checkerror(status);
    return status;
}

//rtp视频传输,val为一帧数据流(264的原始数据),包含0x00 0x00 0x00 0x01信息,length为数据的长度 int H264SendPacket(unsigned char *val, uint32_t length)
{
    int status=0;
    uint32_t  TimestampIncrement;
    uint32_t send_length,valid_len=length-4;
    char NALU=val[4]
,*sendStartAddr=NULL;
    #define  MAX_STREAM_SLICE 1024
   //获取默认设置
   TimestampIncrement=sess.GetDefaultTimestampIncrement();
   //如果数据小于1024字节,直接发送:单一NAL单元模式
    if(valid_len <= MAX_STREAM_SLICE)
    {
        status = sess.SendPacket((void *)&val[4],valid_len,PLOAD_TYPE,true,DefaultTimestampIncrement);
        checkerror(status);
    }
    else
    {

             //切分为很多个包发送,每个包前要对头进行处理,如第一个包
             sendStartAddr=(char *)(val+4);//发送数据的起始地址
             sendStartAddr[pos-1]=(NALU&0x60)|28;//FU indicator
             sendStartAddr[pos]=(NALU&0x1f)|0x80;//FU header
             send_length=MAX_STREAM_SLICE+1;//要发送数据的长度,1025字节
                  ………………………………                   //第二个至倒数第二个包                   ………………………………                   //最后一个包     }     checkerror(status);
end:
    return status;
}
 对了,记得要连接rtp库哦!!!!
五、SDP参数     因为程序是在Hi35XX开发板上运行,所以我的工作是把编码后的视频发送出去就ok了偷笑 在windows下,写了一个脚本xxx.dsp,内容如下:
     m=video 1234 RTP/AVP 98
     a=rtpmap:98 H264/90000;
     a=decode_buf=300;
     a=framerate:15
     c=IN IP4 192.168.2.105   //板子的ip
注:     1)"m=" 行中的媒体名必须是 "video",端口为1234.
    2)"a=rtpmap" 行中的编码名称必须是 "H264".
时钟频率必须是 90000.
    然后把脚本拖到VLCPortable.exe软件中(VLC是一个标准),就能显示摄像头获取的视频,就说明rtp传输没问题,我的工作就完成了再见