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 スレッド以下) に遭遇したことがわかりました。timeSetEvent の代わりに、CreateTimerQueue
/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;
これらの考えられるすべての解決策には同じ問題があります。ボイス フローが途切れることなく、特に 2 つ以上の通話がある場合、再生中にクリック音がはっきりと聞こえます。私が想像できる唯一の理由は、それtimeSetEvent
が他のものよりも正確であるということです. ここで何ができるでしょうか?