捕捉屏幕的各種方法


內容 介紹 用GDI方式捕獲它 還有DirectX方法 用Windows Media API捕獲屏幕 介紹 有時,我們希望通過編程捕獲整個屏幕的內容。下面解釋如何做到這一點。通常,我們可以直接使用GDI和/或DirectX。另一個值得考慮的選擇是Windows Media API。在這里,我們將分別考慮它們,看看如何將它們用於我們的目的。在每種方法中,一旦我們將屏幕截圖放入應用程序定義的內存或位圖中,我們就可以使用它來生成電影。有關以編程方式從位圖序列創建電影的詳細信息,請參閱文章“從HBitmap創建電影”。 用GDI方式捕獲它 當性能不是問題,當我們想要的只是桌面快照時,我們可以考慮GDI選項。這個機制基於桌面也是一個窗口的簡單原理——即它有一個窗口句柄(HWND)和一個設備上下文(DC)。如果我們可以捕獲桌面的設備上下文,我們可以用常規的方式將這些內容blit到我們的應用程序定義的設備上下文。如果我們知道它的窗口句柄,那么獲取桌面的設備上下文非常簡單——可以通過函數GetDesktopWindow()來實現。因此,涉及的步驟是: 使用函數GetDesktopWindow()獲取桌面窗口句柄; 使用函數GetDC()獲取桌面窗口的DC; 為桌面DC創建一個兼容的DC和一個兼容的位圖,以選擇到該兼容的DC。這些可以通過使用CreateCompatibleDC()和CreateCompatibleBitmap()來完成;選擇位圖到我們的DC可以通過SelectObject()完成; 當您准備捕捉屏幕時,只需將桌面DC的內容blit到已創建的兼容DC—這就是全部—您就完成了。我們現在創建的兼容位圖包含了捕獲時屏幕的內容。 不要忘記在完成操作后釋放對象。內存是寶貴的(對於其他應用程序)。 ExampleHide,復制Code

Void CaptureScreen()
{
    int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    HWND hDesktopWnd = GetDesktopWindow();
    HDC hDesktopDC = GetDC(hDesktopWnd);
    HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
    HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC, 
                            nScreenWidth, nScreenHeight);
    SelectObject(hCaptureDC,hCaptureBitmap); 
    BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,
           hDesktopDC,0,0,SRCCOPY|CAPTUREBLT); 
    SaveCapturedBitmap(hCaptureBitmap); //Place holder - Put your code
                                //here to save the captured image to disk
    ReleaseDC(hDesktopWnd,hDesktopDC);
    DeleteDC(hCaptureDC);
    DeleteObject(hCaptureBitmap);
}

在上面的代碼片段中,函數GetSystemMetrics()在與SM_CXSCREEN一起使用時返回屏幕寬度,在與SM_CYSCREEN一起調用時返回屏幕高度。有關如何將捕獲的位圖保存到磁盤以及如何將其發送到剪貼板的詳細信息,請參閱附帶的源代碼。它非常簡單。源代碼實現了上述用於定期捕獲屏幕內容的技術,並從捕獲的圖像序列中創建了一個電影。 還有DirectX方法 用DirectX捕捉屏幕截圖是一項非常簡單的任務。DirectX提供了一種簡單的方法。 每個DirectX應用程序都包含所謂的緩沖區或表面,用於保存與該應用程序相關的視頻內存的內容。這被稱為應用程序的回緩沖。一些應用程序可能有多個回緩沖。另外,每個應用程序默認都可以訪問另一個緩沖區——前端緩沖區。這個緩沖區,即前緩沖區,保存與桌面內容相關的視頻內存,因此本質上是屏幕圖像。 通過從DirectX應用程序訪問前端緩沖區,我們可以捕獲此時屏幕的內容。 從DirectX應用程序訪問前端緩沖區非常簡單和直接。接口IDirect3DDevice9提供了GetFrontBufferData()方法,該方法采用IDirect3DSurface9對象指針並將前端緩沖區的內容復制到該表面上。可以使用方法IDirect3DDevice8::CreateOffscreenPlainSurface()來生成IDirect3DSurfce9對象。一旦屏幕被捕獲到圖面上,我們就可以使用D3DXSaveSurfaceToFile()函數以位圖格式將圖面直接保存到磁盤上。因此,捕捉屏幕的代碼如下所示:復制Code

extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
    IDirect3DSurface9* pSurface;
    g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
        D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
    g_pd3dDevice->GetFrontBufferData(0, pSurface);
    D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
    pSurface->Release(); 
}

在上面的示例中,g_pd3dDevice是一個IDirect3DDevice9對象,並假設已正確初始化。此代碼片段將捕獲的映像直接保存到磁盤上。但是,如果我們只是想直接操作圖像位,我們可以使用方法IDirect3DSurface9::LockRect(),而不是保存到磁盤。這提供了一個指向表面內存的指針——本質上是一個指向捕獲圖像的位的指針。我們可以將比特復制到應用程序定義的內存中,並對其進行操作。下面的代碼片段展示了如何將surface內容復制到應用程序定義的內存中:復制Code

extern void* pBits;
extern IDirect3DDevice9* g_pd3dDevice;
IDirect3DSurface9* pSurface;
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
                                          D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, 
                                          &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DLOCKED_RECT lockedRect;
pSurface->LockRect(&lockedRect,NULL,
                   D3DLOCK_NO_DIRTY_UPDATE|
                   D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)));
for( int i=0 ; i < ScreenHeight ; i++)
{
    memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 , 
        (BYTE*) lockedRect.pBits + i* lockedRect.Pitch , 
        ScreenWidth * BITSPERPIXEL / 8);
}
g_pSurface->UnlockRect();
pSurface->Release();

在上面,pBits是一個void*。在復制到pBits之前,確保我們已經分配了足夠的內存。BITSPERPIXEL的典型值是每像素32位。但是,它可能會根據您當前的監視器設置而有所不同。這里要注意的重要一點是曲面的寬度是n與捕獲的屏幕圖像寬度不同。由於涉及到內存對齊的問題(假定與字邊界對齊的內存訪問速度比非對齊的內存更快),surface可能會在每一行的末尾添加額外的內容,以使它們完全對齊到字邊界。lockedRect。Pitch表示連續兩行的起始點之間的字節數。也就是說,要前進到下一行的正確位置,我們應該按音高前進,而不是按寬度前進。你可以用下面的方法反向復制表面位:隱藏復制Code

for( int i=0 ; i < ScreenHeight ; i++)
{
    memcpy((BYTE*) pBits +( ScreenHeight - i - 1) * 
        ScreenWidth * BITSPERPIXEL/8 , 
        (BYTE*) lockedRect.pBits + i* lockedRect.Pitch , 
        ScreenWidth* BITSPERPIXEL/8);
}

當您在自頂向下和自底向上位圖之間進行轉換時,這可能非常方便。 雖然上面的LockRect()技術是訪問IDirect3DSurface9上捕獲的圖像內容的一種方法,但是我們有另一個為IDirect3DSurface9定義的更復雜的方法,即GetDC()方法。我們可以使用IDirect3DSurface9::GetDC()方法為DirectX圖像表面獲得一個GDI兼容的設備上下文,這使得直接將表面內容blit到我們的應用程序定義的DC成為可能。有興趣的讀者可以探索這種選擇。 本文提供的示例源代碼實現了將屏幕外平面的內容復制到用戶創建的位圖上的技術,以便定期捕獲屏幕內容,並根據捕獲的圖像序列創建影片。 但是,在使用此技術進行屏幕捕獲時,值得注意的一點是文檔中提到的注意事項:GetFrontBufferData()從設計上來說是一個很慢的操作,在性能關鍵的應用程序中不應該考慮使用它。因此,在這種情況下,GDI方法比DirectX方法更可取。 用於捕捉屏幕的Windows Media API Windows Media 9.0使用Windows Media Encoder 9 API支持屏幕截圖。它包括一個名為Windows Media Video 9 Screen的編解碼器,該編解碼器經過特別優化,可以對通過屏幕截圖生成的內容進行操作。Windows Media Encoder API提供了接口IWMEncoder2,可以使用該接口有效地捕獲屏幕內容。 使用Windows Media Encoder API進行屏幕捕獲非常簡單。首先,我們需要使用CoCreateInstance()函數創建IWMEncoder2對象。可以這樣做:隱藏復制Code

IWMEncoder2* g_pEncoder=NULL; 
CoCreateInstance(CLSID_WMEncoder,NULL,CLSCTX_INPROC_SERVER,
        IID_IWMEncoder2,(void**)&g_pEncoder);

因此創建的Encoder對象包含處理捕獲的屏幕數據的所有操作。然而,為了正確地執行它的操作,encoder對象依賴於在所謂的概要文件中定義的設置。配置文件只是一個包含控制編碼操作的所有設置的文件。我們還可以在運行時使用各種定制選項創建自定義配置文件,如編解碼器選項等,這取決於捕獲數據的性質。為了在屏幕捕獲應用程序中使用配置文件,我們創建了一個基於Windows Media Video 9屏幕編解碼器的自定義配置文件。IWMEncProfile2接口支持自定義概要文件對象。我們可以使用CoCreateInstance()函數來創建一個定制的配置文件對象,如下所示:復制Code

IWMEncProfile2* g_pProfile=NULL;
CoCreateInstance(CLSID_WMEncProfile2,NULL,CLSCTX_INPROC_SERVER,
        IID_IWMEncProfile2,(void**)&g_pProfile);

我們需要在配置文件中為編碼器指定目標受眾。每個配置文件可以保存多個受眾配置,它們是接口IWMEncAudienceObj的對象。在這里,我們使用一個受眾對象作為配置文件。我們為概要文件創建觀眾對象使用方法IWMEncProfile:: AddAudience(),它會返回一個指向IWMEncAudienceObj然后可以用於配置如視頻編解碼器設置(IWMEncAudienceObj: put_VideoCodec()),視頻幀大小設置(IWMEncAudienceObj: put_VideoHeight()和IWMEncAudienceObj:: put_VideoWidth())等。例如,我們將視頻編解碼器設置為Windows Media video 9屏幕編解碼器:Hide  復制Code

extern IWMEncAudienceObj* pAudience;
#define VIDEOCODEC MAKEFOURCC('M','S','S','2') 
    //MSS2 is the fourcc for the screen codec

long lCodecIndex=-1;
g_pProfile->GetCodecIndexFromFourCC(WMENC_VIDEO,VIDEOCODEC,
    &lCodecIndex); //Get the Index of the Codec
pAudience->put_VideoCodec(0,lCodecIndex);

fourcc是世界上每個編解碼器的一種唯一標識符。用於Windows Media Video 9屏幕編解碼器的fourcc是MSS2。IWMEncAudienceObj::put_VideoCodec()接受概要文件索引作為輸入,以識別特定的概要文件——可以通過使用方法IWMEncProfile::GetCodecIndexFromFourCC()來獲得。 一旦完成配置概要文件對象,我們就可以通過使用在編碼器的源組對象上定義的方法IWMEncSourceGroup:: put_Profile()來選擇這個概要文件到我們的編碼器中。源組是源的集合,其中每個源可能是視頻流、音頻流或HTML流等。每個encoder對象都可以與許多源組一起工作,從這些源組中獲取輸入數據。因為我們的屏幕捕獲應用程序只使用一個視流,所以我們的編碼器對象需要有一個源組,其中包含一個單獨的源,即視頻源。這個單一的視頻源需要配置為使用屏幕設備作為輸入源,這可以通過使用IWMEn方法完成cVideoSource2: SetInput(型):隱藏,復制Code

extern IWMEncVideoSource2* pSrcVid;
pSrcVid->SetInput(CComBSTR("ScreenCap://ScreenCapture1");

通過使用IWMEncFile::put_LocalFileName()方法,可以將目標輸出配置為保存到視頻文件(wmv movie)中,該方法需要一個IWMEncFile對象。這個IWMEncFile對象可以通過使用方法IWMEncoder::get_File()來獲得:Hide  復制Code

IWMEncFile* pOutFile=NULL;
g_pEncoder->get_File(&pOutFile);
pOutFile->put_LocalFileName(CComBSTR(szOutputFileName);

現在,在對encoder對象完成所有必要的配置之后,我們可以使用IWMEncoder::Start()方法開始捕捉屏幕。方法IWMEncoder::Stop()和IWMEncoder::Pause可以用於停止和暫停捕獲。 在處理全屏捕獲時,我們可以通過調整輸入視頻源流的屬性來交替地選擇捕獲區域。為此,我們需要使用IWmEnVideoSource2對象的IPropertyBag接口,如下所示:復制Code

#define WMSCRNCAP_WINDOWLEFT CComBSTR("Left")
#define WMSCRNCAP_WINDOWTOP CComBSTR("Top")
#define WMSCRNCAP_WINDOWRIGHT CComBSTR("Right")
#define WMSCRNCAP_WINDOWBOTTOM CComBSTR("Bottom")
#define WMSCRNCAP_FLASHRECT CComBSTR("FlashRect")
#define WMSCRNCAP_ENTIRESCREEN CComBSTR("Screen")
#define WMSCRNCAP_WINDOWTITLE CComBSTR("WindowTitle")
extern IWMEncVideoSource2* pSrcVid;
int nLeft, nRight, nTop, nBottom;
pSrcVid->QueryInterface(IID_IPropertyBag,(void**)&pPropertyBag);
CComVariant varValue = false;
pPropertyBag->Write(WMSCRNCAP_ENTIRESCREEN,&varValue);
varValue = nLeft;
pPropertyBag->Write( WMSCRNCAP_WINDOWLEFT, &varValue );
varValue = nRight;
pPropertyBag->Write( WMSCRNCAP_WINDOWRIGHT, &varValue );
varValue = nTop;
pPropertyBag->Write( WMSCRNCAP_WINDOWTOP, &varValue );
varValue = nBottom;
pPropertyBag->Write( WMSCRNCAP_WINDOWBOTTOM, &varValue );

附帶的源代碼實現了這種捕捉屏幕的技術。有趣的一點是,除了輸出的電影質量不錯之外,鼠標光標也被捕獲了。(默認情況下,GDI和DirectX不太可能捕獲鼠標光標)。 注意,您的系統需要安裝Windows Media 9.0 SDK組件來創建使用Windows Media 9.0 API的應用程序。 要運行應用程序,終端用戶必須安裝Windows Media Encoder 9系列。當您發布基於Windows Media Encoder SDK的應用程序時,您還必須包括Windows Media Encoder軟件,通過在您的設置中重新發布Windows Media Encoder,或者要求您的用戶自己安裝Windows Media Encoder。 windowsmediaencoder 9.0可從以下網站下載: Windows媒體編碼器 結論 上面討論的所有技術都針對一個目標——捕捉屏幕的內容。然而,正如很容易猜到的那樣,結果會因程序中使用的特定技術而有所不同。如果我們想要的只是一個偶然的隨機快照,那么GDI方法是一個不錯的選擇,因為它很簡單。然而,如果我們想要更專業的結果,使用Windows Media會是一個更好的選擇。值得注意的一點是,通過這些機制捕獲的內容的質量可能取決於系統的設置。例如,禁用硬件加速(桌面屬性|設置|高級|故障排除)可能會極大地提高捕獲應用程序的總體質量和性能。 本文轉載於:http://www.diyabc.com/frontweb/news5251.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM