Partilhar via


Captura de tela para vídeo

Este artigo descreve como codificar quadros capturados da tela com as APIs Windows.Graphics.Capture para um arquivo de vídeo. Para saber mais sobre a captura de tela de imagens estáticas, confira Captura de tela. Para obter um aplicativo de exemplo simples de ponta a ponta que utiliza os conceitos e as técnicas mostradas neste artigo, confira SimpleRecorder.

Visão geral do processo de captura de vídeo

Este artigo fornece um passo a passo de um aplicativo de exemplo que registra o conteúdo de uma janela em um arquivo de vídeo. Embora possa parecer que há muito código necessário para implementar esse cenário, a estrutura de alto nível de um aplicativo gravador de tela é bem simples. O processo de captura de tela usa três recursos UWP principais:

O código de exemplo mostrado neste artigo pode ser categorizado em algumas tarefas distintas:

  • Inicialização - Isso inclui a configuração das classes UWP descritas acima, a inicialização das interfaces do dispositivo gráfico, a escolha de uma janela para captura e a configuração dos parâmetros de codificação, como resolução e taxa de quadros.
  • Manipuladores de eventos e threading - O driver principal do loop de captura principal é o MediaStreamSource que solicita quadros periodicamente por meio do evento SampleRequested. Este exemplo usa eventos para coordenar as solicitações de novos quadros entre os componentes distintos do exemplo. A sincronização é importante para permitir a captura e a codificação simultâneas de quadros.
  • Copiando quadros - os quadros são copiados do buffer de quadros de captura em uma superfície Direct3D separada que pode ser passada para o MediaStreamSource para que o recurso não seja substituído durante a codificação. As APIs do Direct3D são usadas para executar essa operação de cópia rapidamente.

Sobre as APIs do Direct3D

Como indicado acima, a cópia de cada quadro capturado é provavelmente a parte mais complexa da implementação mostrada neste artigo. Em um nível inferior, essa operação é realizada usando o Direct3D. Para este exemplo, estamos usando a biblioteca SharpDX para executar as operações Direct3D do C#. Essa biblioteca não tem mais suporte oficial, mas foi escolhida porque seu desempenho em operações de cópia de baixo nível é adequado a esse cenário. Tentamos manter as operações Direct3D o mais discretas possível para facilitar a substituição de seu próprio código ou outras bibliotecas por essas tarefas.

Configurar seu projeto

O código de exemplo neste passo a passo foi criado usando o modelo de projeto C# Aplicativo em Branco (Universal Windows) no Visual Studio 2019. Para usar as APIs Windows.Graphics.Capture em seu aplicativo, você deve incluir o recurso Captura de Gráficos no arquivo Package.appxmanifest do seu projeto. Este exemplo salva arquivos de vídeo gerados na Biblioteca de Vídeos no dispositivo. Para acessar essa pasta, você deve incluir o recurso Biblioteca de Vídeos.

Para instalar o pacote Nuget do SharpDX, no Visual Studio, selecione Gerenciar Pacotes Nuget. Na guia Procurar, procure o pacote "SharpDX.Direct3D11" e clique em Instalar.

Observe que, para reduzir o tamanho das listagens de código neste artigo, o código no passo a passo abaixo omite referências explícitas de namespace e a declaração de variáveis de membro da classe MainPage que são nomeadas com um sublinhado à esquerda, "_".

Configuração para codificação

O método SetupEncoding descrito nesta seção inicializa alguns dos principais objetos que serão usados para capturar e codificar quadros de vídeo, e configura os parâmetros de codificação para vídeo capturado. Esse método pode ser chamado de forma programática ou em resposta a uma interação do usuário, como um clique no botão. A listagem de código para SetupEncoding é mostrada abaixo após as descrições das etapas de inicialização.

  • Verifique se há suporte para captura. Antes de iniciar o processo de captura, você precisa chamar GraphicsCaptureSession.IsSupported para certificar-se de que o recurso de captura de tela tenha suporte no dispositivo atual.

  • Inicialize interfaces Direct3D. Este exemplo usa o Direct3D para copiar os pixels capturados da tela em uma textura codificada como um quadro de vídeo. Os métodos auxiliares usados para inicializar as interfaces, CreateD3DDevice e CreateSharpDXDevice, são mostrados mais adiante neste artigo.

  • Inicialize um GraphicsCaptureItem. Um GraphicsCaptureItem representa um item na tela que será capturado, seja uma janela ou a tela inteira. Permita que o usuário escolha um item a ser capturado criando um GraphicsCapturePicker e chamando PickSingleItemAsync.

  • Crie uma textura de composição. Crie um recurso de textura e uma exibição de destino de renderização associada que será usada para copiar cada quadro de vídeo. Essa textura não poderá ser criada até que o GraphicsCaptureItem tenha sido criado e saibamos suas dimensões. Consulte a descrição do WaitForNewFrame para ver como essa textura de composição é usada. O método auxiliar para criar essa textura também é mostrado mais adiante neste artigo.

  • Crie um MediaEncodingProfile e VideoStreamDescriptor. Uma instância da classe MediaStreamSource obterá imagens capturadas da tela e as codificará em um fluxo de vídeo. Depois, o fluxo de vídeo será transcodificado em um arquivo de vídeo pela classe MediaTranscoder. Um VideoStreamDecriptor fornece parâmetros de codificação, como resolução e taxa de quadros, para o MediaStreamSource. Os parâmetros de codificação de arquivo de vídeo para o MediaTranscoder são especificados com um MediaEncodingProfile. Observe que o tamanho usado para codificação de vídeo não precisa ser igual ao tamanho da janela que está sendo capturada, mas para manter este exemplo simples, as configurações de codificação são definidas para usar as dimensões reais do item de captura.

  • Crie os objetos MediaStreamSource e MediaTranscoder. Como mencionado acima, o objeto MediaStreamSource codifica quadros individuais em um fluxo de vídeo. Chame o construtor para essa classe, passando o MediaEncodingProfile criado na etapa anterior. Defina o tempo do buffer como zero e registre manipuladores para os eventos Starting e SampleRequested, que serão mostrados mais adiante neste artigo. Depois, construa uma nova instância da classe MediaTranscoder e habilite a aceleração de hardware.

  • Criar um arquivo de saída A etapa final desse método é criar um arquivo para o qual o vídeo será transcodificado. Para este exemplo, criaremos apenas um arquivo com nome exclusivo na pasta Biblioteca de Vídeos no dispositivo. Observe que, para acessar essa pasta, seu aplicativo deve especificar o recurso "Biblioteca de Vídeos" no manifesto do aplicativo. Após a criação do arquivo, abra-o para leitura e gravação e passe o fluxo resultante para o método EncodeAsync que será mostrado a seguir.

private async Task SetupEncoding()
{
    if (!GraphicsCaptureSession.IsSupported())
    {
        // Show message to user that screen capture is unsupported
        return;
    }

    // Create the D3D device and SharpDX device
    if (_device == null)
    {
        _device = Direct3D11Helpers.CreateD3DDevice();
    }
    if (_sharpDxD3dDevice == null)
    {
        _sharpDxD3dDevice = Direct3D11Helpers.CreateSharpDXDevice(_device);
    }
    


    try
    {
        // Let the user pick an item to capture
        var picker = new GraphicsCapturePicker();
        _captureItem = await picker.PickSingleItemAsync();
        if (_captureItem == null)
        {
            return;
        }

        // Initialize a blank texture and render target view for copying frames, using the same size as the capture item
        _composeTexture = Direct3D11Helpers.InitializeComposeTexture(_sharpDxD3dDevice, _captureItem.Size);
        _composeRenderTargetView = new SharpDX.Direct3D11.RenderTargetView(_sharpDxD3dDevice, _composeTexture);

        // This example encodes video using the item's actual size.
        var width = (uint)_captureItem.Size.Width; 
        var height = (uint)_captureItem.Size.Height;

        // Make sure the dimensions are are even. Required by some encoders.
        width = (width % 2 == 0) ? width : width + 1;
        height = (height % 2 == 0) ? height : height + 1;


        var temp = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD1080p);
        var bitrate = temp.Video.Bitrate;
        uint framerate = 30;

        _encodingProfile = new MediaEncodingProfile();
        _encodingProfile.Container.Subtype = "MPEG4";
        _encodingProfile.Video.Subtype = "H264";
        _encodingProfile.Video.Width = width;
        _encodingProfile.Video.Height = height;
        _encodingProfile.Video.Bitrate = bitrate;
        _encodingProfile.Video.FrameRate.Numerator = framerate;
        _encodingProfile.Video.FrameRate.Denominator = 1;
        _encodingProfile.Video.PixelAspectRatio.Numerator = 1;
        _encodingProfile.Video.PixelAspectRatio.Denominator = 1;

        var videoProperties = VideoEncodingProperties.CreateUncompressed(MediaEncodingSubtypes.Bgra8, width, height);
        _videoDescriptor = new VideoStreamDescriptor(videoProperties);

        // Create our MediaStreamSource
        _mediaStreamSource = new MediaStreamSource(_videoDescriptor);
        _mediaStreamSource.BufferTime = TimeSpan.FromSeconds(0);
        _mediaStreamSource.Starting += OnMediaStreamSourceStarting;
        _mediaStreamSource.SampleRequested += OnMediaStreamSourceSampleRequested;

        // Create our transcoder
        _transcoder = new MediaTranscoder();
        _transcoder.HardwareAccelerationEnabled = true;


        // Create a destination file - Access to the VideosLibrary requires the "Videos Library" capability
        var folder = KnownFolders.VideosLibrary;
        var name = DateTime.Now.ToString("yyyyMMdd-HHmm-ss");
        var file = await folder.CreateFileAsync($"{name}.mp4");
        
        using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))

        await EncodeAsync(stream);
        
    }
    catch (Exception ex)
    {
        
        return;
    }
}

Iniciar codificação

Agora que os objetos principais foram inicializados, o método EncodeAsync é implementado para iniciar a operação de captura. Esse método primeiro verifica se ainda não estamos gravando e, caso contrário, chama o método auxiliar StartCapture para começar a capturar quadros da tela. Esse método será mostrado mais adiante neste artigo. Depois, PrepareMediaStreamSourceTranscodeAsync é chamado para obter o objeto MediaTranscoder pronto para transcodificar o fluxo de vídeo produzido pelo objeto MediaStreamSource para o fluxo do arquivo de saída, usando o perfil de codificação que criamos na seção anterior. Após o preparo do transcodificador, chame TranscodeAsync para iniciar a transcodificação. Para saber mais sobre como usar o MediaTranscoder, consulte Transcodificar arquivos de mídia.


private async Task EncodeAsync(IRandomAccessStream stream)
{
    if (!_isRecording)
    {
        _isRecording = true;

        StartCapture();

        var transcode = await _transcoder.PrepareMediaStreamSourceTranscodeAsync(_mediaStreamSource, stream, _encodingProfile);

        await transcode.TranscodeAsync();
    }
}

Manipular eventos MediaStreamSource

O objeto MediaStreamSource obtém quadros que capturamos da tela e os transforma em um fluxo de vídeo que pode ser salvo em um arquivo usando o MediaTranscoder. Passamos os quadros para o MediaStreamSource por meio de manipuladores para os eventos do objeto.

O evento SampleRequested é gerado quando o MediaStreamSource está pronto para um novo quadro de vídeo. Depois de garantir que estamos gravando no momento, o método auxiliar WaitForNewFrame será chamado para obter um novo quadro capturado da tela. Esse método, mostrado posteriormente neste artigo, retorna um objeto ID3D11Surface que contém o quadro capturado. Para este exemplo, encapsulamos a interface IDirect3DSurface em uma classe auxiliar que também armazena a hora do sistema em que o quadro foi capturado. O quadro e a hora do sistema são passados para o método de fábrica MediaStreamSample.CreateFromDirect3D11Surface e o MediaStreamSample resultante é definido como a propriedade MediaStreamSourceSampleRequest.Sample do MediaStreamSourceSampleRequestedEventArgs. É assim que o quadro capturado é fornecido ao MediaStreamSource.

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

                var timeStamp = frame.SystemRelativeTime;

                var sample = MediaStreamSample.CreateFromDirect3D11Surface(frame.Surface, timeStamp);
                args.Request.Sample = sample;
            }
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            Debug.WriteLine(e.StackTrace);
            Debug.WriteLine(e);
            args.Request.Sample = null;
            Stop();
            Cleanup();
        }
    }
    else
    {
        args.Request.Sample = null;
        Stop();
        Cleanup();
    }
}

No manipulador do evento Starting, chamamos WaitForNewFrame, mas apenas passamos a hora em que o quadro foi capturado para o método MediaStreamSourceStartingRequest.SetActualStartPosition, que o MediaStreamSource usa para codificar corretamente o tempo dos quadros subsequentes.

private void OnMediaStreamSourceStarting(MediaStreamSource sender, MediaStreamSourceStartingEventArgs args)
{
    using (var frame = WaitForNewFrame())
    {
        args.Request.SetActualStartPosition(frame.SystemRelativeTime);
    }
}

Comece a capturar

O método StartCapture mostrado nesta etapa é chamado a partir do método auxiliar EncodeAsync mostrado em uma etapa anterior. Primeiro, esse método inicializa um conjunto de objetos de evento que são usados para controlar o fluxo da operação de captura.

  • _multithread é uma classe auxiliar que encapsula o objeto Multithread da biblioteca SharpDX que será usado para garantir que nenhum outro thread acesse a textura SharpDX enquanto ela está sendo copiada.
  • _frameEvent_frameEvent é usado para sinalizar que um novo quadro foi capturado e pode ser passado para o MediaStreamSource
  • _closedEvent sinaliza que a gravação parou e que não devemos esperar por novos quadros.

Os eventos frame e closed são adicionados a uma matriz para que possamos aguardar um deles no loop de captura.

O restante do método StartCapture configura as APIs Windows.Graphics.Capture que farão a captura de tela real. Primeiro, um evento é registrado para o evento CaptureItem.Closed. Depois, um Direct3D11CaptureFramePool é criado, permitindo que vários quadros capturados sejam armazenados em buffer ao mesmo tempo. O método CreateFreeThreaded é usado para criar o pool de quadros para que o evento FrameArrived seja chamado no próprio thread de trabalho do pool, e não no thread principal do aplicativo. Depois, um manipulador é registrado para o evento FrameArrived. Finalmente, um GraphicsCaptureSession é criado para o CaptureItem selecionado e a captura de quadros é iniciada chamando StartCapture.

public void StartCapture()
{

    _multithread = _sharpDxD3dDevice.QueryInterface<SharpDX.Direct3D11.Multithread>();
    _multithread.SetMultithreadProtected(true);
    _frameEvent = new ManualResetEvent(false);
    _closedEvent = new ManualResetEvent(false);
    _events = new[] { _closedEvent, _frameEvent };

    _captureItem.Closed += OnClosed;
    _framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(
        _device,
        DirectXPixelFormat.B8G8R8A8UIntNormalized,
        1,
        _captureItem.Size);
    _framePool.FrameArrived += OnFrameArrived;
    _session = _framePool.CreateCaptureSession(_captureItem);
    _session.StartCapture();
}

Manipular eventos de captura de gráficos

Na etapa anterior, registramos dois manipuladores para eventos de captura de gráficos e configuramos alguns eventos para ajudar a gerenciar o fluxo do loop de captura.

O evento FrameArrived é gerado quando o Direct3D11CaptureFramePool tem um novo quadro capturado disponível. No manipulador desse evento, chame TryGetNextFrame no remetente para obter o próximo quadro capturado. Depois que o quadro é recuperado, definimos o _frameEvent para que nosso loop de captura saiba que existe um novo quadro disponível.

private void OnFrameArrived(Direct3D11CaptureFramePool sender, object args)
{
    _currentFrame = sender.TryGetNextFrame();
    _frameEvent.Set();
}

No manipulador de eventos Closed, sinalizamos _closedEvent para que o loop de captura saiba quando parar.

private void OnClosed(GraphicsCaptureItem sender, object args)
{
    _closedEvent.Set();
}

Aguarde novos quadros

O método auxiliar WaitForNewFrame descrito nesta seção é onde ocorre o trabalho pesado do loop de captura. Lembre-se de que esse método é chamado do manipulador de eventos OnMediaStreamSourceSampleRequested sempre que o MediaStreamSource está pronto para um novo quadro a ser adicionado ao fluxo de vídeo. Em alto nível, essa função simplesmente copia cada quadro de vídeo capturado na tela de uma superfície Direct3D para outra para que ele possa ser passado para o MediaStreamSource para codificação enquanto um novo quadro está sendo capturado. Este exemplo usa a biblioteca SharpDX para executar a operação de cópia real.

Antes de aguardar um novo quadro, o método descarta qualquer quadro anterior armazenado na variável de classe, _currentFrame, e redefine o _frameEvent. Depois, o método aguarda a sinalização de _frameEvent ou de _closedEvent. Se o evento fechado estiver definido, o aplicativo chamará um método auxiliar para limpar os recursos de captura. Esse método será mostrado mais adiante neste artigo.

Se o evento frame estiver definido, saberemos que o manipulador de eventos FrameArrived definido na etapa anterior foi chamado e iniciaremos o processo de cópia dos dados de quadro capturados em uma superfície Direct3D 11 que será passada para o MediaStreamSource.

Este exemplo usa uma classe auxiliar, SurfaceWithInfo, que simplesmente nos permite passar o quadro de vídeo e a hora do sistema do quadro - ambos exigidos pelo MediaStreamSource - como um único objeto. A primeira etapa do processo de cópia de quadro é instanciar essa classe e definir a hora do sistema.

As próximas etapas são a parte deste exemplo que depende especificamente da biblioteca SharpDX. As funções auxiliares usadas aqui são definidas no final deste artigo. Primeiro, usamos o MultiThreadLock para garantir que nenhum outro thread acesse o buffer de quadros de vídeo enquanto estamos fazendo a cópia. Depois, chamamos o método auxiliar CreateSharpDXTexture2D para criar um objeto Texture2D de SharpDX a partir do quadro de vídeo. Esta será a textura de origem para a operação de cópia.

Depois, copiamos do objeto Texture2D criado na etapa anterior para a textura de composição que criamos anteriormente no processo. Essa textura de composição atua como um buffer de permuta para que o processo de codificação possa operar nos pixels enquanto o próximo quadro está sendo capturado. Para executar a cópia, limpamos a exibição de destino de renderização associada à textura de composição, definimos a região na textura que desejamos copiar - a textura inteira neste caso e, depois, chamamos CopySubresourceRegion para de fato copiar os pixels para a textura de composição.

Criamos uma cópia da descrição da textura para usar quando criamos nossa textura de destino, mas a descrição é modificada, definindo BindFlags como RenderTarget para que a nova textura tenha acesso de gravação. Definir o CpuAccessFlags como Nenhum permite que o sistema otimize a operação de cópia. A descrição da textura é usada para criar um novo recurso de textura e o recurso de textura de composição é copiado para esse novo recurso com uma chamada para CopyResource. Finalmente, CreateDirect3DSurfaceFromSharpDXTexture é chamado para criar o objeto IDirect3DSurface que é retornado desse método.

public SurfaceWithInfo WaitForNewFrame()
{
    // Let's get a fresh one.
    _currentFrame?.Dispose();
    _frameEvent.Reset();

    var signaledEvent = _events[WaitHandle.WaitAny(_events)];
    if (signaledEvent == _closedEvent)
    {
        Cleanup();
        return null;
    }

    var result = new SurfaceWithInfo();
    result.SystemRelativeTime = _currentFrame.SystemRelativeTime;
    using (var multithreadLock = new MultithreadLock(_multithread))
    using (var sourceTexture = Direct3D11Helpers.CreateSharpDXTexture2D(_currentFrame.Surface))
    {

        _sharpDxD3dDevice.ImmediateContext.ClearRenderTargetView(_composeRenderTargetView, new SharpDX.Mathematics.Interop.RawColor4(0, 0, 0, 1));

        var width = Math.Clamp(_currentFrame.ContentSize.Width, 0, _currentFrame.Surface.Description.Width);
        var height = Math.Clamp(_currentFrame.ContentSize.Height, 0, _currentFrame.Surface.Description.Height);
        var region = new SharpDX.Direct3D11.ResourceRegion(0, 0, 0, width, height, 1);
        _sharpDxD3dDevice.ImmediateContext.CopySubresourceRegion(sourceTexture, 0, region, _composeTexture, 0);

        var description = sourceTexture.Description;
        description.Usage = SharpDX.Direct3D11.ResourceUsage.Default;
        description.BindFlags = SharpDX.Direct3D11.BindFlags.ShaderResource | SharpDX.Direct3D11.BindFlags.RenderTarget;
        description.CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.None;
        description.OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None;

        using (var copyTexture = new SharpDX.Direct3D11.Texture2D(_sharpDxD3dDevice, description))
        {
            _sharpDxD3dDevice.ImmediateContext.CopyResource(_composeTexture, copyTexture);
            result.Surface = Direct3D11Helpers.CreateDirect3DSurfaceFromSharpDXTexture(copyTexture);
        }
    }

    return result;
}

Parar a captura e limpar recursos

O método Stop fornece uma maneira de parar a operação de captura. Seu aplicativo pode chamar isso programaticamente ou em resposta a uma interação do usuário, como um clique no botão. Este método simplesmente define o _closedEvent. O método WaitForNewFrame definido nas etapas anteriores procura esse evento e, se definido, desliga a operação de captura.

private void Stop()
{
    _closedEvent.Set();
}

O método Cleanup é usado para descartar corretamente os recursos que foram criados durante a operação de cópia. Isso inclui:

  • O objeto Direct3D11CaptureFramePool usado pela sessão de captura
  • O GraphicsCaptureSession e o GraphicsCaptureItem
  • Os dispositivos Direct3D e SharpDX
  • A textura SharpDX e a exibição de destino de renderização usadas na operação de cópia.
  • O Direct3D11CaptureFrame usado para armazenar o quadro atual.
private void Cleanup()
{
    _framePool?.Dispose();
    _session?.Dispose();
    if (_captureItem != null)
    {
        _captureItem.Closed -= OnClosed;
    }
    _captureItem = null;
    _device = null;
    _sharpDxD3dDevice = null;
    _composeTexture?.Dispose();
    _composeTexture = null;
    _composeRenderTargetView?.Dispose();
    _composeRenderTargetView = null;
    _currentFrame?.Dispose();
}

Classes de wrapper auxiliar

As classes auxiliares a seguir foram definidas para ajudar com o código de exemplo neste artigo.

A classe auxiliar MultithreadLock encapsula a classe Multithread de SharpDX que garante que outros threads não acessem os recursos de textura enquanto estão sendo copiados.

class MultithreadLock : IDisposable
{
    public MultithreadLock(SharpDX.Direct3D11.Multithread multithread)
    {
        _multithread = multithread;
        _multithread?.Enter();
    }

    public void Dispose()
    {
        _multithread?.Leave();
        _multithread = null;
    }

    private SharpDX.Direct3D11.Multithread _multithread;
}

SurfaceWithInfo é usado para associar um IDirect3DSurface a um SystemRelativeTime que representa um quadro capturado e a hora em que ele foi capturado, respectivamente.

public sealed class SurfaceWithInfo : IDisposable
{
    public IDirect3DSurface Surface { get; internal set; }
    public TimeSpan SystemRelativeTime { get; internal set; }

    public void Dispose()
    {
        Surface?.Dispose();
        Surface = null;
    }
}

APIs auxiliares do Direct3D e do SharpDX

As APIs auxiliares a seguir são definidas para abstrair a criação de recursos Direct3D e SharpDX. Uma explicação detalhada dessas tecnologias está fora do escopo deste artigo, mas o código é fornecido aqui para permitir que você implemente o código de exemplo mostrado no passo a passo.

[ComImport]
[Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IDirect3DDxgiInterfaceAccess
{
    IntPtr GetInterface([In] ref Guid iid);
};

public static class Direct3D11Helpers
{
    internal static Guid IInspectable = new Guid("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90");
    internal static Guid ID3D11Resource = new Guid("dc8e63f3-d12b-4952-b47b-5e45026a862d");
    internal static Guid IDXGIAdapter3 = new Guid("645967A4-1392-4310-A798-8053CE3E93FD");
    internal static Guid ID3D11Device = new Guid("db6f6ddb-ac77-4e88-8253-819df9bbf140");
    internal static Guid ID3D11Texture2D = new Guid("6f15aaf2-d208-4e89-9ab4-489535d34f9c");

    [DllImport(
        "d3d11.dll",
        EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice",
        SetLastError = true,
        CharSet = CharSet.Unicode,
        ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall
        )]
    internal static extern UInt32 CreateDirect3D11DeviceFromDXGIDevice(IntPtr dxgiDevice, out IntPtr graphicsDevice);

    [DllImport(
        "d3d11.dll",
        EntryPoint = "CreateDirect3D11SurfaceFromDXGISurface",
        SetLastError = true,
        CharSet = CharSet.Unicode,
        ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall
        )]
    internal static extern UInt32 CreateDirect3D11SurfaceFromDXGISurface(IntPtr dxgiSurface, out IntPtr graphicsSurface);

    public static IDirect3DDevice CreateD3DDevice()
    {
        return CreateD3DDevice(false);
    }

    public static IDirect3DDevice CreateD3DDevice(bool useWARP)
    {
        var d3dDevice = new SharpDX.Direct3D11.Device(
            useWARP ? SharpDX.Direct3D.DriverType.Software : SharpDX.Direct3D.DriverType.Hardware,
            SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport);
        IDirect3DDevice device = null;

        // Acquire the DXGI interface for the Direct3D device.
        using (var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device3>())
        {
            // Wrap the native device using a WinRT interop object.
            uint hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.NativePointer, out IntPtr pUnknown);

            if (hr == 0)
            {
                device = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DDevice;
                Marshal.Release(pUnknown);
            }
        }

        return device;
    }


    internal static IDirect3DSurface CreateDirect3DSurfaceFromSharpDXTexture(SharpDX.Direct3D11.Texture2D texture)
    {
        IDirect3DSurface surface = null;

        // Acquire the DXGI interface for the Direct3D surface.
        using (var dxgiSurface = texture.QueryInterface<SharpDX.DXGI.Surface>())
        {
            // Wrap the native device using a WinRT interop object.
            uint hr = CreateDirect3D11SurfaceFromDXGISurface(dxgiSurface.NativePointer, out IntPtr pUnknown);

            if (hr == 0)
            {
                surface = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DSurface;
                Marshal.Release(pUnknown);
            }
        }

        return surface;
    }



    internal static SharpDX.Direct3D11.Device CreateSharpDXDevice(IDirect3DDevice device)
    {
        var access = (IDirect3DDxgiInterfaceAccess)device;
        var d3dPointer = access.GetInterface(ID3D11Device);
        var d3dDevice = new SharpDX.Direct3D11.Device(d3dPointer);
        return d3dDevice;
    }

    internal static SharpDX.Direct3D11.Texture2D CreateSharpDXTexture2D(IDirect3DSurface surface)
    {
        var access = (IDirect3DDxgiInterfaceAccess)surface;
        var d3dPointer = access.GetInterface(ID3D11Texture2D);
        var d3dSurface = new SharpDX.Direct3D11.Texture2D(d3dPointer);
        return d3dSurface;
    }


    public static SharpDX.Direct3D11.Texture2D InitializeComposeTexture(
        SharpDX.Direct3D11.Device sharpDxD3dDevice,
        SizeInt32 size)
    {
        var description = new SharpDX.Direct3D11.Texture2DDescription
        {
            Width = size.Width,
            Height = size.Height,
            MipLevels = 1,
            ArraySize = 1,
            Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm,
            SampleDescription = new SharpDX.DXGI.SampleDescription()
            {
                Count = 1,
                Quality = 0
            },
            Usage = SharpDX.Direct3D11.ResourceUsage.Default,
            BindFlags = SharpDX.Direct3D11.BindFlags.ShaderResource | SharpDX.Direct3D11.BindFlags.RenderTarget,
            CpuAccessFlags = SharpDX.Direct3D11.CpuAccessFlags.None,
            OptionFlags = SharpDX.Direct3D11.ResourceOptionFlags.None
        };
        var composeTexture = new SharpDX.Direct3D11.Texture2D(sharpDxD3dDevice, description);
       

        using (var renderTargetView = new SharpDX.Direct3D11.RenderTargetView(sharpDxD3dDevice, composeTexture))
        {
            sharpDxD3dDevice.ImmediateContext.ClearRenderTargetView(renderTargetView, new SharpDX.Mathematics.Interop.RawColor4(0, 0, 0, 1));
        }

        return composeTexture;
    }
}

Confira também