diff --git a/Wpf_AiSportsMicrospace/D3DFrameRenderer.cs b/Wpf_AiSportsMicrospace/D3DFrameRenderer.cs new file mode 100644 index 0000000..255c302 --- /dev/null +++ b/Wpf_AiSportsMicrospace/D3DFrameRenderer.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Interop; +using System.Windows.Media.Imaging; +using System.Windows.Media; +using System.Windows; + +namespace Wpf_AiSportsMicrospace +{ + public class D3DFrameRenderer + { + private D3DImage _d3dImage; + + public D3DFrameRenderer(D3DImage d3dImage) + { + _d3dImage = d3dImage; + } + + // 当有新帧时调用 + public void RenderFrame(byte[] jpegBuffer) + { + Task.Run(() => + { + int width, height; + var bgra = ConvertJpegToBGRA32(jpegBuffer, out width, out height); + + // 更新 UI + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + if (!_d3dImage.IsFrontBufferAvailable) return; + + if (_d3dImage.PixelWidth != width || _d3dImage.PixelHeight != height) + { + _d3dImage.Lock(); + _d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); // 先清空 + _d3dImage.Unlock(); + } + + // 用 WriteableBitmap 临时显示 + var wb = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null); + wb.Lock(); + Marshal.Copy(bgra, 0, wb.BackBuffer, bgra.Length); + wb.AddDirtyRect(new Int32Rect(0, 0, width, height)); + wb.Unlock(); + _d3dImage.Lock(); + _d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); // 暂时不绑定真实 D3D Surface + _d3dImage.Unlock(); + + // 临时用 ImageSource 显示 + FrameReady?.Invoke(wb, width, height); + })); + }); + } + + public delegate void FrameReadyHandler(ImageSource frame, int width, int height); + public event FrameReadyHandler FrameReady; + + private byte[] ConvertJpegToBGRA32(byte[] jpegBuffer, out int width, out int height) + { + using var ms = new MemoryStream(jpegBuffer); + using var bmp = new Bitmap(ms); + + width = bmp.Width; + height = bmp.Height; + + var rect = new Rectangle(0, 0, width, height); + var bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + int bytes = bmpData.Stride * height; + byte[] raw = new byte[bytes]; + Marshal.Copy(bmpData.Scan0, raw, 0, bytes); + + bmp.UnlockBits(bmpData); + return raw; + } + } +} diff --git a/Wpf_AiSportsMicrospace/MainWindow.xaml.cs b/Wpf_AiSportsMicrospace/MainWindow.xaml.cs index 46030dd..66fba8e 100644 --- a/Wpf_AiSportsMicrospace/MainWindow.xaml.cs +++ b/Wpf_AiSportsMicrospace/MainWindow.xaml.cs @@ -185,7 +185,7 @@ public partial class MainWindow : Window // UI 线程显示 Application.Current.Dispatcher.Invoke(() => { - if (videoImage == null) return; + if (videoImage == null ) return; if (_videoBitmap == null || _videoBitmap.PixelWidth != bitmap.PixelWidth ||