(*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Any non-GPL usage of this software or parts of this software is strictly
 * forbidden.
 *
 * The "appropriate copyright message" mentioned in section 2c of the GPLv2
 * must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"
 *
 *)

{$I ..\..\source\compiler.inc}

unit MainFormUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, PasLibVlcUnit;

const
  PIPE_BUFF_SIZE = 1024;
  
type
  TPipeServer = class(TThread)
  private
    FOverlapped : Boolean;
    FStream     : TStream;
    FPipeName   : string;
    hEvent      : THandle;
    hPipe       : THandle;
    oOverlap    : TOverlapped;
    fPendingIO  : BOOL;
    dwState     : DWORD;
    cbToWrite   : DWORD;
    cbWritten   : DWORD;
    fSuccess    : BOOL;
    dwWait      : DWORD;
    chBuff      : packed array[0..PIPE_BUFF_SIZE-1] of byte;
    procedure Execute_non_overlapped();
    procedure Execute_overlapped();
  protected
    procedure Execute(); override;
  public
    constructor Create(AStream : TStream; pipeName : string; AOverlapped : Boolean = TRUE);
    destructor Destroy; override;
  end;
  
type
  TMainForm = class(TForm)
    procedure FormDestroy(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    pname : string;
    stream : TFileStream;
    server : TPipeServer;
    p_li : libvlc_instance_t_ptr;
    p_mi : libvlc_media_player_t_ptr;
  public
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

constructor TPipeServer.Create(AStream : TStream; pipeName : string; AOverlapped : Boolean = TRUE);
var
  dwPipeMode : DWORD;
begin
  inherited Create(TRUE);
  FStream   := AStream;
  FPipeName := pipeName;

  FOverlapped := AOverlapped;

  dwPipeMode := PIPE_ACCESS_OUTBOUND;
  
  if FOverlapped then
  begin
    FillChar(oOverlap, sizeof(oOverlap), 0);
    hEvent := CreateEvent(NIL, TRUE, TRUE, NIL);
    oOverlap.hEvent := hEvent;
    dwPipeMode := dwPipeMode or FILE_FLAG_OVERLAPPED;
  end;

  hPipe := CreateNamedPipe(
    PChar(FPipeName),
    dwPipeMode,
    PIPE_TYPE_MESSAGE or
    PIPE_WAIT,
    1,
    PIPE_BUFF_SIZE,
    PIPE_BUFF_SIZE,
    50,
    NIL);
end;

destructor TPipeServer.Destroy;
begin
  CloseHandle(hPipe);
  if (FOverlapped) then
  begin
    CloseHandle(hEvent);
  end;
  
  inherited Destroy;
end;

procedure TPipeServer.Execute_non_overlapped;
var
  bConn : Bool;
begin
  bConn := FALSE; 

  while not Terminated do
  begin
    bConn := ConnectNamedPipe(hPipe, NIL);
    if bConn then break;
    if (GetLastError() = ERROR_PIPE_CONNECTED) then
    begin
      bConn := TRUE;
      break;
    end;
  end;

  if bConn then
  begin
    while not Terminated do
    begin
      cbToWrite := FStream.Read(chBuff, PIPE_BUFF_SIZE);
      if (cbToWrite < 1) then break;
      fSuccess := WriteFile(
         hPipe,
         chBuff,
         cbToWrite,
         cbWritten,
         NIL);
       if (not fSuccess) then break;
       if (cbToWrite <> cbWritten) then break;
    end;
  end;
end;

procedure TPipeServer.Execute_overlapped;
begin
  if (ConnectNamedPipe(hPipe, @oOverlap)) then exit;

  cbToWrite := 0;
  fPendingIO := FALSE;

  case GetLastError() of
    ERROR_IO_PENDING     : fPendingIO := TRUE;
    ERROR_PIPE_CONNECTED : if (not SetEvent(oOverlap.hEvent)) then exit;
  end;

  if (fPendingIO) then dwState := 0 else dwState := 1;
  
  while not Terminated do
  begin
    dwWait := WaitForSingleObject(hEvent, 50);
    if (dwWait = WAIT_TIMEOUT) then continue;
    if (dwWait <> WAIT_OBJECT_0) then break;

    if (fPendingIO) then
    begin
      fSuccess := GetOverlappedResult(hPipe, oOverlap, cbWritten, FALSE);

      if not fSuccess then break;

      if (dwState = 1) then
      begin
        if (cbWritten <> cbToWrite) then break;
      end;
      
      if (dwState = 0) then
      begin
        dwState := 1;
      end;
    end;

    if (dwState = 1) then
    begin
      cbToWrite := FStream.Read(chBuff, PIPE_BUFF_SIZE);

      if (cbToWrite < 1) then break;

      fSuccess := WriteFile(
        hPipe,
        chBuff,
        cbToWrite,
        cbWritten,
        @oOverlap);

      if (fSuccess and (cbWritten = cbToWrite)) then
      begin
        fPendingIO := FALSE;
        continue;
      end;

      if ((not fSuccess) and (GetLastError() = ERROR_IO_PENDING)) then
      begin
        fPendingIO := TRUE;
        continue;
      end;

      break;
    end;
  end;
end;

procedure TPipeServer.Execute();
begin
  if (FOverlapped) then Execute_overlapped() else Execute_non_overlapped();
end;

// =============================================================================

procedure TMainForm.FormCreate(Sender: TObject);
var
  p_md : libvlc_media_t_ptr;
begin
  pname  := '\\.\pipe\vlcpipe' + IntToStr(Round(Now() * SecsPerDay));

  stream := TFileStream.Create('..\..\_testFiles\test.ts', fmOpenRead);
  server := TPipeServer.Create(stream, pname, TRUE);

  libvlc_dynamic_dll_init();

  if (libvlc_dynamic_dll_error <> '') then
  begin
    MessageDlg(libvlc_dynamic_dll_error, mtError, [mbOK], 0);
    exit;
  end;

  with TArgcArgs.Create([
    libvlc_dynamic_dll_path,
    '--intf=dummy',
    '--ignore-config',
    '--quiet',
    '--no-video-title-show',
    '--no-video-on-top'
    // '--vout=vdummy',
    // '--aout=adummy'
  ]) do
  begin
    p_li := libvlc_new(ARGC, ARGS);
    Free;
  end;

  p_mi := NIL;

  p_md := libvlc_media_new_location(p_li, PAnsiChar('stream://\' + pname));
  
  if (p_md <> NIL) then
  begin
    p_mi := libvlc_media_player_new_from_media(p_md);
    if (p_mi <> NIL) then
    begin
      libvlc_media_player_set_display_window(p_mi, SELF.Handle);
    end;
    libvlc_media_player_play(p_mi);
    libvlc_media_release(p_md);
  end;
end;

procedure TMainForm.FormActivate(Sender: TObject);
begin
  OnActivate := NIL;

  Sleep(50);
  
  server.Resume;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  server.Terminate;
  server.WaitFor;
  server.Free;
  
  if (p_mi <> NIL) then
  begin
    libvlc_media_player_stop(p_mi);
    libvlc_media_player_release(p_mi);
    p_mi := NIL;
  end;
  if (p_li <> NIL) then
  begin
    libvlc_release(p_li);
    p_li := NIL;
  end;

  stream.Free;
end;

end.
