我正在尝试从一个Android设备(服务器)录制音频并发送到另一个Android设备(客户端)来实时播放,我使用indy HTTP服务器,将流发送到客户端,我正在调用用于录制音频的Android API,使用AudioRecord并使用AudioTrack在另一侧播放。这两个代码都在下面。
我从录音机接收音频并播放,问题是,播放的音频不连续,他打破了所播放的每个缓冲区,就像差不多一半的时间一样,例如,如果我发送1秒样本,它似乎只有一半是玩的。我已经检查了缓冲区大小,我从AudioRecord读取的缓冲区大小与AudioTrack中播放的大小相同。我不知道是否是来自indy服务器/客户端的延迟时间。我已经尝试了很多缓冲区大小,采样率,音频格式等组合,但音频仍在破碎。
任何帮助都将不胜感激。
这是服务器代码
unit ServerAudio5;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.IOUtils,
System.Diagnostics,
FMX.Platform.Android,
Androidapi.JNI.Net,
Androidapi.JNIBridge,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Media, IdContext, IdCustomHTTPServer, IdBaseComponent,
IdComponent, IdCustomTCPServer, IdHTTPServer, FMX.StdCtrls,
FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo;
type
TForm1 = class(TForm)
Memo1: TMemo;
btnConnect: TButton;
btnDisconnect: TButton;
IdHTTPServer1: TIdHTTPServer;
Timer1: TTimer;
Timer2: TTimer;
procedure FormCreate(Sender: TObject);
procedure IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
procedure IdHTTPServer1Connect(AContext: TIdContext);
procedure IdHTTPServer1Disconnect(AContext: TIdContext);
procedure Timer1Timer(Sender: TObject);
procedure btnConnectClick(Sender: TObject);
procedure btnDisconnectClick(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
Enable_Server: Boolean;
Enable_Audio: Boolean;
AudioRecorder: JAudioRecord;
sampleRate: Integer;
channelConfig: Integer;
audioFormat: Integer;
minBufSize: Integer;
AudioMem: TMemoryStream;
AudioStr: TJavaArray<SmallInt>;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
procedure TForm1.btnConnectClick(Sender: TObject);
begin
// Start server
if Enable_Server = False then
begin
Enable_Server:= True;
IdHTTPServer1.Active := True;
Memo1.Lines.Add('Server Started');
end;
end;
procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
if Enable_Server = True then
begin
Enable_Server:= False;
IdHTTPServer1.Active := False;
Memo1.Lines.Add('Server Finished');
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Enable_Server:= False;
Enable_Audio:= False;
Timer1.Enabled:= False;
sampleRate:= 11025;
channelConfig:= TJAudioFormat.JavaClass.CHANNEL_IN_MONO;
audioFormat:= TJAudioFormat.JavaClass.ENCODING_PCM_16BIT;
// minBufSize = 1024 Bytes
minBufSize:= TJAudioRecord.JavaClass.getMinBufferSize(sampleRate, channelConfig, audioFormat);
// AudioRecover = 1024*4 = 4096
AudioStr:= TJavaArray<SmallInt>.Create(minBufSize*4);
AudioMem:= TMemoryStream.Create;
AudioRecorder:= TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.MIC,
sampleRate,
channelConfig,
audioFormat,
minBufSize*4);
end;
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
if ARequestInfo.Document = '/audiorecover' then
begin
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentType := 'audiorecover';
AResponseInfo.CloseConnection := False;
AResponseInfo.WriteHeader;
// Enable the Audio
Enable_Audio:= True;
end;
end;
procedure TForm1.IdHTTPServer1Connect(AContext: TIdContext);
begin
Memo1.Lines.Add('Connection is make with: '+AContext.Connection.Socket.Binding.PeerIP);
Timer1.Enabled:= True;
end;
procedure TForm1.IdHTTPServer1Disconnect(AContext: TIdContext);
begin
Memo1.Lines.Add('Disconnection is make with: '+AContext.Connection.Socket.Binding.PeerIP);
Enable_Audio:= False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
index: Integer;
begin
if Enable_Audio then
begin
Memo1.Lines.Add('Start Recording');
Timer1.Enabled:= False;
(AudioRecorder as JAudioRecord).startRecording;
Timer2.Enabled:= True;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
var
index: Integer;
NewCount: Integer;
begin
while True do
begin
if Enable_Audio then
begin
// Read from the AudioRecover
NewCount:= (AudioRecorder as JAudioRecord).read(AudioStr, 0,AudioStr.Length);
// The read command does not read 4096, just 2048
AudioMem.Write(AudioStr.Data^, NewCount); // NewCount = 2048 Bytes
AudioMem.Position:= 0;
//Send the stream using socket
with IdHTTPServer1.Contexts.LockList do
try
for index := 0 to Count-1 do
begin
TIdContext( Items[index] ).Connection.IOHandler.WriteLn('audiorecover');
TIdContext( Items[index] ).Connection.IOHandler.WriteLn(IntToStr(AudioMem.Size));
TIdContext( Items[index] ).Connection.IOHandler.Write(AudioMem);
end;
finally
IdHTTPServer1.Contexts.UnlockList;
end;
AudioMem.Clear;
end;
end;
end
end.
这是客户端代码
unit ClientAudio4;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
System.Diagnostics,
FMX.Platform.Android,
Androidapi.JNI.Net,
Androidapi.JNIBridge,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Media, FMX.Edit, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, FMX.ScrollBox, FMX.Memo, FMX.Controls.Presentation, FMX.StdCtrls;
type
TForm1 = class(TForm)
Connect: TButton;
Disconnect: TButton;
Memo1: TMemo;
IdTCPClient1: TIdTCPClient;
Timer1: TTimer;
Host: TEdit;
Timer2: TTimer;
procedure ConnectClick(Sender: TObject);
procedure DisconnectClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure IdTCPClient1Connected(Sender: TObject);
procedure IdTCPClient1Disconnected(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
Enable_Play: Boolean;
AudioPlay: JAudioTrack;
AudioStream: TJavaArray<SmallInt>;
trackmin: Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}
procedure TForm1.ConnectClick(Sender: TObject);
begin
IdTCPClient1.Host:= Host.Text;
IdTCPClient1.Connect;
end;
procedure TForm1.DisconnectClick(Sender: TObject);
begin
IdTCPClient1.Disconnect;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Host.Text:= '192.168.0.100';
trackmin:= TJAudioTrack.JavaClass.getMinBufferSize(11025,
TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);
AudioStream:= TJavaArray<SmallInt>.Create(trackmin);
AudioPlay:= TJAudioTrack.JavaClass.init(TJAudioManager.JavaClass.STREAM_MUSIC,
11025,
TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
trackmin,
TJAudioTrack.JavaClass.MODE_STREAM);
end;
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
Memo1.Lines.Add('Connection to: '+IdTCPClient1.Host);
Timer1.Enabled:= True;
end;
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
Memo1.Lines.Add('Disconnect to: '+IdTCPClient1.Host);
Enable_Play:= False;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
trackmin: Integer;
begin
Timer1.Enabled:= False;
IdTCPClient1.IOHandler.WriteLn('GET '+'/audiorecover'+' HTTP/1.0');
IdTCPClient1.IOHandler.WriteLn;
Enable_Play:= True;
// Start play
(AudioPlay as JAudioTrack).play();
Timer2.Enabled:= True;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
var
S3: String;
S4: String;
CLength: Integer;
AudioRec: TMemoryStream;
begin
while True do
begin
if Enable_Play then
begin
try
// Try to get the header
S3:= Trim(LowerCase(IdTCPClient1.IOHandler.ReadLn));
if S3 = 'audiorecover' then
begin
AudioRec:= TMemoryStream.Create;
// Read the length of the memorystream
S4:= LowerCase(IdTCPClient1.IOHandler.ReadLn);
CLength:= StrToInt(S4);
// Read the stream
IdTCPClient1.IOHandler.ReadStream(AudioRec, CLength);
AudioRec.Position:= 0;
// Set the array
AudioRec.Read(AudioStream.Data^, CLength);
// Write to the AudioTrack to play the video
(AudioPlay as JAudioTrack).write(AudioStream, 0, CLength);
AudioRec.Clear;
AudioRec.Free;
end;
finally
end;
end;
end;
end;
end.