【问题标题】:how to calculate the Framerate and Timecode during screen capturing?如何在屏幕捕获期间计算帧率和时间码?
【发布时间】:2012-12-11 05:53:18
【问题描述】:

Delphi 6 项目

我已经非常彻底地搜索了谷歌,但没有找到我的 delima 的答案。基本上我希望在我的应用程序、状态栏或标签中显示当前屏幕捕获会话的时间码和视频帧速率。我还需要将捕获同步到播放视频的软件播放器的帧速率,否则我会得到很多重复或丢失的帧。视频为 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.

【问题讨论】:

标签: delphi sync screen-capture timecodes


【解决方案1】:

正如所承诺的,这是我根据一些谷歌搜索并在 delphi 中解决的代码。以下链接确实帮助了我一些(但由于 c/c++/c# 我无法轻松地转换为 delphi),所以大部分最终答案都是基于大量的试验和错误:

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

据我所知,该例程完美无缺。但你知道,我喜欢我的数字格式是为了间隔的目的,所以我填充到 2 位数字,这样当数字超过 59 时,就不会来回收缩。

它是如何工作的:

  1. 它根据视频源的帧速率计算时间码(即 29.970 隔行或逐行,23.976 用于 24p 电影).. 所以只需输入帧号,函数就会以字符串格式返回时间码。

示例准备/使用:

  1. 在您的表单上放置两个 Tedit 和一个 Tbutton 控件1
  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.

【讨论】:

    【解决方案2】:

    实际上挂钩正在播放视频的软件并与之同步,我不知道该怎么做。但在时间上工作可能会有所帮助。假设播放视频的软件也适时,应该可以顺利抓拍。

    本教程很有用:http://www.codeproject.com/Articles/1236/Timers-Tutorial

    “多媒体计时器”提供良好的分辨率(在大多数机器上低至 1 毫秒),我发现它们很可靠。

    我会尝试使用性能计时器(queryperformancetimer,正如您已经提到的)来为您的“CaptureWindow”过程计时。然后,当您在多媒体计时器中调用“timesetevent”时,从单帧的总时间中减去捕获所花费的时间,并将其用作“uDelay”值。

    HowLongTimerShouldWait := LengthOfASingleFrame - TimeSpentCapturingPreviousFrame
    

    多媒体计时器的好处在于,它们让您可以将其用作“一次性”,其中每个间隔都可以有不同的延迟时间。我通常将计时器设置为递归调用单个过程,直到它被标记为停止。

    这样,通过一些微调,您应该能够获得在实际视频 FPS +/-1ms 容差范围内的捕获率。

    【讨论】:

    • 我不确定将测试(计时)代码放在哪里,所以我从看起来合乎逻辑的地方开始,在过程捕获窗口中;常规。但我不确定如何在捕获时将其设置为输出 fps 或 ms。我正在尝试查看 bitblt() 的上限有多快……不确定是 fps、ms 还是微秒。
    • 如果你想使用 TimeGetTime,那是以毫秒为单位的。因此,按照现在的设置方式,Finish = MillisecondsToPerformBlit。要获得 FPS,您必须测量调用 CaptureWindow 例程之间的时间量,然后计算平均值。
    • 我认为我正在寻找的是计算中间帧。对于 29.970 帧速率,每帧假设有 33367 毫秒的距离,对于 23.976(电影)帧速率,假设有 41708 毫秒。
    • 让我从上一篇文章中纠正自己..我认为我正在寻找的是计算中间帧:对于 29.970 帧速率,每帧假设为 33367 毫秒,对于帧速率为 41708 毫秒23.976(电影)帧速率。我提到的中间值,(我相信)每个 excel 计算显示距离为 0.041 毫秒。计算是基于我在 excel 中进行的时间码计算。
    • 给定以下帧的时间码:1 [0:00:00.000], 2 [0:00:00.042], 3 [0:00:00.083], ... 那么 0.041ms 是我需要在 timegettime 例程中计算的距离,这是我遇到问题的地方。
    猜你喜欢
    • 1970-01-01
    • 2014-06-13
    • 2013-04-22
    • 1970-01-01
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    相关资源
    最近更新 更多