RTP sender发送示例

UDP socket封装成简单的RTP包并发送的示例

首先使用FFmpeg将一段音频转换为pcm格式的文件

然后不断的读取文件并拼装RTP头发送至接收端比如android的audio track播放;

注意,由于发送的是pcm原始数据,发送和接收播放双方要约定好pcm格式参数,比如16bit编码,小端格式,8000采样率,单通道;接收方也用同样的格式参数播放;

/*
 * rtp sender
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LOCALIP "192.168.43.232"
#define LOCALPORT 50013

#define PEER_IP "192.168.43.99"
#define PEER_PORT 50014

#define RTP_PCM_FILE_NAME "music-pcm16bit-le-1ac-8khz.pcm"
/*
 * ffmpeg -i music.wav -ar 8000 -ac 1 -acodec pcm_s16le -f s16le  music-pcm16bit-le-1ac-8khz.pcm
 */
#define RTP_SAMPLE_RATE 8000
#define RTP_AUDIO_CHANNEL_CNT 1
#define RTP_PAYLOAD_LENGTH 400

#define ASSERT_CONCAT_(a, b) a##b
#define STATIC_ASSERT(e, msg) ;enum { ASSERT_CONCAT_(assert_line_, __LINE__) = 1/(!!(e)) }
#pragma pack(push,1)

#define LOCAL_LITTLE_ENDIAN 1
typedef struct _RTPHeader_T{
    /* rfc3550 rtp header should encode to network sequence */
    /* 
     * The struct must be 12 bytes long, so force the compiler to pack it
     * into a 12bytes long struct and not padding it.
     */
    # if LOCAL_LITTLE_ENDIAN
    uint32_t CsrcCnt:4;   /* 4bit */
    uint32_t Extension:1; /* 1bit */
    uint32_t Padding:1;   /* 1bit */
    uint32_t Version:2;   /* 2bit */

    uint32_t PayloadType:7; /* 7bit */
    uint32_t Marker:1;    /* 1bit */
    # else
    uint32_t Version:2;   /* 2bit */
    uint32_t Padding:1;   /* 1bit */
    uint32_t Extension:1; /* 1bit */
    uint32_t CsrcCnt:4;   /* 4bit */

    uint32_t Marker:1;    /* 1bit */
    uint32_t PayloadType:7; /* 7bit */
    # endif

    uint32_t SeqNo:16;       /* 16bit */
    uint32_t TimeStamp;   /* 32bit */
    uint32_t Ssrc;        /* 32bit */
    //uint32_t Csrc[1];     /* 0-15 */
} RTPHeader_T;

STATIC_ASSERT(sizeof(RTPHeader_T)==12, "RTPHeader_T size doesn't seem to be cool.");
#pragma pack(pop)


void encodeHeader(char *header, const int headerSize, const RTPHeader_T &rtpheader)
{
    if (header == NULL) {
        return;
    }
    if (headerSize < 12) {
        return;
    }
    //printf("%s sizeof=%lu\n", __func__, sizeof(rtpheader));
    memcpy(header, &rtpheader, 12);
    return;
    #if 0
    /* encode to network sequence */
    header[0] = ((rtpheader.Version & 0x3) << 6)
              + ((rtpheader.Padding & 0x1) << 5)
              + ((rtpheader.Extension & 0x1) << 4)
              + (rtpheader.CsrcCnt & 0xF);
    header[1] = ((rtpheader.Marker & 0x1) << 7)
              + ((rtpheader.PayloadType & 0x7F));
    header[2] = (rtpheader.SeqNo & 0xFF00) >> 8;
    header[3] = (rtpheader.SeqNo & 0x00FF);
    header[4] = (rtpheader.TimeStamp & 0xFF000000) >> 24;
    header[5] = (rtpheader.TimeStamp & 0x00FF0000) >> 16;
    header[6] = (rtpheader.TimeStamp & 0x0000FF00) >> 8;
    header[7] = (rtpheader.TimeStamp & 0x000000FF);
    header[8] = (rtpheader.Ssrc & 0xFF000000) >> 24;
    header[9] = (rtpheader.Ssrc & 0x00FF0000) >> 16;
    header[10] = (rtpheader.Ssrc & 0x0000FF00) >> 8;
    header[11] = (rtpheader.Ssrc & 0x000000FF);
    #endif
}

int rtpSender() {
    /* 1 create socket */
    int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket"), exit(-1);
    }

    /* 2 prepare server addr */
    struct sockaddr_in servaddr;
    servaddr.sin_family = PF_INET;
    servaddr.sin_port = htons(LOCALPORT);	//host to network hostshort
    servaddr.sin_addr.s_addr = inet_addr(LOCALIP);

    /* 3 bingd */
    int res = 0;
    res = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (res == -1) { perror("bind"), exit(-1);}
    printf("bind ok\n");

    char buf[1400] = { };
    char arr[1400] = { };
    int len = 0;
    int iloop = 0;

    /* 4 read/write(client connected) recvfrom/sendto */
    struct sockaddr cliaddr;
    socklen_t clilen = sizeof(cliaddr); /* cannot set to 0 */
    memset(&cliaddr, 0, sizeof(cliaddr)); /* should memset */
    (void)clilen;
    //len = recvfrom(sockfd, buf, sizeof(buf), 0, &cliaddr, &clilen);
    char *from = inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr);
    int fromport = ntohs(((struct sockaddr_in *)&cliaddr)->sin_port);
    printf("%d recvfrom %s:%d %s\n", len, from, fromport, buf);

    char filebuff[1400];
    /*
    RTP Header
     0               1               2               3
     0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |V=2|P|X|   CC  |M|     PT      |       sequence number         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                           timestamp                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |            synchronization source (SSRC) identifier           |
    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    |              contributing source (CSRC) identifier            |
    |                         ...defined by CC                      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    +---------------------------------------------------------------+
    |       payload                                                 |
    |         ...                                                   |
    +---------------------------------------------------------------+
    */

    RTPHeader_T rtphead {};
    rtphead.Version = 2; /* 2bit */
    rtphead.Padding = 0; /* 1bit */
    rtphead.Extension = 0; /* 1bit */
    rtphead.CsrcCnt = 0; /* 4bit */
    rtphead.Marker = 0; /* 1bit */
    rtphead.PayloadType = 8; /* 7bit */
    rtphead.SeqNo = 0; /* 16bit */
    rtphead.TimeStamp = htonl(3); /* 32bit */
    rtphead.Ssrc = htonl(0x12345678); /* 32bit */

#if 1
    int pcmfd = open(RTP_PCM_FILE_NAME, O_RDONLY);
    if (pcmfd == -1) {
        printf("open failed %d:%s\n", errno, strerror(errno));
        exit(-1);
    }
    int readlen = 0;
    int rtpPacketlen = 0;
    useconds_t usleeptime = 0;
#endif

    for (iloop=0; ; iloop++) {
		readlen = read(pcmfd, filebuff, RTP_PAYLOAD_LENGTH);
		if (readlen == 0) break;
		rtphead.SeqNo = htons(ntohs(rtphead.SeqNo)+1);
		encodeHeader(arr, 12, rtphead);
		//snprintf(arr, sizeof(arr), "fromserver#%d", iloop);
		memcpy(arr+12, filebuff, readlen);
		rtpPacketlen = readlen + 12;

        printf("%s hex:%x %x %x %x, %x %x %x %x, %x %x %x %x\n", __func__,
                arr[0], arr[1], arr[2], arr[3],
                arr[4], arr[5], arr[6], arr[7],
                arr[8], arr[9], arr[10], arr[11]);

        struct sockaddr_in repaddr;
        socklen_t replen = sizeof(struct sockaddr); /* cannot set to 0 */
        memset(&repaddr, 0, sizeof(struct sockaddr)); /* should memset */

        repaddr.sin_family = PF_INET;
        repaddr.sin_port = htons(PEER_PORT);
        repaddr.sin_addr.s_addr = inet_addr(PEER_IP);
        len = sendto(sockfd, arr, rtpPacketlen, 0, (const struct sockaddr *)&repaddr, replen);

        if (len == -1) {
            printf("errno: %d\n", errno);
            perror("sendto");
        }
        printf("%d sendto: %s:%d\n", len, PEER_IP, PEER_PORT);

        //sleep(1);
        usleeptime = (RTP_PAYLOAD_LENGTH*1000 /*ms*/ *1000/*us*/)/(RTP_SAMPLE_RATE * 2/*bytes*/ * RTP_AUDIO_CHANNEL_CNT);
        usleep(usleeptime*9/10);
    }

    /* 5 close */
    close(sockfd);
    return 0;
}

int main()
{
    int ret = 0;
    for (;;) {
        sleep(1);
        ret = rtpSender();
        if (ret != 0) {
            break;
        }
    }
    return ret;
}

猜你喜欢

转载自blog.csdn.net/halazi100/article/details/104551581
RTP