Video Capturing

From RAD Studio
Jump to: navigation, search

Go Up to Tutorial: FireMonkey Audio-Video

This tutorial demonstrates how to capture and display video data and how to customize the display of video data with FireMonkey.

Form Design

  1. Select File > New > Multi-Device Application - Delphi > Blank Application.
  2. Add a TLayout to the form. With the layout in focus, in the Object Inspector, set the Align property to MostBottom.
  3. Add the following to the TLayout:
    • A TButton to start capturing and displaying data.
    • A TComboBox to choose the video capturing device that will be used, if multiple video devices connected to the station work.
  4. Change the name of the button to Start.
  5. Set Start as disabled by setting the Enable property of the button to False.
  6. Add a TImage to be used as support for displaying the captured data. Set the Align property of the image to Center, in order that the video data is displayed on the center of the form.
    The form should look like this:
    Video capturing Form Design.png

Implementation

1. Include the FMX.Media unit:

Delphi:

// Delphi version of the implementation
uses
 FMX.Media;

C++Builder:

// C++ version of the implementation
#include <FMX.Media.hpp>
2. Declare a TVideoCaptureDevice public member, named VideoCamera, to the TForm1 class:

Delphi:

type
  TForm1 = class(TForm)
   // ...............
  public
    VideoCamera: TVideoCaptureDevice;

C++Builder:

class TForm1 : public TForm
// ...............
public:
  TVideoCaptureDevice* VideoCamera;
3. To be able to display the captured data to the image, add the SampleBufferReady and SampleBufferSync procedures to the TForm1 as public members. SampleBufferReady is the event handler associated to the OnSampleBufferReady event of the capturing device. It calls Synchronize to make sure that the display of the video data on the image is executed using the main thread, thereby avoiding multithread conflicts. SampleBufferSync explicitly buffers the video data on the image.
There are the implementations for this two functions:

Delphi:

//Declaration 
  TForm1 = class(TForm)
   //.........
  public
    //........
    procedure SampleBufferSync;
    procedure SampleBufferReady(Sender: TObject; const ATime: TMediaTime);
  end;

// Implementation 
procedure TForm1.SampleBufferReady(Sender: TObject; const ATime: TMediaTime);
begin
  TThread.Synchronize(TThread.CurrentThread, SampleBufferSync);
  //Resize the image so that the video is buffered in its original size
  Image1.Width:=Image1.Bitmap.Width;
  Image1.Height:=Image1.Bitmap.Height;
end;

procedure TForm1.SampleBufferSync;
begin
  VideoCamera.SampleBufferToBitmap(Image1.Bitmap, true);
end;

C++Builder:

// Declaration
class TForm1 : public TForm
//.........
public:
  //.........
  void __fastcall SampleBufferSync();
  void __fastcall SampleBufferReady(TObject *Sender, const TMediaTime ATime);

// Implementation
void __fastcall TForm1::SampleBufferReady(TObject *Sender, const TMediaTime ATime) {
  TThread::Synchronize(TThread::CurrentThread, (TThreadMethod)&SampleBufferSync);
  // Resize the image so that the video is buffered in its original size
  Image1->Width = Image1->Bitmap->Width;
  Image1->Height = Image1->Bitmap->Height;
}

void __fastcall TForm1::SampleBufferSync() {
  VideoCamera->SampleBufferToBitmap(Image1->Bitmap, true);
}
4. Populate the combo box the OnCreate event of the form with a list of all video devices connected to the station. Do this by using the GetDevicesByMediaType method. Access the method through Current property of the capturing device manager. This is the implementation:

Delphi:

procedure TForm1.FormCreate(Sender: TObject);
var
  DeviceList: TCaptureDeviceList;
  i: integer;
begin
  DeviceList := TCaptureDeviceManager.Current.GetDevicesByMediaType
    (TMediaType.Video);
  for i := 0 to DeviceList.Count - 1 do
  begin
    ComboBox1.Items.Add(DeviceList[i].Name);
  end;
end;

C++Builder:

void __fastcall TForm1::FormCreate(TObject *Sender) {
  TCaptureDeviceList DeviceList;
  int i;

  DeviceList = TCaptureDeviceManager::Current->GetDevicesByMediaType
	(TMediaType::Video);
  for (i = 0; i < DeviceList->Count; i++) {
    ComboBox1->Items->Add(DeviceList->Items[i]->Name);
  }
}
5. On the OnChange event of the combo box, initialize VideoCamera by identifying the video device by name.

Delphi:

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
  VideoCamera := TVideoCaptureDevice
    (TCaptureDeviceManager.Current.GetDevicesByName(ComboBox1.Selected.Text));
  if (VideoCamera <> nil) then
  begin
    StartButton.Enabled := true;
  end;
end;

C++Builder:

void __fastcall TForm1::ComboBox1Change(TObject *Sender) {
  VideoCamera = dynamic_cast<TVideoCaptureDevice*>
	(TCaptureDeviceManager::Current->GetDevicesByName(ComboBox1->Selected->Text));
  if (VideoCamera != NULL) {
    StartButton->Enabled;
  }
}
6. Double-click the Start button to attach OnClick event handlers to it. When the capturing process s running, the Start button is used to stop the process.

Delphi:

procedure TForm1.StartButtonClick(Sender: TObject);
begin
  if (VideoCamera <> nil) then
  begin
    if (VideoCamera.State = TCaptureDeviceState.Stopped) then
    begin
      VideoCamera.OnSampleBufferReady := SampleBufferReady;
      VideoCamera.StartCapture;
      StartButton.Text := 'Stop';
    end
    else
    begin
      VideoCamera.StopCapture;
      StartButton.Text := 'Start';
    end;
  end
  else
  begin
    Caption := 'Video capture devices not available.';
  end;
end;

C++Builder:

void __fastcall TForm1::StartButtonClick(TObject *Sender) {
  if (VideoCamera != NULL) {
    if (VideoCamera->State == TCaptureDeviceState::Stopped) {
      VideoCamera->OnSampleBufferReady = SampleBufferReady;
      VideoCamera->StartCapture();
      StartButton->Text = "Stop";
    }
    else {
      VideoCamera->StopCapture();
      StartButton->Text = "Start";
    }
  }
  else
    Caption = "Video capture devices not available.";
}
7. Make sure that the capturing device stops capturing if the form is closed when the capturing process is still working by calling the StopCapture method when the form is destroyed.

Delphi:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if VideoCamera <> nil then
    VideoCamera.StopCapture;
end;

C++Builder:

void __fastcall TForm1::FormDestroy(TObject *Sender) {
  if (VideoCamera != NULL) {
    VideoCamera->StopCapture();
  }
}

Run the Application

1. To run the project, press F9.
2. Choose a video device from the combo box.
3. To start capturing data, press the Start button. The captured data will be displayed in the middle of the form.

Customize the captured image

In the next step, you can apply FireMonkey effects over the captured image in the current form.

1. To be able to choose different effects, add another TComboBox to the layout added above. Set it as being disabled by setting the Enable property of the button to False. It will be enabled when the capturing device starts working.
2. Populate the added combo box with the currently available FireMonkey effects using the TFilterManager. To have access to effects and filter, include the FMX.Filter and FMX.Filter.Effects units.

Delphi:

uses
 FMX.Media,FMX.Filter,FMX.Filter.Effects ;

C++Builder:

#include <FMX.Filter.hpp>
#include <FMX.Filter.Effects.hpp>
3. Add as public members a TImageFXEffect component, named Effect, and a FillComboxEffect procedure. TImageFXEffect is the base class for all FireMonkey image effects and Effect is used to keep the effect currently applied.
The FillComboxEffect procedure:
  • Gets the list of currently available filters.
  • Extracts the name of the filters.
  • Builds the name of the effects from the filter's names (each filter has defined an effect.) You can see all effects in the Tool Palette, under Effects Category, or in the FMX.Filter.Effects unit.
  • Adds the effect in the combo box list.
This is the implementation:

Delphi:

procedure TForm1.FillComboxEffect();
var
  i, j, pos: integer;
  FiltersCategoryList, FilterList: TStrings;
  str: string;
  filterClass: TFilterClass;
begin
  FiltersCategoryList := TStringList.Create;
  TFilterManager.FillCategory(FiltersCategoryList);
  for j := 0 to FiltersCategoryList.Count - 1 do
  begin
    FilterList := TStringList.Create;
    TFilterManager.FillFiltersInCategory(FiltersCategoryList[j], FilterList);
    i := 0;
    for i := 0 to FilterList.Count - 1 do
    begin
      str := FilterList[i];
      pos := AnsiPos('Filter', str);
      if (pos <> 0) then
        Delete(str, pos, 6);
      str := 'T' + str + 'Effect';
      ComboBox2.Items.Append(str);
    end;
    FilterList.Free;
  end;
  FiltersCategoryList.Free;
  ComboBox2.Items.Add('None');
  ComboBox2.ItemIndex:=ComboBox2.Items.IndexOf('None');
end;

C++Builder:

void __fastcall TForm1::FillComboxEffect() {
  int i, j, pos;
  TStrings *FiltersCategoryList, *FilterList;
  String str;
  TFilterClass filterClass;

  FiltersCategoryList = new TStringList();
  TFilterManager::FillCategory(FiltersCategoryList);
  for (j = 0; j < FiltersCategoryList->Count; j++) {
    FilterList = new TStringList();
    TFilterManager::FillFiltersInCategory
          	(FiltersCategoryList->operator[](j), FilterList);
    i = 0;
    for (i = 0; i < FilterList->Count; i++) {
      str = FilterList->operator[](i);
      pos = AnsiPos("Filter", str);
      if (pos != 0) {
	str.Delete(pos, 6);
      }
      str = "T" + str + "Effect";
      ComboBox2->Items->Append(str);
    }
    FilterList->Free();
  }
  FiltersCategoryList->Free();
  ComboBox2->Items->Add("None");
  ComboBox2->ItemIndex = ComboBox2->Items->IndexOf("None");
}
4. Call FillComboxEffect from the OnCreate event handler of the form.
5. Set the effect combo box as enabled from the OnChange event handler of the first combo box (the one used to choose the video device).
6. Add an OnChange event for the effect combo box, to apply the currently selected effect over the image. This is the implementation:

Delphi:

procedure TForm1.ComboBox2Change(Sender: TObject);
var
  filterClass: TFmxObjectClass;
begin
  if (ComboBox2.ItemIndex <> -1) then
  begin
    if Assigned(Effect) then
      Effect.Free;
    if (ComboBox2.Selected.Text <> 'None') then
    begin
      filterClass := TFmxObjectClass(FindClass(ComboBox2.Selected.Text));
      if Assigned(filterClass) then
      begin
        Effect := TImageFXEffect(filterClass.Create(self));
        Effect.Parent := Image1;
      end;
    end;
  end;
end;

C++Builder:

void __fastcall TForm1::ComboBox2Change(TObject *Sender) {
  TFmxObjectClass filterClass;

  if (ComboBox2->ItemIndex != -1) {
    if (Effect != NULL) {
      Effect->Free();
    }
    if (ComboBox2->Selected->Text != "None") {
      filterClass = TFmxObjectClass(FindClass(ComboBox2->Selected->Text));
      if (filterClass != NULL) {
	Effect = dynamic_cast<TImageFXEffect *>(filterClass->InitInstance(this));
	Effect->Parent = Image1;
      }
    }
  }
}
7. Run the project, start capturing, and observe how the effects are applied while the video data is captured and displayed.

Next

See Also