1

実際、それはさまざまなテクノロジーの組み合わせですが、私の質問に対する答え (私が思うに) は Direct3D 9 に最も近いものです。私は任意の D3D9 アプリケーションに接続しています。ほとんどの場合、それはゲームであり、変更するために独自のコードを挿入しています。 EndScene 関数の動作。バックバッファは、プッシュ ソース DirectShow フィルタのビットマップを指すように設定されたサーフェスにコピーされます。フィルタはビットマップを 25 fps でサンプリングし、ビデオを .avi ファイルにストリーミングします。ゲームの画面全体にテキスト オーバーレイが表示され、ホット キーの組み合わせがゲームプレイ キャプチャを停止することになっていることをユーザーに伝えますが、このオーバーレイは記録されたビデオには表示されません。厄介な事実を除いて、すべてが高速かつ美しく機能します。ランダムな機会に、テキストが過度に含まれるフレームが、記録されたビデオに入り込みます。これは本当に望ましいアーティファクトではありません。エンド ユーザーは自分のゲームプレイをビデオで見たいだけで、他には何も望んでいません。なぜこれが起こっているのかについて誰かがアイデアを共有できるかどうか聞いてみたい. EndScene フックのソース コードは次のとおりです。

using System;
using SlimDX;
using SlimDX.Direct3D9;
using System.Diagnostics;
using DirectShowLib;
using System.Runtime.InteropServices;

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[System.Security.SuppressUnmanagedCodeSecurity]
[Guid("EA2829B9-F644-4341-B3CF-82FF92FD7C20")]

public interface IScene
{
    unsafe int PassMemoryPtr(void* ptr, bool noheaders);
    int SetBITMAPINFO([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]byte[] ptr, bool noheaders);
}

public class Class1
{
    object _lockRenderTarget = new object();
    public string StatusMess { get; set; }
    Surface _renderTarget;
    //points to image bytes
    unsafe void* bytesptr;
    //used to store headers AND image bytes
    byte[] bytes;
    IFilterGraph2 ifg2;
    ICaptureGraphBuilder2 icgb2;
    IBaseFilter push;
    IBaseFilter compressor;
    IScene scene;
    IBaseFilter mux;
    IFileSinkFilter sink;
    IMediaControl media;
    bool NeedRunGraphInit = true;
    bool NeedRunGraphClean = true;
    DataStream s;
    DataRectangle dr;

    unsafe int   EndSceneHook(IntPtr devicePtr)
    {
        int hr;

        using (Device device = Device.FromPointer(devicePtr))
            {
           try
            {
                lock (_lockRenderTarget)
                {

                    bool TimeToGrabFrame = false;

                    //....
                    //logic based on elapsed milliseconds deciding if it is time to grab another frame

                    if (TimeToGrabFrame)
                    {

                        //First ensure we have a Surface to render target data into
                        //called only once
                        if (_renderTarget == null)
                        {

                            //Create offscreen surface to use as copy of render target data
                            using (SwapChain sc = device.GetSwapChain(0))
                            {

                                //Att: created in system memory, not in video memory
                                _renderTarget = Surface.CreateOffscreenPlain(device, sc.PresentParameters.BackBufferWidth, sc.PresentParameters.BackBufferHeight, sc.PresentParameters.BackBufferFormat, Pool.SystemMemory);

                            } //end using
                        } // end if

                        using (Surface backBuffer = device.GetBackBuffer(0, 0))
                        {
                            //The following line is where main action takes place:
                            //Direct3D 9 back buffer gets copied to Surface _renderTarget,
                            //which has been connected by references to DirectShow's
                            //bitmap capture filter
                            //Inside the filter ( code not shown in this listing) the bitmap is periodically
                            //scanned to create a streaming video.
                            device.GetRenderTargetData(backBuffer, _renderTarget);

                            if (NeedRunGraphInit) //ran only once
                            {
                                ifg2 = (IFilterGraph2)new FilterGraph();
                                icgb2 = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
                                icgb2.SetFiltergraph(ifg2);
                                push = (IBaseFilter) new PushSourceFilter();
                                scene = (IScene)push;

                                //this way we get bitmapfile and bitmapinfo headers
                                //ToStream is slow, but run it only once to get the headers
                                s = Surface.ToStream(_renderTarget, ImageFileFormat.Bmp);
                                bytes = new byte[s.Length];

                                s.Read(bytes, 0, (int)s.Length);
                                hr = scene.SetBITMAPINFO(bytes, false);

                                //we just supplied the header to the PushSource
                                //filter. Let's pass reference to
                                //just image bytes from LockRectangle

                                dr = _renderTarget.LockRectangle(LockFlags.None);
                                s = dr.Data;
                                Result r = _renderTarget.UnlockRectangle();
                                bytesptr = s.DataPointer.ToPointer();
                                hr = scene.PassMemoryPtr(bytesptr, true);

                                //continue building graph
                                ifg2.AddFilter(push, "MyPushSource");

                                icgb2.SetOutputFileName(MediaSubType.Avi, "C:\foo.avi", out mux, out sink);

                                icgb2.RenderStream(null, null, push, null, mux);

                                 media = (IMediaControl)ifg2;

                                 media.Run();

                                 NeedRunGraphInit = false;
                                 NeedRunGraphClean = true;

                                 StatusMess = "now capturing, press shift-F11 to stop";

                            } //end if

                        } // end using backbuffer
                    } //  end if Time to grab frame

                } //end lock
            } // end  try

            //It is usually thrown when the user makes game window inactive
            //or it is thrown deliberately when time is up, or the user pressed F11 and
            //it resulted in stopping a capture.
            //If it is thrown for another reason, it is still a good
            //idea to stop recording and free the graph
            catch (Exception ex) 
            {
               //..
               //stop the DirectShow graph and cleanup

            } // end catch

            //draw overlay
            using (SlimDX.Direct3D9.Font font = new SlimDX.Direct3D9.Font(device, new System.Drawing.Font("Times New Roman", 26.0f, FontStyle.Bold)))
            {
                font.DrawString(null, StatusMess, 20, 100, System.Drawing.Color.FromArgb(255, 255, 255, 255));
            }

            return device.EndScene().Code;

        } // end using device

    } //end EndSceneHook
4

1 に答える 1

0

時々起こることですが、誰かが興味を持っているなら、私は最終的にこの質問に対する答えを自分で見つけました. 一部の Direct3D9 アプリのバックバッファーは、フックされた EndScene が呼び出されるたびに必ずしも更新されないことが判明しました。そのため、前の EndScene フック呼び出しからのテキスト オーバーレイを含むバックバッファが、入力フレームの収集を担当する DirectShow ソース フィルタに渡されることがありました。既知の RGB 値を持つ小さな 3 ピクセル オーバーレイを各フレームにスタンプし、このダミー オーバーレイがまだ存在するかどうかを確認してから、フレームを DirectShow フィルターに渡しました。オーバーレイが存在する場合は、現在のフレームではなく、以前にキャッシュされたフレームが渡されました。このアプローチにより、DirectShow グラフに記録されたビデオからテキスト オーバーレイが効果的に削除されました。

于 2012-05-10T05:25:39.690 に答える