我使用LiveAudioPlayer使用Indy通过TCP流音频。这是客户端的代码:
function TForm1.LiveAudioPlayerDataPtr(Sender: TObject;
var Buffer: Pointer; var NumLoops: DWORD;
var FreeIt: Boolean): DWORD;
var
buf:TIdBytes;
begin
if not IdTCPClient1.Connected then
Result := 0 // Stops LiveAudioPlayer
else
IdTCPClient1.Socket.ReadBytes(buf, sizeof(buf), TRUE);
BytesToRaw(buf, WaveFormat, sizeof(WaveFormat));
if AudioBuffer.Get(Buffer, Result) then
FreeIt := True
else
begin
Buffer := nil; // When Buffer is nil,
Result := 10 // Result will be considered as silence milliseconds.
end
end;
在服务器端,我使用LiveAudioRecorder:
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
type
TWaveFormatInfo = packed record
WaveFormatSize: Integer;
WaveFormat: TWaveFormatEx;
end;
var
WFI: TWaveFormatInfo;
Buf: TIdBytes;
Clients : TList;
i: integer;
begin
SetPCMAudioFormatS(@WFI.WaveFormat, LiveAudioRecorder.PCMFormat);
WFI.WaveFormatSize := SizeOf(WFI.WaveFormat);
Buf := RawToBytes(WFI, SizeOf(WFI));
Clients := IdTCPServer1.Contexts.LockList;
try
for i := 0 to Clients.Count-1 do
try
TIdContext(Clients[i]).Connection.IOHandler.Write(buf);
except
end;
finally
IdTCPServer1.Contexts.UnlockList;
end;
但是我需要在服务器端运行一些代码:
procedure TForm2.LiveAudioRecorderData(Sender: TObject;
const Buffer: Pointer;
BufferSize: Cardinal; var FreeIt: Boolean);
var
I: Integer;
Clients : TList;
begin
FreeIt := True;
//** here
end;
如何从WinSock更改为Indy TCP以流式传输声音?
答案 0 :(得分:1)
您当前在IdTCPServer1Execute()
中拥有的代码实际上属于数据所在的LiveAudioRecorderData()
。
不过,我强烈建议从不使用IOHandler.Write()
循环向多个TCP客户端广播数据,就像您所做的那样。一次将您的带宽使用速度降低到仅1个数据包,根本没有并行处理。在向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,以便可以改用子网广播或多播。这样一来,您的服务器代码就可以仅将一个音频数据块的一个数据包发送到一个特殊的广播/多播IP地址,并通过网络自动将该数据包传递给每个对此感兴趣的客户端,而无需您自己进行传递