直接用udp实现简单的RTP思路

直接用udp实现简单的RTP思路 - 加菲 - 视频会议 - 加菲

    RTP完整的协议实现比较复杂,因为要考虑到很多种情况,如果具体到一个简单的应用上,可能就比较简单,因为所有问题都具体化了。比如包的方式,可能具体为单一包和基本的分包,其它情况就不用去考虑了。

    前天下载的那个代码的实现,比较简单,基本上看完了,整理一下它的实现思路。

    以流方式打开一个文件
    查找0x 00 00 01 (或 0x 00 00 00 01)起始码,连续两个起始码之间的内容即为一个H.264的nal,是要用RTP发送的一块内容。这个要考虑一种特殊的情况,即文件尾,处理起来也很简单。
    判断nal的长度,分为二种情况:<=1400和>1400
    <=1400时,直接单一包往外发送;
    >1400时,拆分成1400的几个,最后一个可能不足1400;
    
     对于里面的几个结构体和rtp里的一些基本东西,都看了,但脑子里还是不清晰,比如时间戳在什么情况下如何增长,以及RFC3984里的一些结构体。

    FU_INDICATOR
    FU_HEADER
    NALU_HEADER
    RTP_FIXED_HEADER


    在代码中,分配了一块发送缓冲区,char sendbuf[1500];和一个存放nalu的内存n = AllocNALU(8000000);
    nalu的内容要加上RTP头,也就是说,这个sendbuf在发送前要进行构造,一开始放上rtp头,然后再放nalu
    代码的处理是,取到sendbuf[0]的地址,把它强制转换成RTP_FIXED_HEADER类型的指针,实际就是在发送缓冲区开始的那块解释成了一个此类型的结构体,然后去对结构体字段进行赋值,这个结构体就是RFC3550中说的RTP固定头。

    nalu_hdr =(NALU_HEADER*)&sendbuf[12];
    把发送缓冲区的第12个字节开始的后续字节解释成NALU_HEADER类型,然后对该结构体的各字段赋值。
    为什么是第12字节呢,因为RTP固定头长度是12字节。RFC3550中有详细说明。当然是没有CSRC作用源列表的情况下。也没有头扩展。

    char* nalu_payload;
    nalu_payload=&sendbuf[13];
    memcpy(nalu_payload,n->buf+1,n->len-1);
    把nalu的内容拷到发送缓冲区中,位置是发送缓冲区第23个字节开始

    现在的发送缓冲区是这样的:

    RTP头12字节 | NALU头1字节 | 有效负载
    现在就可以发送了。


    对于>1400的情况


    rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0]; 
    while()
    {
        rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0]; 
        rtp_hdr->.......................

         while( ... )//前面的分包
         {
                if (第1片)
                      fu_ind =(FU_INDICATOR*)&sendbuf[12];
                      fu_hdr =(FU_HEADER*)&sendbuf[13];
                      nalu_payload=&sendbuf[14];


               if (最后一片)
                      fu_ind =(FU_INDICATOR*)&sendbuf[12];
                      fu_hdr =(FU_HEADER*)&sendbuf[13];
                      nalu_payload=&sendbuf[14];

              if (中间那些片)
                      fu_ind =(FU_INDICATOR*)&sendbuf[12];
                      fu_hdr =(FU_HEADER*)&sendbuf[13];
                      nalu_payload=&sendbuf[14];
         }

    }

也就是说,对于大于1400需要分片的,结构会与单片发送的有区别,除了RTP固定头12字节外,第13字节是FU_HEADER,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现UDP接收的RTP流中PCMU格式音频数据并播放,可以按照以下步骤进行: 1. 创建UDP Socket并指定端口,等待数据包到来。 2. 接收数据包,解析RTP头部获取音频数据的相关信息。 3. 解析RTP数据部分,获取PCMU编码的音频数据。 4. 对PCMU数据进行解码,还原成PCM数据,使用Java Sound API播放PCM数据。 下面是一个简单的Java代码示例实现: ```java import javax.sound.sampled.*; import java.net.*; public class RTPAudioPlayer { private static final int RTP_HEADER_SIZE = 12; private static final int AUDIO_PAYLOAD_TYPE = 0; private static final int PCMU_BYTES_PER_SAMPLE = 1; public static void main(String[] args) throws Exception { // 创建UDP Socket并指定端口 DatagramSocket socket = new DatagramSocket(1234); // 创建音频播放器 AudioFormat format = new AudioFormat(8000, 8, 1, true, false); SourceDataLine line = AudioSystem.getSourceDataLine(format); line.open(format); line.start(); byte[] buffer = new byte[1024]; while (true) { DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet); // 解析RTP头部获取音频数据的相关信息 int payloadType = (buffer[1] & 0x7F); int sequenceNumber = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); long timestamp = ((buffer[4] & 0xFF) << 24) | ((buffer[5] & 0xFF) << 16) | ((buffer[6] & 0xFF) << 8) | (buffer[7] & 0xFF); if (payloadType == AUDIO_PAYLOAD_TYPE) { // 解析RTP数据部分,获取PCMU编码的音频数据 int payloadOffset = RTP_HEADER_SIZE; int payloadLength = packet.getLength() - payloadOffset; byte[] payload = new byte[payloadLength]; System.arraycopy(buffer, payloadOffset, payload, 0, payloadLength); // 对PCMU数据进行解码,还原成PCM数据 byte[] pcm = new byte[payloadLength * 2]; for (int i = 0; i < payloadLength; i++) { short sample = (short) ((payload[i] & 0xFF) - 128); pcm[i * 2] = (byte) (sample >> 8); pcm[i * 2 + 1] = (byte) sample; } // 播放PCM数据 line.write(pcm, 0, pcm.length); } } } } ``` 该代码示例中,我们创建了一个DatagramSocket来接收UDP数据包,然后解析RTP头部和数据部分获取PCMU编码的音频数据,对音频数据进行解码还原成PCM数据,最后使用Java Sound API播放PCM数据。 注意:该代码示例只是一个简单的示例,实际应用中还需要处理丢包、延迟等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值