4

我有一个使用 NAudio 播放音频的应用程序。NAudio 的一个已知限制是每次垃圾收集器运行时,每个线程都会暂停,直到完成。

应用程序运行良好,所有 GC 都在可接受的时间内完成,并且没有卡顿。

但是我们还有一个单独的应用程序,它每秒通过 TCP 向主应用程序(带有音频播放器)发送一个缩略图。当编码为 JPEG 时,缩略图大约为 1300 字节。

这是我们目前用来解码图像的代码:

MemoryStream ms = new MemoryStream(data);
BitmapDecoder bdec = BitmapDecoder.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.Default);
BitmapSource source = bdec.Frames[0];
imgPreview.Source = source;

并编码:

JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.QualityLevel = quality;
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget));

byte[] imageArray;
using (MemoryStream outputStream = new MemoryStream())
{
    jpgEncoder.Save(outputStream);
    imageArray = outputStream.ToArray();
}

其中 RenderTarget 是具有图像内容的 RenderTargetBitmap。

现在我们每秒都在创建和丢弃一个 MemoryStream、一个 BitmapDecoder 和一个 BitmapSource。我已经注释掉了代码中的行,看起来 MemoryStream 和 BitmapDecoder 构造函数不会产生任何卡顿,但是一旦通过 Frames[0] 访问它,它就会开始卡顿。

我们也尝试过这种方法而不是 BitmapDecoder,但结果相同:

img.BeginInit();
img.StreamSource = ms;
img.EndInit();

当然有更好的方法来不断更新图像吗?

最好的方法是只发送原始图像数据,并创建一个 WriteableBitmap,它只是每秒重写一次。但是原始图像是 170 kb,是编码图像的 100 多倍,我们真的不想这样做。是否可以将 JPEG 流解码为现有字节数组或现有图像?

4

2 回答 2

4

好的,所以我找到了解决问题的方法。

我没有使用 WPF BitmapDecoder 来解码图像,而是使用 Windows 窗体位图。它是一次性的,对垃圾收集器来说更好。

所以解决方法如下:

var stream = new MemoryStream(data);
var formsBitmap = new Bitmap(stream);

var width = formsBitmap.Width;
var height = formsBitmap.Height;
if (bitmap == null || height != bitmap.PixelHeight || width != bitmap.PixelWidth)
{
    bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null);
    imgPreview.Source = bitmap;
}

BitmapData data = formsBitmap.LockBits(new Rectangle(0, 0, formsBitmap.Width, formsBitmap.Height),
                                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

try
{
    bitmap.WritePixels(new Int32Rect(0, 0, width, height), data.Scan0, data.Stride * data.Height, data.Stride);
}
finally
{
    formsBitmap.UnlockBits(data);
}

formsBitmap.Dispose();

我没有对收到的每个 JPEG 帧进行解码,而是从数据中创建了一个新的位图(来自 WinForms)。然后我只需将像素复制到我使用的 WriteableBitmap。

希望这对其他人也有帮助。

于 2013-05-31T09:12:59.700 回答
1

分析代码确认bdec.Frames[0]; 占用相对较大的 CPU 时间。查看 ILSpy 中的代码,“ Frames ”getter 有一个空实现(虚拟方法未被 JpgBitmapDecoder 子类覆盖),所以我假设那里正在进行某种对底层 Windows API 的调用(?)

底线是解码 JPG 会比 PNG 或 GIF 慢;我会尝试 PNG 编码,因为它仍然应该提供良好的压缩比,但性能更好。

于 2013-05-28T12:32:06.643 回答