首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

使用 DirectShow 擷取影片畫面

     編輯:關於VC++

許多影片播放或編輯軟體都提供擷取畫面功能,讓影片工作者能輕鬆將電影特定時刻的畫面擷取並儲存為圖片檔案。那麼,我們該如何自行編程實現這項功能?若您熟悉 MPEG 或 AVI 等常見影片格式,可直接操作影片檔案;若不熟悉這些格式,想用更簡單方法擷取畫面,Microsoft DirectShow 將帶給您極大驚喜。

DirectShow 簡介與開發環境設定

DirectShow 屬於 DirectX 家族(還包含 Direct3D、DirectInput、DirectDraw、DirectSound 等元件)。使用 DirectShow 開發擷取畫面程式前,必須安裝 DirectX SDK(可從 Microsoft 網站下載,最新版本為 9.0b)。此外,DirectX SDK 以 COM 方式發布,開發人員還需了解 COM 基本原理。若未使用過 COM,建議先閱讀相關入門教學。

VC 6.0 環境設定

安裝完 DirectX 9.0b SDK 後,通常會自動設定好 VC 6.0。若編譯出錯,請檢查 VC 是否包含 DirectX SDK 的標頭檔與程式庫檔:選擇「Tools > Options...」,在彈出的 Options 對話方塊中選擇 Directories 索引標籤,查看 Include files 和 Library files 中是否包含 DirectX SDK 的 Include 路徑與 Lib 路徑,若無則自行加入。

主要實作步驟

在實現擷取影片畫面過程中,DirectShow 的 IMediaDet 介面將是主角,此介面包含方法可從媒體來源檔案提取重要資訊,如媒體類型、畫面速率甚至視訊串流的單一畫面。

必要標頭檔與程式庫

  • 標頭檔:dshow.h、qedit.h
  • 程式庫:strmiids.lib
  • 使用 CComPtr 模板宣告介面實例,還需包含 atlbase.h 標頭檔

逐步實作

  1. 建立對話方塊型應用程式,加入兩個編輯控制項與三個按鈕控制項
  2. 為對話方塊類別加入 HRESULT 型別成員函式 GrabFrameFromMovie,實現擷取畫面功能。函式內建立 IMediaDet 介面實例,呼叫 CoCreateInstance 並傳入 CLSID_MediaDet 類別識別碼
  3. 呼叫 IMediaDet::put_Filename 指定媒體檔案,參數類型為 BSTR
  4. 呼叫 IMediaDet::get_OutputStreams 取得影片輸出串流數目,僅關心視訊串流與音訊串流,自動忽略其他串流
  5. 呼叫 IMediaDet::put_CurrentStream 指定操作串流,因目的是將畫面儲存為圖片,需定位視訊串流
  6. 呼叫 IMediaDet::get_StreamMediaType 取得 VIDEOINFOHEADER 結構,包含描述視訊影像對應點陣圖尺寸、顏色等資訊的 BITMAPINFOHEADER
  7. 呼叫 IMediaDet::WriteBitmapBits 將畫面儲存為點陣圖,第一個參數傳遞時間即可指定擷取哪一畫面(傳遞 0 表示擷取第一畫面)

完整程式碼範例

HRESULT CFrameGrabberDlg::GrabFrameFromMovie()
{
    HRESULT hr;
    // 定義 IMediaDet 介面實例
    CComPtr<IMediaDet> pDet;
    hr = CoCreateInstance(CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER,
                          IID_IMediaDet, (void**)&pDet);
    if (FAILED(hr)) return hr;

    // 將影片檔名轉換為 BSTR 型別
    CComBSTR openBSTR(m_editOpenDir);
    // 設定 IMediaDet 介面的檔案關聯
    hr = pDet->put_Filename(openBSTR);
    if (FAILED(hr)) return hr;

    // 從影片檢索視訊串流與音訊串流
    long lStreams;
    hr = pDet->get_OutputStreams(&lStreams);
    if (FAILED(hr)) return hr;

    // 取出影片的視訊串流
    bool bFound = false;
    for (int i = 0; i < lStreams; i++)
    {
        GUID major_type;
        hr = pDet->put_CurrentStream(i);
        if (SUCCEEDED(hr))
            hr = pDet->get_StreamType(&major_type);
        if (FAILED(hr)) break;
        if (major_type == MEDIATYPE_Video)
        {
            bFound = true;
            break;
        }
    }
    if (!bFound) return VFW_E_INVALIDMEDIATYPE;

    long width = 0, height = 0; // 儲存點陣圖寬高(像素)
    AM_MEDIA_TYPE mt;
    hr = pDet->get_StreamMediaType(&mt);
    if (SUCCEEDED(hr))
    {
        if ((mt.formattype == FORMAT_VideoInfo) &&
            (mt.cbFormat >= sizeof(VIDEOINFOHEADER)))
        {
            // 取得 VIDEOINFOHEADER 結構指標
            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
            width = pVih->bmiHeader.biWidth;
            height = pVih->bmiHeader.biHeight;
        }
        else
            hr = VFW_E_INVALIDMEDIATYPE;
        MyFreeMediaType(mt); // 釋放 AM_MEDIA_TYPE 結構
    }
    if (FAILED(hr)) return hr;

    CComBSTR saveBSTR(m_editSaveDir);
    // 將第一畫面儲存為指定路徑的點陣圖檔案
    hr = pDet->WriteBitmapBits(0, width, height, saveBSTR);
    if (FAILED(hr)) return hr;
    return S_OK;
}

程式執行與測試

程式執行後,選擇一個影片,指定儲存路徑,點擊「擷取」按鈕,即可將影片第一畫面儲存至指定路徑。也可修改 WriteBitmapBits 方法第一個參數來擷取指定畫面。原始碼 DEBUG 資料夾包含測試影片。該程式在 Windows XP、Visual C++ 6 和 DirectX 9.0b 環境下編譯執行通過。

下載本文範例原始碼:https://www.vckbase.com/code/downcode.asp?id=2221

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved