Windows中各類畫面源的截取和合成方法總結

2021-02-13 網易雲信

曹偉

2015年畢業於華中科技大學後入職網易,先後參與過易信PC版、雲信PC demo以及教育直播產品的研發,目前在雲信音視頻組PC端組從事開發。

作者簡介概要當今,視頻直播和實時音視頻技術已經是很多行業必需的,典型的應用場景有教育直播、遠程視頻會議、網際網路娛樂等。在移動端發起直播,其畫面源的種類是十分有限的,無非是取攝像頭、截屏等。PC端由於其系統資源充足,應用程式豐富,畫面源種類多樣,更適合作為主播程序運行的平臺。在實際應用中,經常有一些場景是需要將不同的畫面源合在一起,然後推流出去的。本文粗淺介紹一些我們在開發過程中總結的一些獲取不同畫面源的畫面並將其合併的方法。各類畫面源的截取
1)    攝像頭畫面Windows下採集攝像頭畫面,DShow是最常用的方法之一。通過DShow採集攝像頭數據,創建視頻採集Filter,將其加入到圖表IGraphBuilder中,用IMediaControl接口來控制流媒體在Filter Graph中的流動,再通過Render來獲取視頻的原始數據。以上流程封裝在了我們的SDK中,用戶可以直接調用SDK接口。
2)    桌面取屏及應用程式窗口截取在Windows系統中,桌面和所有應用程式窗口一樣,本身也是一個HWND窗口,因此可以放在一起討論。獲取一個窗口的位圖數據,最常用的方法是:創建一個用來接收窗口畫面的HBITMAP位圖對象以及一個HDC設備上下文對象,用SelectObject將兩者綁定,然後用BitBlt從被截取窗口的HDC將數據拷貝到目標HDC。下面列出關鍵代碼:

    HDChDc = GetDC(capture_hwnd_);

HDCmem_dc = CreateCompatibleDC(hDc); //創建一個兼容DC

HBITMAPcapture_bitmap_ = ::CreateDIBSection(mem_dc, &bmi, DIB_RGB_COLORS,

            (void**)&capture_data_, NULL, 0); //創建HBITMAP

HBITMAPold_hbitmap = (HBITMAP)SelectObject(mem_dc, capture_bitmap_); //將mem_dc和capture_bitmap_綁定

BitBlt(mem_dc, 0, 0, capture_width, capture_height, hDc, real_rect.left, real_rect.top, SRCCOPY);

SelectObject(mem_dc, old_hbitmap); //還原

DeleteDC(mem_dc); //銷毀

ReleaseDC(capture_hwnd_, hDc); //釋放


3)    其他截屏/截窗口方法教育直播中,PPT分享是非常重要的一個場景。但是據我考查,自從Microsoft Office 2013之後,BitBlt就取不到Word、Excel、PPT窗口的內容了,截到的是一片白色。但是用PrintWindow這個Windows API卻可以取到。調用PrintWindow的程序會收到WM_PRINT或WM_PRINTCLIENT消息。PrintWindow的效率比BitBlt低,但當BitBlt無法取到時,可以用PrintWindow。越來越多的程序的畫面是在顯存中的,此時,BitBlt和PrintWindow都不管用(得到的都是一塊黑色的位圖)。可以考慮用DirectX的方法。而且DirectX方法由於使用了GPU,所以相較前面兩種方法效率更高。以下是DirectX截屏的代碼:

      externIDirect3DDevice9* g_pd3dDevice;

voidCaptureScreen()

{

        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();

}

GetFrontBufferData之後,也可以調用IDirect3DSurface9::GetDC()從pSurface得到HDC,然後用BitBlt將其拷貝到目標HDC。


4)    獲取本地圖片的位圖數據將本地圖片(jpg、bmp、png、gif等格式)加載到內存,並取得其位圖句柄或像素首地址的方法有很多種。這裡列舉幾種最常見的。GdiPlus方法比較簡單。首先是通過圖片路徑創建一個Gdiplus::Bitmap對象,通過Gdiplus::Bitmap::LockBits()方法可以得到圖片的數據,存放在一個Gdiplus::BitmapData結構中。Gdiplus::BitmapData::Scan0就是圖片像素數據的首地址。如果想得到該圖片的HBITMAP句柄,只需調Gdiplus::Bitmap::GetHBITMAP()即可。另一種方法是使用Windows API LoadImage來加載一個本地bmp圖片得到HBITMAP句柄,但這種方法似乎只能加載位圖文件(.bmp格式)。使用ATL的CImage只需要3行代碼即可得到一個圖片文件的HBITMAP句柄。

CImagecbmp;

cbmp.Load(path);

HBITMAP image_bitmap = cbmp.Detach();

畫面合成主播常常希望同時將自己的攝像頭畫面和桌面內容或者某個程序的畫面共享給觀眾,有時甚至需要同一時刻分享10個以上的畫面源。這時候,需要將多個畫面粘貼到一個目標畫面上,我們稱這個過程為畫面合成。合成的畫面通常還要支持改變各個畫面的尺寸、位置等操作。這樣一來,程序性能成了瓶頸問題。首先,對於各種畫面源的截取應該儘量採用高效的方式,其次,畫面的拉伸壓縮是比較耗性能的地方。在1秒鐘需要合成20幀畫面的要求下,應該避免直接強行壓縮HBITMAP,而是採用一些有加速的方案。1)    LibYuv方案我們找到一個一個yuv庫(LibYuv Project),支持圖形數據從rgb格式到各種yuv格式之間的互相轉換(定義在libyuv/convert.h中)。比較重要的一點是,它對yuv格式圖形的拉伸和壓縮以及其他各種變換(定義在libyuv/scale.h中)是有加速的。正好我們最終要推流的格式也是yuv格式的,所以我們方案的流程是:取得各個畫面源的畫面之後,先將它們各自轉化為yuv格式,然後把這些yuv畫面按照我們制定的方式粘貼到一個目標yuv畫面上,最後將目標yuv畫面數據推流出去。另外,由於主播的窗口上也要顯示合併畫面,所以還要把目標畫面轉成rgb格式渲染到窗口HDC上。當然,由於存在rgb格式和yuv格式之間反覆的轉換以及頻繁的scale,而且yuv加速畢竟是軟體方式,程序的CPU佔用率還是有點高。如果能採用DirectX、OpenGL等硬體加速解決方案,對程序性能以及用戶體驗的提升應該是比較明顯的。2)    DirectX 9方案在DirectX 9方案中,我們的每個畫面源以及最終的目標合成畫面,都對應一個表面(IDirect3DSurface9)和一個紋理(IDirect3DTexture9)。由於畫面源的顏色內存可能會被頻繁訪問和修改,所以創建其表面或紋理時,應該將其創建在系統內存或AGP中(D3DPOOL_MANAGED)而不是顯存中。對於yuv格式的攝像頭數據或網絡視頻幀,DirectX可以創建能直接接受yuv數據的紋理(D3DFMT_UYVY)。合成的時候,調用IDirect3DDevice9::DrawPrimitive()來將每個畫面源繪製到目標畫面上。而最終合成畫面是要顯示到窗口上的,所以應該創建在顯存中(D3DPOOL_DEFAULT)。渲染的時候,調用IDirect3DDevice9::DrawPrimitive()將目標畫面的紋理繪製到窗口的渲染目標紋理上,或者調用IDirect3DDevice9::StretchRect()將目標畫面的表面粘貼到窗口的back buffer上。另外,由於要取得目標畫面的數據用於推流,我們還要調用IDirect3DDevice9::CreateOffscreenPlainSurface()在系統內存中(D3DPOOL_SYSTEMMEM)創建一個離屏表面,用IDirect3DDevice9::GetRenderTargetData()將目標畫面取到離屏表面上,然後IDirect3DSurface9::LockRect()就能得到目標畫面的rgb格式數據了,將其轉化為yuv格式就可以推流出去了。總結直播產品由於需要對每一幀畫面做處理,畫面的清晰度要高,幀率還不能太低,所以通常會存在消耗系統資源過多的問題。無論是取畫面還是合成畫面,方法有很多,不僅限於上面幾種。Win API效率一般,如果對程序性能要求高,就要在其他方面去想法設法減少資源消耗。而DirectX雖然對2D圖形加速不如3D加速那麼顯著,但還是勝過Win API的。需要注意的是,使用DirectX時要非常清楚各個參數的意義,比如設備類型(D3DDEVTYPE)、內存池類型(D3DPOOL)、用途類型(D3DUSAGE)等等。參數用錯,可能導致其性能還不如Win API。

——【特別推薦】——

歪果仁短視頻花式玩法

 

相關焦點

  • adobe audition怎麼合成音樂 audition合成音樂方法流程
    adobe audition怎麼合成音樂 audition合成音樂方法流程時間:2017-05-27 21:41   來源:綠茶軟體園   責任編輯:玲玲 川北在線核心提示:原標題:adobe audition怎麼合成音樂 audition合成音樂方法流程 adobe audition怎麼合成音樂?
  • 電腦屏幕怎麼截取,常見的幾種電腦截屏方法
    隨著科技的快速發展電腦已經逐漸滲入到我們的工作和生活中,我們需要使用電腦的地方也越來越多,電腦已經成為了一種新式的辦公工具。今天小編不是向大家介紹電腦的應用,而是想要和大家分享一下關於電腦截圖的幾種方法。
  • Windows XP原始碼洩露:消息屬實,安全警鐘再次敲響!
    作為微軟旗下最吃香的作業系統,windows以其簡潔的UI設計和運行流暢吸粉無數。特別是XP和win7系統,作為經典中的經典,放在如今的舊電腦上還能穩定運行;其次win10作為最新版作業系統,目前已經達到10億裝機量,佔據一半的PC系統市場份額,這影響力不是一般得大!
  • 微軟Windows XP 原始碼遭洩露!
    據美國匿名網絡論壇 4chan 中一名博主爆料,微軟用於 Windows XP的原始碼發生洩露(https://boards.4channel.org/g/thread/77879263/soooo-windows-xp-source-code-leaked),根據解壓發現,這份是 Windows XP SP1 的代碼(未驗證)。
  • MySQL字符串截取 和 截取字符進行查詢
    通過mysql自帶的一些字符串截取函數,對數據進行處理,下面是我整理的字符串截取 和 截取字符進行查詢。一、MySQL中字符串的截取MySQL中有專門的字符串截取函數:其中常用的有兩種:substring_index(str,delim,count) 和concat 1.substring_index(str,delim,count) 函數的使用較為普遍
  • Google發展可自動從各類單據截取結構化資料的神經網絡
    Google解釋了雲計算服務Document AI,用來解析發票的技術,這項技術發布於計算機語言學會年會ACL 2020中,Google提出一種可從各式單據截取結構化資料的方法,而這與之前從純文本檔文件截取資料的方法不同,新方法將單據可能出現的欄位類型納入考量,因此能良好地截取單據上的內容。
  • Python中如何截取字符串的內容
    第七十二節:截取字符串字符串實際上也是一個由多個元素組成的集合,所以說也是序列的一種。既然是序列,要截取字符串的內容,我們可以採用一種已經介紹過的序列操作方法—「切片」。使用「切片」方法來截取字符串內容的語法格式是這樣的:string[start:end:step]其中,string就是要截取的字符串;用英文半角的中括號「[ ]」包含參數,三個參數之間使用英文半角冒號「:」分隔;
  • 伊洛納中合成夥伴肢體的方法和建議
    伊洛納中要怎樣才能合成夥伴肢體?
  • 音頻截取軟體有哪些?怎麼對音頻進行部分截取?
    那就必須先將音樂剪輯一下了,那麼音頻截取軟體有哪些?怎麼對音頻進行部分截取?正如我們上述所說,想要剪輯音樂,必須擁有一款音頻截取軟體,那麼到底什麼音頻截取軟體比較好用呢?小穎覺得目前正在使用的這款迅捷音頻轉換器還不錯,有興趣的朋友,不妨看看小穎是如何利用這款軟體來截取音頻的吧!
  • Java之字符串的截取方法
    各為小夥伴們大家好,這次小編要介紹的是字符串的截取方法。重載形式String three=one.substring(0,9);//左閉右開System.out.println(three);//wonderful/*字符串的內容沒有發生改變,有兩個字符串:「musician」,「pianist」,a中保存的是地址值
  • PS圖片合成時與畫面重疊怎麼辦?
    ps圖片合成時與畫面重疊怎麼辦?與畫面重疊的話,我們就需要通過蒙版將它分離出來。下面就來給大家演示操作一下。1.首先可以看到畫面中有一個白色的海鷗。2.我們將一個冰山的圖片合成到圖片中。5.然後建立選區填充為黑色,這個時候將兩者就分離出來了,還有那樣的一種遠景和近景效果。
  • 妙:Excel截取字符串中任一段的公式
    截取字符串,同學位都會想到left函數從左邊截、right函數從右邊截、mid函數從中間截取。但遇到這樣的你如何截取呢?
  • Windows快捷鍵匯總系列之使用頻率最高的快捷鍵
    最常用的都是標藍色的按鍵下面事整理的一個列表,裡面部分內容和之前的一篇有重複,這樣也方便大家更好的整理使用。工作bain的高效快捷二:最常用的快捷鍵之窗口(windows鍵,在fn和alt鍵之間)>【窗口】+D顯示桌面 = 【windows鍵】+D顯示桌面【窗口】+R打開「運行 = 【windows鍵】+R打開「運行【窗口】+L屏幕鎖定 = 【windows
  • Python學習第140課——通過findall方法找到網頁原始碼中的聯繫方式
    之前我們介紹過正則表達式對象的findall方法,它是找到所有匹配的字符串。這節我們介紹findall方法的另外的用法。我們在360瀏覽器通過360搜索「360聯繫方式」,我們會看到搜索結果頁中,聯繫方式是下面的這種:我們點擊上圖所示頁面的「奇虎360 客服電話」,打開360公司的「聯繫我們」頁面,然後右鍵點擊「查看原始碼」:打開的頁面上的原始碼是這樣:我們把原始碼複製到我們的Spyder中,用一個變量text來保存,原始碼前後各用3個引號包裹,這樣就相當於把這個原始碼作為一個很長的字符串
  • 抖音照片和視頻怎麼合成在一起放 圖片視頻剪輯拼在一起方法
    抖音上總是會有一些精彩有趣的視頻,很多人想要模仿和學習。而視頻中照片和視頻同時怎麼剪輯在一起呢?想了解的小夥伴,可以看看下文。  抖音圖片和視頻拼一起方法教程  1.此次教程需要下載【Video Collage】,下載好後打開跳到首界面。
  • 截取的圖片如何拼接成長圖?
    現在應用比較廣泛的肯定非長圖片莫屬了,所以製作長圖片的技術也已經比較成熟了,不僅僅是電腦軟體,現在很多手機APP都能支持多張圖片合成長圖。有時候我們截圖的時候,我們也會有將其拼接成長圖的想法,那麼大家知道快速將截圖拼接成長圖的方法嗎?
  • 手機APP如何截取音頻片段?截取音頻具體步驟?
    我們就來看看手機APP如何截取音頻片段?截取音頻具體步驟?接下來就為大家帶來好的推薦和截取方法,想學的你看過來吧!推薦軟體:錄音轉文字助手步驟一、首先我們打開手機上安裝好的錄音轉文字助手,點擊打開後首頁下方有一個工具選項,點擊工具頁面會有很多詳細功能,我們點擊打開音頻裁剪,然後進入到文件庫頁面。
  • windows 方便的批處理:批量修改文件夾名
    https://www.zhihu.com/question/285586045/answer/445364909裡邊提到了鎖屏和 windows 計劃任務的結合。rundll32.exe user32.dll,LockWorkStation定時關機,1800 秒後關機。
  • 其它老師教照片合成,都是往畫面中添加元素,我卻能讓畫面更簡潔
    今天分享一個picsart的照片合成技巧。我們先看一下原始照片,這是作者@漫天煙月拍攝的一張慢門海景照。畫面整體效果很不錯,近景有礁石,中景有一棵樹,只是感覺遠處的大橋有些多餘了。下圖是我用piscart調整後的效果,沒有了大橋後畫面變得更加簡潔美觀。下面我具體分享一下修圖的步驟。首先在picsart中打開原始照片,點擊「工具」,選擇「剪輯」。用自由剪輯把大橋下面的部分框取出來,點擊「應用」,並保存待用。
  • linux和windows的不同
    講解對象:linux和windows的不同作者:融水公子 rsgz注意:學習linux時候儘量忘記windows思維區分1 windows中的dos命令是不區分大小寫的1 windows中是通過擴展名區分文件的 .txt .doc .ppt .jpg .avi .exe2 windows理論上說有多少軟體就有多少的擴展名3 linxu中完全就可以將你的文件名叫做abc 甚至是abc.abc  abc.exe4 在linux中abc.exe