/// <summary> /// Copies a region of the framebuffer into a bitmap. /// </summary> /// <param name="source">The framebuffer to read.</param> /// <param name="sourceRectangle">The framebuffer region to copy.</param> /// <param name="target">The bitmap to copy into.</param> /// <param name="targetX">The leftmost X coordinate of the bitmap to draw to.</param> /// <param name="targetY">The topmost Y coordinate of the bitmap to draw to.</param> public unsafe static void CopyFromFramebuffer(VncFramebuffer source, VncRectangle sourceRectangle, Bitmap target, int targetX, int targetY) { Throw.If.Null(source, "source").Null(target, "target"); if (sourceRectangle.IsEmpty) { return; } var winformsRect = new Rectangle(targetX, targetY, sourceRectangle.Width, sourceRectangle.Height); var data = target.LockBits(winformsRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb); try { fixed(byte *framebufferData = source.GetBuffer()) { VncPixelFormat.Copy((IntPtr)framebufferData, source.Stride, source.PixelFormat, sourceRectangle, data.Scan0, data.Stride, new VncPixelFormat()); } } finally { target.UnlockBits(data); } }
/// <summary> /// 将需要更新位图的区域复制到framebuffer中 /// </summary> /// <param name="source">图片源.</param> /// <param name="sourceRectangle">要复制的位图区域.</param> /// <param name="target">目标帧缓冲区</param> /// <param name="targetX">帧缓冲区的左顶点X坐标</param> /// <param name="targetY">帧缓冲区的左顶点Y坐标</param> public unsafe static void CopyToFramebuffer(Bitmap source, VncRectangle sourceRectangle, VncFramebuffer target, int targetX, int targetY) { Throw.If.Null(source, "source").Null(target, "target"); if (sourceRectangle.IsEmpty) { return; } var winformsRect = new Rectangle(sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height); var data = source.LockBits(winformsRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb); try { fixed(byte *framebufferData = target.GetBuffer()) { VncPixelFormat.Copy(data.Scan0, data.Stride, new VncPixelFormat(32, 24, 8, 16, 8, 8, 8, 0), sourceRectangle, (IntPtr)framebufferData, target.Stride, target.PixelFormat, targetX, targetY); } } finally { source.UnlockBits(data); } }
/// <summary> /// Copies a region of a bitmap into the framebuffer. /// </summary> /// <param name="source">The bitmap to read.</param> /// <param name="sourceRectangle">The bitmap region to copy.</param> /// <param name="target">The framebuffer to copy into.</param> /// <param name="targetX">The leftmost X coordinate of the framebuffer to draw to.</param> /// <param name="targetY">The topmost Y coordinate of the framebuffer to draw to.</param> public static unsafe void CopyToFramebuffer( Bitmap source, VncRectangle sourceRectangle, VncFramebuffer target, int targetX, int targetY) { if (source == null) { throw new ArgumentNullException(nameof(source)); } if (target == null) { throw new ArgumentNullException(nameof(target)); } if (sourceRectangle.IsEmpty) { return; } var winformsRect = new Rectangle(sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height); var data = source.LockBits(winformsRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb); try { fixed(byte *framebufferData = target.GetBuffer()) { VncPixelFormat.Copy( data.Scan0, data.Stride, new VncPixelFormat(), sourceRectangle, (IntPtr)framebufferData, target.Stride, target.PixelFormat, targetX, targetY); } } finally { source.UnlockBits(data); } }
public VncFramebuffer Capture() { lock (_lock) { using (var bmpRef = Window.GetLastRenderedFrame()) { if (bmpRef?.Item == null) { return(_framebuffer); } var bmp = bmpRef.Item; if (bmp.PixelSize.Width != _framebuffer.Width || bmp.PixelSize.Height != _framebuffer.Height) { _framebuffer = new VncFramebuffer("Avalonia", bmp.PixelSize.Width, bmp.PixelSize.Height, VncPixelFormat.RGB32); } using (var fb = bmp.Lock()) { var buf = _framebuffer.GetBuffer(); if (_framebuffer.Stride == fb.RowBytes) { Marshal.Copy(fb.Address, buf, 0, buf.Length); } else { for (var y = 0; y < fb.Size.Height; y++) { var sourceStart = fb.RowBytes * y; var dstStart = _framebuffer.Stride * y; var row = fb.Size.Width * 4; Marshal.Copy(new IntPtr(sourceStart + fb.Address.ToInt64()), buf, dstStart, row); } } } } } return(_framebuffer); }
/// <summary> /// Asynchronously writes framebuffer packages to a <see cref="Stream"/>. /// </summary> /// <param name="stream"> /// The <see cref="Stream"/> to which to write. /// </param> /// <param name="cancellationToken"> /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation. This method /// will keep recording until cancellation is requested. /// </param> /// <returns> /// A <see cref="Task"/> which represents the asynchronous operation. /// </returns> public async Task WriteAsync(Stream stream, CancellationToken cancellationToken) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } VncFramebuffer framebuffer = this.framebufferSource.Capture(); // The RIFF header, wraps the entire file. await stream.WriteStructAsync( new List() { ListType = FourCC.Riff, FourCC = FourCC.Avi, Size = this.ExpectedSize, }, cancellationToken).ConfigureAwait(false); // HDRL list. Defines the format of the data and is the first required LIST chunk. await stream.WriteStructAsync( new List() { ListType = FourCC.List, FourCC = FourCC.Hdrl, Size = 0x449c, // TBC }, cancellationToken).ConfigureAwait(false); // AVIH chunk. Contains the main AVI header. await stream.WriteStructAsync( new Chunk() { FourCC = FourCC.Avih, Size = Unsafe.SizeOf <AviMainHeader>(), }, cancellationToken).ConfigureAwait(false); await stream.WriteStructAsync( new AviMainHeader() { Flags = MainHeaderFlags.HasIndex, Height = framebuffer.Height, Width = framebuffer.Width, MaxBytesPerSec = 1448660, // TBC MicroSecPerFrame = this.MicrosecondsPerFrame, Streams = 1, SuggestedBufferSize = 0x046bc4, // TBC TotalFrames = this.ExpectedTotalFrames, }, cancellationToken).ConfigureAwait(false); // STRL list. Contains informatio about a stream. await stream.WriteStructAsync( new List() { ListType = FourCC.List, FourCC = FourCC.Strl, Size = 17220, }, cancellationToken).ConfigureAwait(false); // STRH chunk await stream.WriteStructAsync( new Chunk() { FourCC = FourCC.Strh, Size = 0x38, }, cancellationToken).ConfigureAwait(false); await stream.WriteStructAsync( new AviStreamHeader() { Type = FourCC.Vids, Handler = FourCC.VMnc, Length = this.ExpectedTotalFrames, Quality = -1, Rate = this.Rate, Scale = this.Scale, SuggestedBufferSize = 0x00046bc4, Bottom = (short)framebuffer.Height, Right = (short)framebuffer.Width, }, cancellationToken).ConfigureAwait(false); // STRF chunk await stream.WriteStructAsync( new Chunk() { FourCC = FourCC.Strf, Size = 0x28, }, cancellationToken).ConfigureAwait(false); // BitmapInfoHeader await stream.WriteStructAsync( new BitmapInfoHeader() { BitCount = 0x20, Compression = FourCC.VMnc, Height = framebuffer.Height, Planes = 1, Size = 0x28, // TBC Width = framebuffer.Width, }, cancellationToken).ConfigureAwait(false); // Index chunk var indexSize = 0x000042c8; // TBC await stream.WriteStructAsync( new AviIndexChunck() { FourCC = FourCC.Indx, Size = indexSize, EntriesInUse = 1, ChunkId = FourCC.Dc00, LongsPerEntry = 4, IndexType = IndexType.IndexOfIndexes, IndexSubType = IndexSubType.None, }, cancellationToken).ConfigureAwait(false); // Super index chunk await stream.WriteStructAsync( new AviSuperIndexEntry() { Duration = this.ExpectedTotalFrames, Offset = 0x000000000004196a, Size = 0x4000, }, cancellationToken); await stream.GrowAsync(indexSize - Unsafe.SizeOf <AviSuperIndexEntry>() - Unsafe.SizeOf <AviIndexChunck>() + Unsafe.SizeOf <Chunk>(), cancellationToken).ConfigureAwait(false); // ODML chunk await stream.WriteStructAsync( new List() { ListType = FourCC.List, FourCC = FourCC.Odml, Size = 0x00000104, }, cancellationToken).ConfigureAwait(false); // DMLH chunk indexSize = 0xf8; await stream.WriteStructAsync( new AviIndexChunck() { FourCC = FourCC.Dmlh, Size = indexSize, IndexType = IndexType.IndexOfIndexes, IndexSubType = IndexSubType.None, ChunkId = (FourCC)BinaryPrimitives.ReverseEndianness((uint)VncEncoding.Raw), EntriesInUse = 0, LongsPerEntry = 0x13e, }, cancellationToken); await stream.GrowAsync(indexSize - Unsafe.SizeOf <AviIndexChunck>() + Unsafe.SizeOf <Chunk>(), cancellationToken).ConfigureAwait(false); var junkSize = 0x00000348; await stream.WriteStructAsync( new Chunk() { FourCC = FourCC.Junk, Size = junkSize, // TBC }, cancellationToken).ConfigureAwait(false); var junk = "VMware Workstation"; byte[] buffer = new byte[128]; Encoding.ASCII.GetBytes(junk, 0, junk.Length, buffer, 0); stream.Write(buffer, 0, junk.Length); await stream.GrowAsync(junkSize - junk.Length, cancellationToken).ConfigureAwait(false); // The movie list await stream.WriteStructAsync( new List() { FourCC = FourCC.Movi, ListType = FourCC.List, Size = this.ExpectedSize - 0x4800, }, cancellationToken); FramebufferUpdate framebufferUpdate = new FramebufferUpdate() { MessageType = 0, NumberOfRectangles = 2, }; FramebufferUpdateRectangle rectangle = new FramebufferUpdateRectangle(); DisplayModeChange displayModeChange = new DisplayModeChange(); Chunk dcChunk = new Chunk() { FourCC = FourCC.Dc00, Size = 4 + (2 * framebufferUpdate.Buffer.Length) + displayModeChange.Buffer.Length + (framebuffer.Stride * framebuffer.Width * framebuffer.PixelFormat.BytesPerPixel), }; Stopwatch timer = new Stopwatch(); while (!cancellationToken.IsCancellationRequested) { // Start a timer which will timeout after the timeframe allotted to each frame. timer.Restart(); var interval = Task.Delay(this.MicrosecondsPerFrame / 1000, cancellationToken).ConfigureAwait(false); framebuffer = this.framebufferSource.Capture(); if (framebuffer == null) { break; } rectangle.Width = (ushort)framebuffer.Width; rectangle.Height = (ushort)framebuffer.Height; await stream.WriteStructAsync(dcChunk, cancellationToken).ConfigureAwait(false); // Framebuffer update message, with 2 rectangles await stream.WriteAsync(framebufferUpdate.Buffer, cancellationToken).ConfigureAwait(false); // Pseudo-rectangle: display mode change pseudo-encoding rectangle.EncodingType = VncEncoding.VMWi; await stream.WriteAsync(rectangle.Buffer, cancellationToken).ConfigureAwait(false); displayModeChange.BitsPerSample = (byte)framebuffer.PixelFormat.BitsPerPixel; displayModeChange.Depth = (byte)framebuffer.PixelFormat.BitDepth; displayModeChange.MaxBlue = framebuffer.PixelFormat.BlueMax; displayModeChange.MaxGreen = framebuffer.PixelFormat.GreenMax; displayModeChange.MaxRed = framebuffer.PixelFormat.RedMax; displayModeChange.BlueShift = (byte)framebuffer.PixelFormat.BlueShift; displayModeChange.GreenShift = (byte)framebuffer.PixelFormat.GreenShift; displayModeChange.RedShift = (byte)framebuffer.PixelFormat.RedShift; displayModeChange.TrueColor = !framebuffer.PixelFormat.IsPalettized; await stream.WriteAsync(displayModeChange.Buffer, cancellationToken).ConfigureAwait(false); // Rectangle: framebuffer rectangle.EncodingType = VncEncoding.Raw; await stream.WriteAsync(rectangle.Buffer, cancellationToken).ConfigureAwait(false); var rawFramebuffer = framebuffer.GetBuffer(); await stream.WriteAsync(rawFramebuffer, 0, rawFramebuffer.Length, cancellationToken).ConfigureAwait(false); Debug.WriteLine($"Completed in {timer.ElapsedMilliseconds} ms out of allowed {this.MicrosecondsPerFrame / 1000} ms"); // Wait for the timer to complete. await interval; } }