[RVMedia] 通过 TRVMicrophone 将音频录制为 WAV 文件

好吧,声明一下,本文需要修改RVMedia 本身的源码,RVMedia 4.0.2 本身是不支持这一功能的,所以,如果你想要支持这一功能,需要对 RVMedia 打补丁。因为不想对RVMedia 进行大手术,所以改的内容尽量少,剩下的事情由额外的东西来解决:

1、打开 MRVTypes.pas,为 RVMedia 打第 1 处补丁:

1.1、找到 TCustomRVMicrophone,在其前面定义一个事件类型:

TRVWaveBufferReadyEvent = procedure(Sender: TObject; AData: PByte;ALen: Integer) of object;

这个事件类型,用于将捕获到的 WAV 数据传给事件的响应函数中。

1.2、为 TCustomRVMicrophone 增加一个事件,及触发事件的接口函数 BufferReady:

protected
   FOnBufferReady: TRVWaveBufferReadyEvent;
   procedure BufferReady(ABuf: PByte; ASize: Integer);
published
   property OnBufferReady: TRVWaveBufferReadyEvent read FOnBufferReady write FOnBufferReady;

1.3、在 BufferReady 函数中添加下面的代码:

procedure TCustomRVMicrophone.BufferReady(ABuf: PByte; ASize: Integer);
begin
  if Assigned(FOnBufferReady) then
    FOnBufferReady(Self, ABuf, ASize);
end;

3、实现下面的类,用于完成录制音频数据到 WAV 文件的封装。

uses ...,MRVWinMicrophone, MRVMicrophone, MRVWavFile;
type
  TRVWaveRecorder = class
  private
    FWaveHeader: TWaveHeader;
    FDataHeader: TDataHeader;
    FWaveStream: TFileStream;
    FWaveFile: String;
    FMicrophone: TCustomRVMicrophone;
    procedure DoBufferReady(ASender: TObject; ABuf: PByte; ALen: Integer);
    function GetDuration: Integer;
    function GetWaveFile: String;
    procedure SetMicrophone(const Value: TCustomRVMicrophone);
    procedure SetWaveFile(const Value: String);
    procedure PrepareWaveFile;
  public
    constructor Create; overload;
    destructor Destroy; override;
    procedure Start;
    procedure Stop;
    property Microphone: TCustomRVMicrophone read FMicrophone
      write SetMicrophone;
    property WaveFile: String read FWaveFile write SetWaveFile;
    property Duration: Integer read GetDuration;
  end;

{ TRVWaveRecorder }

constructor TRVWaveRecorder.Create;
begin

end;

destructor TRVWaveRecorder.Destroy;
begin
  Stop;
  if Assigned(FMicrophone) then
    FMicrophone.OnBufferReady := nil;
  inherited;
end;

procedure TRVWaveRecorder.DoBufferReady(ASender: TObject; ABuf: PByte;
  ALen: Integer);
begin
  if Assigned(FWaveStream) then
  begin
    Inc(FDataHeader.DataLen, ALen);
    FWaveStream.WriteBuffer(ABuf^, ALen);
  end;
end;

function TRVWaveRecorder.GetDuration: Integer;
begin
  if FWaveHeader.BytesPerSec <> 0 then
    Result := FDataHeader.DataLen div FWaveHeader.BytesPerSec
  else
    Result := 0;
end;

function TRVWaveRecorder.GetWaveFile: String;
begin
  if Assigned(FWaveStream) then
    Result := FWaveStream.Filename
  else
    SetLength(Result, 0);
end;

procedure TRVWaveRecorder.PrepareWaveFile;
begin
  if not Assigned(FWaveStream) then
    FWaveStream := TFileStream.Create(FWaveFile, fmCreate)
  else if FWaveStream.Filename <> FWaveFile then
  begin
    FreeAndNil(FWaveStream);
    FWaveStream := TFileStream.Create(FWaveFile, fmCreate);
  end;
  FWaveStream.Size := 0;
  with FWaveHeader do
  begin
    idRiff := 'RIFF';
    RiffLen := 38;
    idWave := 'WAVE';
    idFmt := 'fmt ';
    InfoLen := 16;
    WaveType := 1;
    Ch := 1;
    case Microphone.SamplesPerSec of
      rvsps8000:
        Freq := 8000;
      rvsps11025:
        Freq := 11025;
      rvsps22050:
        Freq := 22050;
      rvsps44100:
        Freq := 44100;
    end;
    case Microphone.BitsPerSample of
      rvbps8:
        Bits := 8;
      rvbps16:
        Bits := 16;
    end;
    BytesPerSec := Freq * Bits div 8;
    align := Bits div 8;
  end;
  FWaveStream.WriteBuffer(FWaveHeader, SizeOf(TWaveHeader));
  FDataHeader.idData := 'data';
  FDataHeader.DataLen := 0;
  FWaveStream.WriteBuffer(FDataHeader, SizeOf(FDataHeader));
end;

procedure TRVWaveRecorder.SetMicrophone(const Value: TCustomRVMicrophone);
begin
  if FMicrophone <> Value then
  begin
    if Assigned(FMicrophone) then
      FMicrophone.OnBufferReady := nil;
    FMicrophone := Value;
    if Assigned(FMicrophone) then
    begin
      FMicrophone.OnBufferReady := DoBufferReady;
      if FMicrophone.Active then
        PrepareWaveFile;
    end;
  end;
end;

procedure TRVWaveRecorder.SetWaveFile(const Value: String);
var
  ARecording: Boolean;
begin
  if Value <> WaveFile then
  begin
    if Assigned(FMicrophone) then
    begin
      ARecording := Microphone.Active;
      Microphone.Active := False;
    end
    else
      ARecording := False;
    if Assigned(FWaveStream) then
      FreeAndNil(FWaveStream);
    FWaveFile := Value;
    if ARecording then
      Start;
  end;
end;

procedure TRVWaveRecorder.Start;
begin
  if Assigned(Microphone) then
  begin
    PrepareWaveFile;
    Microphone.Active := True;
  end;
end;

procedure TRVWaveRecorder.Stop;
begin
  if Assigned(Microphone) then
  begin
    Microphone.Active := False;
    FWaveStream.Position := 0;
    FWaveHeader.RiffLen := FWaveStream.Size - 8;
    FWaveStream.WriteBuffer(FWaveHeader, SizeOf(FWaveHeader));
    FWaveStream.WriteBuffer(FDataHeader, SizeOf(FDataHeader));
    FreeAndNil(FWaveStream);
  end;
end;

2、打开 MRVWinMicrophone.pas 文件,打第 2 处补丁:

2.1、找到 RecorderCallBack 函数;

2.2、找到 with 后面的语句,加入对 BufferReady 的调用:

...
with Microphone do
  begin
    if not Active then
      exit;
    Microphone.BufferReady(PByte(FWaveHdr.lpData),FWaveHdr.dwBufferLength);//<<<加入这一行

3、OK,现在一切已经就绪,可以创建一个 TRVWaveRecorder 的实例,然后录制音频了。

FWaveRecorder := TRVWaveRecorder.Create;
FWaveRecorder.Microphone := mpSource;
//开始录制视频
FWaveRecorder.WaveFile:=APath + TempFileName + '.wav';
FWaveRecorder.Start;
...
//停止录制视频
FWaveRecorder.Stop;

这样,音频能够捕获,视频也能够抓取,最终也就能形成音频和视频混合的mp4等格式了,具体这里就不再阐述了。

 

分享到: