1

Delphi 6 プロジェクト

私はかなり徹底的にグーグルを検索しましたが、デリマへの答えが見つかりません. 基本的に、現在のスクリーン キャプチャ セッションのタイムコードとビデオ フレームレートをアプリのステータスバーまたはラベルに表示したいと考えています。ビデオを再生しているソフトウェアプレーヤーのフレームレートにキャプチャを同期することに関してもこれが必要です。そうしないと、多くの重複または欠落したフレームが発生します。ビデオは 29.970 および 23.976 fps です。したがって、どういうわけか、両方を構成できる必要があります。

現在、テレビ カードや、vlc、ffplay、mplayer、virtualdub などのソフトウェア ビデオ プレーヤーからスクリーン キャプチャを行うことができます。

必要なルーチンを私のものに実装する方法がわかりません。私は以下の項目について多くのことを読んできましたが、何度も試してみましたが、頭の中がいっぱいです。

  1. timer1 コントロール -- 間隔を 34 に設定するのは正確ではありません。スクリーン キャプチャ中にフレームが重複または欠落します。
  2. gettimetick と timegettime
  3. timeBeginPeriod と timeEndPeriod
  4. QueryPerformanceTimer と QueryPerformanceCounter

プロセスを簡略化するために、元のプロジェクトの多くのコードを切り取って、画面キャプチャのみを取り上げました。以下は、このための完全なルーチンです (いくつかの注目された実験的コードと共に):

(事前に助けてくれてありがとう)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, mmsystem,
  ExtCtrls, clipbrd, DXClass;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Panel1: TPanel;
    m1: TMemo;
    btnCapOnOff: TButton;
    txtHandle: TEdit;
    Edit2: TEdit;
    stDataRate: TStaticText;
    btnCopy: TButton;
    btnSetHDC: TButton;
    dxt1: TDXTimer;
    sb1: TScrollBox;
    Splitter1: TSplitter;
    im1: TImage;
    procedure btnCapOnOffClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure capturewindow;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnCopyClick(Sender: TObject);
    procedure btnSetHDCClick(Sender: TObject);
    procedure dxt1Timer(Sender: TObject; LagCount: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  start,
  finish : cardinal; //int64;
  i : integer;
  s : string;
  bm: tbitmap;
  dc: hdc=0;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  form1.DoubleBuffered:=true;
  sb1.DoubleBuffered:=true; // this is a scrollbox control
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  im1.Picture.Bitmap.PixelFormat:=pf24bit;
  im1.Width:=352;
  im1.Height:=240;
end;

procedure TForm1.btnSetHDCClick(Sender: TObject);
begin
  if dc=0 then dc := getdc(strToint(txtHandle.text));
end;

procedure TForm1.capturewindow;
begin
  //timeBeginPeriod(1);
  start := timegettime;
  //sleep(1);
  bitblt(bm.canvas.Handle, 0,0, 352,240, dc, 0,0, srccopy);
  finish := timegettime-start;
  //m1.lines.Add(intTostr(finish)); // debugging: to spill out timing values, etc.
  im1.Picture.Bitmap := bm;
  stDataRate.Caption := 'Date Rate: '+intTostr(finish) + ' fps or ms';
end;

procedure TForm1.dxt1Timer(Sender: TObject; LagCount: Integer);
begin
  capturewindow;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
//  capturewindow; // timer1 is too slow or unpredictable
end;

// button: a cheeters way to turn On or Off capturing
procedure TForm1.btnCapOnOffClick(Sender: TObject);
begin
  if btnCapOnOff.caption='Cap is Off' then begin
    btnCapOnOff.caption:='Cap is On';
    //timer1.Enabled:=true; // capture the window // too slow
    dxt1.Enabled:=true;   // capture the window // a better timer control component (delphiX)

  end else begin
    btnCapOnOff.Caption:='Cap is Off';
    //timer1.Enabled:=false; // too slow
    dxt1.Enabled:=false; // stop capturing the window // a better timer control component (delphiX)
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  bm.free;
  releaseDC(dc,dc);
  //timeEndPeriod(1);
end;

procedure TForm1.btnCopyClick(Sender: TObject);
begin
  clipboard.assign(im1.picture.bitmap); // to take quick pics
end;

initialization
  bm := tbitmap.Create;
  bm.PixelFormat:=pf24bit;
  bm.Width:=352;
  bm.Height:=240;  beep;
end.
4

2 に答える 2

2

ビデオを再生しているソフトウェアを実際に接続して同期する方法はわかりません。しかし、タイミングに取り組むことは役立つかもしれません。ビデオを再生するソフトウェアのタイミングも適切であると仮定すると、スムーズにキャプチャできるはずです。

このチュートリアルは役に立ちます: http://www.codeproject.com/Articles/1236/Timers-Tutorial

「マルチメディア タイマー」は優れた分解能 (ほとんどのマシンで 1 ミリ秒まで) を提供し、信頼できることがわかりました。

私が試みることは、「CaptureWindow」手順の時間を計るために Performance Timer (既に述べたように queryperformancetimer) を使用することです。次に、マルチメディア タイマーで「timesetevent」を呼び出すときに、単一フレームの全体の時間からキャプチャにかかった時間を減算し、それを「uDelay」値として使用します。

HowLongTimerShouldWait := LengthOfASingleFrame - TimeSpentCapturingPreviousFrame

マルチメディア タイマーの優れた点は、各間隔に異なる遅延期間を設定できる「ワン ショット」として使用できることです。私は通常、停止するようにフラグが立てられるまで、単一のプロシージャを再帰的に呼び出すようにタイマーを設定しました。

このように、少し微調整することで、キャプチャ レートを実際のビデオ FPS の +/-1ms 許容範囲内に収めることができるはずです。

于 2012-12-25T04:57:13.657 に答える
0

約束どおり、これは私がいくつかのグーグル検索に基づいて思いついたコードであり、デルファイでそれらを解決しています。ただし、次のリンクは私を助けてくれました(ただし、c/c++/c# のため、デルファイに簡単に変換できませんでした)。最終的な回答のほとんどは、多くの試行錯誤に基づいていました。

  1. http://www.andrewduncan.ws/Timecodes/Timecodes.html
  2. http://puredata.hurleur.com/sujet-990-framenumber-timecode-conversion

私の知る限り、ルーチンは完璧に機能します。しかし、ご存知のように、私は間隔を空ける目的でフォーマットされた数字が好きなので、2 桁にパディングしました。このようにして、数字が 59 を過ぎても前後に縮小することはありません。

仕組みは次のとおりです。

  1. ビデオ ソースのフレーム レート (つまり、29.970 インターレースまたはプログレッシブ、および 24p フィルムの場合は 23.976) に基づいてタイムコードを計算します。フレーム番号を入力するだけで、関数はタイムコードを文字列形式で返します。

調製・使用例:

  1. form1 に 2 つの Tedit コントロールと 1 つの Tbutton コントロールを配置します。
  2. button1 onClick イベントで、次のように入力します: edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
  3. 次に、プログラムを実行し、最初の edit1.text にフレーム番号を入力します
  4. 次に、button1 コントロールを押すと、edit2.text のタイムコードが計算されます。

タイムコードを計算するソースコード:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function FrameNo2Timecode(fn: longint; rate: real): string;
var
  hours,mins,secs,milli: extended;
  hoursStr, minsStr, secsStr, milliStr: string;
function padzero(N: longint; Len: Integer): string;
begin
  FmtStr(Result, '%d', [N]);
  while Length(Result) < Len do
    Result := '0' + Result;
end;
begin
    hours := floor( (fn/rate)/3600) mod 60;
    hoursStr := padzero(floor(hours),2);
    mins  := floor( (fn/rate)/60.0) mod 60;
    minsstr  := padzero(floor(mins),2);
    secs  := floor( (fn/rate)) mod 60;
    secsstr  := padzero(floor(secs),2);
    milli := floor( (1000*fn/rate)) mod 6000 mod 1000;
    millistr := padzero(floor(milli),3);
    result := hoursStr +':'+ minsStr  +':'+ secsStr  +'.'+ milliStr;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  edit2.text := frameNo2timecode(strToint(edit1.text), 29.970);
end;

end.
于 2012-12-26T04:56:08.820 に答える