Github每日精选(第16期):录屏工具ScreenToGif

ScreenToGif

ScreenToGif允许您记录屏幕的选定区域、网络摄像头的实时信息或素描板上的实时绘图。之后,您可以编辑动画并将其保存为 gif、apng、视频、psd 或 png 图像。

在这里插入图片描述

ScreenToGif在github下的地址在这里

功能介绍:

  • 1.记录你的屏幕,并直接保存到一个GIF动画的环形。

  • 2.暂停和继续记录。

  • 3.移动窗口来记录你想要的。

  • 4.您可以添加文字,字幕和标题框。

  • 5.编辑框,添加过滤器,还原,做优悠风格,斯洛伐克运动,加边框。

  • 6.导出帧。

  • 7.作物和调整。

  • 8.你可以工作,即使在节目录制。

  • 9.删除你不想要的帧。

  • 10.选择一个文件夹,自动保存文件或选择一个enconding之前。

ScreenToGif也比较简单,主要的有两个功能,一个是录取屏幕一个是录取摄像头的视频。

在这里插入图片描述
还有一个白板的功能,也是挺好玩的:

在这里插入图片描述

主要代码分析

获取摄像头的数据:

protected void CreateGraph()
    {
        //Skip if already created
        if ((int)ActualGraphState < (int)GraphState.Created)
        {
            //Make a new filter graph.
            GraphBuilder = (ExtendStreaming.IGraphBuilder)Activator.CreateInstance(Type.GetTypeFromCLSID(Uuid.Clsid.FilterGraph, true));

            //Get the Capture Graph Builder.
            var clsid = Uuid.Clsid.CaptureGraphBuilder2;
            var riid = typeof(ExtendStreaming.ICaptureGraphBuilder2).GUID;
            CaptureGraphBuilder = (ExtendStreaming.ICaptureGraphBuilder2)Activator.CreateInstance(Type.GetTypeFromCLSID(clsid, true));

            //Link the CaptureGraphBuilder to the filter graph
            var hr = CaptureGraphBuilder.SetFiltergraph(GraphBuilder);

            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);

            var comType = Type.GetTypeFromCLSID(Uuid.Clsid.SampleGrabber);

            if (comType == null)
                throw new Exception("DirectShow SampleGrabber not installed/registered!");

            var comObj = Activator.CreateInstance(comType);
            SampGrabber = (EditStreaming.ISampleGrabber)comObj; comObj = null;

            _baseGrabFlt = (CoreStreaming.IBaseFilter) SampGrabber;

            var media = new CoreStreaming.AmMediaType();

            //Get the video device and add it to the filter graph
            if (VideoDevice != null)
            {
                VideoDeviceFilter = (CoreStreaming.IBaseFilter)Marshal.BindToMoniker(VideoDevice.MonikerString);

                hr = GraphBuilder.AddFilter(VideoDeviceFilter, "Video Capture Device");

                if (hr < 0)
                    Marshal.ThrowExceptionForHR(hr);

                media.majorType = Uuid.MediaType.Video;
                media.subType = Uuid.MediaSubType.RGB32;//RGB24;
                media.formatType = Uuid.FormatType.VideoInfo;
                media.temporalCompression = true; //New

                hr = SampGrabber.SetMediaType(media);

                if (hr < 0)
                    Marshal.ThrowExceptionForHR(hr);

                hr = GraphBuilder.AddFilter(_baseGrabFlt, "Grabber");

                if (hr < 0)
                    Marshal.ThrowExceptionForHR(hr);
            }

            // Retrieve the stream control interface for the video device
            // FindInterface will also add any required filters
            // (WDM devices in particular may need additional
            // upstream filters to function).

            // Try looking for an interleaved media type
            object o;
            var cat = Uuid.PinCategory.Capture;
            var med = Uuid.MediaType.Interleaved;
            var iid = typeof(ExtendStreaming.IAMStreamConfig).GUID;
            hr = CaptureGraphBuilder.FindInterface(cat, med, VideoDeviceFilter, iid, out o);

            if (hr != 0)
            {
                // If not found, try looking for a video media type
                med = Uuid.MediaType.Video;
                hr = CaptureGraphBuilder.FindInterface(cat, med, VideoDeviceFilter, iid, out o);

                if (hr != 0)
                    o = null;
            }

            VideoStreamConfig = o as ExtendStreaming.IAMStreamConfig;

            // Retrieve the media control interface (for starting/stopping graph)
            MediaControl = (ControlStreaming.IMediaControl)GraphBuilder;

            // Reload any video crossbars
            //if (videoSources != null) videoSources.Dispose(); videoSources = null;

            _videoInfoHeader = (EditStreaming.VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(EditStreaming.VideoInfoHeader));
            Marshal.FreeCoTaskMem(media.formatPtr); media.formatPtr = IntPtr.Zero;

            hr = SampGrabber.SetBufferSamples(false);
            if (hr == 0)
                hr = SampGrabber.SetOneShot(false);
            if (hr == 0)
                hr = SampGrabber.SetCallback(null, 0);
            if (hr < 0)
                Marshal.ThrowExceptionForHR(hr);
        }

        //Update the state now that we are done.
        ActualGraphState = GraphState.Created;
    }

每一帧的数据:

public Bitmap GetFrame()
    {
        //TODO: Verify any possible leaks.

        //Asks for the buffer size.
        var bufferSize = 0;
        SampGrabber.GetCurrentBuffer(ref bufferSize, IntPtr.Zero);

        //Allocs the byte array.
        var handleObj = GCHandle.Alloc(_savedArray, GCHandleType.Pinned);

        //Gets the address of the pinned object.
        var address = handleObj.AddrOfPinnedObject();

        //Puts the buffer inside the byte array.
        SampGrabber.GetCurrentBuffer(ref bufferSize, address);

        //Image size.
        var width = _videoInfoHeader.BmiHeader.Width;
        var height = _videoInfoHeader.BmiHeader.Height;

        var stride = width * 3;
        //address += (height - 1) * stride;
        address += height * stride;

        var bitmap = new Bitmap(width, height, -stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, address);
        handleObj.Free();

        return bitmap;
    }

截取屏幕:

public override int Capture(FrameInfo frame)
    {
        var res = new Result(-1);
        var wasCaptured = false;

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

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

            #region Process changes

            //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 scree area rectangle.
                    var left = Math.Max(movedRectangles[movedIndex].DestinationRect.Left, Left);
                    var right = Math.Min(movedRectangles[movedIndex].DestinationRect.Right, Left + Width);
                    var top = Math.Max(movedRectangles[movedIndex].DestinationRect.Top, Top);
                    var bottom = Math.Min(movedRectangles[movedIndex].DestinationRect.Bottom, Top + Height);

                    //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, top - Top);
                        wasCaptured = true;
                    }
                }

                #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);
                    var right = Math.Min(dirtyRectangles[dirtyIndex].Right, Left + Width);
                    var top = Math.Max(dirtyRectangles[dirtyIndex].Top, Top);
                    var bottom = Math.Min(dirtyRectangles[dirtyIndex].Bottom, Top + Height);

                    //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, top - Top);
                        wasCaptured = true;
                    }
                }

                #endregion

                if (!wasCaptured)
                {
                    //Nothing was changed within the capture region, so ignore this frame.
                    resource.Dispose();
                    return FrameCount;
                }
            }

            #endregion

            #region Gets the image data

            //Gets the staging texture as a stream.
            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);
            }

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

            //Set frame details.
            FrameCount++;
            frame.Path = $"{Project.FullPath}{FrameCount}.png";
            frame.Delay = FrameRate.GetMilliseconds();
            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 success in capturing it.
                if (res.Success)
                    DuplicatedOutput.ReleaseFrame();
            }
            catch (Exception e)
            {
                LogWriter.Log(e, "It was not possible to release the frame.");
            }
        }
    }

白板数据:

定时获取白板内容:

private void Normal_Elapsed(object sender, EventArgs e)
    {
        var fileName = $"{Project.FullPath}{FrameCount}.png";

        //TODO: GetRender fails to create useful image when the control has decimals values as size.

        var render = MainBorder.GetRender(_dpi); //TODO: Too heavy! Maybe just save the strokes? like layers?

        Project.Frames.Add(new FrameInfo(fileName, FrameRate.GetMilliseconds()));

        ThreadPool.QueueUserWorkItem(delegate { AddFrames(fileName, render); });

        FrameCount++;
    }

数据加入每一帧中:

   private void AddFrames(string fileName, BitmapSource bitmap)
    {
        //var mutexLock = new Mutex(false, bitmap.GetHashCode().ToString());
        //mutexLock.WaitOne();

        using (var stream = new FileStream(fileName, FileMode.Create))
        {
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(bitmap));
            encoder.Save(stream);
            stream.Flush();
            stream.Close();
        }

        //GC.Collect(1);
        //mutexLock.ReleaseMutex();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go2coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值