Visualizzazione video veloce WPF

Sto lavorando a un’applicazione WPF che deve visualizzare diversi flussi video con una frequenza fotogrammi elevata (vorremmo 30 fps). I flussi video sono frame grezzi (RGB24) di 1920×1080 (sono memorizzati in un System.Drawing.Bitmap). Qualcuno ha qualche idea su come raggiungere questo objective?

Più dettagli:

  • I nostri tentativi precedenti hanno utilizzato un controllo immagine WPF standard, cambiando la sua sorgente per ciascun fotogramma. Questo ha funzionato bene per un singolo stream ma ora che dobbiamo rendere più stream, sta rallentando.
  • Abbiamo anche provato a utilizzare Direct2D per gestire il disegno, utilizzando una superficie condivisa D3D9 come origine per un controllo Image. Mentre questo è stato più veloce, non siamo ancora in grado di ottenere un 30fps stabile da esso (salta tra 24-32 fps al momento del backup).
  • Il stream video è in arrivo su un thread in background, quindi viene eseguito il marshalling (utilizzando il Dispatcher della finestra) sul thread dell’interfaccia utente appropriato per il disegno. Tutto il disegno viene quindi eseguito sul thread dell’interfaccia utente. Abbiamo anche provato a dare a ciascuna finestra il proprio thread.

Posso fornire esempi di codice di ciò che abbiamo provato se qualcuno vuole vedere.

Grazie!

Chiunque stia cercando una soluzione, abbiamo scritto un controllo Winforms personalizzato utilizzando DirectX 11 con una copia altamente ottimizzata nella memoria grafica, quindi ha ospitato il controllo in WinformsHost , posso fornire del codice a chiunque sia interessato.

Copia ottimizzata nella memoria gpu

  // Using native memcpy for the fastest possible copy [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] private static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count); ///  /// Copies a bitmap to gpu memory ///  /// The image to copy to the gpu /// A texture in gpu memory for the bitmap public Texture2D CopyFrameToGpuMemory(Bitmap frame) { SampleDescription sampleDesc = new SampleDescription(); sampleDesc.Count = 1; sampleDesc.Quality = 0; Texture2DDescription texDesc = new Texture2DDescription() { ArraySize = 1, MipLevels = 1, SampleDescription = sampleDesc, Format = Format.B8G8R8A8_UNorm, CpuAccessFlags = CpuAccessFlags.Write, BindFlags = BindFlags.ShaderResource, Usage = ResourceUsage.Dynamic, Height = frame.Height, Width = frame.Width }; Texture2D tex = new Texture2D(Device, texDesc); Surface surface = tex.AsSurface(); DataRectangle mappedRect = surface.Map(SlimDX.DXGI.MapFlags.Write | SlimDX.DXGI.MapFlags.Discard); BitmapData pixelData = frame.LockBits(new Rectangle(0, 0, frame.Width, frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb); unsafe //!!! { byte* pixelDataStart = (byte*)pixelData.Scan0.ToPointer(); byte* mappedRectDataStart = (byte*)mappedRect.Data.DataPointer.ToPointer(); for (int y = 0; y < texDesc.Height; y++) { byte* lineLocationDest = mappedRectDataStart + (y * mappedRect.Pitch); byte* lineLocationSrc = pixelDataStart + (y * pixelData.Stride); // Copy line by line for best performance memcpy((IntPtr)lineLocationDest, (IntPtr)lineLocationSrc, (UIntPtr)(texDesc.Width * 4)); } } //!!! frame.UnlockBits(pixelData); mappedRect.Data.Dispose(); surface.Unmap(); surface.Dispose(); return tex; }