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