{******************************************************************************}
{ FileName............: ShowingStreamDataUnit001                               }
{ Project.............: DVB                                                    }
{ Author(s)...........: MM                                                     }
{ Version.............: 1.00                                                   }
{------------------------------------------------------------------------------}
{  StreamReader and showing stream                                             } 
{                                                                              }
{  Copyright (C) 2003-2006  M.Majoor                                           }
{                                                                              }
{  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. }
{                                                                              }
{------------------------------------------------------------------------------}
{                                                                              }
{ The default settings should provide you with a video/audio of the german ARD }
{ program being transmitted. It is assumed the dish is facing Astra 19.2E and  }
{ that the PIDs have not changed....                                           }
{ If no video/audio is shown (without an error being reported) then check the  }
{ also the graphics setup (see note below).                                    }
{                                                                              }
{ Note: Only one DirectShow per process can be run ....                        }
{ Note: Stream decoding not active ...                                         }
{ Note: The supplied VIDEO.GRF has no connected output pins. This is not       }
{       absolute necessary because these are automatically connected to the    }
{       'correct' video/audio filters.                                         }
{       These video/audio filters are typically part of a DVD viewer (like     }
{       InterVideo / PowerDVD etc. You can also connect these pins as required }
{       so they are forced to a specific connection. You need the FilterGraph  }
{       from DirectX 9 for this.                                               }
{------------------------------------------------------------------------------}
{                                                                              }
{ Version   Date   Comment                                                     }
{  1.00   20060404 - Initial release                                           }
{******************************************************************************}
unit ShowingStreamDataUnit001;

interface
uses
  Forms, ExtCtrls, StdCtrls, Mask, Controls, Classes, Gauges;

type
  TfrmMain = class(TForm)
    btnExit: TButton;
    mmoDriver: TMemo;
    pnlVideo: TPanel;
    Timer1: TTimer;
    StaticText2: TStaticText;
    lblCallbacks: TStaticText;
    grpTransponder: TGroupBox;
    rgPolarity: TRadioGroup;
    StaticText3: TStaticText;
    StaticText1: TStaticText;
    StaticText4: TStaticText;
    StaticText6: TStaticText;
    StaticText5: TStaticText;
    mskFrequency: TMaskEdit;
    mskSymbolRate: TMaskEdit;
    mskLof1: TMaskEdit;
    mskLof2: TMaskEdit;
    mskLofSw: TMaskEdit;
    StaticText7: TStaticText;
    StaticText15: TStaticText;
    StaticText8: TStaticText;
    StaticText13: TStaticText;
    StaticText14: TStaticText;
    btnSet: TButton;
    grpFilter: TGroupBox;
    StaticText9: TStaticText;
    StaticText10: TStaticText;
    mskVideoPid: TMaskEdit;
    mskAudioPid: TMaskEdit;
    mskPidFilter: TMaskEdit;
    btnSetFilter: TButton;
    StaticText16: TStaticText;
    Label1: TLabel;
    Label2: TLabel;
    StaticText18: TStaticText;
    mskPmtPid: TMaskEdit;
    grpDiSEqC: TGroupBox;
    rgDiSEqCType: TRadioGroup;
    mskDiSEqCData: TMaskEdit;
    StaticText20: TStaticText;
    btnsetDiSEqC: TButton;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    procedure btnExitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure rgPolarityClick(Sender: TObject);
    procedure mskSymbolRateChange(Sender: TObject);
    procedure mskFrequencyChange(Sender: TObject);
    procedure mskLof1Change(Sender: TObject);
    procedure mskLof2Change(Sender: TObject);
    procedure mskLofSwChange(Sender: TObject);
    procedure mskFrequencyKeyPress(Sender: TObject; var Key: Char);
    procedure mskSymbolRateKeyPress(Sender: TObject; var Key: Char);
    procedure mskLof1KeyPress(Sender: TObject; var Key: Char);
    procedure mskLof2KeyPress(Sender: TObject; var Key: Char);
    procedure mskLofSwKeyPress(Sender: TObject; var Key: Char);
    procedure mskVideoPidChange(Sender: TObject);
    procedure mskPidFilterChange(Sender: TObject);
    procedure mskVideoPidKeyPress(Sender: TObject; var Key: Char);
    procedure mskPidFilterKeyPress(Sender: TObject; var Key: Char);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure btnSetClick(Sender: TObject);
    procedure btnSetFilterClick(Sender: TObject);
    procedure btnsetDiSEqCClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  POnePacket = ^TOnePacket;
  TOnePacket = array[0..187] of Byte;


var
  frmMain: TfrmMain;

implementation

{$R *.DFM}

uses
  DvbDirectShow,
  SyncObjs,
  Windows, Messages, SysUtils, Graphics, Dialogs,
  StreamReaderDefines;

var
  StreamReaderHandle      : THandle;
  StreamReaderCheckForDvb : TCheckForDvb;
  StreamReaderStartDVB    : TStartDvb;
  StreamReaderStopDVB     : TStopDvb;
  StreamReaderSetFilter   : TSetFilter;
  StreamReaderDelFilter   : TDelFilter;
  StreamReaderSetChannel  : TSetChannel;
  StreamReaderSendDiSEqC  : TSendDiSEqC;

  ShowVideo           : Boolean;                           // True shows video
  PidVideo            : Integer;
  FilterVideo         : Dword;
  PidAudio            : Integer;
  FilterAudio         : Dword;
  PidPmt              : Integer;
  FilterPmt           : Dword;
  FilterPat           : Dword;

  Polarity            : Integer;
  Symbolrate          : Integer;
  Frequency           : Integer;
  Lof1                : Integer;
  Lof2                : Integer;
  LofSw               : Integer;
  PidFilter           : Integer;
  
  Callbacks           : Integer;

{------------------------------------------------------------------------------
  Params  : <Data>  Pointer to stream data
  Returns : -

  Descript: Data received from the StreamReader DLL is received here
  Notes   :
 ------------------------------------------------------------------------------}
procedure StreamReaderData(Data: Pointer; Length: Integer); stdcall;
var
  Pid : Word;
  ThePacket: POnePacket;
begin
  Inc(Callbacks);
  if Callbacks > 1000000 then
    Callbacks := 0;
  // Sanity check
  if Length <> 188 then
    Exit;

  ThePacket := Data;
  // Sanity check: First byte MUST be the sync byte !!
  if ThePacket[0] <> $47 then
    Exit;
  // Get the PID
  Pid := ((ThePacket[1] and $1F) shl 8) or ThePacket[2];
  // We could do a check on the PID here (checking if it belongs to data we want to
  // send to DirectShow), but we assume all data is intended for DirectShow
  DvbDirectShow.DirectShowGraph.DirectShowPushSourceSendData(0, @ThePacket[0], 188);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Creation of form.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmMain.FormCreate(Sender: TObject);
var
  ErrorMessage: string;
  DSSuccess   : Boolean;
begin
  frmMain.mmoDriver.Lines.Add('StreamReader.dll loaded');

  if StreamReaderCheckForDvb then
    frmMain.mmoDriver.Lines.Add('StreamReader reports DVB driver detected')
  else
    frmMain.mmoDriver.Lines.Add('StreamReader reports -NO- DVB driver detected!!');
  if StreamReaderStartDvb then
    frmMain.mmoDriver.Lines.Add('StreamReader DVB started')
  else
    frmMain.mmoDriver.Lines.Add('StreamReader DVB -NOT- started!!');

  DsSuccess := DvbDirectShow.DirectShowGraph.DirectShowStart(pnlVideo.Handle, 'VIDEO.GRF', 0, 0, ErrorMessage);
  if not DsSuccess then
    frmMain.mmoDriver.Lines.Add('DirectShow error')
  else
  begin
    frmMain.mmoDriver.Lines.Add('DirectShow initialized');
    frmMain.MskVideoPidChange(nil);  // Send PID info to DirectShow
    ShowVideo := True;
  end;
  mskFrequency.OnChange(nil);
  mskSymbolrate.OnChange(nil);
  mskLof1.OnChange(nil);
  mskLof2.OnChange(nil);
  mskLofSw.OnChange(nil);
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Form destruction.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  // Note: We MUST make sure that <DSSendDataDirect> is never called when DirectShow
  //       has stopped, therefore we inform the thread
  ShowVideo := False;
  Sleep(1);

  DvbDirectShow.DirectShowGraph.DirectShowStop;
end;


{------------------------------------------------------------------------------
  Params  : <Sender>  Sender
  Returns : -

  Descript: Exit application.
  Notes   :
 ------------------------------------------------------------------------------}
procedure TfrmMain.btnExitClick(Sender: TObject);
begin
  Application.Terminate;
end;


procedure TfrmMain.rgPolarityClick(Sender: TObject);
begin
  case rgPolarity.ItemIndex of
    0: Polarity := 0;
    1: Polarity := 1;
  end;
end;


procedure TfrmMain.mskSymbolRateChange(Sender: TObject);
var
  Error:      Integer;
begin
  Val(mskSymbolRate.EditText, SymbolRate, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert symbolrate.');
    Exit;
  end;
end;


procedure TfrmMain.mskFrequencyChange(Sender: TObject);
var
  Error:     Integer;
begin
  Val(mskFrequency.EditText, Frequency, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert frequency.');
    Exit;
  end;
end;

procedure TfrmMain.mskLof1Change(Sender: TObject);
var
  Error:     Integer;
begin
  Val(mskLof1.EditText, Lof1, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert LOF1.');
    Exit;
  end;
end;

procedure TfrmMain.mskLof2Change(Sender: TObject);
var
  Error:     Integer;
begin
  Val(mskLof2.EditText, Lof2, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert LOF2.');
    Exit;
  end;
end;

procedure TfrmMain.mskLofSwChange(Sender: TObject);
var
  Error:     Integer;
begin
  Val(mskLofSw.EditText, LofSw, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert LOF switch.');
    Exit;
  end;
end;

procedure TfrmMain.mskFrequencyKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskFrequencyChange(nil);
end;

procedure TfrmMain.mskLof1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskLof1Change(nil);
end;

procedure TfrmMain.mskLof2KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskLof2Change(nil);
end;

procedure TfrmMain.mskLofSwKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskLofSwChange(nil);
end;

procedure TfrmMain.mskSymbolRateKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskSymbolrateChange(nil);
end;

procedure TfrmMain.mskVideoPidKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskVideoPidChange(nil);
end;

procedure TfrmMain.mskPidFilterKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
    mskPidFilterChange(nil);
end;

procedure TfrmMain.mskVideoPidChange(Sender: TObject);
var
  Error    : Integer;
begin
  // Remove old filters
  if FilterVideo <> $FFFFFFFF then
  begin
    StreamReaderDelFilter(FilterVideo);
    FilterVideo := $FFFFFFFF;
    mmoDriver.Lines.Add('Video filter removed');
  end;
  if FilterAudio <> $FFFFFFFF then
  begin
    StreamReaderDelFilter(FilterAudio);
    FilterAudio := $FFFFFFFF;
    mmoDriver.Lines.Add('Audio filter removed');
  end;
  if FilterPmt <> $FFFFFFFF then
  begin
    StreamReaderDelFilter(FilterPmt);
    FilterPmt := $FFFFFFFF;
    mmoDriver.Lines.Add('PMT filter removed');
  end;
  if FilterPat <> $FFFFFFFF then
  begin
    StreamReaderDelFilter(FilterPat);
    FilterPat := $FFFFFFFF;
    mmoDriver.Lines.Add('PAT filter removed');
  end;

  Val(mskVideoPid.EditText, PidVideo, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert Video PID.');
    Exit;
  end;
  Val(mskAudioPid.EditText, PidAudio, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert Audio PID.');
    Exit;
  end;
  Val(mskPmtPid.EditText, PidPmt, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert PMT PID.');
    Exit;
  end;

  // Set filters for callback with data
  StreamReaderSetFilter(PidVideo, @StreamReaderData, Ord(cbStd), 188, @FilterVideo);
  StreamReaderSetFilter(PidAudio, @StreamReaderData, Ord(cbStd), 188, @FilterAudio);
  StreamReaderSetFilter(PidPmt,   @StreamReaderData, Ord(cbStd), 188, @FilterPmt);
  StreamReaderSetFilter(0,        @StreamReaderData, Ord(cbStd), 188, @FilterPat);
  // Set DirectShow
  DvbDirectShow.DirectShowGraph.DirectShowMpeg2DemultiplexerSetNewPids(0, PidVideo, PidAudio, False);

  mmoDriver.Lines.Add(format('Video PID %4.4d set, StreamReader filter identifier %d', [PidVideo, FilterVideo]));
  mmoDriver.Lines.Add(format('Audio PID %4.4d set, StreamReader filter identifier %d', [PidAudio, FilterAudio]));
  mmoDriver.Lines.Add(format('PMT PID %4.4d set, StreamReader filter identifier %d', [PidPmt, FilterPmt]));
  mmoDriver.Lines.Add(format('PAT PID 0000 set, StreamReader filter identifier %d', [FilterPat]));
end;


procedure TfrmMain.mskPidFilterChange(Sender: TObject);
var
  Error: Integer;
begin
  Val(mskPidFilter.EditText, PidFilter, Error);
  if Error <> 0 then
  begin
    mmoDriver.Lines.Add('Could not convert filter PID.');
    Exit;
  end;
end;


procedure TfrmMain.Timer1Timer(Sender: TObject);
begin
  lblCallbacks.Caption := format('%d', [Callbacks]);
end;



{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Initialize
  Notes   :
 ------------------------------------------------------------------------------}
procedure Initialize;
begin
  ShowVideo     := False;
  FilterVideo   := $FFFFFFFF;
  FilterAudio   := $FFFFFFFF;
  FilterPmt     := $FFFFFFFF;
  FilterPat     := $FFFFFFFF;
  Callbacks     := 0;

  // Get handle to DLL and get (dynamically) the addresses of the functions we use
  StreamReaderHandle := LoadLibrary(PChar(ExtractFilepath(Application.ExeName)+'StreamReader.dll'));
  if StreamReaderHandle = 0 then
  begin
    ShowMessage(format('LoadLibrary(%s): failed',['StreamReader.dll']));
    Application.Terminate;
  end;

  StreamReaderCheckForDvb := GetProcAddress(StreamReaderHandle, 'CheckForDVB');
  if not Assigned(@StreamReaderCheckForDvb) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['CheckForDVB']));
    Application.Terminate;
  end;

  StreamReaderStartDVB := GetProcAddress(StreamReaderHandle, 'StartDVB');
  if not Assigned(@StreamReaderStartDVB) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['StartDVB']));
    Application.Terminate;
  end;

  StreamReaderStopDVB := GetProcAddress(StreamReaderHandle, 'StopDVB');
  if not Assigned(@StreamReaderStopDVB) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['StopDVB']));
    Application.Terminate;
  end;

  StreamReaderSetFilter := GetProcAddress(StreamReaderHandle, 'SetFilter');
  if not Assigned(@StreamReaderSetFilter) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['SetFilter']));
    Application.Terminate;
  end;

  StreamReaderDelFilter := GetProcAddress(StreamReaderHandle, 'DelFilter');
  if not Assigned(@StreamReaderDelFilter) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['DelFilter']));
    Application.Terminate;
  end;

  StreamReaderSetChannel := GetProcAddress(StreamReaderHandle, 'SetChannel');
  if not Assigned(@StreamReaderSetChannel) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['SetChannel']));
    Application.Terminate;
  end;

  StreamReaderSendDiSEqC := GetProcAddress(StreamReaderHandle, 'SendDiSEqC');
  if not Assigned(@StreamReaderSendDiSEqC) then
  begin
    ShowMessage(format('GetProcAddress(%s): failed', ['SendDiSEqC']));
    Application.Terminate;
  end;
end;


{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Finalize
  Notes   :
 ------------------------------------------------------------------------------}
procedure Finalize;
begin
  if Assigned(@StreamReaderDelFilter) then
  begin
    if FilterVideo <> $FFFFFFFF then
      StreamReaderDelFilter(FilterVideo);
    if FilterAudio <> $FFFFFFFF then
      StreamReaderDelFilter(FilterAudio);
    if FilterPmt <> $FFFFFFFF then
      StreamReaderDelFilter(FilterPmt);
    if FilterPat <> $FFFFFFFF then
      StreamReaderDelFilter(FilterPat);
  end;
  // Note: We should also release any filters we added 'manually' ......

   
  if Assigned(@StreamReaderStopDvb) then
    StreamReaderStopDvb;
  if StreamReaderHandle <> 0 then
    FreeLibrary(StreamReaderHandle);
end;


procedure TfrmMain.btnSetClick(Sender: TObject);
begin
  if StreamReaderSetChannel(Frequency, Symbolrate * 1000, Polarity, 0, Lof1, Lof2, LofSw) then
    mmoDriver.Lines.Add(format('Channel set to %d kHz, %d kS', [Frequency, Symbolrate]))
  else
    mmoDriver.Lines.Add(format('Failed to set channel to %d kHz, %d kS', [Frequency, Symbolrate]))
end;

procedure TfrmMain.btnSetFilterClick(Sender: TObject);
var
  Dummy: Dword;
begin
  if StreamReaderSetFilter(PidFilter, @StreamReaderData, Ord(cbStd), 188, @Dummy) then
    mmoDriver.Lines.Add(format('Added StreamReader filter PID %d using identifier %d', [PidFilter, Dummy]))
  else
    mmoDriver.Lines.Add('Error adding filter to StreamReader');
end;

procedure TfrmMain.btnSetDiSEqCClick(Sender: TObject);
var
  Error: Integer;
  Data : Integer;
begin
  Val(mskDiSEqCData.Text, Data, Error);
  if StreamReaderSendDiSEqC(rgDiSEqCType.ItemIndex, Data) then
    mmoDriver.Lines.Add('Sending DiSEqC command StreamReader success')
  else
    mmoDriver.Lines.Add('Error sending DiSEqC command StreamReader');
end;

initialization
  Initialize;


finalization
  Finalize;
end.
