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

VC++ 從 AVI 視頻擷取幀畫面:AVIFile API 實戰與 BMP 儲存全流程

     編輯:VC 教學

前言

需求:從 AVI 視頻任意幀擷取畫面存成 BMP。DirectShow 雖強大但過重,**AVIFile API (vfw.h)** 輕量、原生、無需額外運行時,是 VC++ 擷取幀的首選。本文封裝 `CAvi` 類,實現開啟、定位、讀取、存檔全流程。

AVIFile API 核心流程

  1. AVIFileInit() 初始化程式庫
  2. AVIFileOpen() 取得 `PAVIFILE`
  3. AVIFileInfo() 取得寬高、幀數、壓縮格式
  4. AVIFileGetStream(..., streamtypeVIDEO, 0) 取得視頻流 `PAVISTREAM`
  5. AVIStreamRead() 讀取指定幀 → DIB 緩衝區
  6. 構造 BITMAPFILEHEADER + BITMAPINFOHEADER + 像素資料 → 寫入 `.bmp`
  7. AVIFileExit() 清理

CAvi 類別設計


class CAvi {
public:
    int cx, cy;                 // 影像寬高
    BYTE* pData;                // 幀像素緩衝區 (RGB24)
    BITMAPINFO* m_pBMI;         // BMP 資訊頭
    PAVISTREAM pavi;            // 視頻流介面
    PAVIFILE pfile;             // 檔案介面
    AVIFILEINFO* pfi;           // 檔案資訊

    CAvi() { AVIFileInit(); cx=cy=0; pData=NULL; m_pBMI=NULL; pavi=NULL; pfile=NULL; pfi=NULL; }
    ~CAvi() { Cleanup(); AVIFileExit(); }

    BOOL Create(LPCTSTR lpszPath);  // 開啟檔案、初始化所有成員
    BOOL ReadFrame(int nFrame);     // 讀取第 n 幀到 pData
    BOOL SaveBMP(LPCTSTR lpszBmp);  // 將 pData 存為 BMP
private:
    void Cleanup();
};

關鍵實作細節

1. Create:開啟並解析視頻資訊


BOOL CAvi::Create(LPCTSTR lpszPath) {
    HRESULT hr = AVIFileOpen(&pfile, lpszPath, OF_READ, NULL);
    if (FAILED(hr)) return FALSE;

    pfi = new AVIFILEINFO;
    hr = AVIFileInfo(pfile, pfi, sizeof(AVIFILEINFO));
    if (FAILED(hr)) return FALSE;

    cx = pfi->dwWidth; cy = pfi->dwHeight;

    hr = AVIFileGetStream(pfile, &pavi, streamtypeVIDEO, 0);
    if (FAILED(hr)) return FALSE;

    // 建立 BITMAPINFO (24-bit RGB, 自下而上)
    m_pBMI = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFOHEADER)];
    m_pBMI->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_pBMI->bmiHeader.biWidth = cx;
    m_pBMI->bmiHeader.biHeight = cy; // 正數 = 底部在前 (DIB)
    m_pBMI->bmiHeader.biPlanes = 1;
    m_pBMI->bmiHeader.biBitCount = 24;
    m_pBMI->bmiHeader.biCompression = BI_RGB;
    m_pBMI->bmiHeader.biSizeImage = cx * cy * 3;

    pData = new BYTE[cx * cy * 3];
    return TRUE;
}

2. ReadFrame:精準定位幀


BOOL CAvi::ReadFrame(int nFrame) {
    // AVIStreamRead: 從 nFrame 開始讀 1 幀
    LONG cbBuffer = cx * cy * 3;
    HRESULT hr = AVIStreamRead(pavi, nFrame, 1, pData, cbBuffer, NULL, NULL);
    return SUCCEEDED(hr);
}

3. SaveBMP:標準 BMP 檔案結構


BOOL CAvi::SaveBMP(LPCTSTR lpszBmp) {
    if (!pData || !m_pBMI) return FALSE;

    BITMAPFILEHEADER bfh = {0};
    bfh.bfType = 0x4D42; // 'BM'
    bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bfh.bfSize = bfh.bfOffBits + m_pBMI->bmiHeader.biSizeImage;

    CFile file(lpszBmp, CFile::modeCreate | CFile::modeWrite);
    file.Write(&bfh, sizeof(bfh));
    file.Write(&m_pBMI->bmiHeader, sizeof(BITMAPINFOHEADER));
    file.Write(pData, m_pBMI->bmiHeader.biSizeImage);
    return TRUE;
}

使用範例:擷取第 100 幀


CAvi avi;
if (avi.Create(_T("C:\\Video\\demo.avi"))) {
    if (avi.ReadFrame(100)) { // 0-based
        avi.SaveBMP(_T("C:\\Output\\frame_100.bmp"));
        AfxMessageBox(_T("擷取成功!"));
    } else {
        AfxMessageBox(_T("幀索引超出範圍或讀取失敗"));
    }
} else {
    AfxMessageBox(_T("無法開啟 AVI 檔案"));
}

進階議題與避坑

議題說明對策
壓縮格式不支援AVIStreamRead 回傳錯誤 (如 MJPEG、H.264 無解碼器)系統需安裝對應 Video for Windows (VFW) 解碼器;或改用 DirectShow/ICM
色序錯誤 (藍紅互換)AVI 多為 RGB24,BMP 也是 RGB,但部分編碼器輸出 BGR讀取後手動交換 R/B 通道,或用 `biCompression = BI_BITFIELDS` 指定遮罩
大檔案記憶體不足一次性分配全影像緩衝改用 `AVIStreamRead` 分塊讀取、或 `AVIStreamGetFrameOpen` 逐幀解碼
非標準幀率/可變幀率索引對應時間複雜用 `AVIStreamTimeToSample` / `AVIStreamSampleToTime` 換算
多音軌/字幕流AVIFileGetStream 需指定 streamtypeAUDIO/streamtypeTEXT同樣 API,僅 streamtype 不同

現代替代:Media Foundation / DirectShow / FFmpeg

  • Media Foundation:Win7+ 原生、硬體加速、支援 H.264/HEVC、非同步管線
  • DirectShow:經典濾鏡圖、`ISampleGrabber` 擷取幀、生態豐富但已棄用
  • FFmpeg (libavformat/libavcodec):跨平台、格式最全、C API、需自行編譯/部署授權

結語

`AVIFile API` 雖屬上世代技術,但在 **舊專案維護、輕量級工具、無網路環境部署** 仍具不可替代的優勢:零依賴、幾十行代碼搞定開啟→讀幀→存 BMP。封裝好 `CAvi` 類,下次需求來時,`avi.Create → avi.ReadFrame(n) → avi.SaveBMP` 三行解決,優雅高效。

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