RTP打包音频g711

RFC3550 RTP:http://www.networksorcery.com/enp/rfc/rfc3550.txt

音频资源

g711a音频资源链接
测试音频: 麻雀 音频格式:PCMA 声道数:1 采样率:8000HZ 码率:64kbps

如何计算打包发送间隔、打包字节数
音频的帧率 				fps = 20
采样率        			sample_rate = 8000 HZ
码率			 		    bitrate = 64000	bps
打包发送间隔   		 	send_interval = 1 / 20 = 0.05s = 50000us
每次打包需要音频数据长度    audio_need_len = 64000 bps * 0.05s = 3200 bit = 400 bytes
udp发送数据长度			send_len =  (rtp包头12 bytes )+ audio_need_len 
C++ RTP打包代码
//rtp.h

#ifndef MMSER_RTP_H
#define MMSER_RTP_H

#include <iostream>
#include <vector>


enum eRtpPayloadType
{
    RTP_PAYLOAD_TYPE_PCMU    = 0,   // g711u
    RTP_PAYLOAD_TYPE_PCMA    = 8,   // g711a
    RTP_PAYLOAD_TYPE_JPEG    = 26,
    RTP_PAYLOAD_TYPE_H264    = 96,
    RTP_PAYLOAD_TYPE_H265    = 97,
    RTP_PAYLOAD_TYPE_OPUS    = 98,
    RTP_PAYLOAD_TYPE_AAC     = 99,
    RTP_PAYLOAD_TYPE_G726    = 100,
    RTP_PAYLOAD_TYPE_G726_16 = 101,
    RTP_PAYLOAD_TYPE_G726_24 = 102,
    RTP_PAYLOAD_TYPE_G726_32 = 103,
    RTP_PAYLOAD_TYPE_G726_40 = 104,
    RTP_PAYLOAD_TYPE_SPEEX   = 105,
};


class RtpPacket
{
public:
    //V(version):2 bits,RTP的版本,这里统一为2
    int8_t version;
    //P(padding):1 bit,如果置1,在packet的末尾被填充,填充有时是方便一些针对固定长度的算法的封装
    int8_t padding;
    //X(extension):1 bit,如果置1,在RTP Header会跟着一个header extension
    int8_t extension;
    //CC(CSRC count): 4 bits,表示头部后 特约信源 的个数
    int8_t csrc_count;
    //M(marker): 1 bit,不同的有效载荷有不同的含义,marker=1; 对于视频,标记一帧的结束;对于音频,标记会话的开始。
    int8_t marker;
    //PT(playload type): 7 bits,表示所传输的多媒体的类型,
    int8_t playload_type;
    //sequence number: 16 bits,每个RTP packet的sequence number会自动加一,以便接收端检测丢包情况
    uint16_t sequence_number;
    //timestamp: 32 bits,时间戳
    uint32_t timestamp;
    //SSRC: 32 bits,同步源的id,每两个同步源的id不能相同
    uint32_t ssrc;
    //CSRC: CC指定,每个CSRC标识符占32位,可以有0~15个
    std::vector<uint32_t> csrc;

public:
    RtpPacket(uint32_t ssrc_val);
    virtual ~RtpPacket();

    /**
     *  rtp 打包 g711a方法: | 12bytes RTP头 | G711 负载 |
     * @param[in][out]  payload  输入的负载数据,添加rtp头后返回
     * @param[in][out]  len      输入的负载数据长度,添加rtp头后返回新的长度
     */
    void packet_g711a(uint8_t* payload, int& len);

};

#endif //MMSER_RTP_H

//rtp.cpp

#include <cstring>
#include "rtp.h"

RtpPacket::RtpPacket(uint32_t ssrc_val)
{
    version = 2;
    padding = 0;
    extension = 0;
    csrc_count = 0;

    marker = 1;
    playload_type = 0;

    sequence_number = 0;
    timestamp = 0;
    ssrc = ssrc_val;
}

RtpPacket::~RtpPacket()
{
}

void RtpPacket::packet_g711a(uint8_t* payload, int& len)
{
    playload_type = eRtpPayloadType::RTP_PAYLOAD_TYPE_PCMA;
    sequence_number++;
    timestamp += len;

    uint8_t* start = payload;
    int rtp_head_len = 12;

    memmove(payload + rtp_head_len, payload, len);
    len += rtp_head_len;

    *payload++ = (version << 6) +  (padding << 5) + (extension << 4) + csrc_count;
    *payload++ = (marker << 7) + playload_type;
    *payload++ = sequence_number / (0xff + 1);
    *payload++ = sequence_number % (0xff + 1);
    *payload++ = ( timestamp / (0xffff + 1) ) / (0xff + 1);
    *payload++ = ( timestamp / (0xffff + 1) ) % (0xff + 1);
    *payload++ = ( timestamp % (0xffff + 1) ) / (0xff + 1);
    *payload++ = ( timestamp % (0xffff + 1) ) % (0xff + 1);
    *payload++ = ( ssrc / (0xffff + 1) ) / (0xff + 1);
    *payload++ = ( ssrc / (0xffff + 1) ) % (0xff + 1);
    *payload++ = ( ssrc % (0xffff + 1) ) / (0xff + 1);
    *payload   = ( ssrc % (0xffff + 1) ) % (0xff + 1);

    payload = start; 
}

JAVA RTP打包代码
#目录结构
	com/RtpPacket.java
	main.java
	mq.g711a
//RtpPacket.java

package com;

import java.util.Arrays;

class RtpPayloadType 
{
    public static int RTP_PAYLOAD_TYPE_PCMU = 0;
    public static int RTP_PAYLOAD_TYPE_PCMA = 8;
}

public class RtpPacket
{	
	
	private byte version = 2;

	private byte padding = 0;

	private byte extension = 0;

	private byte csrc_count = 0;

	private byte marker = 1;

	private byte playload_type = 0;

	private int sequence_number = 0;

    private long timestamp = 0;

    private long ssrc = 0;


	public RtpPacket(long ssrcVal) {
		ssrc = ssrcVal;
	}

	public byte[] packet_g711a(byte[] audioData, int audioLen) {

		int rtp_head_len = 12;
		byte[] payload = new byte[rtp_head_len + audioLen];
        
        playload_type = (byte)RtpPayloadType.RTP_PAYLOAD_TYPE_PCMA;
        sequence_number++;
        timestamp +=  audioLen;

		payload[0] = (byte)( (version << 6) +  (padding << 5) + (extension << 4) + csrc_count );
		payload[1] = (byte)( (marker << 7) + playload_type );
	    payload[2] = (byte)( sequence_number / (0xff + 1) );
	    payload[3] = (byte)( sequence_number % (0xff + 1) );
	    payload[4] = (byte)( ( timestamp / (0xffff + 1) ) / (0xff + 1) );
	    payload[5] = (byte)( ( timestamp / (0xffff + 1) ) % (0xff + 1) );
	    payload[6] = (byte)( ( timestamp % (0xffff + 1) ) / (0xff + 1) );
	    payload[7] = (byte)( ( timestamp % (0xffff + 1) ) % (0xff + 1) );
	    payload[8] = (byte)( ( ssrc / (0xffff + 1) ) / (0xff + 1) );
	    payload[9] = (byte)( ( ssrc / (0xffff + 1) ) % (0xff + 1) );
	    payload[10] = (byte)( ( ssrc % (0xffff + 1) ) / (0xff + 1) );
	    payload[11] = (byte)( ( ssrc % (0xffff + 1) ) % (0xff + 1) );

	    System.arraycopy(audioData, 0, payload, rtp_head_len, audioLen);
	    return payload;
	}
}
// main.java

import java.util.Arrays;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.io.IOException;
import java.io.InputStream;   
import java.io.FileInputStream;  

import com.RtpPacket;

public class main extends Thread {

	public static void main(String[] args)  throws IOException {

		long ssrc = 255;
		int send_interval_ms = 50;
		int audio_need_len = 400;
		int localPort = 56200;
		int peerPort = 15062;
		String peerIP = "192.168.1.101";
		DatagramSocket ds = null;
		InetAddress peerAddress = null;
		InputStream audio_stream = new FileInputStream("./mq.g711a");

		try {  
			ds = new DatagramSocket(localPort);
		} catch (SocketException e) {  
            e.printStackTrace();
            System.exit(1);   
        }  

        try {  
            peerAddress = InetAddress.getByName(peerIP);
        } catch (UnknownHostException e) {  
            e.printStackTrace();  
            System.exit(1);   
        }  

		RtpPacket rtp = new RtpPacket(ssrc);
		byte[] audio_data = new byte[audio_need_len]; 

		while (audio_stream.read(audio_data) == audio_need_len) {

		   	byte[] payload = rtp.packet_g711a(audio_data, audio_data.length);
		   	DatagramPacket dp = new DatagramPacket(payload, payload.length, peerAddress, peerPort);

            ds.send(dp); 
			//System.out.println(Arrays.toString(payload));
			try {
				Thread.sleep(send_interval_ms);

			} catch (InterruptedException e) {
				e.printStackTrace();
				break;
			}
		}
		ds.close();
	}	
}
# 编译
javac ./com/RtpPacket.java
javac ./main.java
# 执行
java main

测试音频使用工具 vlc 或 ffplay
在这里插入图片描述

ffplay -i rtp://192.168.1.101:15062
  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值