Captura de tela

O espaço de nomes Windows.Graphics.Capture fornece APIs para capturar fotogramas de um ecrã ou de uma janela de aplicação, para criar fluxos de vídeo ou instantâneos e criar experiências colaborativas e interativas.

Com a captura de ecrã, os programadores invocam uma interface segura do sistema para que os utilizadores finais escolham a janela de ecrã ou aplicação a capturar, e uma borda amarela de notificação é desenhada pelo sistema em torno do item capturado ativamente. No caso de múltiplas sessões de captura simultâneas, é desenhada uma borda amarela em torno de cada item capturado.

Note

As APIs de captura de ecrã são suportadas apenas em dispositivos desktop Windows e headsets imersivos Windows Mixed Reality.

Este artigo descreve a captura de uma única imagem do ecrã ou da janela da aplicação.

Verifique se há suporte para captura de ecrã

Antes de tentar capturar, verifique se o dispositivo atual suporta captura de ecrã. Use o método IsSupported no GraphicsCaptureSession para determinar se a captura de ecrã está disponível:

// Check if screen capture is supported
if (!GraphicsCaptureSession.IsSupported())
{
    // Hide capture UI
    CaptureControlsPanel.Visibility = Visibility.Collapsed;
    PreviewPlaceholderText.Text = "Screen capture isn't supported on this device.";
    UpdateStatus("Screen capture isn't supported on this device.");
    return;
}

Existem várias razões pelas quais a captura de ecrã pode não ser suportada, incluindo se o dispositivo não cumprir os requisitos de hardware.

Inicie a interface do sistema para iniciar a captura de ecrã

Utilize a classe GraphicsCapturePicker para invocar a interface de utilizador do seletor do sistema. O utilizador final usa esta interface para selecionar a janela de ecrã ou aplicação a capturar. O seletor devolve um GraphicsCaptureItem que é usado para criar uma sessão de captura.

Numa aplicação WinUI 3, tem de inicializar o seletor com o manipulador da janela antes de chamar PickSingleItemAsync:

// Launch picker and start capture
var picker = new GraphicsCapturePicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
GraphicsCaptureItem item = await picker.PickSingleItemAsync();
if (item != null)
{
    StartCaptureInternal(item);
}

Crie um pool de frames de captura e uma sessão de captura

Usando o GraphicsCaptureItem, crie um Direct3D11CaptureFramePool com o seu dispositivo D3D, um formato de píxel suportado (DXGI_FORMAT_B8G8R8A8_UNORM), número de frames desejados (que pode ser qualquer número inteiro) e tamanho de frame. A propriedade Size da classe GraphicsCaptureItem pode ser usada como o tamanho do seu frame:

// Create frame pool and capture session
_framePool = Direct3D11CaptureFramePool.Create(
    _canvasDevice,
    CaptureDirectXPixelFormat.B8G8R8A8UIntNormalized,
    BufferCount,
    item.Size);
_session = _framePool.CreateCaptureSession(item);

Note

Em sistemas com a Cor HD do Windows ativada, o formato de píxel do conteúdo pode não ser, necessariamente, DXGI_FORMAT_B8G8R8A8_UNORM. Para evitar o recorte excessivo dos píxeis (ou seja, o conteúdo capturado parece desbotado) ao capturar conteúdo HDR, considere utilizar DXGI_FORMAT_R16G16B16A16_FLOAT em todos os componentes do pipeline de captura, incluindo o Direct3D11CaptureFramePool e o destino, como CanvasBitmap. Dependendo da necessidade, pode ser necessário processamento adicional, como guardar em formato de conteúdo HDR ou mapeamento tonal de HDR para SDR. Este artigo foca-se na captura de conteúdo SDR. Para mais informações, consulte Usar DirectX com ecrãs de alta gama dinâmica e cor avançada.

Uma vez que o utilizador tenha dado consentimento explícito para capturar uma janela ou exibição de uma aplicação na interface do sistema, o GraphicsCaptureItem pode ser associado a múltiplas sessões de captura. Desta forma, a sua aplicação pode escolher capturar o mesmo item para várias experiências.

Adquirir fotogramas de captura

Com o seu conjunto de frames e sessão de captura criados, chame StartCapture na sua instância de GraphicsCaptureSession para notificar o sistema para começar a enviar imagens de captura para a sua aplicação.

Para adquirir estes frames de captura, que são objetos Direct3D11CaptureFrame , utilize o evento Direct3D11CaptureFramePool.FrameArrived :

// Handle frame arrival
_framePool.FrameArrived += OnFrameArrived;

Recomenda-se evitar realizar operações pesadas no thread da interface para FrameArrived, pois este evento é acionado sempre que um novo frame fica disponível. Se optares por processar FrameArrived na thread da IU, tem em atenção a quantidade de trabalho que fazes de cada vez que o evento é acionado.

Alternativamente, podes puxar manualmente os frames com o método Direct3D11CaptureFramePool.TryGetNextFrame até conseguires todos os frames de que precisas.

O objeto Direct3D11CaptureFrame contém as propriedades ContentSize, Surface e SystemRelativeTime. O SystemRelativeTime é o tempo QPC (QueryPerformanceCounter) que pode ser usado para sincronizar outros elementos multimédia.

Quadros de captura de processos

Cada frame do Direct3D11CaptureFramePool é obtido ao chamar TryGetNextFrame e devolvido de acordo com o ciclo de vida do objeto Direct3D11CaptureFrame. Para aplicações geridas, recomenda-se usar o método Direct3D11CaptureFrame.Dispose . O Direct3D11CaptureFrame implementa o IDisposable, pelo que ao descartar o frame devolve o buffer ao pool.

As aplicações não devem guardar referências a objetos Direct3D11CaptureFrame , nem devem guardar referências à superfície Direct3D subjacente depois de o frame ter sido novamente registado.

Neste exemplo, cada frame é convertido num CanvasBitmap, que faz parte da biblioteca Win2D :

// Convert frame to Win2D bitmap and display
CanvasBitmap canvasBitmap = CanvasBitmap.CreateFromDirect3D11Surface(
    _canvasDevice,
    frame.Surface);

A superfície Direct3D subjacente tem sempre o tamanho especificado ao criar (ou recriar) o Direct3D11CaptureFramePool. Se o conteúdo for maior do que a moldura, o conteúdo é cortado ao tamanho da moldura. Se o conteúdo for menor do que o frame, então o resto do frame contém dados indefinidos. Recomenda-se que as aplicações copiem um sub-retângulo usando a propriedade ContentSize desse Direct3D11CaptureFrame para evitar a apresentação de conteúdo indefinido.

Guardar uma captura de ecrã

Depois de tiveres um CanvasBitmap, podes guardá-lo como ficheiro de imagem. O exemplo seguinte guarda o frame atual como ficheiro PNG usando um FileSavePicker. Numa aplicação WinUI 3, tens de inicializar o seletor com o handler da janela:

// Save screenshot
var savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
savePicker.SuggestedFileName = "screen-capture";
savePicker.FileTypeChoices.Add("PNG image", new List<string> { ".png" });
WinRT.Interop.InitializeWithWindow.Initialize(savePicker, _hwnd);
StorageFile? file = await savePicker.PickSaveFileAsync();
if (file is not null)
{
    using var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite);
    await frameToSave.SaveAsync(fileStream, CanvasBitmapFileFormat.Png, 1f);
}

Reagir para capturar o redimensionamento de itens ou dispositivo perdido

Durante o processo de captura, as aplicações podem desejar alterar aspetos do seu Direct3D11CaptureFramePool. Isto inclui fornecer um novo dispositivo Direct3D, alterar o tamanho dos buffers de frames ou até alterar o número de buffers dentro do pool. Em cada um destes cenários, o método Recriar no objeto Direct3D11CaptureFramePool é a abordagem recomendada.

Quando Recreate é invocado, todos os fotogramas existentes são descartados. Isto serve para evitar a distribuição de frames cujas superfícies Direct3D subjacentes pertencem a um dispositivo ao qual a aplicação já não tenha acesso. Por esta razão, pode ser sensato processar todos os fotogramas pendentes antes de chamar Recreate.

Considerações sobre o WinUI 3

Ao migrar código de captura de ecrã do UWP para o WinUI 3 (SDK de Aplicações Windows), tenha em conta as seguintes diferenças:

  • Inicialização do identificador da janela — Seletores como GraphicsCapturePicker e FileSavePicker têm de ser inicializados com o identificador da janela através de InitializeWithWindow. Para mais informações, consulte Recuperar um identificador de janela (HWND).
  • APIs de composição no thread UI — No WinUI 3, as operações de superfície CanvasComposition (como desenhar para um CompositionDrawingSurface) devem ser despachadas para o thread UI usando DispatcherQueue.TryEnqueue. A captura de frames e a criação de bitmap podem acontecer no thread de fundo do frame pool, mas a atualização do visual de composição tem de ser feita no thread da interface.
  • Alterações ao espaço de nomes — Utilize Microsoft.UI.Composition, Microsoft.UI.Xaml.Hosting e Microsoft.UI.Dispatching em vez dos equivalentes em Windows.UI. O espaço de nomes Windows.Graphics.Capture mantém-se inalterado.

Consulte também