www.pudn.com > rtp&rtcp.rar > rtp_rcv.c, change:2002-11-01,size:10624b
/*
本程序在UDP通信程序的基础上建立,程序演示了如何
生成简单的RTP报文头标并通过UDP协议发送、接收的过程。
连接的时候需要库:ws2_32.lib
Multimedia Communications Lab
University of Science & Technology
network@ustc.edu.cn
*/
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>//存在double数据类型运算的时候,最好将此头文件加上,否则容易运算结果不正确,原因不明
//显示详细的RTP数据报信息并根据接收时刻的时间戳计算抖动参数
#define DETAIL_PRINT
//显示详细的RTP数据报信息用于研究数据报是否正常到达
#define NORMAL_PRINT
//仅仅显示数据报的静荷内容,用于观察实际接收的效果
//注意:SIMPLE_PRINT和NORMAL_PRINT不能同时定义
//#define SIMPLE_PRINT
//定义数据流的采样频率,在本例中仅仅是根据发送字符串
//的长度计算出来的每秒钟需发送的字节数
//注意:两个数据流的发送速率是不一样的,
//数据流1每20ms发送一个数据报;数据流2每10ms发送一个数据报
#define STREAM_1_SAMPLE_FREQ 500.0/1000.0
#define STREAM_2_SAMPLE_FREQ 500.0/1000.0
//定义需要的数据结构-begin
/* Type sizes */
typedef unsigned long int u_int32;
typedef unsigned short int u_int16;
typedef unsigned char u_int8;
// 32-bit sequence number
typedef u_int16 RtpSeqNumber;
// Middle 32-bit of NTP
typedef u_int32 RtpTime;
// 32-bit source number
typedef u_int32 RtpSrc;
// RTP packet header
struct RtpHeaderStruct
{
u_int32 version : 2; // protocal version
u_int32 padding : 1; // padding flag - for encryption
u_int32 extension : 1; // header extension flag
u_int32 count : 4; // csrc count
u_int32 marker : 1; // marker bit - for profile
u_int32 type : 7; // payload type
u_int32 sequence : 16; // sequence number of this packet
RtpTime timestamp; // timestamp of this packet
RtpSrc ssrc; // source of packet
RtpSrc startOfCsrc; // list of contributing sources
};
typedef struct RtpHeaderStruct RTP_HEADER;
// RTCP header
struct RtcpHeaderStruct
{
u_int32 version : 2; // protocal version
u_int32 padding : 1; // padding flag
u_int32 count : 5; // depending on packet type
u_int32 type : 8; // type of RTCP packet
u_int32 length : 16; // lenght of RTCP packet in octlets minus 1
};
typedef struct RtcpHeaderStruct RTCP_HEADER;
//定义需要的数据结构-end
//全局变量用来存放数据流中数据报到达时生成的时间戳
//根据数据流的速率定义每个数据包时间戳的增量,此处定义为数据报中字节的数目
//根据我们对数据流1和数据流2取样频率的定义可知:
//数据流1相邻两个数据报之间时间戳差值为10;
//数据流2相邻两个数据报之间时间戳差值为5
//此处定义的时间戳和发送程序中定义的时间戳位于两个不同的随机数空间
RtpTime rtp_timestamp_1 = 0 , rtp_timestamp_2 = 0;
RtpTime last_rtp_timestamp_1 = 0,last_rtp_timestamp_2=0;
//系统时钟频率
double dfFreq = 0;
//用于存放系统时间有关信息的全局变量
LARGE_INTEGER liTime;
//存放每个数据报接收到时候的系统时间
double rtp_sampletime_1 = 0.0 , rtp_sampletime_2 = 0.0;
//存放同步源标识的数组,由于示例程序中仅实现了2个数据流的传输,定义如下
RtpSrc ssrc_array[2] = {0};
//定义数据流中相邻数据报的发送时间戳和到达时间戳的差值
/*
If Si is the RTP timestamp from packet i, and Ri is the time of
arrival in RTP timestamp units for packet i, then for two packets i
and j, D may be expressed as
D(i,j)=(Rj-Ri)-(Sj-Si)=(Rj-Sj)-(Ri-Si)
*/
//定义数据流的抖动参数
unsigned long delay_packet_1 = 0,delay_packet_2 = 0;
/*
The interarrival jitter is calculated continuously as each data
packet i is received from source SSRC_n, using this difference D for
that packet and the previous packet i-1 in order of arrival (not
necessarily in sequence), according to the formula
J=J+(|D(i-1,i)|-J)/16
*/
double jitter_stream_1=0.0 , jitter_stream_2=0.0;
//根据接收到的数据解析RTP头标、显示数据静荷内容、计算抖动参数、分别处理两个不同的数据流
void DecodeRtpHeaderDetails(char *RecvBuf)
{
char *szMessage;
RTP_HEADER *rtp_header;
rtp_header = (RTP_HEADER*)RecvBuf;
szMessage = RecvBuf + sizeof(RTP_HEADER);
printf("\"%s\" received \n", szMessage);
printf("\trtp_header->version = %u\n",rtp_header->version);
printf("\trtp_header->padding = %u\n",rtp_header->padding);
printf("\trtp_header->extension = %u\n",rtp_header->extension);
printf("\trtp_header->count = %u\n",rtp_header->count);
printf("\trtp_header->marker = %u\n",rtp_header->marker);
printf("\trtp_header->type = %u\n",rtp_header->type);
printf("\trtp_header->sequence = %u\n",rtp_header->sequence);
printf("\trtp_header->timestamp = %u\n",rtp_header->timestamp);
printf("\trtp_header->ssrc = %u\n",rtp_header->ssrc);
#ifdef DETAIL_PRINT
//根据同步源标识判断数据报属于那个数据流
if(ssrc_array[0] == 0)
{
if(ssrc_array[1] == 0)//第一次进行数据接收
{
ssrc_array[0] = rtp_header->ssrc;
}
}
else
{
if(ssrc_array[1] == 0)//第一次接收ssrc_array[1]表示的数据流的数据报
{
ssrc_array[1] = rtp_header->ssrc;
}
}
if(ssrc_array[0] == rtp_header->ssrc) //ssrc_array[0]表示的数据流的数据报
{
//获取当前系统时钟频率信息
QueryPerformanceCounter(&liTime);
//计算接收时刻的时间戳
rtp_timestamp_1 = rtp_timestamp_1
+ floor( (liTime.QuadPart*1000.0 - rtp_sampletime_1) / dfFreq * 0.5);
//记录当前时刻的时钟频率信息
rtp_sampletime_1 = liTime.QuadPart*1000.0;
//计算抖动参数
if(delay_packet_1!=0)
{
jitter_stream_1 = abs(jitter_stream_1
+ abs( abs(rtp_timestamp_1 - rtp_header->timestamp) - delay_packet_1))*0.0625;
}
printf("\tReceived rtp timestamp_1:%u\n",rtp_timestamp_1);
printf("\tDelay_1:%u\n",delay_packet_1);
printf("\tjitter_1:%f\n",jitter_stream_1);
delay_packet_1 = abs(rtp_timestamp_1 - rtp_header->timestamp);
}
if(ssrc_array[1] == rtp_header->ssrc) //ssrc_array[1]表示的数据流的数据报
{
//获取当前系统时钟频率信息
QueryPerformanceCounter(&liTime);
//计算接收时刻的时间戳
rtp_timestamp_2 = rtp_timestamp_2
+ floor( (liTime.QuadPart*1000.0 - rtp_sampletime_2) / dfFreq * 0.5);
//记录当前时刻的时钟频率信息
rtp_sampletime_2 = liTime.QuadPart*1000.0;
//计算抖动参数
if(delay_packet_2!=0)
{
jitter_stream_2 = abs(jitter_stream_2
+ abs( abs(rtp_timestamp_2 - rtp_header->timestamp) - delay_packet_2))*0.0625;
}
printf("\tReceived rtp timestamp_2:%u\n",rtp_timestamp_2);
printf("\tDelay_2:%u\n",delay_packet_2);
printf("\tjitter_2:%f\n",jitter_stream_2);
delay_packet_2 = abs(rtp_timestamp_2 - rtp_header->timestamp);
}
#endif
return;
}
//根据接收到的数据并显示数据静荷内容
void DecodeRtpHeaderSimple(char *RecvBuf)
{
char *szMessage;
szMessage = RecvBuf + sizeof(RTP_HEADER);
printf(szMessage);
return;
}
int main(int argc, int **argv)
{
WSADATA wsda;
char RecvBuf[64];
int iMessageLen;
int ret;
unsigned short iPort;
SOCKET s; // Our TCP socket handle
SOCKADDR_IN addr, // The local interface address
remote_addr; // The sender's address
int iRemoteAddrLen; // Contains the length of remte_addr, passed to recvfrom
//用于控制程序运行时间的两个变量
DWORD timeout = 10000;
DWORD start = GetTickCount();
int PacketReceived = 0;
srand( (unsigned)time( NULL ) );
//产生随机的时间戳初始化数值
rtp_timestamp_1 = rand();
rtp_timestamp_2 = rand();
//获取系统时钟频率,这个时钟频率将用于计算时间戳
QueryPerformanceFrequency(&liTime);
dfFreq = (double)liTime.QuadPart;
ssrc_array[0] = 0;
ssrc_array[1] = 0;
//检查命令行参数
if(argc != 2 ||
(argc==2 && strcmp((char *) &argv[1][0], "/?")==0))
{
printf("wsudprcv port\n");
printf(" port: the port the server should listen to\n");
exit(1);
}
//读取命令行参数中的端口号信息
iPort = atoi((char *) &argv[1][0]);
if(iPort<0 || iPort>65563)
{
printf("Invalid port number! (%s)\n", argv[2]);
exit(1);
}
//初始化Winsock
WSAStartup(MAKEWORD(1,1), &wsda);
//创建一个UDP socket
printf("Creating socket...");
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
//错误处理
if(s == SOCKET_ERROR)
{
printf("Error\nCall to socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); failed with:\n%d\n", WSAGetLastError());
exit(1);
}
printf("OK\n");
// Fill in the interface information
printf("Binding socket...");
addr.sin_family = AF_INET;
addr.sin_port = htons(iPort);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR)
{
printf("Error\nCall to bind(s, (struct sockaddr *) &addr, sizeof(addr)); failed with:\n%d\n", WSAGetLastError());
exit(1);
}
printf("OK\n");
//如果需要在程序执行的过程中显示具体RTP数据报头标的信息,需要在文件的头部定义DETAIL_PRINT
#ifdef NORMAL_PRINT
//10秒钟的接收等待时间
while(1)
{
//计时,10s超时
if((GetTickCount() - start) >= timeout) break;
iMessageLen = 64;
//PacketReceived记录收到的
PacketReceived++;
// 准备接收数据数据报的个数
printf("Waiting for packets (Press Ctrl-C to exit)...\n");
iRemoteAddrLen = sizeof(remote_addr);
ret = recvfrom(s, RecvBuf, iMessageLen, 0, (struct sockaddr *) &remote_addr, &iRemoteAddrLen);
if(ret == SOCKET_ERROR)
{
printf("Error\nCall to recvfrom(s, RecvBuf, iMessageLen, 0, (struct sockaddr *) &remote_addr, &iRemoteAddrLen); failed with:\n%d\n", WSAGetLastError());
exit(1);
}
iMessageLen = ret; // Length of the data received
RecvBuf[iMessageLen] = '\0'; // Convert to cstring
printf("Packet Length = %d; Packet From : %s\n",iMessageLen,inet_ntoa(remote_addr.sin_addr));
DecodeRtpHeaderDetails(RecvBuf);
printf("Total Packet Received:%d\n",PacketReceived);
}
#endif
//如果不需要显示解析过的RTP数据报的信息,仅仅显示静荷数据,需要在文件的头部定义SIMPLE_PRINT
#ifdef SIMPLE_PRINT
printf("Waiting for packets (Press Ctrl-C to exit)...\n");
//10秒钟的接收等待时间
while(1)
{
//计时,10s超时
if((GetTickCount() - start) >= timeout) break;
iMessageLen = 64;
//PacketReceived记录收到的
PacketReceived++;
// 准备接收数据数据报的个数
iRemoteAddrLen = sizeof(remote_addr);
ret = recvfrom(s, RecvBuf, iMessageLen, 0, (struct sockaddr *) &remote_addr, &iRemoteAddrLen);
if(ret == SOCKET_ERROR)
{
printf("Error\nCall to recvfrom(s, RecvBuf, iMessageLen, 0, (struct sockaddr *) &remote_addr, &iRemoteAddrLen); failed with:\n%d\n", WSAGetLastError());
exit(1);
}
iMessageLen = ret; // Length of the data received
RecvBuf[iMessageLen] = '\0'; // Convert to cstring
DecodeRtpHeaderSimple(RecvBuf);
}
printf("Total Packet Received:%d\n",PacketReceived);
#endif
closesocket(s);
WSACleanup();
return 0;
}