unit AvPlayer;

interface

uses
  AnyQBase,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, Buttons, mmsystem, VFW, VideoDisp, ACMWaveOut;

type
  TAvPlayeForm = class(TAnyQBaseForm)
    Panel1: TPanel;
    VideoDisplayer: TVideoDisp;
    spbtnPlay: TSpeedButton;
    spbtnStop: TSpeedButton;
    Image1: TImage;
    ACMPlayer: TACMWaveOut;
    TimerPlay: TTimer;
    Panel2: TPanel;
    paintStatus: TPaintBox;
    spbtnClose: TSpeedButton;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure spbtnPlayClick(Sender: TObject);
    procedure spbtnStopClick(Sender: TObject);
    procedure TimerPlayTimer(Sender: TObject);
    procedure paintStatusPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ACMPlayerDone(Sender: TObject);
    procedure parentResize(Sender: TObject);
    procedure spbtnCloseClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    RecordTime: Cardinal;
    PlayedTime: Cardinal;
    Playing: Boolean;

    Avi: IAVIFile;
    videos: IAVIStream;
    Audios: IAVIStream;
    VPos: Integer;
    APos: Integer;
    VInfo: TAVIStreamInfo;
    AInfo: TAVIStreamInfo;
    VEnd: Boolean;
    AEnd: Boolean;

    procedure PlayAVI;
    procedure StopAVI;

    procedure PlayWAV;
    procedure stopWAV;
  public
    { Public declarations }
    PlayFileName: string;
    procedure Play;
    procedure Stop;
  end;

var
  AvPlayeForm: TAvPlayeForm;

procedure PlayAVFile(fn: string; Parent: TWincontrol = nil);
procedure PlayAnyQAV(fn: string; Parentcontrol: TWincontrol = nil);

implementation

uses shellApi, G729, AnyQv, Jabber1, ExSession, Session;
{$R *.dfm}

const
  PCMFOMAT : tWAVEFORMATEX =
    ( wFormatTag: WAVE_FORMAT_PCM;
      nChannels: 1;
      nSamplesPerSec: 8000;
      nAvgBytesPerSec: 8000;
      nBlockAlign: 1;
      wBitsPerSample: 8;
      cbSize: 0;
    );

  GSMFOMAT : tWAVEFORMATEX =
    ( wFormatTag: 49;
      nChannels: 1;
      nSamplesPerSec: 8000;
      nAvgBytesPerSec: 1625;
      nBlockAlign: 65;
      wBitsPerSample: 0;
      cbSize: 2;
    );

  AnyQFOMAT : tWAVEFORMATEX =
    ( wFormatTag: 0;
      nChannels: 1;
      nSamplesPerSec: 8000;
      nAvgBytesPerSec: 1000;
      nBlockAlign: 10;
      wBitsPerSample: 16;
      cbSize: 2;
    );
  AnyQPCMFOMAT : tWAVEFORMATEX =
    ( wFormatTag: WAVE_FORMAT_PCM;
      nChannels: 1;
      nSamplesPerSec: 8000;
      nAvgBytesPerSec: 16000;
      nBlockAlign: 2;
      wBitsPerSample: 16;
      cbSize: 0;
    );

procedure CheckPlayWAV(fn: string; Parent: TWincontrol = nil);
var
  hWAV : HMMIO;
  mmckinfoParent, mmckinfoSubchunk: MMCKINFO;
  size, i: Integer;
  Pformat: array [0..50] of Byte;
  SBuf: array [0..9] of byte;
begin
  if Parent<>nil then
  begin
    for i := Parent.ControlCount-1 downto 0 do
      Parent.Controls[i].Free;
  end;
  Parent.Hide;

  hWAV := mmioOpen(pchar(fn), nil, MMIO_READ or MMIO_EXCLUSIVE);
  if hWAV=-1 then
    raise Exception.Create('无法播放文件');
  mmckinfoParent.fccType := mmioStringToFOURCC('WAVE',0);

  if mmioDescend(hWAV, @mmckinfoParent, nil, MMIO_FINDRIFF) <> MMSYSERR_NOERROR then
  begin
     mmioClose(hWAV, 0);
    raise Exception.Create('无法识别的文件格式');
  end;

  mmckinfoSubchunk.ckid := mmioStringToFOURCC('fmt ',0);
  if mmioDescend(hWAV, @mmckinfoSubchunk, @mmckinfoParent, MMIO_FINDCHUNK) <> MMSYSERR_NOERROR then
  begin
    mmioAscend(hWAV, @mmckinfoParent, 0);
    mmioClose(hWAV, 0);
    raise Exception.Create('无法识别的文件格式');
  end;

  mmioRead(hWAV, @Pformat, mmckinfoSubchunk.cksize);
  if mmioAscend(hWAV, @mmckinfoSubchunk, 0) <> MMSYSERR_NOERROR then
  begin
    mmioAscend(hWAV, @mmckinfoParent, 0);
    mmioClose(hWAV, 0);
    Abort;
  end;

  mmioAscend(hWAV, @mmckinfoParent, 0);
  mmioClose(hWAV, 0);

  if PWAVEFORMATEX(@Pformat)^.wFormatTag in [0,WAVE_FORMAT_PCM,49] then
    PlayAnyQAV(fn, Parent)
  else
    if UpperCase(ExtractFileExt(fn))='.WAV' then
      ShellExecute(Application.Handle, 'open', pchar(fn),nil,nil,SW_NORMAL);
end;

procedure CheckPlayAVI(fn: string; Parent: TWincontrol = nil);
var
  Avi: IAVIFile;
  videos: IAVIStream;
  VInfo: TAVIStreamInfo;
  i: Integer;
begin
  if Parent<>nil then
  begin
    for i := Parent.ControlCount-1 downto 0 do
      Parent.Controls[i].Free;
  end;
  Parent.Hide;

  AVIFileInit;
  AVIFileOpen(avi,pchar(fn), OF_READ, nil);

  AVIFileGetStream(avi, videos, streamtypeVIDEO	,0);
  AVIStreamInfo(videos, VInfo, sizeof(Vinfo));
  videos := nil;
  Avi := nil;
  AVIFileExit;

  if VInfo.szName = 'AnyQ XVID ENCODER' then
    PlayAnyQAV(fn, Parent)
  else
    ShellExecute(Application.Handle, 'open', pchar(fn),nil,nil,SW_NORMAL);
end;

procedure PlayAVFile(fn: string; Parent: TWincontrol = nil);
begin
  if UpperCase(ExtractFileExt(fn))='.SQA' then
    CheckPlayWAV(fn, Parent);
  if UpperCase(ExtractFileExt(fn))='.WAV' then
    CheckPlayWAV(fn, Parent);
  if UpperCase(ExtractFileExt(fn))='.AVI' then
    CheckPlayAVI(fn, Parent);
end;

procedure PlayAnyQAV(fn: string; Parentcontrol: TWincontrol = nil);
begin
  with TAvPlayeForm.Create(Application) do
  begin
    PlayFileName := fn;
    if Parentcontrol<>nil then
    begin
      Parentcontrol.Visible := true;
      Parent := Parentcontrol;
      Width := ClientWidth;
      Height := ClientHeight;
      BorderStyle := bsNone;
      Parentcontrol.Width := Width + 8;
      Left := (Parentcontrol.Width-Width) div 2;
      Top := (Parentcontrol.Height-Height) div 2;
      if Parentcontrol is TPanel then
        TPanel(Parentcontrol).OnResize := parentResize;
    end;
    Show;
    parentResize(TPanel(Parentcontrol));
    Play;
  end;
end;

{ TAvPlayeForm }

procedure TAvPlayeForm.Play;
begin
  spbtnPlay.Enabled := False;
  spbtnStop.Enabled := True;
  Playing := true;
  if UpperCase(ExtractfileExt(PlayFileName))='.AVI' then
    PlayAVI
  else
    PlayWav;
  //
end;

procedure TAvPlayeForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  if Parent<>nil then
    Parent.Hide;
  Action := caFree;
end;

procedure TAvPlayeForm.spbtnPlayClick(Sender: TObject);
begin
  Play;
end;

procedure TAvPlayeForm.Stop;
begin
  spbtnPlay.Enabled := True;
  spbtnStop.Enabled := False;
  Playing := False;
  if UpperCase(ExtractfileExt(PlayFileName))='.AVI' then
    StopAVI
  else
    StopWav;
  paintStatus.Refresh;
end;

procedure TAvPlayeForm.spbtnStopClick(Sender: TObject);
begin
  Stop;
end;

procedure TAvPlayeForm.PlayAVI;
var
  Info: TAVIFileInfo;
  size: Integer;
  Bufsize: Integer;
  samples: Integer;
  wavformat: tWAVEFORMATEX;
  Pwavfmt: PWaveFormatEx;
  Pvidfmt: Pointer;
  Pbuf: Pointer;
  Buf: array [0..4095] of byte;
  i,j,a,s: integer;
  tick: cardinal;
  lendTime: Integer;
  lTime: Integer;
  lFrame: Integer;
  vidfmt: TBitmapInfoHeader;
  DFRAM: xvid_dec_frame_t;
  Dimg_t: xvid_image_t;
begin
  if not CodeInited then
    InitCode();
  VideoDisplayer.visible := true;
  Image1.Visible := False;
  AVIFileInit;
  AVIFileOpen(avi,pchar(PlayFileName), OF_READ, nil);
  AVIFileInfo(avi,Info,sizeof(info));

  VideoDisplayer.Width := Info.dwWidth;
  VideoDisplayer.Height := Info.dwHeight;
  VideoDisplayer.Streaming := true;
  VideoDisplayer.StreamRate := Info.dwScale;


  AVIFileGetStream(avi, Audios, streamtypeAUDIO	,0);
  AEnd := Audios=nil;
  if Audios<>nil then
  begin
    AVIStreamInfo(Audios, AInfo, sizeof(Vinfo));
    AVIStreamReadFormat(Audios, AVIStreamStart(Audios), nil, @size);
    Pbuf := GetMemory(size);
    AVIStreamReadFormat(Audios, AVIStreamStart(Audios), Pbuf, @size);
    ACMPlayer.Open(PWaveFormatEx(Pbuf), MainSession.Prefs.getInt('WavOutDriver'));
    wavformat := PWaveFormatEx(Pbuf)^;
    FreeMemory(Pbuf);
  end;

  AVIFileGetStream(avi, videos, streamtypeVIDEO	,0);
  VEnd := videos=nil;
  if videos<> nil then
  begin
    AVIStreamInfo(videos, VInfo, sizeof(Vinfo));
    AVIStreamReadFormat(videos, AVIStreamStart(videos), nil, @size);
    Pvidfmt := GetMemory(Size);
    AVIStreamReadFormat(videos, AVIStreamStart(videos), Pvidfmt, @size);
    vidfmt := PBitmapInfoHeader(Pvidfmt)^;
    vidfmt.biCompression := BI_RGB;
    VideoDisplayer.BitMapInfoHeader := vidfmt;
    FreeMemory(Pvidfmt);
  end;

  aPos := 0;
  s := 0;
  tick := GetTickCount;
  lFrame := 0;
  Bufsize := 0;
  RecordTime := AVIStreamLengthTime(videos);
  for i:= 0 to VInfo.dwLength do
  begin
    lTime := GetTickCount-tick;
    if Audios<>nil then
      if aPos = 0 then
      begin
        for j:= 1 to 2 do
        begin
          Bufsize := 0;
          while (Bufsize = 0) and (aPos < AInfo.dwLength) do
          begin
            AVIStreamRead(Audios, aPos, AVISTREAMREAD_CONVENIENT, nil,0, @size, @samples);
            AVIStreamRead(Audios, aPos, AVISTREAMREAD_CONVENIENT, @Buf[Bufsize], size, nil, nil);
            Bufsize := Bufsize + size;
            if samples>0 then
              inc(aPos, samples)
            else
              inc(aPos);
          end;
          if Bufsize > 0 then
            ACMPlayer.PlayBack(@buf[0],Bufsize);
        end;
      end;

    while GetTickCount-tick < AVIStreamSampleToTime(videos, i) do
    begin
      Application.ProcessMessages;
      if not Playing then
        break;
    end;
      if not Playing then
      break;

    AVIStreamRead(videos,i,1, nil,0, @size, nil);
    pbuf := GetMemory(Size);
    AVIStreamRead(videos,i,1, Pbuf, size, nil, nil);
    if VInfo.fccHandler = mmioStringToFOURCC('xvid',0) then
      try
        FillMemory(@Dimg_t, Sizeof(Dimg_t), 0);
        Dimg_t.csp := XVID_CSP_BGR or XVID_CSP_VFLIP;
        Dimg_t.plane[0] := @Decbuf[0];
        Dimg_t.stride[0] := 160*3;

        FillMemory(@DFRAM, sizeof(DFRAM), 0);
        DFRAM.version := XVID_VERSION;
        DFRAM.general := XVID_LOWDELAY or XVID_DEBLOCKY or XVID_DEBLOCKUV;
        DFRAM.bitstream := Pbuf;
        DFRAM.length := size;
        DFRAM.output := Dimg_t;

        if not CodeInited then exit;
        if not assigned(VDecodeproc) then
          exit;
        VDecodeproc(DECodehandle, XVID_DEC_DECODE, @DFRAM, nil);
        VideoDisplayer.DrawStream(@Decbuf[0], false);
      except
      end
    else
      try
        VideoDisplayer.DrawStream(Pbuf, false);
      except
      end;
    FreeMemory(pbuf);
    PlayedTime := AVIStreamSampleToTime(videos, i);
    try
      paintStatusPaint(paintStatus);
    except
    end;
  end;

  VEnd := true;

  if AEnd and VEnd then
    Stop
end;

procedure TAvPlayeForm.PlayWAV;
var
  hWAV : HMMIO;
  mmckinfoParent, mmckinfoSubchunk: MMCKINFO;
  size, i: Integer;
  Pformat: array [0..50] of Byte;
  SBuf: array [0..9] of byte;
  buf: array [0..65535] of Byte;
  bufSize: Integer;
begin
  hWAV := mmioOpen(pchar(PlayFileName), nil, MMIO_READ or MMIO_EXCLUSIVE);
  mmckinfoParent.fccType := mmioStringToFOURCC('WAVE',0);
  mmioDescend(hWAV, @mmckinfoParent, nil, MMIO_FINDRIFF);

  mmckinfoSubchunk.ckid := mmioStringToFOURCC('fmt ',0);
  mmioDescend(hWAV, @mmckinfoSubchunk, @mmckinfoParent, MMIO_FINDCHUNK);
  mmioRead(hWAV, @Pformat, mmckinfoSubchunk.cksize);
  mmioAscend(hWAV, @mmckinfoSubchunk, 0);

  if PWAVEFORMATEX(@Pformat)^.wFormatTag <> 0 then
    ACMPlayer.Open(@Pformat, MainSession.Prefs.getInt('WavOutDriver'))
  else
    ACMPlayer.Open(@AnyQPCMFOMAT, MainSession.Prefs.getInt('WavOutDriver'));

  if PWAVEFORMATEX(@Pformat)^.wFormatTag <> 0 then
    bufSize := PWAVEFORMATEX(@Pformat)^.nAvgBytesPerSec
  else
    bufSize := 10;
  TimerPlay.Enabled := true;

  mmckinfoSubchunk.ckid := mmioStringToFOURCC('data',0);
  mmioDescend(hWAV, @mmckinfoSubchunk, @mmckinfoParent, MMIO_FINDCHUNK);
  RecordTime := (1000*mmckinfoSubchunk.cksize) div PWAVEFORMATEX(@Pformat)^.nAvgBytesPerSec;
  for i:= 0 to mmckinfoSubchunk.cksize div bufSize do
  begin
    if PWAVEFORMATEX(@Pformat)^.wFormatTag = 0 then
    begin
      mmioRead(hWAV, @SBuf, bufSize);
      va_g729a_decoder(@SBuf, @Buf, 0);
      ACMPlayer.PlayBack(@Buf, bufSize*16);
      Continue;
    end;
    if i<mmckinfoSubchunk.cksize div bufSize then
    begin
      mmioRead(hWAV, @Buf, bufSize);
      ACMPlayer.PlayBack(@Buf, bufSize);
    end
    else
    begin
      mmioRead(hWAV, @Buf, mmckinfoSubchunk.cksize mod bufSize);
      ACMPlayer.PlayBack(@Buf, mmckinfoSubchunk.cksize mod bufSize);
    end;
  end;
  mmioAscend(hWAV, @mmckinfoSubchunk, 0);
  mmioAscend(hWAV, @mmckinfoParent, 0);
  mmioClose(hWAV, 0);
end;

procedure TAvPlayeForm.StopAVI;
begin
  if AVI<>nil then
  begin
    videos := nil;
    Audios := nil;
    Avi := nil;
    AVIFileExit;
  end;
  ACMPlayer.Close;
  paintStatusPaint(paintStatus);
end;

procedure TAvPlayeForm.stopWAV;
begin
  TimerPlay.Enabled :=false;
  ACMPlayer.Close;
  paintStatusPaint(paintStatus);
end;

procedure TAvPlayeForm.TimerPlayTimer(Sender: TObject);
begin
  PlayedTime := ACMPlayer.getsample div 8;
  paintStatusPaint(paintStatus);
  if PlayedTime>=RecordTime-1 then
    Stop;
end;

procedure TAvPlayeForm.paintStatusPaint(Sender: TObject);
var
  R: TRect;
  txt: String;
begin
  with paintStatus.Canvas do
  begin
    Lock;
    //画分隔线
    pen.Color := clLtGray;
    pen.Width := 1;
    pen.Style := psSolid;
    MoveTo(0,0);
    LineTo(paintStatus.Width,0);

    //显示当前状态
    R := Rect(2,2, 58, 19);
    if Playing then
      DrawText(Handle, '播放中', 6, R, DT_LEFT or DT_TOP)
    else
      DrawText(Handle, '停止', 4, R, DT_LEFT or DT_TOP);

    //显示时间
    R := Rect(60,2, 158, 19);
    if Playing then
      txt := formatfloat('0.00', PlayedTime/1000)+
          '/'+formatfloat('0.00', RecordTime/1000)+'秒'
    else
      txt := '长度:'+formatfloat('0.00', RecordTime/1000)+'秒';
    DrawText(Handle, Pchar(txt), length(txt), R, DT_LEFT or DT_TOP);

    UnLock;
  end;
end;

procedure TAvPlayeForm.FormCreate(Sender: TObject);
begin
  PlayedTime := 0;
  RecordTime := 0;
  Playing := false;
  ACMPlayer.Visible := false;
end;

procedure TAvPlayeForm.ACMPlayerDone(Sender: TObject);
var
  i: Integer;
  BufSize, Size: Integer;
  Buf: array [0..4095] of byte;
  samples: Integer;
begin
  if (avi<>nil) and (Audios<>nil) then
  begin
    PlayedTime := ACMPlayer.getsample div 8;
    AEnd := PlayedTime+1 >= AVIStreamLengthTime(Audios);
//    Caption := inttostr(PlayedTime)+'/'+inttostr(AVIStreamLengthTime(Audios));

    Bufsize := 0;
    while (Bufsize = 0) and (aPos < AInfo.dwLength) do
    begin
      AVIStreamRead(Audios, aPos, AVISTREAMREAD_CONVENIENT, nil,0, @size, @samples);
      AVIStreamRead(Audios, aPos, AVISTREAMREAD_CONVENIENT, @Buf[Bufsize], size, nil, nil);
      Bufsize := Bufsize + size;
      if samples>0 then
        inc(aPos, samples)
      else
        inc(aPos);
    end;
    if Bufsize > 0 then
      ACMPlayer.PlayBack(@buf[0],Bufsize); {}

    if AEnd and VEnd then
      Stop;
  end;
end;

procedure TAvPlayeForm.parentResize(Sender: TObject);
begin
  Left := (Twincontrol(Sender).Width-Width) div 2;
  Top := (Twincontrol(Sender).Height-Height) div 2;
end;

procedure TAvPlayeForm.spbtnCloseClick(Sender: TObject);
begin
  Close;
end;

procedure TAvPlayeForm.FormDestroy(Sender: TObject);
begin
  Stop;
end;

procedure TAvPlayeForm.FormShow(Sender: TObject);
begin
  if frmAnyQ.SkinData1.Active then
    frmAnyQ.SkinData1.SkinForm(self.Handle);
end;

end.
