วิธีจับภาพหน้าจอที่เร็วที่สุดบน Windows


159

ฉันต้องการเขียนโปรแกรม screencasting สำหรับแพลตฟอร์ม Windows แต่ไม่แน่ใจว่าจะจับภาพหน้าจอได้อย่างไร วิธีเดียวที่ฉันรู้คือใช้ GDI แต่ฉันอยากรู้ว่ามีวิธีอื่นที่จะไปเกี่ยวกับเรื่องนี้หรือไม่และถ้ามีมีสิ่งใดที่เกิดขึ้นอย่างน้อยที่สุด? ความเร็วเป็นสิ่งสำคัญ

โปรแกรม screencasting จะใช้สำหรับการบันทึกวิดีโอเกมแม้ว่ามันจะแคบลง แต่ตัวเลือกฉันยังคงเปิดรับข้อเสนอแนะอื่น ๆ ที่หลุดออกจากขอบเขตนี้ ความรู้ไม่เลวหลังจากทั้งหมด

แก้ไข : ฉันมาข้ามบทความนี้: วิธีการต่างๆสำหรับการจับหน้าจอ มันแนะนำให้ฉันรู้จักกับวิธีการทำ Windows Media API และวิธี DirectX ในการทำมัน โดยกล่าวถึงในข้อสรุปว่าการปิดใช้งานการเร่งด้วยฮาร์ดแวร์สามารถปรับปรุงประสิทธิภาพของแอปพลิเคชันการจับภาพได้อย่างมาก ฉันอยากรู้ว่าทำไมถึงเป็นแบบนี้ ใครช่วยเติมเต็มช่องว่างที่ขาดหายไปให้ฉันได้ไหม

แก้ไข : ฉันอ่านโปรแกรม screencasting เช่น Camtasia ใช้ไดรเวอร์การจับภาพของตัวเอง มีคนให้คำอธิบายในเชิงลึกเกี่ยวกับวิธีการทำงานและทำไมมันเร็วกว่า ฉันอาจต้องการคำแนะนำในการดำเนินการบางอย่างเช่นนั้น แต่ฉันแน่ใจว่ามีเอกสารที่มีอยู่แล้ว

นอกจากนี้ฉันรู้แล้วว่า FRAPS บันทึกหน้าจอได้อย่างไร มันขอ API กราฟิกพื้นฐานที่จะอ่านจากบัฟเฟอร์ด้านหลัง จากสิ่งที่ฉันเข้าใจนี่เป็นเร็วกว่าการอ่านจากบัฟเฟอร์ด้านหน้าเพราะคุณกำลังอ่านจาก RAM ระบบมากกว่าวิดีโอ RAM คุณสามารถอ่านบทความที่นี่


คุณได้พิจารณามากกว่าการบันทึกเนื้อหาของหน้าจอโดยใช้ระบบการเล่นซ้ำหรือไม่?
Benjamin Lindley

2
คุณไม่ต้องขออะไรเลย คุณเพียงแค่ต้องเขียนกิจกรรมอินพุตของคุณเพื่อให้พวกเขาไม่สามารถควบคุมเกมได้โดยตรง แต่แทนที่จะเรียกใช้ฟังก์ชั่นอื่น ๆ แทน ตัวอย่างเช่นหากผู้เล่นกดปุ่มซ้ายคุณจะไม่ลดตำแหน่งผู้เล่น x ลง แต่คุณเรียกฟังก์ชั่นMovePlayerLeft()แทน และคุณยังบันทึกเวลาและระยะเวลาของการกดปุ่มและอินพุตอื่น ๆ จากนั้นเมื่อคุณอยู่ในโหมดเล่นคุณจะไม่สนใจอินพุตและอ่านข้อมูลที่บันทึกไว้แทน MovePlayerLeft()ถ้าในข้อมูลที่คุณเห็นกดปุ่มซ้ายที่คุณเรียก
Benjamin Lindley

1
@PigBen นี่จะเป็นแอปพลิเคชั่นทั่วไปสำหรับบันทึกวิดีโอเกม มันไม่ใช่เกมที่เจาะจง บางคนที่กดปุ่มซ้ายอาจหมายถึงการเลื่อนไปทางขวาสำหรับสิ่งที่ฉันรู้ นอกจากนี้คุณยังไม่ได้พิจารณากิจกรรมที่ไม่ได้รับอิทธิพลจากผู้ใช้ 'แล้วเรนเดอร์ล่ะ
someguy

1
@someguy โอเคฉันคิดว่าคุณกำลังทำอะไรที่เข้มข้นกว่านี้ฉันเพิ่มกิจวัตรประจำวันโดยใช้วิธีการข้างต้นเพื่อประหยัดการเล่นซ้ำ AVIs ในเกมที่มีความเร็ว 30 เฟรมต่อวินาทีโดยไม่มีข้อผูกมัด ฉันสร้างเครื่องบันทึกหน้าจอมอนิเตอร์หลายจอโดยใช้ windows API สำหรับ "การเพิ่มประสิทธิภาพกำลังคน" แต่มันทำงานได้ไม่ดีแม้จะอยู่ที่ 4fps เป้าหมาย
AJG85

1
มีคนขับรถมาเปิดกระจกสำหรับ Windows ในเว็บไซต์ที่เก็บ UltraVNC นี่คือultravnc.svn.sourceforge.net/viewvc/ultravnc/...
เกยตื้น

คำตอบ:


62

นี่คือสิ่งที่ฉันใช้ในการรวบรวมเฟรมเดี่ยว แต่ถ้าคุณแก้ไขและเก็บเป้าหมายสองเปิดตลอดเวลาจากนั้นคุณสามารถ "สตรีม" มันไปยังดิสก์โดยใช้ตัวนับคงที่สำหรับชื่อไฟล์ - ฉันจำไม่ได้ว่าฉันพบสิ่งนี้ที่ไหน แต่มันได้รับการแก้ไขแล้วขอบคุณใครก็ตาม!

void dump_buffer()
{
   IDirect3DSurface9* pRenderTarget=NULL;
   IDirect3DSurface9* pDestTarget=NULL;
     const char file[] = "Pickture.bmp";
   // sanity checks.
   if (Device == NULL)
      return;

   // get the render target surface.
   HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
   // get the current adapter display mode.
   //hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);

   // create a destination surface.
   hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
                         DisplayMde.Height,
                         DisplayMde.Format,
                         D3DPOOL_SYSTEMMEM,
                         &pDestTarget,
                         NULL);
   //copy the render target to the destination surface.
   hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
   //save its contents to a bitmap file.
   hr = D3DXSaveSurfaceToFile(file,
                              D3DXIFF_BMP,
                              pDestTarget,
                              NULL,
                              NULL);

   // clean up.
   pRenderTarget->Release();
   pDestTarget->Release();
}

ขอบคุณ ฉันได้ยินเกี่ยวกับวิธีนี้เมื่อไม่นานมานี้ที่บอกว่าเร็วกว่าการอ่านจากบัฟเฟอร์ด้านหน้า คุณทำแบบนั้นอย่างสุจริตและทำงานอย่างถูกต้องหรือไม่?
someguy

ปัญหาเกี่ยวกับบัฟเฟอร์ด้านหน้าเป็นหนึ่งในการเข้าถึงนั่นคือพยายามคัดลอกธรรมดาที่ขณะนี้กำลังแสดง "ขัดจังหวะ" การคัดลอก มันทำงานได้ดีพอสำหรับฉันและกินฮาร์ดไดรฟ์ของฉัน!
Brandrew

@bobobobo ฉันไม่รู้ว่ามันจะออกมาได้อย่างไร แต่ฉันคิดว่าจะใช้บางอย่างเช่น Huffyuv แก้ไข: หรือให้ผู้ใช้เลือกจากตัวกรอง directshow ที่มีอยู่
someguy

1
ฉันไม่สามารถจัดการให้มันทำงานได้ ... DirectX ใช้สายที่ไม่ถูกต้องในส่วนของ GetRenderTargetData เห็นได้ชัดว่าวิธีที่คุณสร้างอุปกรณ์ของคุณจะต้องมีความสำคัญมาก
LightStriker

12
downvote ใช้งานได้กับแอปพลิเคชันของคุณเท่านั้นดังนั้นจึงไม่สามารถใช้ในการบันทึกโปรแกรมทั่วไปได้
user3125280

32

แก้ไข: ฉันเห็นว่านี่อยู่ภายใต้ลิงค์แก้ไขครั้งแรกของคุณเป็น "วิธี GDI" นี่ยังเป็นวิธีที่เหมาะสมในการใช้งานแม้จะมีคำแนะนำด้านประสิทธิภาพในไซต์นั้นคุณก็สามารถไปถึง 30fps ได้อย่างง่ายดาย

จากความคิดเห็นนี้ (ฉันไม่มีประสบการณ์ในการทำเช่นนี้ฉันแค่อ้างอิงคนที่ทำ):

HDC hdc = GetDC(NULL); // get the desktop device context
HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself

// get the height and width of the screen
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);

// create a bitmap
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);

// use the previously created device context with the bitmap
SelectObject(hDest, hbDesktop);

// copy from the desktop device context to the bitmap device context
// call this once per 'frame'
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);

// after the recording is done, release the desktop context you got..
ReleaseDC(NULL, hdc);

// ..delete the bitmap you were using to capture frames..
DeleteObject(hbDesktop);

// ..and delete the context you created
DeleteDC(hDest);

ฉันไม่ได้บอกว่านี่เป็นวิธีที่เร็วที่สุด แต่BitBltโดยทั่วไปการดำเนินการจะรวดเร็วมากหากคุณคัดลอกระหว่างบริบทอุปกรณ์ที่รองรับ

สำหรับการอ้างอิงOpen Broadcaster Softwareใช้สิ่งนี้เป็นส่วนหนึ่งของ วิธี"dc_capture"ของพวกเขาแม้ว่าจะสร้างบริบทปลายทางhDestโดยใช้CreateCompatibleDCพวกเขาใช้IDXGISurface1ซึ่งทำงานกับ DirectX 10+ CreateCompatibleDCหากมีการสนับสนุนสำหรับการนี้พวกเขาถอยกลับไป

การเปลี่ยนไปใช้แอพลิเคชันเฉพาะคุณจะต้องเปลี่ยนบรรทัดแรกที่GetDC(game)ที่gameเป็นหมายเลขอ้างอิงของหน้าต่างเกมและจากนั้นตั้งค่าที่เหมาะสมheightและwidthของหน้าต่างเกมมากเกินไป

เมื่อคุณมีพิกเซลใน hDest / hbDesktop คุณยังคงต้องบันทึกลงในไฟล์ แต่ถ้าคุณทำการจับภาพหน้าจอฉันคิดว่าคุณต้องการบัฟเฟอร์จำนวนหนึ่งในหน่วยความจำและบันทึกลงในไฟล์วิดีโอ ในหน่วยย่อยดังนั้นฉันจะไม่ชี้ไปที่รหัสสำหรับการบันทึกอิมเมจคงที่ลงดิสก์


3
msdn.microsoft.com/en-us/library/dd183370%28VS.85%29.aspx ข้อความที่ตัดตอนมา: หากรูปแบบสีของบริบทอุปกรณ์ต้นทางและปลายทางไม่ตรงกันฟังก์ชัน BitBlt จะแปลงรูปแบบสีของแหล่งที่มาให้ตรงกับปลายทาง รูปแบบ.

2
หลักฐานบางอย่างที่ยืนยันว่าน่าจะดี คุณเคยเผยแพร่การเปรียบเทียบประสิทธิภาพที่ไหนสักแห่งหรือเห็นสิ่งที่ถูกต้องหรือไม่?

2
ลองสร้างโปรไฟล์ อย่างที่ฉันพูดในโพสต์นั้นฉันอ้างอิงคนที่มีประสบการณ์กับ GDI หากหน่วยความจำรั่วและคุณรู้วิธีแก้ไขให้แก้ไขโพสต์เพื่อกำจัดการรั่วไหล

1
ฉันได้ 5 fps ด้วยวิธีนี้ นี่เป็นแล็ปท็อปที่มีกราฟิกในตัวดังนั้นฉันคาดว่าจะดีขึ้นจากเดสก์ท็อปที่มีการ์ดกราฟิกจริง แต่ก็ยังช้ามาก
Timmmm

1
@Timmmm ฉันได้เพิ่มการอ้างอิงถึงวิธีที่ OBS ใช้สิ่งนี้ หวังว่ามันจะเร่งความเร็วให้คุณซักหน่อย

19

ฉันเขียนซอฟต์แวร์จับภาพวิดีโอคล้ายกับ FRAPS สำหรับแอปพลิเคชัน DirectX มีซอร์สโค้ดและบทความของฉันอธิบายเทคนิคทั่วไป ดูที่http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/

เคารพในคำถามของคุณที่เกี่ยวข้องกับประสิทธิภาพ

  • DirectX ควรเร็วกว่า GDI ยกเว้นเมื่อคุณอ่านจาก frontbuffer ซึ่งช้ามาก วิธีการของฉันคล้ายกับ FRAPS (อ่านจาก backbuffer) ฉันดักจับชุดของเมธอดจากอินเตอร์เฟส Direct3D

  • สำหรับการบันทึกวิดีโอแบบเรียลไทม์ (ที่มีผลกระทบต่อแอปพลิเคชั่นน้อยที่สุด) จำเป็นต้องมี codec ที่รวดเร็ว FRAPS ใช้ตัวแปลงสัญญาณวิดีโอแบบไม่สูญเสียของตัวเอง Lagarith และ HUFFYUV เป็นตัวแปลงสัญญาณวิดีโอแบบ lossless ทั่วไปที่ออกแบบมาสำหรับการใช้งานแบบเรียลไทม์ คุณควรดูพวกเขาหากคุณต้องการส่งออกไฟล์วิดีโอ

  • อีกวิธีหนึ่งในการบันทึก screencasts ก็คือการเขียน Mirror Driver ตามวิกิพีเดีย: เมื่อการทำมิเรอร์วิดีโอเปิดใช้งานทุกครั้งที่ระบบดึงอุปกรณ์วิดีโอหลักที่ตำแหน่งภายในพื้นที่มิเรอร์จะมีการดำเนินการคัดลอกการดำเนินการบนอุปกรณ์วิดีโอแบบเรียลไทม์ ดูคนขับกระจกที่ MSDN: http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315(v=vs.85).aspx


16

ฉันใช้ d3d9 เพื่อรับ backbuffer และบันทึกลงในไฟล์ png โดยใช้ไลบรารี d3dx:

    IDirect3DSurface9 * พื้นผิว;

    // GetBackBuffer
    idirect3ddevice9-> GetBackBuffer (0, 0, D3DBACKBUFFER_TYPE_MONO และพื้นผิว);

    // บันทึกพื้นผิว
    D3DXSaveSurfaceToFileA ("filename.png", D3DXIFF_PNG, พื้นผิว, NULL, NULL);

    SAFE_RELEASE (พื้นผิว);

ในการทำเช่นนี้คุณควรสร้าง swapbuffer ของคุณด้วย

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.

(ดังนั้นคุณรับประกันได้ว่า backbuffer จะไม่ยุ่งเหยิงก่อนที่คุณจะจับภาพหน้าจอ


ทำให้รู้สึกขอบคุณ คุณรู้หรือไม่ว่าสิ่งนี้แตกต่างกันและGetRenderTargetคืออะไร?
someguy

นั่นเพิ่งได้รับเป้าหมายการเรนเดอร์ปัจจุบัน (อาจเป็นพื้นผิวหน้าจอแบบอื่นถ้ามีคนเรนเดอร์พื้นผิวในขณะที่คุณโทร)
bobobobo

13

ในความประทับใจของฉันแนวทางของ GDI และวิธี DX นั้นแตกต่างกันในลักษณะของมัน การทาสีโดยใช้ GDI ใช้วิธีการล้างวิธีการล้างวิธีวาดกรอบแล้วล้างมันและวาดอีกเฟรมในบัฟเฟอร์เดียวกันซึ่งจะส่งผลในการริบหรี่ในเกมต้องอัตราเฟรมสูง

  1. ทำไม DX ถึงเร็วกว่า ใน DX (หรือโลกกราฟิก) วิธีการที่เป็นผู้ใหญ่มากกว่าที่เรียกว่าการเรนเดอร์บัฟเฟอร์แบบคู่จะถูกนำไปใช้โดยที่มีบัฟเฟอร์สองตัวอยู่เมื่อนำเสนอบัฟเฟอร์ด้านหน้ากับฮาร์ดแวร์คุณสามารถแสดงผลไปยังบัฟเฟอร์อื่นได้เช่นกัน การเรนเดอร์เสร็จแล้วระบบจะสลับไปยังบัฟเฟอร์อื่น (ล็อคไว้เพื่อนำเสนอฮาร์ดแวร์และปล่อยบัฟเฟอร์ก่อนหน้า) ด้วยวิธีนี้การปรับปรุงประสิทธิภาพการเรนเดอร์จะลดลงอย่างมาก
  2. ทำไมการเร่งความเร็วฮาร์ดแวร์ถึงเร็วขึ้น? แม้ว่าจะมีการเรนเดอร์บัฟเฟอร์สองเท่า แต่ FPS ก็ถูกปรับปรุงให้ดีขึ้น แต่เวลาในการเรนเดอร์ยังมี จำกัด ฮาร์ดแวร์กราฟิกที่ทันสมัยมักจะเกี่ยวข้องกับการปรับแต่งมากมายระหว่างการเรนเดอร์โดยทั่วไปเช่นการลบรอยหยักซึ่งเป็นการคำนวณที่เข้มข้นมากหากคุณไม่ต้องการกราฟิกคุณภาพสูงแน่นอนว่าคุณสามารถปิดการใช้งานตัวเลือกนี้ได้ และสิ่งนี้จะช่วยคุณประหยัดเวลา

ฉันคิดว่าสิ่งที่คุณต้องการจริงๆคือระบบเล่นซ้ำซึ่งฉันเห็นด้วยอย่างยิ่งกับสิ่งที่ผู้คนพูดถึง


1
ดูการอภิปรายว่าทำไมระบบการเล่นซ้ำจึงไม่สามารถทำได้ โปรแกรม screencasting ไม่ได้สำหรับเกมเฉพาะใด ๆ
someguy

10

ฉันเขียนคลาสที่ใช้วิธีการ GDI สำหรับการจับภาพหน้าจอ ฉันก็ต้องการความเร็วเพิ่มเช่นกันหลังจากค้นพบวิธี DirectX (ผ่าน GetFrontBuffer) ฉันลองแล้วคาดว่ามันจะเร็วขึ้น

ฉันผิดหวังที่พบว่า GDI ทำงานได้เร็วขึ้นประมาณ 2.5 เท่า หลังจากการทดลอง 100 ครั้งที่จับการแสดงผลจอแสดงผลคู่ของฉันการใช้งาน GDI เฉลี่ย 0.65 วินาทีต่อการจับภาพหน้าจอในขณะที่วิธี DirectX เฉลี่ย 1.72 วินาที ดังนั้น GDI จึงเร็วกว่า GetFrontBuffer แน่นอนตามการทดสอบของฉัน

ฉันไม่สามารถรับรหัสของ Brandrew เพื่อทดสอบ DirectX ผ่าน GetRenderTargetData การคัดลอกหน้าจอออกมาเป็นสีดำอย่างหมดจด อย่างไรก็ตามมันสามารถคัดลอกหน้าจอว่างเปล่าที่เร็วสุด ๆ ! ฉันจะทำการแก้ไขต่อไปและหวังว่าจะได้รับเวอร์ชันที่ใช้งานได้เพื่อดูผลลัพธ์ที่แท้จริงจากมัน


ขอขอบคุณสำหรับข้อมูล. ฉันยังไม่ได้ทดสอบโค้ดของ Brandrew แต่ฉันรู้ว่าGetRenderTargetDataวิธีการใช้งานได้ผล บางทีฉันอาจจะเขียนคำตอบของตัวเองเมื่อสมัครเสร็จ หรือคุณสามารถอัปเดตของคุณเมื่อคุณได้รับทุกอย่างที่ทำงาน
someguy

0.65 ต่อ Screencapture! การใช้งาน GDI ที่ดี (การดูแลรักษาอุปกรณ์ ฯลฯ ) ควรทำ 30fps ในปี 1920x1200 ได้อย่างง่ายดายบนคอมพิวเตอร์ที่ทันสมัย
Christopher Oezbek

ฉันคิดว่าคุณภาพของภาพที่แสดงโดย GDI นั้นแย่กว่า DX
zinking เมื่อ

ฉันได้ทำการทดสอบนี้ใน C # ด้วย SlimDX และน่าประหลาดใจที่พบผลลัพธ์เดียวกัน บางทีนี่อาจจะเกี่ยวกับข้อเท็จจริงที่ว่าเมื่อใช้ SlimDX เราต้องสร้างสตรีมใหม่และบิตแมปใหม่สำหรับการอัพเดททุกเฟรมแทนที่จะสร้างเพียงครั้งเดียวย้อนกลับและเขียนทับตำแหน่งเดิมต่อไป
Cesar

เพียงชี้แจง: ที่จริงแล้ว "ผลลัพธ์เดียวกัน" นั้นหมายถึง GDI ที่เร็วขึ้น - ตามที่ @Christopher กล่าวถึง 30fps + นั้นทำได้ดีมากและยังคงมี CPU สำรองเหลือเฟือ
Cesar

8

บางสิ่งที่ฉันสามารถรวบรวมได้: เห็นได้ชัดว่าการใช้ "ไดรเวอร์กระจก" นั้นรวดเร็วแม้ว่าฉันจะไม่ทราบถึง OSS

ทำไม RDP จึงเร็วกว่าซอฟต์แวร์ควบคุมระยะไกลอื่น ๆ ?

เห็นได้ชัดว่าการใช้ความเชื่อใจของ StretchRect นั้นเร็วกว่า BitBlt

http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-5193

และสิ่งที่คุณกล่าวถึง (การเชื่อมต่อเข้ากับ D3D dll) อาจเป็นวิธีเดียวสำหรับแอปพลิเคชัน D3D แต่จะไม่ทำงานกับการจับภาพเดสก์ท็อป Windows XP ดังนั้นตอนนี้ฉันแค่หวังว่ามันจะมีความเร็วที่เทียบเท่าสำหรับ Windows desktop ทั่วไป ... ทุกคน?

(ฉันคิดว่าด้วย aero คุณอาจใช้ hooks เหมือน fraps ได้ แต่ผู้ใช้ XP คงจะไม่โชคดี)

เห็นได้ชัดว่าเปลี่ยนความลึกบิตของหน้าจอและ / หรือปิดใช้งานฮาร์ดแวร์เร่งความเร็ว อาจช่วย (และ / หรือปิดการใช้งาน aero)

https://github.com/rdp/screen-capture-recorder-programรวมถึงยูทิลิตี้การจับภาพด้วย BitBlt ที่รวดเร็วพอสมควรและตัววัดประสิทธิภาพซึ่งเป็นส่วนหนึ่งของการติดตั้งซึ่งช่วยให้คุณสามารถวัดความเร็ว BitBlt ได้

VirtualDub ยังมีโมดูลจับภาพหน้าจอ "opengl" ที่กล่าวว่าเร็วและทำสิ่งต่าง ๆ เช่นการตรวจจับการเปลี่ยนแปลงhttp://www.virtualdub.org/blog/pivot/entry.php?id=290


ฉันสงสัยว่ามันจะเร็วกว่าหากคุณใช้ "screen-capture-recorder" หรือใช้ BitBlt ด้วยตัวเอง? มีการเพิ่มประสิทธิภาพบางอย่างในโครงการของคุณ?
blez

หากคุณใช้ Bitblt ด้วยตัวคุณเองคุณอาจหลีกเลี่ยงการเพิ่ม memcpy (โดยทั่วไป memcpy ไม่ใช่คอขวดที่ใหญ่ที่สุดถึงแม้ว่ามันจะเพิ่มเวลา - ฉันแค่พูดถึงมันที่นี่เพื่อใช้เป็นเกณฑ์มาตรฐาน แต่ถ้าคุณต้องการอะไรหรือสั่งโดยตรง )
rogerdpack

8

สำหรับ C ++ คุณสามารถใช้: http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html
สิ่งนี้อาจไม่สามารถใช้ได้กับแอปพลิเคชัน 3D / แอปวิดีโอทุกประเภท จากนั้นเชื่อมโยงนี้อาจจะมีประโยชน์มากขึ้นตามที่อธิบายถึง 3 วิธีที่แตกต่างกันที่คุณสามารถใช้

คำตอบเก่า (C #):
คุณสามารถใช้System.Drawing.Graphics.Copyแต่มันไม่เร็วมาก

โครงการตัวอย่างที่ฉันเขียนทำสิ่งนี้: http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/

ฉันวางแผนที่จะอัปเดตตัวอย่างนี้โดยใช้วิธีที่เร็วกว่าเช่น Direct3D: http://spazzarama.com/2009/02/07/screencapture-with-direct3d/

และนี่คือลิงค์สำหรับการจับภาพวิดีโอ: วิธีจับภาพหน้าจอเป็นวิดีโอโดยใช้ C # .Net


2
อาฉันลืมที่จะพูดถึงว่าฉันกำลังเขียนโปรแกรมใน C (อาจเป็น C ++) และฉันไม่ได้วางแผนที่จะใช้. NET ขออภัยชะมัด: /
someguy

ฉันได้รับทราบ BitBlt (GDI) แล้ว แม้ว่าฉันจะดูใน Direct3D ขอบคุณ!
someguy

ฉันกำลังดูข้อมูลนี้เมื่อไม่กี่สัปดาห์ก่อน แต่ยังไม่ได้นำไปใช้เลย Direct3D เป็นวิธี !! เร็วกว่าวิธี C # builtin ซึ่งใช้ GDI +
Tedd Hansen

ฉันได้อัปเดตโพสต์ดั้งเดิมของฉันแล้ว ตามลิงค์ DirectX ช้าเมื่อต้องโทร GetFrontBufferData () นี่คือสิ่งที่ต้องพิจารณาเมื่อบันทึกวิดีโอเกมหรือไม่ คุณช่วยอธิบายบริบทนี้ให้ฉันได้ไหม
someguy

1
GDI ช้าดังนั้นจึงไม่เหมาะกับโดเมนปัญหา DirectX หรือ OpenGL จะเป็นคำแนะนำที่สมเหตุสมผลเท่านั้น

6

ด้วย Windows 8 Microsoft ได้เปิดตัว Windows Desktop Duplication API นั่นเป็นวิธีที่แนะนำอย่างเป็นทางการในการทำมัน คุณสมบัติที่ดีอย่างหนึ่งที่มีไว้สำหรับการสแกนหน้าจอคือการตรวจจับการเคลื่อนไหวของหน้าต่างดังนั้นคุณสามารถส่งบล็อกเดลตาเมื่อหน้าต่างเคลื่อนที่ไปรอบ ๆ แทนที่จะเป็นพิกเซลดิบ นอกจากนี้มันยังบอกคุณว่าสี่เหลี่ยมอันใดที่เปลี่ยนไปจากเฟรมหนึ่งเป็นเฟรมถัดไป

โค้ดตัวอย่างของ Microsoft นั้นค่อนข้างซับซ้อน แต่ API นั้นใช้งานง่ายและใช้งานง่าย ฉันได้รวบรวมตัวอย่างโครงการที่ง่ายกว่าตัวอย่างทางการ:

https://github.com/bmharper/WindowsDesktopDuplicationSample

เอกสาร: https://docs.microsoft.com/en-gb/windows/desktop/direct3ddxgi/desktop-dup-api

รหัสตัวอย่างอย่างเป็นทางการของ Microsoft: https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a


โปรเจ็กต์ Github ทำงานได้อย่างสมบูรณ์บน Windows 10 ทดสอบกับ web video และ Resident Evil 7 สิ่งที่ดีที่สุดคือโหลดซีพียูที่มีอัตราการอัปเกรดกราฟิก
jw_

ทันทีที่คุณเปิดตัว GPU-Z โปรแกรมนี้จะหยุดการจับหน้าจอ
Marino Šimić

5

คุณสามารถลองโครงการโอเพ่นซอร์ส c ++ WinRobot @gitเครื่องมือจับภาพหน้าจอที่ทรงพลัง

CComPtr<IWinRobotService> pService;
hr = pService.CoCreateInstance(__uuidof(ServiceHost) );

//get active console session
CComPtr<IUnknown> pUnk;
hr = pService->GetActiveConsoleSession(&pUnk);
CComQIPtr<IWinRobotSession> pSession = pUnk;

// capture screen
pUnk = 0;
hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk);

// get screen image data(with file mapping)
CComQIPtr<IScreenBufferStream> pBuffer = pUnk;

การสนับสนุน:

  • หน้าต่าง UAC
  • Winlogon
  • DirectShowOverlay

1
นั่นคือ ... ตะขอระดับต่ำมากมาย ความเร็วในการจับภาพนั้นน่าทึ่งถ้าคุณมีสิทธิ์ของผู้ดูแลระบบ
toster-cx

ฉันพบว่า winrobot มีประสิทธิภาพและราบรื่นมาก
ashishgupta_mca

ฉันศึกษารหัส WinRobot และไม่เห็นการจับภาพหน้าจอ wrt ที่ก้าวล้ำ: มันใช้ CreateCompatibleDC..BitBlt เดียวกัน นอกจากจะมีเวทย์มนตร์บ้างไหมเมื่อดำเนินการในบริบทบริการ
shekh

@shekh: การศึกษาของคุณตื้นเกินไป รหัสใช้ IDirectDrawSurface7-> BltFast () เพื่อคัดลอกหน้าจอจากพื้นผิวการวาดหน้าจอไปยังพื้นผิวการคัดลอก DD จากนั้นจะใช้การแม็ปไฟล์เพื่อคัดลอกรูปภาพ มันค่อนข้างซับซ้อนเนื่องจากรหัสกำลังทำงานอยู่ในบริการที่คุณไม่สามารถเข้าถึงเดสก์ท็อปได้อย่างง่ายดาย
Elmue

2

ฉันเองทำกับ directx และคิดว่ามันเร็วเท่าที่คุณต้องการให้เป็น ฉันไม่มีตัวอย่างโค้ดอย่างรวดเร็ว แต่ฉันพบสิ่งนี้ซึ่งน่าจะมีประโยชน์ รุ่น directx11 ไม่ควรแตกต่างกันมาก directx9 อาจจะมากกว่านี้เล็กน้อย แต่นั่นเป็นวิธีที่จะไป


2

ฉันรู้ว่าคำแนะนำต่อไปนี้ไม่ตอบคำถามของคุณ แต่วิธีที่ง่ายที่สุดที่ฉันพบในการจับภาพมุมมอง DirectX ที่เปลี่ยนแปลงอย่างรวดเร็วคือการเสียบกล้องวิดีโอเข้ากับพอร์ต S-video ของการ์ดวิดีโอและบันทึกภาพเป็น ภาพยนตร์. จากนั้นถ่ายโอนวิดีโอจากกล้องกลับไปยังไฟล์ MPG, WMV, AVI ฯลฯ บนคอมพิวเตอร์


2

บันทึกหน้าจอที่สามารถทำได้ในC #ใช้VLC API ฉันได้ทำโปรแกรมตัวอย่างเพื่อสาธิตสิ่งนี้ มันใช้LibVLCSharpและVideoLAN.LibVLC.Windowsห้องสมุด คุณสามารถใช้คุณสมบัติอื่น ๆ อีกมากมายที่เกี่ยวข้องกับการแสดงผลวิดีโอโดยใช้ API ข้ามแพลตฟอร์มนี้

สำหรับเอกสาร API โปรดดู: LibVLCSharp API Github

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;

namespace ScreenRecorderNetApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Core.Initialize();

            using (var libVlc = new LibVLC())
            using (var mediaPlayer = new MediaPlayer(libVlc))
            {
                var media = new Media(libVlc, "screen://", FromType.FromLocation);
                media.AddOption(":screen-fps=24");
                media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
                media.AddOption(":sout-keep");

                mediaPlayer.Play(media);
                Thread.Sleep(10*1000);
                mediaPlayer.Stop();
            }
        }
    }
}

1
ผู้ที่อาจกังวล: โปรดทราบว่า libvlc นั้นเปิดตัวภายใต้ GPL หากคุณไม่ต้องการปล่อยรหัสของคุณภายใต้ GPL อย่าใช้ libvlc
Sebastian Cabot

นั่นไม่ถูกต้องเลยทีเดียว LibVLC เป็น LGPL - ซึ่งได้รับการขึ้นทะเบียนใหม่ในปี 2554 แอปพลิเคชัน VLC นั้นยังคงเป็น GPL: videolan.org/press/lgpl-libvlc.html videolan.org
Andrew

0

เดสก์ท็อป DXGI

โครงการที่จับภาพเดสก์ท็อปด้วยการทำสำเนา DXGI บันทึกภาพที่จับไปยังไฟล์ในรูปแบบภาพต่างๆ (* .bmp; * .jpg; * .tif)

ตัวอย่างนี้เขียนใน C ++ คุณต้องมีประสบการณ์กับ DirectX (D3D11, D2D1) ด้วย

แอปพลิเคชันสามารถทำอะไรได้บ้าง

  • หากคุณมีจอภาพเดสก์ทอปมากกว่าหนึ่งจอคุณสามารถเลือกได้
  • ปรับขนาดภาพเดสก์ทอปที่ถ่าย
  • เลือกโหมดการปรับสเกลที่แตกต่างกัน
  • คุณสามารถแสดงหรือซ่อนไอคอนเมาส์ในภาพออก
  • คุณสามารถหมุนภาพสำหรับภาพที่ส่งออกหรือปล่อยให้มันเป็นค่าเริ่มต้น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.