Building a Basic Media Player

From Appmethod Topics
Jump to: navigation, search

Go Up to Tutorial: FireMonkey Audio-Video

This tutorial demonstrates how to build a basic media player using TMediaPlayer and TMediaPlayerControl with FireMonkey. The media player contains standard functions (play, pause, next and previous), and also offers the possibility to manipulate the current played media (volume and current position). This basic media player allows to load a set of media files into a TListBox and to start playing by double-clicking an item in the list.

Form Design

  1. Select File > New > FireMonkey Desktop Application - Object Pascal > HD FireMonkey Application.
  2. Add a TLayout to the form. With the layout in focus, in the Object Inspector, set the Align property to Left and the Name property to MainLayout. Resize the MainLayout as you consider necessary to display video images.
  3. Add the following to the MainLayout:
    • A TLayout. Name it ControlPlayLayout and align it to the bottom of the MainLayout. This layout contains all components that control the media file.
    • A TLayout. Name it TextLayout and align it to the top of the MainLayout. This layout contains the labels used to display the name and duration of the current played media.
    • A TMediaPlayerControl. Set its Align property to Client.
  4. Add the following to the ControlPlayLayout:
    • Five TButton objects, one for each standard function of a media player. Change the names of the buttons to PreviousButton, PlayButton, PauseButton, StopButton and NextButton.
    • Two TTrackBar objects: one to control the volume and one to control the current played position. Change the names of the track bars to VolumeTrackBar and PositionTrackBar. Align the PositionTrackBar to the top of the ControlPlayLayout.
    • A TCheckBox and name it ShuffleCheckBox.
  5. Add two TLabel objects to the TextLayout: one to display the name of the current played media and one to display the total duration of the current played files, measured in minutes and seconds. Name the two labels NameLabel and DurationLabel. Set the DurationLabel to be aligned to the right and its AutoSize property to True. Set the Align property of the NameLabel to Client and its text to be displayed on the center by setting its TextAlign property to Center.
  6. Add another TLayout to the form, set its Align property to Client and rename it as PlayListLayout.
  7. Add the following to the PlayListLayout:
    • A TLayout to group the buttons used to manipulate the playlist. Align the added layout to the bottom of its parent and rename it as ControlPlayListLayout.
    • A TListBox. Name it PlayListBox and set its Align property to Client.
  8. Add three TButton objects to the PlayListLayout. Name them LoadFileButton, ClearAllButton and ClearButton. Align all three buttons to the left of their parent.
  9. Add the following to the form:
    The form should look like this:
    Basic Media Player Form.png

Implementation

The implementation can be split in multiple steps:

  1. Build and manipulate the playlist
  2. Implement the basic functions of a media player
  3. Implement helpful methods
  4. Customize the current played media file

Build and manipulate the playlist

1 Double-click the LoadFileButton button to attach OnClick event handlers to it, so as to populate the PlayListBox. When LoadFileButton is pressed, the open dialog window opens. Select one or more files to be added to the list when the OK button of the open dialog window is pressed.
// Delphi version of the implementation
procedure TForm1.LoadFileButtonClick(Sender: TObject);
var
  Files: TStrings;
  I: integer;
  Media: TMedia;
begin
  Files := TStrings.Create;
  //sets the Filter so only the supported files to be displayed
  OpenDialog1.Filter := TMediaCodecManager.GetFilterString;
  if (OpenDialog1.Execute) then
  begin
    Files := OpenDialog1.Files;
    for I := 0 to Files.Count - 1 do
    begin
      Media := TMedia.Create(Files[I]);
      PlayListBox.Items.AddObject(extractFileName(Files[I]), Media);
    end;
  end;
end;
2. Double-click the ClearAllButton and ClearButton buttons to attach OnClick event handlers to them.
ClearAllButton ClearButton
// Delphi version of the implementation
procedure TForm1.ClearAllButtonClick(Sender: TObject);
var
  I: integer;
begin
  if (PlayListBox.Count > 0) then
  begin
    for I := 0 to PlayListBox.Count - 1 do
      PlayListBox.Items.Objects[I].Free;
    PlayListBox.Clear;
    PlayListBox.ItemIndex := -1;
    SetLength(Played, 0);
    CurrentPlayedIndex := -1;
    MediaPlayer1.Clear;
    NameLabel.Text := '';
    DurationLabel.Text := '';
  end;
end;
// Delphi version of the implementation
procedure TForm1.ClearButtonClick(Sender: TObject);
var
  I, J: integer;
begin
  if PlayListBox.Count < 0 then
    Exit;
  PlayListBox.Items.Objects[PlayListBox.ItemIndex].Free;
  PlayListBox.Items.Delete(PlayListBox.ItemIndex);
  J := 0;
  //makes sure that the deleted media is not on the Playlist
  while J < Length(Played) - 1 do
  begin
    if (Played[J] = PlayListBox.ItemIndex) then
    begin
      for I := J to Length(Played) - 1 do
        Played[I] := Played[I + 1];
      SetLength(Played, Length(Played) - 1);
    end;
    J := J + 1;
  end;
end;
3. To avoid keeping memory objects that are no longer used, make sure that every object added to PlayListBox is freed. When removing an item from PlayListBox, only the link between the list box item and the attached object is freed and the object remains allocated in memory.
// Delphi version of the implementation
procedure TForm1.FormDestroy(Sender: TObject);
var
  I: integer;
begin
  if (PlayListBox.Count > 0) then
    for I := 0 to PlayListBox.Count - 1 do
      PlayListBox.Items.Objects[I].Free;
end;

Implement the basic functions of a media player

To start the player press the PlayButton, the NextButton or double-click an item in the playlist. Attach an OnDblClick event handler to the PlayListBox. Double-click each button in ControlPlayLayout to attach OnClick event handlers to each of them and to implement the basic functions of a media player.

PlayButton

PlayListBoxDblClick

// Delphi version of the implementation
procedure TForm1.PlayButtonClick(Sender: TObject);
begin
  if MediaPlayer1.Media <> nil then
  begin
    if (MediaPlayer1.State = TMediaState.Stopped) and
      (MediaPlayer1.CurrentTime < MediaPlayer1.Duration) then
    begin
      MediaPlayer1.Play;
      PositionTrackBar.Max := MediaPlayer1.Duration;
      DisplayText(extractFileName(MediaPlayer1.filename));
    end
    else
    begin
      MediaPlayer1.CurrentTime := 0;
    end;
  end
  else
  begin
    if (PlayListBox.Count = 0) then
      Exit;
    If (PlayListBox.ItemIndex = -1) then
      PlayListBox.ItemIndex := 0;
    FindAndPlay(PlayListBox.ItemIndex);
    if (ShuffleCheckBox.IsChecked) then
      AddToPlayedList(PlayListBox.ItemIndex);
  end;
end;
// Delphi version of the implementation
procedure TForm1.PlayListBoxDblClick(Sender: TObject);
begin
  if (PlayListBox.Count < 0) then
    Exit;
  MediaPlayer1.Clear;
  MediaPlayer1.filename :=
    TMedia(PlayListBox.Items.Objects[PlayListBox.ItemIndex]).filename;
  if MediaPlayer1.Media <> nil then
  begin
    MediaPlayer1.Play;
    PositionTrackBar.Max := MediaPlayer1.Duration;
    DisplayText(extractFileName(MediaPlayer1.filename));
    AddToPlayedList(PlayListBox.Items.IndexOf
      (extractFileName(MediaPlayer1.filename)));
  end;
end;

PauseButton

StopButton

// Delphi version of the implementation
procedure TForm1.PauseButtonClick(Sender: TObject);
begin
  if MediaPlayer1.Media <> nil then
    MediaPlayer1.Stop;
end;
// Delphi version of the implementation
procedure TForm1.StopButtonClick(Sender: TObject);
begin
  if MediaPlayer1.Media <> nil then
  begin
    MediaPlayer1.Stop;
    MediaPlayer1.CurrentTime := 0;
  end;
end;

NextButton

PreviousButton

// Delphi version of the implementation
procedure TForm1.NextButtonClick(Sender: TObject);
var
  Index: integer;
begin
  if (PlayListBox.Count > 0) then
  begin
    PlayNext;
  end;
end;
// Delphi version of the implementation
procedure TForm1.PreviousButtonClick(Sender: TObject);
begin
  if ShuffleCheckBox.IsChecked then
  begin
    if (Length(Played) > 0) and (CurrentPlayedIndex > 0) then
    begin
      CurrentPlayedIndex := CurrentPlayedIndex - 1;
      FindAndPlay(Played[CurrentPlayedIndex]);
      PlayListBox.ItemIndex := Played[CurrentPlayedIndex];
    end;
  end
  else if (PlayListBox.ItemIndex > 0) then
  begin
    FindAndPlay(PlayListBox.ItemIndex - 1);
    PlayListBox.ItemIndex := PlayListBox.ItemIndex - 1;
  end;
end;

Implement helpful methods

1. Add the following public members to the TForm1 class:
// Delphi version of the implementation
  public
    { Public declarations }
    Played: array of integer;
    CurrentPlayedIndex: integer;
    procedure AddToPlayedList(Index: integer);
    procedure DisplayText(str: string);
    procedure PlayNext();
    procedure FindAndPlay(Index: integer);
  end;
2. Played array is used to keep the indexes of the played media files within the PlayListBox when the media player runs in Shuffle mode (ShuffleChekBox is checked). Played media is saved so as to be able to get the previous or next played media. CurrentPlayedIndex keeps the index of the current played media within Played. AddToPlayedList method adds a specified media to the Played list.
// Delphi version of the implementation
  procedure TForm1.AddToPlayedList(Index: integer);
var
  Len: integer;
begin
  Len := Length(Played);
  SetLength(Played, Len + 1);
  Played[Len] := Index;
  CurrentPlayedIndex := Len;
  PlayListBox.ItemIndex := index;
end;
3. FindAndPlay method identifies the media to be played using its index within PlayListBox and PlayNext gets the index of the media that should be played and then calls FindAndPlay to identify and play the media with the calculated index.
FindAndPlay PlayNext
// Delphi version of the implementation
procedure TForm1.FindAndPlay(Index: integer);
begin
  MediaPlayer1.Clear;
  PositionTrackBar.Value := PositionTrackBar.Min;
  MediaPlayer1.filename := TMedia(PlayListBox.Items.Objects[Index]).filename;
  if MediaPlayer1.Media <> nil then
  begin
    MediaPlayer1.Play;
    PositionTrackBar.Max := MediaPlayer1.Duration;
    DisplayText(extractFileName(MediaPlayer1.filename));
  end;
end;
// Delphi version of the implementation
procedure TForm1.PlayNext();
var
  Index: integer;
begin
  if (ShuffleCheckBox.IsChecked) then
  begin
    if (CurrentPlayedIndex < Length(Played) - 1) then
    begin
      CurrentPlayedIndex := CurrentPlayedIndex + 1;
      FindAndPlay(Played[CurrentPlayedIndex]);
    end
    else
    begin
      index := Random(PlayListBox.Count - 1);
      FindAndPlay(index);
      AddToPlayedList(index);
    end;
    PlayListBox.ItemIndex := Played[CurrentPlayedIndex];
  end
  else
  begin
    FindAndPlay(PlayListBox.ItemIndex + 1);
    PlayListBox.ItemIndex := PlayListBox.ItemIndex + 1;
  end;
end;
4. DisplayText displays the name and duration (in minutes and seconds) of the current played media.
// Delphi version of the implementation
procedure TForm1.DisplayText(str: string);
var
  DurationMin, DurationSec: integer;
begin
  NameLabel.Text := str;
  DurationMin := MediaPlayer1.Duration div 10000 div 60000;
  DurationSec := MediaPlayer1.Duration div 10000 mod 60000 div 1000;
  DurationLabel.Text := IntToStr(DurationMin) + ':' + IntToStr(DurationSec);
end;

Customize the current played media file

1. Volume

Double-click each button in VolumeTrackBar to attach OnChange event handlers to it and customize the volume of the media player.

// Delphi version of the implementation
procedure TForm1.VolumeTrackBarChange(Sender: TObject);
begin
  MediaPlayer1.Volume := VolumeTrackBar.Value;
end;
2. Current position

To update the PositionTrackBar value according to the current played position of the running media, use the TTimer on a certain interval. The timer is also used to make the media player play the next media, when the current played media ends. Double-click the PositionTrackBar bar to attach OnChange event handlers to it.

// Delphi version of the implementation
procedure TForm1.PositionTrackBarChange(Sender: TObject);
begin
  //The Tag of the track bar is used as a flag to avoid 
  //updating the MediaPlayer1.CurrentTime and the track bar
  //at the same time
  if PositionTrackBar.Tag = 0 then
    MediaPlayer1.CurrentTime := Round(PositionTrackBar.Value);
end;

Double-click TTimer to attach OnTimer event handlers to it.

// Delphi version of the implementation
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  PositionTrackBar.Tag := 1;
  PositionTrackBar.Value := MediaPlayer1.CurrentTime;
  PositionTrackBar.Tag := 0;
  //when the current media ends the player starts playing
  //the next media in playlist
  if (MediaPlayer1.State = TMediaState.Playing) and
    (MediaPlayer1.CurrentTime = MediaPlayer1.Duration) then
    PlayNext;
end;
3. Shuffle on/off

If the ShuffleCheckBox is not checked, then the next media to be played is the next media in the PlayListBox. If it is checked, the next media to be played is a random media in the PlayListBox. If the media player is not set to shuffle mode, there is no need to use Played vector, as the playing order is the same as the order in PlayListBox.

// Delphi version of the implementation
procedure TForm1.ShuffleCheckBoxChange(Sender: TObject);
begin
  If (not ShuffleCheckBox.IsChecked) then
  begin
    SetLength(Played, 0);
    CurrentPlayedIndex := -1;
  end;
end;

Run the Application

1. To run the project, press F9.
2. Load files to the PlayListBox by pressing the LoadFileButton button.
3. Start playing a media by pressing PlayButton or NextButton or by double-clicking an PlayListBox item.

Previous

See Also