Jak ve vlastním programu zachytávat video?

Dnes se již stává skoro běžným vybavením počítače nějaké zařízení na zachytávání videa. Ukažme si proto, jak lze vytvořit vlastní program pro zachytávání videosekvencí.
Dnes se již stává skoro běžným vybavením počítače nějaké zařízení na zachytávání videa. Může to být běžná webová kamera (např. WebCam od firmy Creative), speciální karta (např. AV-Master) nebo jako doplněk některých běžných VGA karet, které mají vstup právě pro zachytávání videa. Ukažme si proto, jak lze vytvořit vlastní program pro zachytávání videosekvencí.

Pro zachytávání videa ve Windows se používají funkce (již v tomto seriálu zmíněné) knihovny „Video for Windows“.

Protože tato problematika, pokud budeme chtít zajít trochu více do podrobností, je obsáhlejší než rozsah tohoto článku, rozdělíme si ji na více (2-3) pokračování. V tomto prvním článku bude naším cílem vytvořit nejjednodušší funkční aplikaci, která bude umět zachytávat video nebo jednotlivé snímky, z nichž vytvoří video sekvenci (AVI soubor). Nebudeme se zatím zabývat speciálním nastavením driveru a použijeme výchozí nastavení, resp. to nastavení, které si uživatel nastaví v systému, třeba v jiné aplikaci, která obvykle bývá nainstalována s dodaným hardwarem. Nebudeme ani provádět kontrolu, zda zařízení opravdu je správně nainstalováno. To vše si necháme na příště.

Vytvoříme si tedy aplikaci (nejjednodušeji založenou na MFC dialogu), do níž si přidáme třídu (generic class), která nám bude zapouzdřovat funkce zachytávání videa.

Prvním krokem je vytvořit okno zachytávání videa. Pro tento účel nemusíme registrovat třídu a používat „klasickou“ funkci CreateWindowEx, neboť v knihovně „Video for Windows“ je pro tento účel již připravená funkce capCreateCaptureWindow:

HWND VFWAPI capCreateCaptureWindow(
  LPCSTR lpszWindowName, // “jméno” oknba
  DWORD dwStyle,         // styl okna
  int x,                 // pozice okna (x-ová souřadnice)
  int y,                 // pozice okna (y-ová souřadnice)
  int nWidth,            // šířka okna
  int nHeight,           // výška okna
  HWND hWnd,             // rodičovské okno
  int nID                // identifikátor okna
);

Tato funkce nám vrátí handle okna, na které se budeme dále odkazovat.

Pokud máme takto úspěšně vytvořené okno, můžeme se „připojit“ k ovladači zachytávání videa pomocí makra capDriverConnect:

BOOL capDriverConnect(
  hwnd,  // handle okna
  iIndex // index driveru (počítáno od 0)
);

Index driveru určuje (pokud je nainstalováno víc ovladačů zachytávání videa), který ovladač se má použít. Hodnota 0 je výchozí ovladač.

Nyní si tedy vytvoříme členskou funkci naší třídy, která vytvoří okno a připojí driver. Jejím parametrem je rodičovské okno, v našem případě handle okna dialogu naší aplikace.

BOOL CRPJVideoCapture::Create(HWND hwndParent)
{
  if ( IsWindow(m_hWnd) )
    return FALSE;
  m_hWnd = capCreateCaptureWindow(
    (LPSTR)"RPJ Video Capture",
    WS_CHILD | WS_VISIBLE,
    0, 0, 320, 240,
    (HWND)hwndParent,
    (int)0);
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  if ( !capDriverConnect(m_hWnd, 0) ) // připojíme výchozí driver
    return FALSE;
  return TRUE;
}

Nyní bychom již mohli spustit zachytávání videa, ale bývá zvykem nejdříve spustit náhled (preview). K tomu slouží makro capPreview:

BOOL capPreview(
  HWND hwnd, 
  BOOL f        // TRUE spustí preview, FALSE jej zastaví
);

Ještě před tím však musíme (alespoň ve většině případů) nastavit frekvenci obrázků při náhledu. V opačném případě bychom viděli statický „snímek“ okamžiku připojení driveru. Tato frekvence se nastavuje v milisekundách, tj. jako doba trvání jednoho snímku. Standardní hodnota bývá 66 milisekund, odpovídající cca 15 snímkům za sekundu. K tomuto nastavení slouží makro capPreviewRate:

BOOL capPreviewRate(
  HWND hwnd,  // handle okna
  LONG wMS    // hodnota v milisekundách
);

Vytvořme si tedy členskou funkci, která spustí zobrazení náhledu:

BOOL CRPJVideoCapture::ShowPreview()
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  ShowWindow(m_hWnd, SW_SHOW);
  capPreviewRate(m_hWnd, 66);
  return capPreview(m_hWnd, TRUE);
}

a podobně funkci na jeho zastavení a skrytí:

BOOL CRPJVideoCapture::HidePreview()
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  if ( !capPreview(m_hWnd, FALSE) )
    return FALSE;
  ShowWindow(m_hWnd, SW_HIDE);
  return TRUE;
}

K tomu na vysvětlenou: pouhým zastavením náhledu funkcí capPreview s parametrem FALSE okno nezmizí, pouze zůstane zobrazen poslední snímek náhledu. Proto používáme funkci ShowWindow na skrytí a opětné zobrazení okna s náhledem. Můžeme si také vytvořit funkci, která pouze spustí nebo zastaví náhled, ale okno bude viditelné.

BOOL CRPJVideoCapture::Preview(BOOL Start)
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  capPreviewRate(m_hWnd, 66);
  return capPreview(m_hWnd, Start);
}

Ještě se zmíním o funkci, která povolí overlay při zobrazování videa. Je však třeba, aby tuto vlastnost podporoval hardware a příslušný ovladač. Pomocí makra capDriverGetCaps:

BOOL capDriverGetCaps(
  hwnd,   
  psCaps, 
  wSize   
);

kterým se budeme podrobněji zabývat příště, zjistíme schopnosti ovladače. V případě, že to ovladač podporuje, povolíme overlay funkcí capOverlay:

BOOL capOverlay(
  HWND  hwnd,  // handle okna
  BOOL  f    // povolit overlay
);

Naše členská funkce bude tedy vypadat třeba takto:

BOOL CRPJVideoCapture::EnableOverlay(BOOL Overlay)
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  if ( !Overlay )
  {
    return capOverlay(m_hWnd, FALSE);
  }
  else
  {
    CAPDRIVERCAPS CapDrvCaps;
    capDriverGetCaps(m_hWnd, &CapDrvCaps, sizeof (CAPDRIVERCAPS));
    if (!CapDrvCaps.fHasOverlay)
    {
      Error("Ovladač nepodporuje overlay!");
      return FALSE;
    }
    else
      return capOverlay(m_hWnd, TRUE);
  }
}

Nyní můžeme přistoupit k vlastnímu zachytávání videa do souboru. Jméno výsledného souboru je standardně nastaveno na „c:\capture.avi“. Zatím jej necháme beze změny, dalším nastavením se budeme zabývat v příštím článku. Ke spuštění zachytávání videa nám slouží makro capCaptureSequence:

BOOL capCaptureSequence(
  HWND  hwnd 
);

Tato funkce spustí zachytáváni videa do souboru „c:\capture.avi“ (pokud nenastavíme jiný soubor) a zachytávání pokračuje, dokud uživatel nestiskne klávesu <ESCAPE> nebo pokud zachytávání nezastavíme programově pomocí makra capCaptureStop:

BOOL capCaptureStop(
  HWND  hwnd  // handle okna
);

V naší třídě si tedy vytvoříme 2 členské funkce pro spuštění a zastavení zachytávání:

BOOL CRPJVideoCapture::StartCapture()
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  return capCaptureSequence(m_hWnd);
}

BOOL CRPJVideoCapture::StopCapture()
{
  if ( !IsWindow(m_hWnd) )
    return FALSE;
  return capCaptureStop(m_hWnd);
}

Nakonec si ještě ukážeme, že je možné do výstupního videosouboru (.avi) ukládat jednotlivé samostatně zachycené snímky. Nejdříve musíme otevřít tento výstup pomocí makra capCaptureSingleFrameOpen:

BOOL capCaptureSingleFrameOpen(
  WND hwnd
);

Nyní můžeme do tohoto výstupního souboru přidávat jednotlivé zachycené snímky pomocí makra capCaptureSingleFrame:

BOOL capCaptureSingleFrame
(
  HWND  hwnd  // handle okna
);

Zavření výstupního souboru pak provedeme funkcí capCaptureSingleFrameClose:

BOOL capCaptureSingleFrameClose
(
  HWND  hwnd 
);

Výsledkem je videosoubor tvořený sekvencí zachycených snímků.

Na závěr ještě pár upřesňujících poznámek: Když se podíváte do dokumentace (nejlépe MSDN), zjistíte, že většina zde uvedených maker, jako třeba capCapturePreview, nemá u parametru(-ů) uveden typ. Ve skutečnosti tato makra realizují svoji funkci pomocí zaslání příslušné zprávy oknu. Například uvedené makro capCapturePreview posílá oknu zprávu WM_CAP_SET_PREVIEW

WM_CAP_SET_PREVIEW
wParam = (WPARAM) (BOOL) (f); // volba náhledu
lParam = 0L;

tudíž všechny parametry těchto maker jsou ve skutečnosti typu WPARAM nebo LPARAM (parametry zprávy Windows), což jsou ve Win32 API 32bitové hodnoty. A parametr hwnd u těchto maker je samozřejmě typu HWND, což je první parametr funkce SendMessage, jejíž syntaxe je tato:

LRESULT SendMessage(
  HWND hWnd,      // handle cílového okna
  UINT Msg,        // identifikátor zprávy
  WPARAM wParam,  // 1. parametr zprávy
  LPARAM lParam    // 2. parametr zprávy
);

Ukázku ke dnešnímu článku si můžete stáhnout zde: video_capture.zip (34 kB)

Diskuze (2) Další článek: Aktualizace virové databáze od Symantecu podražila

Témata článku: , , , , , , , , , , , , , , , , , , ,