Код, который у вас есть в IdTCPServer1Execute()
, действительно принадлежит вместо LiveAudioRecorderData()
, где данные.
Хотя я настоятельно предлагаю НИКОГДА вещаниеДанные для нескольких клиентов TCP, используя цикл IOHandler.Write()
, как вы делаете.Это замедляет использование полосы пропускания до 1 пакета за раз, параллельная обработка вообще отсутствует.Пока вы отправляете пакет одному клиенту, все другие клиенты блокируются в ожидании отправки своих собственных пакетов.
Лучшее решение - предоставить каждому клиенту свою собственную потокобезопасную очередь для исходящих данных, затем LiveAudioRecorderData()
может отправить копию данных в очередь каждого клиента по мере необходимости, а IdTCPServer1Execute()
может отправить очередь вызывающего клиента независимо от того, что делают другие клиенты.
Например:
type
TWaveFormatInfo = packed record
WaveFormatSize: Integer;
WaveFormat: TWaveFormatEx;
end;
TMyContext = class(TIdServerContext)
private
// on modern Delphi versions, consider using
// TThreadList<TIdBytes> instead...
Queue: TThreadList;
QueueHasData: Boolean;
public
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdThreadList = nil); override;
destructor Destroy; override;
procedure AddToQueue(const WFI: TWaveFormatInfo; const Buffer: Pointer; BufferSize: Cardinal);
procedure CheckQueue;
end;
...
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdThreadList = nil);
begin
inherited;
Queue := TThreadList.Create;
end;
destructor TMyContext.Destroy;
var
List: TList;
i: Integer;
begin
// if not using TThreadList<TIdBytes>...
if QueueHasData then
begin
List := Queue.LockList;
try
for i := 0 to List.Count-1 do
TIdBytes(List[i]) := nil; // decrement the array's refcount
finally
Queue.UnlockList;
end;
end;
// end if
Queue.Free;
inherited;
end;
procedure TMyContext.AddToQueue(const WFI: TWaveFormatInfo; const Buffer: Pointer; BufferSize: Cardinal);
begin
Buf: TIdBytes;
Offset: Integer;
P: Pointer; // if not using TThreadList<TIdBytes>...
end;
// each client needs its own local copy of the
// input Buffer, so copy the data
// and add it to the queue...
Offset := Sizeof(Integer) + WFI.WaveFormatSize;
SetLength(Buf, Offset + BufferSize);
Move(WFI, Buf[0], Offset);
if BufferSize > 0 then
Move(Buffer^, Buf[Offset], BufferSize);
// if using TThreadList<TIdBytes>...
{with Queue.LockList do
try
Add(Buf);
QueueHasData := True;
finally
Queue.UnlockList;
end;}
// else
TIdBytes(P) := Buf; // increment the array's refcount
try
with Queue.LockList do
try
Add(P);
QueueHasData := True;
finally
Queue.UnlockList;
end;
except
TIdBytes(P) := nil; // decrement the array's refcount
raise;
end;
// end if
end;
procedure TMyContext.CheckQueue;
var
List: TList;
P: Pointer; // if not using TThreadList<TIdBytes>...
begin
if QueueHasData then
begin
List := Queue.LockList;
try
while List.Count > 0 do
begin
// if using TThreadList<TIdBytes>...
{Connection.IOHandler.Write(List[0]);
List.Delete(0);}
// else
P := List[0];
List.Delete(0);
try
Connection.IOHandler.Write(TIdBytes(P));
finally
TIdBytes(P) := nil; // decrement the array's refcount
end;
// end if
end;
finally
QueueHasData := List.Count > 0;
Queue.UnlockList;
end;
end;
end;
private
WFI: TWaveFormatInfo;
...
procedure TForm2.FormCreate(Sender: TObject);
begin
SetPCMAudioFormatS(@WFI.WaveFormat, LiveAudioRecorder.PCMFormat);
WFI.WaveFormatSize := SizeOf(WFI.WaveFormat);
IdTCPServer1.ContextClass := TMyContext;
end;
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
begin
TMyContext(AContext).CheckQueue;
with AContext.Connection.IOHandler do
begin
if CheckForDataOnSource(0) then
InputBuffer.Clear;
CheckForDisconnect;
end;
end;
procedure TForm2.LiveAudioRecorderData(Sender: TObject; const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
var
Clients : TList;
i: integer;
begin
FreeIt := True;
Clients := IdTCPServer1.Contexts.LockList;
try
for i := 0 to Clients.Count-1 do
TMyContext(TIdContext(Clients[i])).AddToQueue(WFI, Buffer, BufferSize);
finally
IdTCPServer1.Contexts.UnlockList;
end;
end;
С другой стороны, TCP - очень плохой выбор для трансляции мультимедийных файлов в режиме реального времени нескольким клиентам.Вместо этого вам действительно следует использовать UDP, чтобы вместо этого вы могли использовать широковещательные или многоадресные рассылки.Это позволило бы коду вашего сервера отправлять только 1 пакет на блок аудиоданных на специальный широковещательный / многоадресный IP-адрес, и этот пакет автоматически доставлялся бы сетью каждому клиенту, который заинтересован в этом, а не доставлял вас сам.