有人试过SIP Delphi 组件吗?我前段时间以合理的价格购买了它,以替换为 Dialogic HMP 编写的旧代码。似乎没有暗示邮件支持,文档和帮助也不存在,尽管使用可用的代码我不会遇到麻烦。直到现在,当我陷入无法找到解决方案的问题时,它们才出现。在调用过程中,库每 20 毫秒通过 UDP 发送一次小型 RTP 数据包,为了保持这些间隔相等,它使用了 winsdk 函数timeSetEvent
。这是代码的摘录(我对其进行了简化以使事情更清楚):
Interface
type
// RTP packet header
TRTPHeader = packed record
Byte80: Byte;
PayloadType: Byte;
SeqNo: WORD;
TimeStamp: DWORD;
SSRC: DWORD;
end;
//RTP packet structure
TRTP = packed record
H: TRTPHeader;
Payload: packed array [0 .. 1023] of Byte;
end;
//class realisation of ISipCall interface
TCall = class(TInterfacedObject, ISipCall)
FRtpPacketToSend:TRTP;//RTP packet
//callback function, it is invoked by TMicrophoneThread regularly
procedure OnMicrophone(const Buffer: Pointer);
end;
//Thread class for timing purposes
TMicrophoneThread = class(TThread)
public
FCall: TCall;//call associated with this thread
FEvent: THandle;// Event handle
FTimerHandle: THandle;// Timer handle
procedure Execute; override;
constructor Create(const ACall: TCall);
destructor Destroy; override;
end;
implementation
procedure TCall.OnMicrophone(const Buffer: Pointer); //callback function, it is invoked by TMicrophoneThread regularly
var socket: TSocket;
begin
//preparing FRtpPacketToSend data, initializing socket, Remote server address
//win32 function, sends data to the “Remote” server
sendto(socket, FRtpPacketToSend, sizeof(FRtpPacketToSend), 0, @Remote, SizeOf(Remote));
end;
//callback function invoked by windows timer every 20 ms
procedure Timer20ms(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD_PTR); stdcall;
begin
SetEvent(TMicrophoneThread(dwUser).FEvent);//Sets the TMicrophoneThread event
end;
constructor TMicrophoneThread.Create(ACall: TCall);
begin
inherited;
FCall:=ACall;
FEvent := CreateEvent(nil, False, False, nil);
//Setting timer
FTimerHandle := timeSetEvent(20, 0, @Timer20ms, Cardinal(Self), TIME_CALLBACK_FUNCTION + TIME_PERIODIC);
end;
destructor TMicrophoneThread.Destroy;
begin
timeKillEvent(FTimerHandle);//removing timer
CloseHandle(FEvent);
inherited;
end;
procedure TMicrophoneThread.Execute;
var
buf: array [0 .. 159] of SmallInt;//buffer data, looks like for storing data between function calls
begin
FillChar(buf, SizeOf(buf), 0);
Repeat
//waiting for the timer to set FEvent from Timer20ms function
if (WaitForSingleObject(FEvent, INFINITE) <> WAIT_TIMEOUT) and not Terminated then
begin
if not Terminated then
try
FCall.OnMicrophone(@buf);
except
end;
end;
until Terminated;
end;
//Using these classes:
// Sip call object
Call:=TCall.Create;
// TMicrophoneThread object creates timer and every 20 ms invokes OnMicrophone function to send UDP data in realtime
Mth= TMicrophoneThread.Create(Call);
此代码工作正常,语音数据流畅。但令我惊讶的是,它可以完美运行,直到同时呼叫的数量超过 16 个,第 17 个和其他呼叫都没有收到计时器信号。我发现这个函数已经被标记为过时了,有些人遇到了这个函数的相同未记录的限制——不超过 16 个线程。我尝试使用带有不同参数的CreateTimerQueue
/而不是 timeSetEvent :CreateTimerQueueTimer
implementation
var
TimerQueue: THandle;
....
procedure WaitOrTimerCallback(lpParameter: Pointer; TimerOrWaitFired: BOOL); stdcall;
begin
SetEvent(TMicrophoneThread(lpParameter).FEvent);
end;
constructor TMicrophoneThread.Create(ACall: TCall);
begin
inherited;
FCall:=ACall;
FEvent := CreateEvent(nil, False, False, nil);
//Setting timer
CreateTimerQueueTimer(FTimerHandle, TimerQueue, @WaitOrTimerCallback, Self, 0, 20, 0);
end;
...
initialization
TimerQueue := CreateTimerQueue;
我也尝试Sleep
了基于QueryPerformanceFrequency
/的更高级实现QueryPerformanceCounter
:
procedure TMicrophoneThread.Execute;
var
buf: array [0 .. 159] of SmallInt;
waittime: integer;
begin
FillChar(buf, SizeOf(buf), 0);
repeat
if not Terminated then
try
FCall.OnMicrophone(@buf);
waittime:=round((Now - FCall.GetStartTime)*MSecsPerDay)
if waittime<20 then
Sleep(20-waittime)
except
end;
until Terminated;
end;
所有这些可能的解决方案都有相同的问题 - 语音流停止连续,并且在播放过程中您会明显听到咔嗒声,尤其是在您有两个或多个通话时。我能想象的唯一原因是这timeSetEvent
比其他人更准确。在这里可以做什么?