#include <ortp/ortp.h>
#include <string.h>
#define STREAMS_COUNT 1000
enum
{
EVENT_STOP,
EVENT_RTP,
EVENT_COUNT // Always last
};
RtpSession * m_Session[STREAMS_COUNT];
int m_nPacket_Size = 160;
int m_nTimestamp_Inc = 160;
char * m_pBuffer = NULL;
char * m_SSRC = NULL;
int m_nChannels = 0;
int m_nPort = 0;
HANDLE m_hEvents[EVENT_COUNT];
BOOL m_bExit = FALSE;
static char *help="usage: mrtpsend filename ip port nstreams [--packet-size size] [--ts-inc value]n";
{
switch (fdwCtrlType)
{
// Handle the CTRL+C signal.
// CTRL+CLOSE: confirm that the user wants to exit.
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
m_bExit = TRUE;
SetEvent(m_hEvents[EVENT_STOP]);
return TRUE;
default:
return FALSE;
}
}
{
int nCounter;
// Check the number of arguments
if (argc<5)
{
printf(help);
return -1;
}
m_nChannels = atoi(argv[4]); // 流数目
// Get the number of channels
if (m_nChannels == 0)
{
printf(help);
return -1;
}
for (nCounter=5; nCounter<argc; nCounter++)
{
if (strcmp(argv[nCounter],"--packet-size")==0)
{
if ( nCounter+1 < argc ){
m_nPacket_Size=atoi(argv[nCounter+1]);
}
else {
printf(help);
return -1;
}
if (m_nPacket_Size==0)
{
printf("Packet size can't be %s.n",argv[nCounter+1]);
return -1;
}
nCounter+=1;
}
else if (strcmp(argv[nCounter],"--ts-inc")==0)
{
if ( nCounter+1 < argc )
{
m_nTimestamp_Inc=atoi(argv[nCounter+1]);
}
else {
printf(help);
return -1;
}
if (m_nTimestamp_Inc==0)
{
printf("Timestanp increment can't be %s.n",argv[nCounter+1]);
return -1;
}
nCounter+=1;
}
}
return 0;
}
{
char strBuffer[255];
printf("====================================n");
printf("Author : Simon Morlat =n");
printf("Porting : Yann STEPHAN =n");
printf("====================================n");
memset(&strBuffer, 0x0, sizeof(strBuffer));
sprintf((char *) &strBuffer, "= RTPSender V1.0 - Date : %s - %sn", __DATE__, __TIME__);
printf(strBuffer);
printf("====================================n");
}
{
FILE * infile = NULL;
SessionSet * pSessionSet = NULL;
int nCounter = 0;
UINT32 m_nUser_Timestamp = 0;
ProductVersion();
if (GetCommandArguments(argc, argv) != 0)
{
printf("==> Sorry dude...n");
Sleep(1000);
return -1;
}
printf("==> Starting the RTP Sender testn");
// =============== INSTALL THE CONTROL HANDLER ===============
if (SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrlHandlerFunction, TRUE) == 0)
{
printf("==> Cannot handle the CTRL-C...n");
}
printf("==> Timestamp increment will be %in" , m_nTimestamp_Inc);
printf("==> Packet size will be %in" , m_nPacket_Size);
m_pBuffer = (char *) ortp_malloc(m_nPacket_Size); // 申请内存。与malloc同等
ortp_init(); // 初始化oRTP库,在使用oRTP API前使用
ortp_scheduler_init(); // 初始化oRTP调度器. 如果在应用程序中使用RtpSession模式,则必须调用。调度器可以管理多个session。
printf("==> Scheduler initializedn");
m_SSRC = getenv("SSRC"); // 获取同步源标识,即SSRC环境变量值
m_nPort = atoi(argv[3]); // 端口地址
for (nCounter=0; nCounter < m_nChannels; nCounter++)
{
//printf("==> Channel [#%d]n", nCounter);
m_Session[nCounter] = rtp_session_new(RTP_SESSION_SENDONLY);
rtp_session_set_scheduling_mode(m_Session[nCounter],1); // RtpScheduler管理多个session的调度和收发的控制,本函数设置是否使用该session调度管理功能。
rtp_session_set_blocking_mode(m_Session[nCounter],0);
rtp_session_set_remote_addr(m_Session[nCounter],argv[2], m_nPort);
rtp_session_set_send_payload_type(m_Session[nCounter],0);
if (m_SSRC != NULL)
{ // Sets the SSRC for the outgoing stream.
rtp_session_set_ssrc(m_Session[nCounter],atoi(m_SSRC));
}
m_nPort+=2;
}
infile=fopen(argv[1],"rb"); // 打开传输的文件
if (infile==NULL)
{
printf("==> Cannot open file !!!!");
Sleep(1000);
return -1;
}
// printf("==> Open filen");
pSessionSet = session_set_new(); // Allocates and initialize a new empty session set.
// printf("==> Session setn");
while( ((nCounter= (int) fread(m_pBuffer,1,m_nPacket_Size,infile))>0) && (m_bExit == FALSE) )
{
int k;
//g_message("Sending packet.");
for (k=0;k<m_nChannels;k++){
session_set_set(pSessionSet,m_Session[k]); // This macro adds the rtp session to the set.
//printf("==> Session set set %dn", k);
}
session_set_select(NULL,pSessionSet,NULL); // 选择发送会话集。
//printf("==> Session set selectn");
for (k=0;k<m_nChannels;k++)
{
//printf("---n");
if (session_set_is_set(pSessionSet,m_Session[k]))
{
//printf("==> Session set is set %dn", k);
rtp_session_send_with_ts(m_Session[k],m_pBuffer,nCounter,m_nUser_Timestamp);
//g_message("packet sended !");
}
}
m_nUser_Timestamp+=m_nTimestamp_Inc;
}
fclose(infile);
printf("==> Close filen");
for(nCounter=0;nCounter<m_nChannels;nCounter++)
{
rtp_session_destroy(m_Session[nCounter]);
}
session_set_destroy(pSessionSet);
// Give us some time
Sleep(250);
ortp_exit();
ortp_global_stats_display();
ortp_free(m_pBuffer);
printf("==> Remove the CTRL-C handler...n");
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrlHandlerFunction, FALSE);
// Wait for an input key
printf("Waiting for exit : ");
for (nCounter = 0; nCounter < 4*5; nCounter++)
{
printf(".");
Sleep(250);
}
return 0;
}
__cdecl 是C DECLaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
PASCAL 是Pascal语言的函数调用方式,也可以在C/C++中使用,参数压栈顺序与前两者相反。返回时的清栈方式忘记了……
_fastcall是编译器指定的快速调用方式。由于大多数的函数参数个数很少,使用堆栈传递比较费时。因此_fastcall通常规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递。不同编译器编译的程序规定的寄存器不同。返回方式和_stdcall相当。
_thiscall 是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax。返回方式和_stdcall相当。
_fastcall 和 _thiscall涉及的寄存器由编译器决定,因此不能用作跨编译器的接口。所以Windows上的COM对象接口都定义为_stdcall调用方式。
C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。
带有可变参数的函数必须且只能使用_cdecl方式,例如下面的函数:
int printf(char * fmtStr, ...);
int scanf(char * fmtStr, ...);
来源:http://andylin02.javaeye.com/blog/661431
SetConsoleCtrlHandler 处理控制台消息
一、如何处理所有的控制台消息。
第一步,首先要安装一个事件钩子,也就是说要建立一个回调函数。调用Win32 API,原型如下:
BOOL SetConsoleCtrlHandler(
PHANDLER_ROUTINE HandlerRoutine, // 回调函数
BOOL Add // 表示添加还是删除
);
参数HandlerRoutine是一个指向函数的指针,原型如下:
BOOL WINAPI HandlerRoutine(
DWORD dwCtrlType // 控制事件类型
);
所有的HandlerRoutine函数只有一个参数dwCtrlType,他表示控制台发出了什么消息。参数有下列值:
CTRL_C_EVENT - 当用户按下了CTRL+C,或者由GenerateConsoleCtrlEvent API发出.
CTRL_BREAK_EVENT - 用户按下CTRL+BREAK, 或者由GenerateConsoleCtrlEvent API发出.
CTRL_CLOSE_EVENT - 当试图关闭控制台程序,系统发送关闭消息。
CTRL_LOGOFF_EVENT - 用户退出时,但是不能决定是哪个用户.
CTRL_SHUTDOWN_EVENT - 当系统被关闭时.
当收到事件的时候,HandlerRoutine可以选择处理,或者简单的忽略。如果回调函数选择忽略,函数返回FALSE,系统将处理下一个钩子程序。如果处理消息,程序在处理完消息后应该返回TRUE。
CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT和CTRL_SHUTDOWN_EVENT通常被用来处理一些程序的清理工作,然后调用ExitProcess API。另外,这三个事件有超时机制,CTRL_CLOSE_EVENT是5秒,另外两个是20秒。如果程序超时候,系统将会弹出结束进程的对话框。如果用户选择了结束进程,任何清理工作都不会做,所以应该在超时时间内完成工作。下面是一个回调函数的例子:
BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
char mesg[128];
switch(CEvent)
{
case CTRL_C_EVENT:
MessageBox(NULL, "CTRL+C received!","CEvent",MB_OK);
break;
case CTRL_BREAK_EVENT:
MessageBox(NULL, "CTRL+BREAK received!","CEvent",MB_OK);
break;
case CTRL_CLOSE_EVENT:
MessageBox(NULL, "Program being closed!","CEvent",MB_OK);
break;
case CTRL_LOGOFF_EVENT:
MessageBox(NULL, "User is logging off!","CEvent",MB_OK);
break;
case CTRL_SHUTDOWN_EVENT:
MessageBox(NULL, "User is logging off!","CEvent",MB_OK);
break;
}
return TRUE;
}
好,现在已经有了回调函数,再来看看怎么安装钩子:
if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler,TRUE)==FALSE)
{
// unable to install handler...
// display message to the user
printf("Unable to install handler!n");
return -1;
}
第一个参数是函数指针,就是上面的那个函数。第二个参数是标志,如果为TRUE那么就安装钩子,如果为FALSE那么删除钩子。
好了,在安装了钩子后,我们就能收到控制台消息了,在程序退出前,要删除钩子。很简单吧。
原形:char *getenv(const char *name)
用法:s=getenv("环境变量名");
需先定义char *s;
功能:返回一给定的环境变量值,环境变量名可大写或小写。如果指定的变量在环境中未定义,则返回一空串。
头文件:stdlib.h
eg:
String strEnv ="";
strEnv = getenv( "TMP_DIR");
if( ( NULL == strEnv ) || ( 0 == strlen(strEnv ) ) )
{
//error
}
RtpSession * rtp_session_new (int mode)
Creates a new rtp session.
If the session is able to send data (RTP_SESSION_SENDONLY or RTP_SESSION_SENDRECV), then a random SSRC number is choosed for the outgoing stream.
@param mode One of the RtpSessionMode flags.
@return the newly created rtp session.
函数原型:void rtp_session_set_blocking_mode (RtpSession * session, int yesno)
函数功能:设置是否使用阻塞模式,
参数含义:
session: rtp会话结构体
yesno: 是否使用阻塞模式
说明:
阻塞模式只有在scheduling mode被开启的情况下才能使用,本函数决定了rtp_session_recv_with_ts() 和 rtp_session_send_with_ts()两个函数的行为,如果启用了阻塞模式,则rtp_session_recv_with_ts()会一直阻塞直到接收RTP包的时间点到达(这个时间点由该函数参数中所定义的时间戳来决定),当接收完RTP数据包后,该函数才会返回。同样,rtp_session_send_with_ts()也会一直阻塞直到需要被发送的RTP包的时间点到达,发送结束后,函数才返回。
This function implicitely enables the scheduling mode if yesno is TRUE.
rtp_session_set_blocking_mode() defines the behaviour of the rtp_session_recv_with_ts() and rtp_session_send_with_ts() functions. If @yesno is TRUE, rtp_session_recv_with_ts() will block until it is time for the packet to be received, according to the timestamp passed to the function. After this time, the function returns.
For rtp_session_send_with_ts(), it will block until it is time for the packet to be sent.
If @yesno is FALSE, then the two functions will return immediately.
@param session a rtp session
@param yesno a boolean
函数原型:int rtp_session_set_remote_addr (RtpSession * session, const char * addr, int port)
函数功能:设置RTP发送的目的地址
参数含义:
session: rtp会话结构体
addr: 目的IP地址
port: 目的地址的监听端口号
返回值: 0表示成功
函数原型:void rtp_session_set_scheduling_mode (RtpSession * session, int yesno)
函数功能: RtpScheduler管理多个session的调度和收发的控制,本函数设置是否使用该session调度管理功能。
参数含义:
session:rtp会话结构体
yesno: 是否使用rtp session的系统调度功能
说明:
如果yesno为1,则表明使用系统的session调度管理功能,意味着可以使用以下功能:
1. 可以使用session_set_select在多个rtp会话之间进行选择,根据时间戳判定某个会话是否到达了收发的时间。
2. 可以使用rtp_session_set_blocking_mode()设置是否使用阻塞模式来进行rtp包的发送和接收。
如果yesno为0,则表明该会话不受系统管理和调度。
关于rtp session的管理和调度,由全局的变量RtpScheduler *__ortp_scheduler来负责,该变量必须通过ortp_scheduler_init() 来进行初始化操作。
Sets the scheduling mode of the rtp session. If @yesno is TRUE, the rtp session is in the scheduled mode, that means that you can use session_set_select() to block until it's time to receive or send on this session according to the timestamp passed to the respective functions.
You can also use blocking mode (see rtp_session_set_blocking_mode() ), to simply block within the receive and send functions.
If @yesno is FALSE, the ortp scheduler will not manage those sessions, meaning that blocking mode and the use of session_set_select() for this session are disabled.
@param session a rtp session.
@param yesno a boolean to indicate the scheduling mode.
rtp_session_set_send_payload_type
函数原型:int rtp_session_set_send_payload_type (RtpSession * session, int paytype)
函数功能:设置RTP发送数据的负载类型
参数含义:
session: rtp会话结构体
paytype:负载类型
返回值: 0表示成功,-1表示负载未定义
说明:
负载类型在payloadtype.h文件中有详细的定义,RTP接收端有着类似的负载类型设置函数,int rtp_session_set_recv_payload_type ( RtpSession * session, int paytype ) ,注意,发送的负载类型必须与接收的负载类型一致才能正常完成收发。
void rtp_session_set_ssrc (RtpSession * session, uint32_t ssrc)
Sets the SSRC for the outgoing stream.
If not done, a random ssrc is used.
@param session a rtp session.
@param ssrc an unsigned 32bit integer representing the synchronisation source identifier (SSRC).
SessionSet* session_set_new ( void )
Allocates and initialize a new empty session set.
Returns: the empty session set.
int session_set_select ( SessionSet * recvs,
SessionSet * sends,
SessionSet * errors
)
This function performs similarly as libc select() function, but performs on RtpSession instead of file descriptors. session_set_select() suspends the calling process until some events arrive on one of the three sets passed in argument. Two of the sets can be NULL. The first set is interpreted as a set of RtpSession waiting for receive events: a new buffer (perhaps empty) is availlable on one or more sessions of the set, or the last receive operation with rtp_session_recv_with_ts() would have finished if it were in blocking mode. The second set is interpreted as a set of RtpSession waiting for send events, i.e. the last rtp_session_send_with_ts() call on a session would have finished if it were in blocking mode.
When some events arrived on some of sets, then the function returns and sets are changed to indicate the sessions where events happened. Sessions can be added to sets using session_set_set(), a session has to be tested to be part of a set using session_set_is_set().
Parameters:
recvs a set of rtp sessions to be watched for read events
sends a set of rtp sessions to be watched for write events
errors a set of rtp sessions to be watched for errors
Returns:
: the number of sessions on which the selected events happened.
Definition at line 98 of file sessionset.c.
References session_set_select().
Referenced by session_set_select().
#define session_set_set ( ss, rtpsession) ORTP_FD_SET((rtpsession)->mask_pos,&(ss)->rtpset)
This macro adds the rtp session to the set.
Parameters:
ss a set (SessionSet object)
rtpsession a RtpSession
Definition at line 104 of file sessionset.h.
Referenced by rtp_session_recvm_with_ts().
#define session_set_is_set (ss, rtpsession) ORTP_FD_ISSET((rtpsession)->mask_pos,&(ss)->rtpset)
This macro tests if the session is part of the set. 1 is returned if true, 0 else.
Parameters:
ss a set (SessionSet object)
rtpsession a rtp session
Definition at line 112 of file sessionset.h.
函数原型:int rtp_session_send_with_ts (RtpSession * session, const char * buffer, int len,uint32_t userts)
函数功能:发送RTP数据包
参数含义:
session: rtp会话结构体
buffer: 需要发送的RTP数据的缓冲区
len: 需要发送的RTP数据的长度
userts: 本RTP数据包的时间戳
返回值: 成功发送到网络中的字节数
说明:
发送RTP数据需要自己管理时间戳的递增,每调用一次本函数,请根据实际情况对userts进行递增,具体递增的规则见RTP协议中的说明。
例如:如果发送的是采样率为90000Hz的视频数据包,每秒25帧,则时间戳的增量为:90000/25 = 3600
时间戳的起始值为随机值,建议设置为0 。
int rtp_session_send_with_ts ( RtpSession * session,
const uint8_t * buffer,
int len,
uint32_t userts
)
Send a rtp datagram to the destination set by rtp_session_set_remote_addr() containing the data from with timestamp . This is a high level function that uses rtp_session_create_packet() and rtp_session_sendm_with_ts() to send the data.
Parameters:
session a rtp session.
buffer a buffer containing the data to be sent in a rtp packet.
len the length of the data buffer, in bytes.
userts the timestamp of the data to be sent. Refer to the rfc to know what it is.
return the number of bytes sent over the network.
Definition at line 892 of file rtpsession.c.
References rtp_session_create_packet(), rtp_session_create_packet_with_data(), rtp_session_send_with_ts(), and rtp_session_sendm_with_ts().
Referenced by rtp_session_send_with_ts().
rtp_session_destroy
【原型】: void rtp_session_destroy(RtpSession *session)
【功能】:摧毁rtp会话对象,释放资源
【参数】:session已经创建的RTP会话对象
Destroys a rtp session. All memory allocated for the RtpSession is freed.
Parameters:
session a rtp session.
Definition at line 1481 of file rtpsession.c.
References rtp_session_destroy().
函数原型:int rtp_session_recv_with_ts (RtpSession * session, char * buffer,int len, uint32_t time, int * have_more)
函数功能:接收RTP数据包
参数含义:
session: rtp会话结构体
buffer: 存放接收的RTP数据的缓冲区
len: 期望接收的RTP数据的长度
time: 期望接收的RTP数据的时间戳
have_more:标识接收缓冲区是否还有数据没有传递完。当用户给出的缓冲区不够大时,为了标识缓冲区数据未取完,则have_more指向的数据为1,期望用户以同样的时间戳再次调用本函数;否则为0,标识取完。
rtp_session_destroy
【原型】: void rtp_session_destroy(RtpSession *session)
【功能】:摧毁rtp会话对象,释放资源
【参数】:session已经创建的RTP会话对象
void session_set_destroy ( SessionSet * set )
Frees a SessionSet.
Destroys a session set.
Definition at line 43 of file sessionset.c.
References session_set_destroy().
Referenced by session_set_destroy().
void ortp_exit ( void )
Gracefully uninitialize the library, including shutdowning the scheduler if it was started.
Definition at line 116 of file ortp.c.
References ortp_exit().
Referenced by ortp_exit().
void ortp_global_stats_display ( void)
Display global statistics (cumulative for all RtpSession)
Definition at line 273 of file ortp.c.
References ortp_global_stats_display(), and rtp_stats_display().
Referenced by ortp_global_stats_display().
mrtpsend filename ip port nstreams [--packet-size size] [--ts-inc value]
mrtpsend ------- 命令
filename ------- 发送的文件名
ip ------- ip 地址,发送目的地地址
port ------- 端口地址,发送目的地端口地址
nstreams ------- 流数
--packet-size size ------ 数据报大小,默认值为160字节
--ts-inc value ------ 邮戳增量,默认值为160
参考:
http://ticktick.blog.51cto.com/823160/345642
http://blog.csdn.net/ren911/archive/2010/07/09/5724509.aspx
http://linphone.sourcearchive.com/lines/3.2.1-1ubuntu4/mrtpsend_8c-source.html