簡體   English   中英

使用 Windows Graphics Capture API 暫停/恢復屏幕錄制

[英]Pausing / Resuming Screen recording with Windows Graphics Capture API

我正在使用Windows Graphics Capture API 在 C# 構建一個屏幕錄制應用程序。我正在使用這個腳本 我可以 select 監控並可以將其錄制到 mp4 文件。 我正在嘗試添加暫停/恢復功能。

這是啟動錄音的主要代碼 Window

try
{
    newFile = GetTempFile();
    using (var stream = new FileStream(newFile, FileMode.CreateNew).AsRandomAccessStream())
    using (_encoder = new Encoder(_device, item))
    {
        await _encoder.EncodeAsync(
            stream,
            width, height, bitrate,
            frameRate);
    }
}
catch (Exception ex)
{
  //
}

這是上面使用的編碼器 class的主要 function

private async Task EncodeInternalAsync(IRandomAccessStream stream, uint width, uint height, uint bitrateInBps, uint frameRate)
{
    if (!_isRecording)
    {
        _isRecording = true;

        _frameGenerator = new CaptureFrameWait(
            _device,
            _captureItem,
            _captureItem.Size);

        using (_frameGenerator)
        {
            var encodingProfile = new MediaEncodingProfile();
            encodingProfile.Container.Subtype = "MPEG4";
            encodingProfile.Video.Subtype = "H264";
            encodingProfile.Video.Width = width;
            encodingProfile.Video.Height = height;
            encodingProfile.Video.Bitrate = bitrateInBps;
            encodingProfile.Video.FrameRate.Numerator = frameRate;
            encodingProfile.Video.FrameRate.Denominator = 1;
            encodingProfile.Video.PixelAspectRatio.Numerator = 1;
            encodingProfile.Video.PixelAspectRatio.Denominator = 1;
            var transcode = await _transcoder.PrepareMediaStreamSourceTranscodeAsync(_mediaStreamSource, stream, encodingProfile);

            await transcode.TranscodeAsync();
        }
    }
}

最后這是CaptureFrameWait class 中的初始化器 function

private void InitializeCapture(SizeInt32 size)
{
    _framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(
        _device,
        DirectXPixelFormat.B8G8R8A8UIntNormalized,
        1,
        size);
    _framePool.FrameArrived += OnFrameArrived;
    _session = _framePool.CreateCaptureSession(_item);
    _session.IsBorderRequired = false;
    _session.StartCapture();
}

我們如何修改它以暫停錄制? 我試圖在暫停時處理_framepool_session對象,並在CaptureFrameWait class 中的恢復時再次初始化它們,如下所示。 它工作正常,但有時 TranscodeAsync function 在暫停期間終止並結束錄制。 我們怎樣才能避免這種情況?

bool _paused = false;
public void PauseSession(bool status)
{
    if (status) {
        _paused = true;
        _framePool?.Dispose();
        _session?.Dispose();
    }
    else {
        InitializeCapture(_size);
        _paused = false;
    }
}

一種解決方案是延期 醫生說:

然后 MediaStreamSource 將等待您提供 MediaStreamSample,直到您將延遲標記為完成。

因此,例如,將兩個私有成員添加到Encoder class 和兩個方法:

private MediaStreamSourceSampleRequestedEventArgs _args;
private MediaStreamSourceSampleRequestDeferral _def;

public bool IsPaused { get; private set; }

public void Pause()
{
    IsPaused = true;
}

public void Resume()
{
    IsPaused = false;

    // complete the request we saved earlier
    OnMediaStreamSourceSampleRequested(_mediaStreamSource, _args);
}

並像這樣修改OnMediaStreamSourceSampleRequested方法(我在其中發表評論):

private void OnMediaStreamSourceSampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
    if (_isRecording && !_closed)
    {
        // if paused get a deferral and save the current arguments.
        // OnMediaStreamSourceSampleRequested will not be called again until we complete the deferral
        if (IsPaused)
        {
            _def = args.Request.GetDeferral();
            _args = args;
            return;
        }

        try
        {
            using (var frame = _frameGenerator.WaitForNewFrame())
            {
                if (frame == null)
                {
                    args.Request.Sample = null;
                    DisposeInternal();
                    return;
                }

                var timeStamp = frame.SystemRelativeTime;

                var sample = MediaStreamSample.CreateFromDirect3D11Surface(frame.Surface, timeStamp);
                args.Request.Sample = sample;

                // when called again (manually by us) complete the work
                // and reset members
                if (_def != null)
                {
                    _def.Complete();
                    _def = null;
                    _args = null;
                }
            }
        }
        catch (Exception e)
        {
          ...
        }
    }
    else
    {
      ...
    }
}

另一種解決方案是簡單地凍結幀時間戳,因此添加這些成員:

private TimeSpan _pausedTimestamp;
public bool IsPaused { get; private set; }

public void Pause()
{
    IsPaused = true;
}

public void Resume()
{
    IsPaused = false;
}

並像這樣修改OnMediaStreamSourceSampleRequested方法(我在其中發表評論):

private void OnMediaStreamSourceSampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
    if (_isRecording && !_closed)
    {
        try
        {
            using (var frame = _frameGenerator.WaitForNewFrame())
            {
                if (frame == null)
                {
                    args.Request.Sample = null;
                    DisposeInternal();
                    return;
                }

                // if paused, "freeze" the timestamp
                TimeSpan timeStamp;
                if (IsPaused)
                {
                    timeStamp = _pausedTimestamp;
                }
                else
                {
                    timeStamp = frame.SystemRelativeTime;
                    _pausedTimestamp = timeStamp;
                }

                var sample = MediaStreamSample.CreateFromDirect3D11Surface(frame.Surface, timeStamp);
                args.Request.Sample = sample;
            }
        }
        catch (Exception e)
        {
          ...
        }
    }
    else
    {
      ...
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM