共用方式為


將螢幕擷取到影片

本文介紹如何將使用 Windows.Graphics.Capture API 從螢幕擷取的畫面編碼為視訊檔案。 如需螢幕擷取靜止影像的相關資訊,請參閱螢幕擷取。 如需利用本文所示概念和技術的簡單端對端範例應用程式,請參閱SimpleRecorder

視訊擷取程序概述

本文提供範例應用程式的逐步解說,將視窗的內容記錄到視訊檔案。 雖然實作此案例可能需要許多程式碼,但螢幕錄製程式應用程式的高階結構相當簡單。 螢幕擷取程式使用三個主要UWP功能:

本文所示的範例程式碼可以分類為幾個不同的工作:

  • 初始化 - 這包括設定上述的 UWP 類別、初始化圖形裝置介面、挑選要擷取的視窗,以及設定編碼參數,例如解析度和畫面速率。
  • 事件處理常式和執行緒 - 主要擷取迴圈的主要驅動程式是 MediaStreamSource,它會定期透過 SampleRequested 事件要求畫面。 這個範例會使用事件來協調範例不同元件之間新畫面的要求。 同步處理對於允許同時擷取和編碼畫面很重要。
  • 複製畫面 - 畫面會從擷取畫面緩衝區複製到可傳遞至 MediaStreamSource 的個別 Direct3D 介面,以便在編碼時不會覆寫資源。 Direct3D API 可用來快速執行此複製作業。

關於 Direct3D API

如上所述,每個擷取畫面的複製可能是本文所示實作中最複雜的部分。 在低層級,此作業是使用 Direct3D 來完成。 在此範例中,我們使用 SharpDX 程式庫從 C# 執行 Direct3D 作業。 此程式庫已不再正式支援,但已選擇它,因為它在低層級複製作業的效能非常適合此案例。 我們嘗試盡可能讓 Direct3D 作業保持離散,讓您更輕鬆地取代您自己的程式碼或其他程式庫來執行這些工作。

設定您的專案

本逐步解說中的範例程式碼是使用 Visual Studio 2019中的空白應用程式 (通用 Windows) C# 專案範本所建立。 若要在應用程式中使用 Windows.Graphics.Capture API,您必須在專案的 Package.appxmanifest 檔案中包含圖形擷取功能。 本範例會將產生的視訊檔案儲存到裝置上的影片庫。 若要存取此資料夾,您必須包含影片庫功能。

若要安裝 SharpDX Nuget 套件,請在 Visual Studio 中選取 管理 Nuget 套件。 在 [瀏覽] 索引標籤中,搜尋「SharpDX.Direct3D11」套件,然後按一下安裝

請注意,為了減少本文中的程式碼清單大小,下列逐步解說中的程式碼會省略明確的命名空間參考和 MainPage 類別成員變數的宣告,這些變數是以前置底線「_」命名。

編碼的設定

本節所述的 SetupEncoding 方法會初始化一些將用來擷取和編碼視訊畫面的主要物件,並設定所擷取視訊的編碼參數。 這個方法可以用程式設計方式呼叫,或回應使用者互動,例如按一下按鈕。 SetupEncoding 的程式碼清單會顯示在初始化步驟的描述之後。

  • 檢查擷取支援。 開始擷取程式之前,您必須呼叫 GraphicsCaptureSession.IsSupported,以確保目前裝置上支援螢幕擷取功能。

  • 初始化 Direct3D 介面。 此範例會使用 Direct3D 將從畫面擷取的像素複製到編碼為視訊畫面的紋理。 本文稍後會顯示用來初始化 Direct3D 介面 CreateD3DDeviceCreateSharpDXDevice 的協助程式方法。

  • 初始化 GraphicsCaptureItem。 GraphicsCaptureItem 代表要擷取的畫面上專案,可能是視窗或整個畫面。 允許使用者建立 GraphicsCapturePicker 並呼叫 PickSingleItemAsync 來挑選要擷取的專案。

  • 建立組合紋理。 建立紋理資源和相關聯的轉譯目標檢視,用來複製每個視訊畫面。 在建立 GraphicsCaptureItem 且我們知道其維度之前,無法建立此紋理。 請參閱 WaitForNewFrame 的描述,以查看如何使用這個組合紋理。 本文稍後也會顯示建立此紋理的協助程式方法。

  • 建立 MediaEncodingProfile 和 VideoStreamDescriptor。 MediaStreamSource 類別的執行個體會擷取從畫面擷取的影像,並將其編碼成視訊串流。 然後,MediaTranscoder 類別會將視訊串流轉碼為視訊檔案。 VideoStreamDecriptor 提供 MediaStreamSource 的編碼參數,例如解析度和畫面速率。 MediaTranscoder 的視訊檔案編碼參數是使用 MediaEncodingProfile 來指定。 請注意,用於視訊編碼的大小不一定與所擷取的視窗大小相同,但為了讓這個範例保持簡單,編碼設定會硬式編碼,以使用擷取專案的實際維度。

  • 建立 MediaStreamSource 和 MediaTranscoder 物件。 如上所述,MediaStreamSource 物件會將個別畫面編碼成視訊串流。 呼叫這個類別的建構函式,傳入在上一個步驟中建立的 MediaEncodingProfile。 將緩衝區時間設定為零,並註冊 StartingSampleRequested 事件的處理常式,本文稍後將會顯示。 接下來,建構 MediaTranscoder 類別的新執行個體,並啟用硬體加速。

  • 建立輸出檔案此方法的最後一個步驟是建立要轉碼影片的檔案。 在此範例中,我們只會在裝置的 [影片庫] 資料夾中建立唯一命名的檔案。 請注意,若要存取此資料夾,您的應用程式必須在應用程式資訊清單中指定「影片庫」功能。 建立檔案之後,請開啟它以供讀取和寫入,並將產生的串流傳遞至 EncodeAsync 方法,下一個將會顯示。

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;
    }
}

開始編碼

現在,主要使用者已經初始化 EncodeAsync 方法,以開始擷取作業。 此方法會先檢查以確定我們尚未錄製,如果尚未錄製,它會呼叫協助程式方法 StartCapture 開始從螢幕擷取畫面。 本文稍後會顯示這個方法。 接下來,呼叫 PrepareMediaStreamSourceTranscodeAsync,以使用我們在上一節中建立的編碼設定檔,讓 MediaTranscoder 準備好將 MediaStreamSource 物件所產生的視訊串流轉碼至輸出檔案串流。 備妥轉碼器之後,請呼叫 TranscodeAsync 以開始轉碼。 如需使用 MediaTranscoder 的詳細資訊,請參閱 轉碼媒體檔案


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

        StartCapture();

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

        await transcode.TranscodeAsync();
    }
}

處理 MediaStreamSource 事件

MediaStreamSource 物件會擷取我們從畫面擷取的畫面,並將其轉換成可使用 MediaTranscoder 儲存至檔案的視訊串流。 我們會透過物件的事件的處理常式,將畫面傳遞至 MediaStreamSource

MediaStreamSource 準備好用於新的視訊畫面時,就會引發 SampleRequested 事件。 確定我們目前正在錄製之後,會呼叫協助程式方法 WaitForNewFrame,以取得從螢幕擷取的新畫面。 本文稍後所示的這個方法會傳回包含所擷取畫面的 ID3D11Surface 物件。 在此範例中,我們會將 IDirect3DSurface 介面包裝在協助程式類別中,該類別也會儲存擷取畫面的系統時間。 畫面和系統時間都會傳遞到 MediaStreamSample.CreateFromDirect3D11Surface 處理站方法中,並將產生的 MediaStreamSample 設定為 MediaStreamSourceSampleRequestedEventArgsMediaStreamSourceSampleRequest.Sample 屬性。 這就是將擷取的畫面提供給 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();
    }
}

Starting 事件的處理常式中,我們呼叫 WaitForNewFrame,但只會將畫面擷取的系統時間傳遞給 MediaStreamSourceStartingRequest.SetActualStartPosition 方法,MediaStreamSource 會用來正確編碼後續畫面的計時。

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

開始擷取

此步驟中顯示的 StartCapture 方法會從上一個步驟所示的 EncodeAsync 協助程式方法呼叫。 首先,這個方法會初始化一組用來控制擷取作業流程的事件物件。

  • _multithread 是一個協助程式類別,包裝 SharpDX 程式庫的 Multithread 物件,用來確保不會有其他執行緒在複製時存取 SharpDX 紋理。
  • _frameEvent 用來發出已擷取新畫面的訊號,並可傳遞至 MediaStreamSource
  • _closedEvent 表示錄製已經停止,而且我們不應該等待任何新的畫面。

畫面事件和封閉式事件會新增至陣列,以便我們可以在擷取迴圈中等候其中一個事件。

StartCapture 方法的其餘部分會設定執行實際螢幕擷取的 Windows.Graphics.Capture API。 首先,會為 CaptureItem.Closed 事件註冊事件。 接下來,會建立 Direct3D11CaptureFramePool,以便一次緩衝處理多個擷取的畫面。 CreateFreeThreaded 方法可用來建立畫面集區,以便在集區本身的背景工作執行緒上呼叫 FrameArrived 事件,而不是在應用程式的主要執行緒上呼叫。 接下來,針對 FrameArrived 事件註冊處理常式。 最後,會針對選取的 CaptureItem 建立 GraphicsCaptureSession,而擷取畫面的擷取則由呼叫 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();
}

處理圖形擷取事件

在上一個步驟中,我們註冊了兩個圖形擷取事件的處理常式,並設定一些事件來協助管理擷取迴圈的流程。

Direct3D11CaptureFramePool 有新的擷取畫面可用時,就會引發 FrameArrived 事件。 在此事件的處理常式中,呼叫傳送者上的 TryGetNextFrame 以取得下一個擷取的畫面。 擷取畫面之後,我們會設定 _frameEvent,讓擷取迴圈知道有新的畫面可用。

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

Closed 事件處理常式中,我們會發出 _closedEvent 訊號,讓擷取迴圈知道何時停止。

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

等候新畫面

本節所述的 WaitForNewFrame 協助程式方法是擷取循環繁重的地方。 請記住,每當 MediaStreamSource 準備好要新增至視訊串流的新畫面時,就會從 OnMediaStreamSourceSampleRequested 事件處理常式呼叫這個方法。 在高層級,此函式只會將每個螢幕擷取的視訊畫面從一個 Direct3D 表面複製到另一個介面,以便在擷取新畫面時傳遞至 MediaStreamSource 進行編碼。 此範例會使用 SharpDX 程式庫來執行實際的複製作業。

在等候新的畫面之前,方法會處置類別變數中儲存的任何先前畫面,_currentFrame,並重設 _frameEvent。 然後,方法會等候 _frameEvent_closedEvent 收到訊號。 如果已設定關閉的事件,則應用程式會呼叫協助程式方法來清除擷取資源。 本文稍後會顯示這個方法。

如果已設定畫面事件,則我們知道已呼叫上一個步驟中定義的 FrameArrived 事件處理常式,並開始將擷取的畫面資料複製到將傳遞至 MediaStreamSource 的 Direct3D 11 介面的程式。

此範例會使用協助程式類別 SurfaceWithInfo,這只允許我們傳遞視訊畫面和畫面的系統時間,這兩者都是 MediaStreamSource 所需的單一物件。 畫面複製程式的第一個步驟是具現化這個類別,並設定系統時間。

後續步驟是此範例中特別依賴 SharpDX 程式庫的一部分。 本文結尾會定義此處使用的協助程式函式。 首先,我們會使用 MultiThreadLock 來確保在進行複製時,沒有其他執行緒存取視訊畫面緩衝區。 接下來,我們會呼叫協助程式方法 CreateSharpDXTexture2D,以從視訊畫面建立 SharpDX Texture2D object from the video frame 物件。 這會是複製作業的來源紋理。

接下來,我們會從上一個步驟中建立的 Texture2D 物件複製到我們稍早在程式中建立的組合紋理。 這個組合紋理可做為交換緩衝區,以便在擷取下一個畫面時,編碼程式可以在像素上運作。 若要執行複製,我們會清除與組合紋理相關聯的轉譯目標檢視,然後定義我們想要複製的紋理內的區域 - 在此情況下,整個紋理,然後呼叫 CopySubresourceRegion 以實際將像素複製到組合紋理。

當我們建立目標紋理時,我們會建立要使用的紋理描述複本,但描述已修改,將 BindFlags 設定為 RenderTarget,讓新紋理具有寫入存取權。 將 CpuAccessFlags 設定為 None 可讓系統將複製作業最佳化。 紋理描述是用來建立新的紋理資源,而組合紋理資源會複製到這個新的資源,並呼叫 CopyResource。 最後,呼叫 CreateDirect3DSurfaceFromSharpDXTexture 來建立從這個方法傳回的 IDirect3DSurface 物件。

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;
}

停止擷取和清除資源

Stop 方法提供停止擷取作業的方法。 您的應用程式可能會以程式設計方式呼叫此專案,或回應使用者互動,例如按鍵。 這個方法只會設定 _closedEvent。 在先前步驟中定義的 WaitForNewFrame 方法會尋找此事件,如果設定,則會關閉擷取作業。

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

Cleanup 方法可用來正確處置在複製作業期間建立的資源。 這包括:

  • 擷取工作階段所使用的 Direct3D11CaptureFramePool 物件
  • GraphicsCaptureSessionGraphicsCaptureItem
  • Direct3D 和 SharpDX 裝置
  • 複製作業中使用的 SharpDX 紋理和轉譯目標檢視。
  • 用於儲存目前畫面的 Direct3D11CaptureFrame
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();
}

協助程式包裝函式類別

下列協助程式類別已定義,以協助本文中的範例程式碼。

MultithreadLock 協助程式類別會包裝 SharpDX Multithread 類別,以確保其他執行緒在複製時不會存取紋理資源。

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 用來將 IDirect3DSurfaceSystemRelativeTime 產生關聯,分別代表擷取的畫面格和擷取的時間。

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

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

Direct3D 和 SharpDX 協助程式 API

下列協助程式 API 已定義,以抽象化 Direct3D 和 SharpDX 資源的建立。 這些技術的詳細說明不在本文的範圍內,但此處提供程式碼,可讓您實作逐步解說中顯示的範例程式碼。

[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;
    }
}

另請參閱