/// <summary>
            /// Retrieves the moved and dirty regions of the currently duplicated frame, populating the specified desktop frame.
            /// </summary>
            /// <param name="frame"></param>
            /// <param name="frameInfo"></param>
            public void RetrieveFrameMetadata(DesktopFrame frame, OutduplFrameInfo frameInfo)
            {
                if (frameInfo.TotalMetadataBufferSize > 0)
                {
                    var movedRectangles = new OutduplMoveRect[frameInfo.TotalMetadataBufferSize];

                    // Get moved regions
                    _outputDuplication.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out int movedRegionsLength);
                    var movedRegions = new MovedRegion[movedRegionsLength / Marshal.SizeOf(typeof(OutduplMoveRect))];
                    for (int i = 0; i < movedRegions.Length; i++)
                    {
                        movedRegions[i] = new MovedRegion()
                        {
                            Source      = new Point(movedRectangles[i].SourcePoint.X, movedRectangles[i].SourcePoint.Y),
                            Destination = new Rectangle(movedRectangles[i].DestinationRect.Left, movedRectangles[i].DestinationRect.Top, movedRectangles[i].DestinationRect.Right, movedRectangles[i].DestinationRect.Bottom)
                        };
                    }
                    frame.MovedRegions = movedRegions;

                    // Get dirty regions
                    var dirtyRectangles = new RawRect[frameInfo.TotalMetadataBufferSize];
                    _outputDuplication.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out int dirtyRegionsLength);
                    var updatedRegions = new Rectangle[dirtyRegionsLength / Marshal.SizeOf(typeof(Rectangle))];
                    for (int i = 0; i < updatedRegions.Length; i++)
                    {
                        updatedRegions[i] = new Rectangle(dirtyRectangles[i].Left, dirtyRectangles[i].Top, dirtyRectangles[i].Right, dirtyRectangles[i].Bottom);
                    }
                    frame.UpdatedRegions = updatedRegions;
                }
                else
                {
                    frame.MovedRegions   = Array.Empty <MovedRegion>();
                    frame.UpdatedRegions = Array.Empty <Rectangle>();
                }
            }
 /// <summary>
 /// Updates the specified frame with the next retrieved frame.
 /// </summary>
 private void UpdateFrame(DesktopDuplicatorInternal ddupe, DesktopFrame frame)
 {
     // Try to get the latest frame;
     if (ddupe.TryRetrieveFrame(out var frameInfo))
     {
         try
         {
             ddupe.RetrieveFrameMetadata(frame, frameInfo);
             ddupe.RetrieveCursorMetadata(frame, frameInfo);
             ddupe.ProcessFrame(frame);
         }
         finally
         {
             ddupe.ReleaseFrame(_logger);
         }
     }
 }
            /// <summary>
            /// Using the staging texture
            /// </summary>
            /// <param name="frame"></param>
            public void ProcessFrame(DesktopFrame frame)
            {
                frame.DesktopWidth  = Math.Abs(_desktopRect.Right - _desktopRect.Left);
                frame.DesktopHeight = Math.Abs(_desktopRect.Bottom - _desktopRect.Top);

                // Get the desktop capture texture
                var mapSource = _d3dDevice.ImmediateContext.Map(_desktopImageTexture, 0, MapMode.Read, MapFlags.None);
                var buffer    = new byte[frame.DesktopHeight * mapSource.RowPitch];

                // Copy pixels from screen capture Texture to the image buffer.
                var bufferSpan = new Span <byte>(buffer);

                mapSource.AsSpan <byte>(buffer.Length).CopyTo(bufferSpan);

                // Release source and dest locks
                _d3dDevice.ImmediateContext.Unmap(_desktopImageTexture, 0);

                frame.DesktopFrameBuffer        = buffer;
                frame.IsDesktopImageBufferEmpty = bufferSpan.Trim((byte)0x00).IsEmpty;
            }
        /// <summary>
        /// Duplicates the desktop yielding Desktop Frames until cancelled.
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public IEnumerable <DesktopFrame> DuplicateDesktop(CancellationToken token)
        {
            _logger.LogInformation($"Beginning Desktop Duplication");
            using var duplicator = DesktopDuplicatorInternal.CreateDesktopDuplicator(_logger, _adapterIndex, _outputDeviceIndex);

            while (token.IsCancellationRequested == false)
            {
                var desktopFrame = new DesktopFrame();
                UpdateFrame(duplicator, desktopFrame);

                if (token.IsCancellationRequested == false)
                {
                    if (desktopFrame.IsDesktopImageBufferEmpty)
                    {
                        continue;
                    }

                    yield return(desktopFrame);
                }
            }
        }
예제 #5
0
        public static DesktopFrame FromFile(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentNullException(nameof(path));
            }

            var result = new DesktopFrame();

            using var image = SixLabors.ImageSharp.Image.Load(path);
            {
                using var imageClone = image.CloneAs <Bgra32>();
                {
                    var bpp   = imageClone.PixelType.BitsPerPixel / 8;
                    int bytes = imageClone.Height * imageClone.Width * bpp;

                    var frameBuffer = new byte[bytes];
                    var bufferSpan  = new Span <byte>(frameBuffer);
                    if (imageClone.TryGetSinglePixelSpan(out Span <Bgra32> imageSpan))
                    {
                        for (int i = 0; i < imageSpan.Length; i++)
                        {
                            bufferSpan[i * bpp]     = imageSpan[i].B;
                            bufferSpan[i * bpp + 1] = imageSpan[i].G;
                            bufferSpan[i * bpp + 2] = imageSpan[i].R;
                            bufferSpan[i * bpp + 3] = imageSpan[i].A;
                        }

                        result.DesktopFrameBuffer        = frameBuffer;
                        result.DesktopWidth              = imageClone.Width;
                        result.DesktopHeight             = imageClone.Height;
                        result.IsDesktopImageBufferEmpty = bufferSpan.Trim((byte)0x00).IsEmpty;
                    }
                }
            }

            return(result);
        }
            /// <summary>
            /// Retrieves the cursor information contained in the currently duplicated frame, populating the specified desktop frame.
            /// </summary>
            /// <param name="frame"></param>
            /// <param name="frameInfo"></param>
            public void RetrieveCursorMetadata(DesktopFrame frame, OutduplFrameInfo frameInfo)
            {
                // A non-zero mouse update timestamp indicates that there is a mouse position update and optionally a shape change
                if (frameInfo.LastMouseUpdateTime == 0)
                {
                    frame.PointerPosition.Visible = _pointerInfo.Visible;
                    frame.PointerPosition.X       = _pointerInfo.Position.X;
                    frame.PointerPosition.Y       = _pointerInfo.Position.Y;
                    return;
                }

                bool updatePosition = true;

                // Make sure we don't update pointer position wrongly
                // If pointer is invisible, make sure we did not get an update from another output that the last time that said pointer
                // was visible, if so, don't set it to invisible or update.

                if (!frameInfo.PointerPosition.Visible && (_pointerInfo.WhoUpdatedPositionLast != _outputDeviceIndex))
                {
                    updatePosition = false;
                }

                // If two outputs both say they have a visible, only update if new update has newer timestamp
                if (frameInfo.PointerPosition.Visible && _pointerInfo.Visible &&
                    (_pointerInfo.WhoUpdatedPositionLast != _outputDeviceIndex) &&
                    (_pointerInfo.LastTimeStamp > frameInfo.LastMouseUpdateTime))
                {
                    updatePosition = false;
                }

                // Update position
                if (updatePosition)
                {
                    _pointerInfo.Position = new Point(frameInfo.PointerPosition.Position.X, frameInfo.PointerPosition.Position.Y);
                    _pointerInfo.WhoUpdatedPositionLast = _outputDeviceIndex;
                    _pointerInfo.LastTimeStamp          = frameInfo.LastMouseUpdateTime;
                    _pointerInfo.Visible = frameInfo.PointerPosition.Visible;
                }

                frame.PointerPosition.Visible = _pointerInfo.Visible;
                frame.PointerPosition.X       = _pointerInfo.Position.X;
                frame.PointerPosition.Y       = _pointerInfo.Position.Y;

                // No new shape
                if (frameInfo.PointerShapeBufferSize == 0)
                {
                    return;
                }

                if (_pointerInfo.ShapeBuffer == null || frameInfo.PointerShapeBufferSize != _pointerInfo.ShapeBuffer.Length)
                {
                    _pointerInfo.ShapeBuffer = new byte[frameInfo.PointerShapeBufferSize];
                }

                try
                {
                    // Create a new pinned region of memory, copy the contents of the PtrShapeBuffer to it, use it to retrieve the pointer shape and then free the pinned region.
                    unsafe
                    {
                        fixed(byte *ptrShapeBufferPtr = _pointerInfo.ShapeBuffer)
                        {
                            _outputDuplication.GetFramePointerShape(frameInfo.PointerShapeBufferSize, (IntPtr)ptrShapeBufferPtr, out int bufferSize, out _pointerInfo.ShapeInfo);
                        }
                    }
                }
                catch (SharpGenException ex)
                {
                    if (ex.ResultCode.Failure)
                    {
                        throw new DesktopDuplicationException("Failed to get frame pointer shape.");
                    }
                }

                var pointerShapeSpan = new ReadOnlySpan <byte>(_pointerInfo.ShapeBuffer);

                if (!pointerShapeSpan.Trim((byte)0x00).IsEmpty)
                {
                    frame.PointerShape.Width    = _pointerInfo.ShapeInfo.Width;
                    frame.PointerShape.Height   = _pointerInfo.ShapeInfo.Height;
                    frame.PointerShape.Pitch    = _pointerInfo.ShapeInfo.Pitch;
                    frame.PointerShape.Type     = _pointerInfo.ShapeInfo.Type;
                    frame.PointerShape.HotSpotX = _pointerInfo.ShapeInfo.HotSpot.X;
                    frame.PointerShape.HotSpotY = _pointerInfo.ShapeInfo.HotSpot.Y;
                    frame.PointerShapeBuffer    = _pointerInfo.ShapeBuffer;
                }
            }