Пример #1
0
        public override int Capture(FrameInfo frame)
        {
            var res = Result.Ok;

            try
            {
                //Try to get the duplicated output frame within given time.
                res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

                //Somehow, it was not possible to retrieve the resource or any frame.
                if (res.Failure || resource == null || info.AccumulatedFrames == 0)
                {
                    resource?.Dispose();
                    return(FrameCount);
                }

                //Copy resource into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    //Copies from the screen texture only the area which the user wants to capture.
                    Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), StagingTexture, 0);
                }

                //Get the desktop capture texture.
                var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream);

                if (data.IsEmpty)
                {
                    Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);

                    stream?.Dispose();
                    resource.Dispose();
                    return(FrameCount);
                }

                #region Get image data

                //Set frame details.
                FrameCount++;
                frame.Path       = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay      = FrameRate.GetMilliseconds(SnapDelay);
                frame.DataLength = stream.Length;
                frame.Data       = new byte[stream.Length];

                //BGRA32 is 4 bytes.
                for (var height = 0; height < Height; height++)
                {
                    stream.Position = height * data.RowPitch;
                    Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4);
                }

                BlockingCollection.Add(frame);

                #endregion

                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);

                resource.Dispose();
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
            {
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code)
            {
                //When the device gets lost or reset, the resources should be instantiated again.
                DisposeInternal();
                Initialize();

                return(FrameCount);
            }
            catch (Exception ex)
            {
                LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

                OnError.Invoke(ex);
                return(FrameCount);
            }
            finally
            {
                try
                {
                    //Only release the frame if there was a sucess in capturing it.
                    if (res.Success)
                    {
                        DuplicatedOutput.ReleaseFrame();
                    }
                }
                catch (Exception e)
                {
                    LogWriter.Log(e, "It was not possible to release the frame.");
                }
            }
        }
Пример #2
0
        public override int CaptureWithCursor(FrameInfo frame)
        {
            var res = Result.Ok;

            try
            {
                //Try to get the duplicated output frame within given time.
                res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

                //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured.
                if (res.Failure || resource == null || (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime <= LastProcessTime))
                {
                    //Somehow, it was not possible to retrieve the resource, frame or metadata.
                    //frame.WasDropped = true;
                    //BlockingCollection.Add(frame);

                    resource?.Dispose();
                    return(FrameCount);
                }
                else if (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime > LastProcessTime)
                {
                    //Gets the cursor shape if the screen hasn't changed in between, so the cursor will be available for the next frame.
                    GetCursor(null, info, frame);
                    return(FrameCount);
                }

                //Saves the most recent capture time.
                LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime);

                //Copy resource into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    //Copies from the screen texture only the area which the user wants to capture.
                    Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), BackingTexture, 0);

                    //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it.
                    Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture);

                    //Gets the cursor image and merges with the staging texture.
                    GetCursor(StagingTexture, info, frame);
                }

                //Get the desktop capture texture.
                var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None);

                if (data.IsEmpty)
                {
                    //frame.WasDropped = true;
                    //BlockingCollection.Add(frame);

                    Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                    resource.Dispose();
                    return(FrameCount);
                }

                #region Get image data

                var bitmap     = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb);
                var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height);

                //Copy pixels from screen capture Texture to the GDI bitmap.
                var mapDest   = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
                var sourcePtr = data.DataPointer;
                var destPtr   = mapDest.Scan0;

                for (var y = 0; y < Height; y++)
                {
                    //Copy a single line.
                    Utilities.CopyMemory(destPtr, sourcePtr, Width * 4);

                    //Advance pointers.
                    sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch);
                    destPtr   = IntPtr.Add(destPtr, mapDest.Stride);
                }

                //Release source and dest locks.
                bitmap.UnlockBits(mapDest);

                //Set frame details.
                FrameCount++;
                frame.Path  = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay = FrameRate.GetMilliseconds(SnapDelay);
                frame.Image = bitmap;
                BlockingCollection.Add(frame);

                #endregion

                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);

                resource.Dispose();
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
            {
                return(FrameCount);
            }
            catch (Exception ex)
            {
                LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

                OnError.Invoke(ex);
                return(FrameCount);
            }
            finally
            {
                try
                {
                    //Only release the frame if there was a sucess in capturing it.
                    if (res.Success)
                    {
                        DuplicatedOutput.ReleaseFrame();
                    }
                }
                catch (Exception e)
                {
                    LogWriter.Log(e, "It was not possible to release the frame.");
                }
            }
        }
Пример #3
0
    public override int CaptureWithCursor(FrameInfo frame)
    {
        var res = new Result(-1);

        try
        {
            //Try to get the duplicated output frame within given time.
            res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

            //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured.
            if (FrameCount == 0 && info.LastMouseUpdateTime == 0 && (res.Failure || resource == null))
            {
                //Somehow, it was not possible to retrieve the resource, frame or metadata.
                resource?.Dispose();
                return(FrameCount);
            }
            else if (FrameCount == 0 && info.TotalMetadataBufferSize == 0 && info.LastMouseUpdateTime > 0)
            {
                //Sometimes, the first frame has cursor info, but no screen changes.
                GetCursor(null, info, frame);
                resource?.Dispose();
                return(FrameCount);
            }

            #region Process changes

            //Something on screen was moved or changed.
            if (info.TotalMetadataBufferSize > 0)
            {
                //Copies the screen data into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    #region Moved rectangles

                    var movedRectangles = new OutputDuplicateMoveRectangle[info.TotalMetadataBufferSize];
                    DuplicatedOutput.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out var movedRegionsLength);

                    for (var movedIndex = 0; movedIndex < movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle)); movedIndex++)
                    {
                        //Crop the destination rectangle to the screen area rectangle.
                        var left   = Math.Max(movedRectangles[movedIndex].DestinationRect.Left, Left - OffsetLeft);
                        var right  = Math.Min(movedRectangles[movedIndex].DestinationRect.Right, Left + Width - OffsetLeft);
                        var top    = Math.Max(movedRectangles[movedIndex].DestinationRect.Top, Top - OffsetTop);
                        var bottom = Math.Min(movedRectangles[movedIndex].DestinationRect.Bottom, Top + Height - OffsetTop);

                        //Copies from the screen texture only the area which the user wants to capture.
                        if (right > left && bottom > top)
                        {
                            //Limit the source rectangle to the available size within the destination rectangle.
                            var sourceWidth  = movedRectangles[movedIndex].SourcePoint.X + (right - left);
                            var sourceHeight = movedRectangles[movedIndex].SourcePoint.Y + (bottom - top);

                            Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0,
                                                                          new ResourceRegion(movedRectangles[movedIndex].SourcePoint.X, movedRectangles[movedIndex].SourcePoint.Y, 0, sourceWidth, sourceHeight, 1),
                                                                          BackingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop));
                        }
                    }

                    #endregion

                    #region Dirty rectangles

                    var dirtyRectangles = new RawRectangle[info.TotalMetadataBufferSize];
                    DuplicatedOutput.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out var dirtyRegionsLength);

                    for (var dirtyIndex = 0; dirtyIndex < dirtyRegionsLength / Marshal.SizeOf(typeof(RawRectangle)); dirtyIndex++)
                    {
                        //Crop screen positions and size to frame sizes.
                        var left   = Math.Max(dirtyRectangles[dirtyIndex].Left, Left - OffsetLeft);
                        var right  = Math.Min(dirtyRectangles[dirtyIndex].Right, Left + Width - OffsetLeft);
                        var top    = Math.Max(dirtyRectangles[dirtyIndex].Top, Top - OffsetTop);
                        var bottom = Math.Min(dirtyRectangles[dirtyIndex].Bottom, Top + Height - OffsetTop);

                        //Copies from the screen texture only the area which the user wants to capture.
                        if (right > left && bottom > top)
                        {
                            Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(left, top, 0, right, bottom, 1), BackingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop));
                        }
                    }

                    #endregion
                }
            }

            if (info.TotalMetadataBufferSize > 0 || info.LastMouseUpdateTime > 0)
            {
                //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it.
                Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture);

                //Gets the cursor image and merges with the staging texture.
                GetCursor(StagingTexture, info, frame);
            }

            //Saves the most recent capture time.
            LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime);

            #endregion

            #region Gets the image data

            //Get the desktop capture texture.
            var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None);

            if (data.IsEmpty)
            {
                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                resource?.Dispose();
                return(FrameCount);
            }

            var bitmap     = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb);
            var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height);

            //Copy pixels from screen capture Texture to the GDI bitmap.
            var mapDest   = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
            var sourcePtr = data.DataPointer;
            var destPtr   = mapDest.Scan0;

            for (var y = 0; y < Height; y++)
            {
                //Copy a single line.
                Utilities.CopyMemory(destPtr, sourcePtr, Width * 4);

                //Advance pointers.
                sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch);
                destPtr   = IntPtr.Add(destPtr, mapDest.Stride);
            }

            //Releases the source and dest locks.
            bitmap.UnlockBits(mapDest);

            //Set frame details.
            FrameCount++;
            frame.Path  = $"{Project.FullPath}{FrameCount}.png";
            frame.Delay = FrameRate.GetMilliseconds();
            frame.Image = bitmap;

            if (IsAcceptingFrames)
            {
                BlockingCollection.Add(frame);
            }

            #endregion

            Device.ImmediateContext?.UnmapSubresource(StagingTexture, 0);

            resource?.Dispose();
            return(FrameCount);
        }
        catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
        {
            return(FrameCount);
        }
        catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code)
        {
            //When the device gets lost or reset, the resources should be instantiated again.
            DisposeInternal();
            Initialize();

            return(FrameCount);
        }
        catch (Exception ex)
        {
            LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

            MajorCrashHappened = true;

            if (IsAcceptingFrames)
            {
                Application.Current.Dispatcher.Invoke(() => OnError.Invoke(ex));
            }

            return(FrameCount);
        }
        finally
        {
            try
            {
                //Only release the frame if there was a success in capturing it.
                if (res.Success)
                {
                    DuplicatedOutput.ReleaseFrame();
                }
            }
            catch (Exception e)
            {
                LogWriter.Log(e, "It was not possible to release the frame.");
            }
        }
    }
Пример #4
0
        public override int CaptureWithCursor(FrameInfo frame)
        {
            var res = Result.Ok;

            try
            {
                //Try to get the duplicated output frame within given time.
                res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

                //Checks how to proceed with the capture. It could have failed, or the screen, cursor or both could have been captured.
                if (res.Failure || resource == null || (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime <= LastProcessTime))
                {
                    //Somehow, it was not possible to retrieve the resource, frame or metadata.
                    resource?.Dispose();
                    return(FrameCount);
                }
                else if (info.AccumulatedFrames == 0 && info.LastMouseUpdateTime > LastProcessTime)
                {
                    //Gets the cursor shape if the screen hasn't changed in between, so the cursor will be available for the next frame.
                    GetCursor(null, info, frame);
                    return(FrameCount);

                    //TODO: if only the mouse changed, but there's no frame accumulated, but there's data in the texture from the previous frame, I need to merge with the cursor and add to the list.
                }

                //Saves the most recent capture time.
                LastProcessTime = Math.Max(info.LastPresentTime, info.LastMouseUpdateTime);

                //Copy resource into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    //Copies from the screen texture only the area which the user wants to capture.
                    Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), BackingTexture, 0);

                    //Copy the captured desktop texture into a staging texture, in order to show the mouse cursor and not make the captured texture dirty with it.
                    Device.ImmediateContext.CopyResource(BackingTexture, StagingTexture);

                    //Gets the cursor image and merges with the staging texture.
                    GetCursor(StagingTexture, info, frame);
                }

                //Get the desktop capture texture.
                var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream);

                if (data.IsEmpty)
                {
                    Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                    stream?.Dispose();
                    resource.Dispose();
                    return(FrameCount);
                }

                #region Get image data

                //Set frame details.
                FrameCount++;
                frame.Path       = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay      = FrameRate.GetMilliseconds(SnapDelay);
                frame.DataLength = stream.Length;
                frame.Data       = new byte[stream.Length];

                //BGRA32 is 4 bytes.
                for (var height = 0; height < Height; height++)
                {
                    stream.Position = height * data.RowPitch;
                    Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4);
                }

                BlockingCollection.Add(frame);

                #endregion

                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                stream.Dispose();
                resource.Dispose();

                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
            {
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code)
            {
                //When the device gets lost or reset, the resources should be instantiated again.
                DisposeInternal();
                Initialize();

                return(FrameCount);
            }
            catch (Exception ex)
            {
                LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

                OnError.Invoke(ex);
                return(FrameCount);
            }
            finally
            {
                try
                {
                    //Only release the frame if there was a sucess in capturing it.
                    if (res.Success)
                    {
                        DuplicatedOutput.ReleaseFrame();
                    }
                }
                catch (Exception e)
                {
                    LogWriter.Log(e, "It was not possible to release the frame.");
                }
            }
        }
Пример #5
0
        public override int CaptureWithCursor(FrameInfo frame)
        {
            try
            {
                new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();

                var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);

                if (!success)
                {
                    return(FrameCount);
                }

                #region Cursor

                try
                {
                    var cursorInfo = new Native.CursorInfo();
                    cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);

                    if (Native.GetCursorInfo(out cursorInfo))
                    {
                        if (cursorInfo.flags == Native.CursorShowing)
                        {
                            var hicon = Native.CopyIcon(cursorInfo.hCursor);

                            if (hicon != IntPtr.Zero)
                            {
                                if (Native.GetIconInfo(hicon, out var iconInfo))
                                {
                                    frame.CursorX = cursorInfo.ptScreenPos.X - Left;
                                    frame.CursorY = cursorInfo.ptScreenPos.Y - Top;

                                    var ok = Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);

                                    if (!ok)
                                    {
                                        CursorStep = 0;
                                        Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);
                                    }
                                    else
                                    {
                                        CursorStep++;
                                    }
                                }

                                Native.DeleteObject(iconInfo.hbmColor);
                                Native.DeleteObject(iconInfo.hbmMask);
                            }

                            Native.DestroyIcon(hicon);
                        }

                        Native.DeleteObject(cursorInfo.hCursor);
                    }
                }
                catch (Exception)
                {
                    //LogWriter.Log(e, "Impossible to get the cursor");
                }

                #endregion

                //Set frame details.
                FrameCount++;
                frame.Path  = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay = FrameRate.GetMilliseconds(SnapDelay);
                frame.Image = Image.FromHbitmap(CompatibleBitmap);

                BlockingCollection.Add(frame);
            }
            catch (Exception)
            {
                //LogWriter.Log(ex, "Impossible to get the screenshot of the screen");
            }

            return(FrameCount);
        }
Пример #6
0
        public override int Capture(FrameInfo frame)
        {
            var res = new Result(-1);

            try
            {
                //Try to get the duplicated output frame within given time.
                res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

                //Somehow, it was not possible to retrieve the resource or any frame.
                if (res.Failure || resource == null || info.AccumulatedFrames == 0)
                {
                    resource?.Dispose();
                    return(FrameCount);
                }

                //Copy resource into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    //Copies from the screen texture only the area which the user wants to capture.
                    Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(TrueLeft, TrueTop, 0, TrueRight, TrueBottom, 1), StagingTexture, 0);
                }

                //Get the desktop capture texture.
                var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None);

                if (data.IsEmpty)
                {
                    //frame.WasDropped = true;
                    //BlockingCollection.Add(frame);

                    Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                    resource.Dispose();
                    return(FrameCount);
                }

                #region Get image data

                var bitmap     = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb);
                var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height);

                //Copy pixels from screen capture Texture to the GDI bitmap.
                var mapDest   = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
                var sourcePtr = data.DataPointer;
                var destPtr   = mapDest.Scan0;

                for (var y = 0; y < Height; y++)
                {
                    //Copy a single line.
                    Utilities.CopyMemory(destPtr, sourcePtr, Width * 4);

                    //Advance pointers.
                    sourcePtr = IntPtr.Add(sourcePtr, data.RowPitch);
                    destPtr   = IntPtr.Add(destPtr, mapDest.Stride);
                }

                //Release source and dest locks.
                bitmap.UnlockBits(mapDest);

                //Set frame details.
                FrameCount++;
                frame.Path  = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay = FrameRate.GetMilliseconds(SnapDelay);
                frame.Image = bitmap;
                BlockingCollection.Add(frame);

                #endregion

                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);

                resource.Dispose();
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
            {
                return(FrameCount);
            }
            catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code)
            {
                //When the device gets lost or reset, the resources should be instantiated again.
                DisposeInternal();
                Initialize();

                return(FrameCount);
            }
            catch (Exception ex)
            {
                LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

                OnError.Invoke(ex);
                return(FrameCount);
            }
            finally
            {
                try
                {
                    //Only release the frame if there was a sucess in capturing it.
                    if (res.Success)
                    {
                        DuplicatedOutput.ReleaseFrame();
                    }
                }
                catch (Exception e)
                {
                    LogWriter.Log(e, "It was not possible to release the frame.");
                }
            }
        }
Пример #7
0
    public override int Capture(FrameInfo frame)
    {
        var res = new Result(-1);

        try
        {
            //Try to get the duplicated output frame within given time.
            res = DuplicatedOutput.TryAcquireNextFrame(0, out var info, out var resource);

            if (FrameCount == 0 && (res.Failure || resource == null))
            {
                //Somehow, it was not possible to retrieve the resource, frame or metadata.
                resource?.Dispose();
                return(FrameCount);
            }

            #region Process changes

            //Something on screen was moved or changed.
            if (info.TotalMetadataBufferSize > 0)
            {
                //Copy resource into memory that can be accessed by the CPU.
                using (var screenTexture = resource.QueryInterface <Texture2D>())
                {
                    #region Moved rectangles

                    var movedRectangles = new OutputDuplicateMoveRectangle[info.TotalMetadataBufferSize];
                    DuplicatedOutput.GetFrameMoveRects(movedRectangles.Length, movedRectangles, out var movedRegionsLength);

                    for (var movedIndex = 0; movedIndex < movedRegionsLength / Marshal.SizeOf(typeof(OutputDuplicateMoveRectangle)); movedIndex++)
                    {
                        //Crop the destination rectangle to the screen area rectangle.
                        var left   = Math.Max(movedRectangles[movedIndex].DestinationRect.Left, Left - OffsetLeft);
                        var right  = Math.Min(movedRectangles[movedIndex].DestinationRect.Right, Left + Width - OffsetLeft);
                        var top    = Math.Max(movedRectangles[movedIndex].DestinationRect.Top, Top - OffsetTop);
                        var bottom = Math.Min(movedRectangles[movedIndex].DestinationRect.Bottom, Top + Height - OffsetTop);

                        //Copies from the screen texture only the area which the user wants to capture.
                        if (right > left && bottom > top)
                        {
                            //Limit the source rectangle to the available size within the destination rectangle.
                            var sourceWidth  = movedRectangles[movedIndex].SourcePoint.X + (right - left);
                            var sourceHeight = movedRectangles[movedIndex].SourcePoint.Y + (bottom - top);

                            Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0,
                                                                          new ResourceRegion(movedRectangles[movedIndex].SourcePoint.X, movedRectangles[movedIndex].SourcePoint.Y, 0, sourceWidth, sourceHeight, 1),
                                                                          StagingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop));
                        }
                    }

                    #endregion

                    #region Dirty rectangles

                    var dirtyRectangles = new RawRectangle[info.TotalMetadataBufferSize];
                    DuplicatedOutput.GetFrameDirtyRects(dirtyRectangles.Length, dirtyRectangles, out var dirtyRegionsLength);

                    for (var dirtyIndex = 0; dirtyIndex < dirtyRegionsLength / Marshal.SizeOf(typeof(RawRectangle)); dirtyIndex++)
                    {
                        //Crop screen positions and size to frame sizes.
                        var left   = Math.Max(dirtyRectangles[dirtyIndex].Left, Left - OffsetLeft);
                        var right  = Math.Min(dirtyRectangles[dirtyIndex].Right, Left + Width - OffsetLeft);
                        var top    = Math.Max(dirtyRectangles[dirtyIndex].Top, Top - OffsetTop);
                        var bottom = Math.Min(dirtyRectangles[dirtyIndex].Bottom, Top + Height - OffsetTop);

                        //Copies from the screen texture only the area which the user wants to capture.
                        if (right > left && bottom > top)
                        {
                            Device.ImmediateContext.CopySubresourceRegion(screenTexture, 0, new ResourceRegion(left, top, 0, right, bottom, 1), StagingTexture, 0, left - (Left - OffsetLeft), top - (Top - OffsetTop));
                        }
                    }

                    #endregion
                }
            }

            #endregion

            #region Gets the image data

            //Gets the staging texture as a stream.
            var data = Device.ImmediateContext.MapSubresource(StagingTexture, 0, MapMode.Read, MapFlags.None, out var stream);

            if (data.IsEmpty)
            {
                Device.ImmediateContext.UnmapSubresource(StagingTexture, 0);
                stream?.Dispose();
                resource?.Dispose();
                return(FrameCount);
            }

            //Set frame details.
            FrameCount++;
            frame.Path       = $"{Project.FullPath}{FrameCount}.png";
            frame.Delay      = FrameRate.GetMilliseconds();
            frame.DataLength = stream.Length;
            frame.Data       = new byte[stream.Length];

            //BGRA32 is 4 bytes.
            for (var height = 0; height < Height; height++)
            {
                stream.Position = height * data.RowPitch;
                Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), frame.Data, height * Width * 4, Width * 4);
            }

            if (IsAcceptingFrames)
            {
                BlockingCollection.Add(frame);
            }

            #endregion

            Device.ImmediateContext?.UnmapSubresource(StagingTexture, 0);

            resource?.Dispose();
            return(FrameCount);
        }
        catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
        {
            return(FrameCount);
        }
        catch (SharpDXException se) when(se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceRemoved.Result.Code || se.ResultCode.Code == SharpDX.DXGI.ResultCode.DeviceReset.Result.Code)
        {
            //When the device gets lost or reset, the resources should be instantiated again.
            DisposeInternal();
            Initialize();

            return(FrameCount);
        }
        catch (Exception ex)
        {
            LogWriter.Log(ex, "It was not possible to finish capturing the frame with DirectX.");

            MajorCrashHappened = true;

            if (IsAcceptingFrames)
            {
                Application.Current.Dispatcher.Invoke(() => OnError.Invoke(ex));
            }

            return(FrameCount);
        }
        finally
        {
            try
            {
                //Only release the frame if there was a sucess in capturing it.
                if (res.Success)
                {
                    DuplicatedOutput.ReleaseFrame();
                }
            }
            catch (Exception e)
            {
                LogWriter.Log(e, "It was not possible to release the frame.");
            }
        }
    }
Пример #8
0
        public override int CaptureWithCursor(FrameInfo frame)
        {
            try
            {
                new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();

                //var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);
                var success = Native.StretchBlt(CompatibleDeviceContext, 0, 0, StartWidth, StartHeight, WindowDeviceContext, Left, Top, Width, Height, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);

                if (!success)
                {
                    return(FrameCount);
                }

                #region Cursor

                try
                {
                    var cursorInfo = new Native.CursorInfo();
                    cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);

                    if (Native.GetCursorInfo(out cursorInfo))
                    {
                        if (cursorInfo.flags == Native.CursorShowing)
                        {
                            var hicon = Native.CopyIcon(cursorInfo.hCursor);

                            if (hicon != IntPtr.Zero)
                            {
                                if (Native.GetIconInfo(hicon, out var iconInfo))
                                {
                                    frame.CursorX = cursorInfo.ptScreenPos.X - Left;
                                    frame.CursorY = cursorInfo.ptScreenPos.Y - Top;

                                    //If the cursor rate needs to be precisely captured.
                                    //https://source.winehq.org/source/dlls/user32/cursoricon.c#2325
                                    //int rate = 0, num = 0;
                                    //var ok1 = Native.GetCursorFrameInfo(cursorInfo.hCursor, IntPtr.Zero, 17, ref rate, ref num);

                                    //CursorStep
                                    var ok = Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);

                                    if (!ok)
                                    {
                                        CursorStep = 0;
                                        Native.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);
                                    }
                                    else
                                    {
                                        CursorStep++;
                                    }

                                    //Set to fix all alpha bits back to 255.
                                    //frame.RemoveAnyTransparency = iconInfo.hbmMask != IntPtr.Zero;
                                }

                                Native.DeleteObject(iconInfo.hbmColor);
                                Native.DeleteObject(iconInfo.hbmMask);
                            }

                            Native.DestroyIcon(hicon);
                        }

                        Native.DeleteObject(cursorInfo.hCursor);
                    }
                }
                catch (Exception e)
                {
                    //LogWriter.Log(e, "Impossible to get the cursor");
                }

                #endregion

                //Set frame details.
                FrameCount++;
                frame.Path       = $"{Project.FullPath}{FrameCount}.png";
                frame.Delay      = FrameRate.GetMilliseconds(SnapDelay);
                frame.DataLength = _byteLength;
                frame.Data       = new byte[_byteLength];

                Native.GetDIBits(WindowDeviceContext, CompatibleBitmap, 0, (uint)StartHeight, frame.Data, ref _infoHeader, Native.DibColorMode.DibRgbColors);

                BlockingCollection.Add(frame);
            }
            catch (Exception e)
            {
                //LogWriter.Log(ex, "Impossible to get the screenshot of the screen");
            }

            return(FrameCount);
        }
Пример #9
0
    public override int CaptureWithCursor(FrameInfo frame)
    {
        try
        {
            //var success = Native.BitBlt(CompatibleDeviceContext, 0, 0, Width, Height, WindowDeviceContext, Left, Top, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
            var success = Gdi32.StretchBlt(CompatibleDeviceContext, 0, 0, StartWidth, StartHeight, WindowDeviceContext, Left, Top, Width, Height, PixelOperations);

            if (!success)
            {
                return(FrameCount);
            }

            #region Cursor

            try
            {
                var cursorInfo = new CursorInfo();
                cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);

                if (User32.GetCursorInfo(out cursorInfo))
                {
                    if (cursorInfo.flags == Native.Constants.CursorShowing)
                    {
                        var hicon = User32.CopyIcon(cursorInfo.hCursor);

                        if (hicon != IntPtr.Zero)
                        {
                            if (User32.GetIconInfo(hicon, out var iconInfo))
                            {
                                frame.CursorX = cursorInfo.ptScreenPos.X - Left;
                                frame.CursorY = cursorInfo.ptScreenPos.Y - Top;

                                //(int)(SystemParameters.CursorHeight * Scale)
                                //(int)(SystemParameters.CursorHeight * Scale)

                                var ok = User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);

                                if (!ok)
                                {
                                    CursorStep = 0;
                                    User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, CursorStep, IntPtr.Zero, 0x0003);
                                }
                                else
                                {
                                    CursorStep++;
                                }
                            }

                            Gdi32.DeleteObject(iconInfo.hbmColor);
                            Gdi32.DeleteObject(iconInfo.hbmMask);
                        }

                        User32.DestroyIcon(hicon);
                    }

                    Gdi32.DeleteObject(cursorInfo.hCursor);
                }
            }
            catch (Exception)
            {
                //LogWriter.Log(e, "Impossible to get the cursor");
            }

            #endregion

            //Set frame details.
            FrameCount++;
            frame.Path  = $"{Project.FullPath}{FrameCount}.png";
            frame.Delay = FrameRate.GetMilliseconds();
            frame.Image = Image.FromHbitmap(CompatibleBitmap);

            if (IsAcceptingFrames)
            {
                BlockingCollection.Add(frame);
            }
        }
        catch (Exception)
        {
            //LogWriter.Log(ex, "Impossible to get the screenshot of the screen");
        }

        return(FrameCount);
    }