我有一个 Delphi 6 应用程序,它以每秒 25 帧的速度实时将位图发送到 DirectShow DLL。DirectShow DLL 也是我的代码,也是使用 DSPACK DirectShow 组件套件在 Delphi 6 中编写的。我有一个简单的代码块,它通过位图中的每个像素修改图像的亮度和对比度,如果设置了某个标志,否则位图被推出未修改的 DirectShow DLL(推送源视频过滤器)。该代码曾经位于主应用程序中,然后我将其移至 DirectShow DLL 中。当它在主应用程序中时,它运行良好。我可以按预期看到位图中的变化。但是,既然代码驻留在 DirectShow DLL 中,它就有以下问题:
当下面的代码块处于活动状态时,DirectShow DLL 真的很慢。我有一个四核 i5,它真的很慢。我还可以看到 CPU 消耗的大幅飙升。相比之下,在主应用程序中运行的相同代码在旧的单核 P4 上运行良好。它确实在那台旧机器上明显地击中了 CPU,但视频很流畅,没有任何问题。图像尺寸仅为 352 x 288 像素。
我没有看到可见位图的预期变化。我可以跟踪 DirectShow DLL 中的代码并查看代码正确更改的每个像素的数值,但图形编辑 ActiveMovie 窗口中的可视图像看起来完全没有变化。
如果我停用我可以实时执行的代码,ActiveMovie 窗口会显示像玻璃一样光滑的视频,在 CPU 几乎没有接触的情况下完美呈现。如果我重新激活代码,视频现在真的很不稳定,可能每秒只显示 1 到 2 帧,并且在显示第一帧之前有很长的延迟,并且 CPU 会出现峰值。不完全,但比我预期的要多得多。
我尝试使用包括范围检查、溢出检查等在内的所有内容编译 DirectShow DLL,并且在运行时没有警告或错误。然后我尝试以最快的速度进行编译,但它仍然存在上面列出的完全相同的问题。确实出了点问题,我不知道是什么。请注意,我确实在修改位图之前锁定了画布,并在完成后将其解锁。如果不是因为我上面提到的“一切都在”编译运行,我会说它感觉就像每次像素计算都会引发 FPU 异常并默默吞噬,但正如我所说,没有错误或异常正在发生。
更新:我将其放在这里,以便嵌入在 Roman R 的评论之一中的解决方案清晰可见。在访问 ScanLine 属性之前我没有将PixelFormat属性设置为pf24Bit的问题。正如 Roman 建议的那样,不这样做必须使 TBitmap 代码创建位图的临时副本。一旦我添加了下面的代码行,问题就消失了,更改不可见和软页面错误都消失了。这是一个隐蔽的问题,因为受影响的唯一对象是用于访问 ScanLine 属性的指针,因为(假设)它包含指向位图临时副本的指针。这就是为什么后续的 TextOut() 调用仍然有效的原因,因为它在位图的原始副本上工作。
clip.PixelFormat := pf24bit; // The missing code line that fixes the problem.
这是我一直提到的代码块:
function IntToByte(i: Integer): Byte;
begin
if i > 255 then
Result := 255
else if i < 0 then
Result := 0
else
Result := i;
end;
// ---------------------------------------------------------------
procedure brightnessTurboBoost(var clip: TBitmap; rangeExpansionPowerOf2: integer; shiftValue: Byte);
var
p0: PByte;
x,y: Integer;
begin
if (rangeExpansionPowerOf2 = 0) and (shiftValue = 0) then
exit; // These parameter settings will not change the pixel values.
for y := 0 to clip.Height-1 do
begin
p0 := clip.scanline[y];
// Can't just do the whole buffer as a big block of bytes since the
// individual scan lines may be padded for CPU alignment.
for x := 0 to (clip.Width - 1) * 3 do
begin
if rangeExpansionPowerOf2 >= 1 then
p0^ := IntToByte((p0^ shl rangeExpansionPowerOf2) + shiftValue)
else
p0^ := IntToByte(p0^ + shiftValue);
Inc(p0);
end;
end;
end;