LoginSignup
2
0

More than 1 year has passed since last update.

MediaFoundationAPIをMfPackを通じてDelphiから使ってみる

Posted at

はじめに

前回

古い方法で連続したビットマップをAVIファイルにする方法が古くて今は使えないのを記事にした

今回はMediaFoundationを使用して動画ファイルmp4から静止画を取り出す方法をやってみたいと思う

MediaFoundationとは

20年ぐらいにあったDirectShowの後継だそうです
※記事を書きながら調べてプログラムを書いてる
APIの数も多くて複雑なのでDelphiから使えるようになっているMfPackからソースファイルをダウンロードして使うのが便利そうです

サンプル

フォームにボタン、エディット、スクロールバー、イメージを配置して、ボタンを押すと動画ファイルを読み込んでスクロールバーでシークしたフレームの映像をイメージにコピーします

sample.pas
unit MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,FileCapture, Vcl.StdCtrls,System.TimeSpan,
  Vcl.ExtCtrls;

type
  TFormMain = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    ScrollBar1: TScrollBar;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ScrollBar1Change(Sender: TObject);
  private
    { Private 宣言 }
    FCapture      : TFileCapture;
    FFrequency    : int64;
    FCaptureStart : int64;
    procedure VideoOpen(const Filename : string);
    procedure VideoFrameRequest(const Position : Integer);
    procedure OnFrameFound(ABitmap: TBitmap;ATimeStamp: TTimeSpan);
  public
    { Public 宣言 }
  end;

var
  FormMain: TFormMain;

implementation

uses System.Math,WinApi.ComBaseApi,WinApi.ActiveX.ObjBase,FileCapture.Asynchronous,
  FileCapture.Synchronous, WinApi.MediaFoundationApi.MfApi,
  WinApi.MediaFoundationApi.MfUtils;


{$R *.dfm}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  CoInitializeEx(Nil,
                 COINIT_APARTMENTTHREADED);
  FCapture :=  TFileCaptureASync.Create;
  FCapture.OnFrameFound := OnFrameFound;

  MFStartup(MF_VERSION, 0);
end;

procedure TFormMain.FormDestroy(Sender: TObject);
begin
  FCapture.Free;
end;

procedure TFormMain.OnFrameFound(ABitmap: TBitmap; ATimeStamp: TTimeSpan);
var
  iCaptureEnd: int64;

begin
  QueryPerformanceCounter(iCaptureEnd);
  try
    Image1.Picture.Bitmap.Assign(ABitmap);
  finally
    FreeAndNil(ABitmap);
  end;
end;

procedure TFormMain.ScrollBar1Change(Sender: TObject);
begin
  VideoFrameRequest(ScrollBar1.Position);
end;

procedure TFormMain.VideoFrameRequest(const Position: Integer);
var
  span: TTimeSpan;
begin
  if FCapture.SourceOpen then
    begin
      span := TTimeSpan.Create(0, 0,Position);

      QueryPerformanceFrequency(FFrequency);
      QueryPerformanceCounter(FCaptureStart);

      FCapture.MaxFramesToSkip := 200;
      FCapture.Accuracy := 120;

      FCapture.RequestFrame(span);
    end;
end;

procedure TFormMain.VideoOpen(const Filename : string);
var
  oPreviousRounding: TRoundingMode;
  f : Boolean;
begin
  if not FileExists(Filename) then exit;

  f := FCapture.OpenSource(Filename);

  if f then begin
      Edit1.Text := FCapture.URL;
      ScrollBar1.Position := 0;

      oPreviousRounding := GetRoundMode;
    try
      SetRoundMode(rmDown);
      ScrollBar1.Max := Round(FCapture.Duration.TotalSeconds);
    finally
      SetRoundMode(oPreviousRounding);
    end;
  end;
end;

procedure TFormMain.Button1Click(Sender: TObject);
begin
  // 動画ファイルをフルパスで指定
  VideoOpen('');
end;

end.

注意点

あまり巨大なファイルを使うとメモリ消費がすごくて落ちるかもしれません、それとシークを繰り返す度にメモリが消費されます。
タスクバーなどでメモリ使用量を確認しながら動作確認してください
フレームの画像を要求する処理でメモリが解放されていないようです
MfPackのサンプルでも同じ現象になります

結論

メモリの問題を抜きにしても1フレーム取り出すのに0.5秒かかっているので使うのは厳しい、MediaFoundation自体の再生機能はDirectShowの後継だけあってスムーズに再生できるので、再生とフレームの取り出しはまた違うのだろう

さらに別の方法が必要そうだ

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0