Delphi - 记录和流式传输Android音频

时间:2015-07-09 02:09:27

标签: android delphi indy audiotrack android-audiorecord

我正在尝试从一个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.

0 个答案:

没有答案