/// <summary> /// Captures a screenshot. Remember, you should always use this class from the same thread. /// </summary> /// <returns></returns> public FragmentedImage Capture(SHRDLib.NetCommand.DesktopScreen screen) { if (screen == null) { throw new NullReferenceException("screen cannot be null"); } long thisMs = timing.ElapsedMilliseconds; // Learn about the display configuration if (displaySettingsChanged) { displaySettingsChanged = false; ClearCaptureHandle(); DesktopManager.ShouldReassociate = true; } if (DesktopManager.ShouldReassociate || thisMs > nextDesktopCheck) { DesktopManager.ShouldReassociate = false; nextDesktopCheck = thisMs + desktopCheckInterval; if (DesktopManager.AssociateCurrentThreadWithDefaultDesktop()) { ClearCaptureHandle(); // Desktop was changed. } } // Capture Screenshot screenshot = CaptureScreenshot(screen.X, screen.Y, screen.Width, screen.Height); if (screenshot == null) { return(new FragmentedImage()); } FragmentedImage img = new FragmentedImage(new MovedImageFragment[0], new DirtyImageFragment[] { new DirtyImageFragment(screen.Y, screen.X + screen.Width, screen.Height + screen.Y, screen.X, screenshot) }); return(img); }
private void streamLoop_inner(object objArgs) { Stopwatch frameTimer = new Stopwatch(); frameTimer.Start(); long nextFrameStart = 0; StreamThreadArgs args = (StreamThreadArgs)objArgs; while (!args.abortFlag.abort) { try { int sleepTime = (int)(nextFrameStart - frameTimer.ElapsedMilliseconds); while (sleepTime > 0 || (Interlocked.Read(ref args.numSentFrames) >= Interlocked.Read(ref args.numAcknowledgedFrames) + maxUnacknowledgedFrames)) { if (args.abortFlag.abort) { return; } Thread.Sleep(BPMath.Clamp(sleepTime, 1, 10)); sleepTime = (int)(nextFrameStart - frameTimer.ElapsedMilliseconds); } if (args.abortFlag.abort) { return; } nextFrameStart = frameTimer.ElapsedMilliseconds + (1000 / maxFPS); if (streamerController == null) { return; } FragmentedImage fragmentedImage = streamerController.GetRawDesktopCapture(imgFlags, jpegQuality, args.abortFlag); if (args.abortFlag.abort) { return; } if (fragmentedImage == null) { fragmentedImage = new FragmentedImage(); } fragmentedImage.streamId = (byte)args.myStreamNumber; using (MemoryDataStream mds = new MemoryDataStream(fragmentedImage.GetMaximumRequiredBufferSize())) { byte[] compressionBuffer = null; fragmentedImage.WriteToDataStream(mds, ref compressionBuffer); Interlocked.Increment(ref args.numSentFrames); socket.Send(mds.ToArray()); } } catch (ThreadAbortException) { throw; } catch (Exception ex) { Logger.Debug(ex); } } }
private static void desktopCaptureThreadRunner() { try { byte[] compressToBuffer = null; while (!isExiting && static_sm != null) { Thread.Sleep(1); DesktopCaptureTask task; while (!isExiting && static_sm != null && desktopCaptureTasks.TryDequeue(out task)) { turbojpegCLI.SubsamplingOption subsamp = GetSubsamplingOptionFromImgFlags(task.imgFlags); FragmentedImage img = CaptureRawDesktopImage(task.imgFlags.HasFlag(ImgFlags.Refresh)); SharedMemoryStream sm = static_sm; if (sm == null) { break; } lock (sm) { img.WriteToDataStream(static_sm, ref compressToBuffer, task.jpegQuality, subsamp); } } } } catch (ThreadAbortException) { } catch (StreamDisconnectedException ex) { Logger.Info("Exiting because: " + ex.Message); } catch (Exception ex) { Logger.Debug(ex); Logger.Info("Exiting due to main thread runner exception"); } finally { Try.Catch(() => { dxgiDuplicator?.Dispose(); }); Try.Catch(() => { screenCapturer?.Dispose(); }); //Try.Catch(() => { inputEmulator?.Dispose(); }); RobustExit(); } }
private static FragmentedImage CaptureRawDesktopImage(bool fullFrame) { if (DxgiOutputDuplicator.CurrentOSSupportsThisMethod) { bool proceedWithFastMethod = true; if (compatibleDesktopCaptureModeClock.IsRunning) { if (compatibleDesktopCaptureModeClock.ElapsedMilliseconds < timeToUseCompatibleDesktopCaptureMode) { proceedWithFastMethod = false; // Not enough time has passed. Keep using compatible method. } else { compatibleDesktopCaptureModeClock.Reset(); // time is up -- we can try fast mode again Logger.Info("Switching back to fast desktop capture mode"); } } if (proceedWithFastMethod) { if (fullFrame) { dxgiDuplicator.ResetOutputDuplicator(); } FragmentedImage imgFast = dxgiDuplicator.Capture(); if (imgFast != null) { return(imgFast); } // Most likely the console session is logging off, at the login screen, or just logged on and hasn't initialized the necessary directx parts yet. // Switch to the compatible capture mode for a while. compatibleDesktopCaptureModeClock.Start(); Logger.Info("Switching to compatible desktop capture mode for next " + timeToUseCompatibleDesktopCaptureMode + " ms"); } } // If we get here, we need to use a more-compatible capture method because we are probably at the login screen. FragmentedImage imgCompatible = screenCapturer.Capture(desktopInfo.GetScreen(0, 0)); return(imgCompatible); }
public FragmentedImage Capture() { if (duplicatedOutput == null && !ResetOutputDuplicator()) { return(null); } OutputDuplicateFrameInformation frameInfo; SharpDX.DXGI.Resource screenResource = null; bool success = false; try { int frameDisposeFailures = 0; while (true) { try { screenResource = null; duplicatedOutput.AcquireNextFrame(10000, out frameInfo, out screenResource); if (frameInfo.AccumulatedFrames > 0) { if (screenResource == null) { Logger.Debug("screenResource was null in DxgiOutputDuplicator"); return(null); } // Copy the texture so we can access the pixel data of the copy using (Texture2D screenTexture2D = screenResource.QueryInterface <Texture2D>()) device.ImmediateContext.CopyResource(screenTexture2D, screenTexture); // Learn which rectangles moved OutputDuplicateMoveRectangle[] moveRects = GetMoveRectangles(); // Learn which rectangles were made dirty RawRectangle[] dirtyRects = GetDirtyRectangles(); FragmentedImage img = new FragmentedImage(new MovedImageFragment[moveRects.Length], new DirtyImageFragment[dirtyRects.Length]); int i = 0; foreach (OutputDuplicateMoveRectangle moveRect in moveRects) { img.movedFragments[i++] = new MovedImageFragment( moveRect.DestinationRect.Top , moveRect.DestinationRect.Right , moveRect.DestinationRect.Bottom , moveRect.DestinationRect.Left , moveRect.SourcePoint.X , moveRect.SourcePoint.Y); } // Get the desktop capture pixel data i = 0; DataBox mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, MapFlags.None); foreach (RawRectangle dirtyRect in dirtyRects) { Screenshot screenshot = new Screenshot(dirtyRect.Right - dirtyRect.Left, dirtyRect.Bottom - dirtyRect.Top, 32); IntPtr source = mapSource.DataPointer; source += dirtyRect.Top * mapSource.RowPitch; // Offset source to the correct row source += dirtyRect.Left * 4; // Offset source to the correct column int destOffset = 0; for (int y = dirtyRect.Top; y < dirtyRect.Bottom; y++) { Marshal.Copy(source, screenshot.Buffer, destOffset, screenshot.Stride); source += mapSource.RowPitch; destOffset += screenshot.Stride; } img.dirtyFragments[i++] = new DirtyImageFragment( dirtyRect.Top , dirtyRect.Right , dirtyRect.Bottom , dirtyRect.Left , screenshot); } device.ImmediateContext.UnmapSubresource(screenTexture, 0); //DebugDrawRects(screenshot, moveRects, dirtyRects); success = true; return(img); } } catch (SharpDXException e) { if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) { throw e; } } finally { if (screenResource != null) { try { screenResource.Dispose(); duplicatedOutput.ReleaseFrame(); } catch { if (++frameDisposeFailures > 2) { throw; } ResetOutputDuplicator(); } } } } } finally { if (!success) { DestroyOutputDuplicator(); } } // TODO: Delete cursor drawing stuff. //{ // IntPtr dc = _renderSurface.GetDC(new RawBool(true)); // NativeMethods.SelectObject(_hdc, dc); // NativeMethods.BitBlt(dc, 0, 0, bounds.Right - bounds.Left // , bounds.Bottom - bounds.Top, _hdc, bounds.Left // , bounds.Top, System.Drawing.CopyPixelOperation.SourceCopy); // NativeMethods.CURSORINFO pci; // pci.cbSize = NativeMethods.SizeOfCursorInfo; // if (NativeMethods.GetCursorInfo(out pci) && pci.flags > 0) // NativeMethods.DrawIcon(dc, pci.ptScreenPos.X, pci.ptScreenPos.Y, pci.hCursor); // _renderSurface.ReleaseDC(); // return null; //} }